Monday, January 16, 2012

Reply to Tom on AMD

Tom Dale had a good post about why he dislikes AMD. Please go check it out. We talked about it a bit while he was writing it. It is great he took the time to write it out. We need more of that. We are all trying to find the best path, and it is great we are talking this out.

While I appreciate the critique, what is missing is a solid alternative to AMD. In particular, "use build tools to wrap up CommonJS modules" is not a generic solution. It does not allow for generic dynamic loading of resources, particularly from CDNs. Dynamic loading is needed for big sites that still do builds but want to stage loading of the site as the user uses it.

On his concern about too much ceremony -- this is the minimum amount of ceremony:

define(function(require) {
    //Module code in here

By doing that, it is easier for other people to consume your module since module authors do not have to provide "built" versions of their module to use on CDNs. And if they did have to do builds, what format would they publish it in? It would need a function wrapper. Hey, AMD has that!

That simple function wrapper allows moving away from globals, although you still can use globals if you want. It allows for dynamic loading. Recall I'm talking about built layers for staged loading/performance, not "load each module with a separate HTTP request". It allows very simple module sharing now. It allows easier sharing of built files too, since AMD specifies the built file format.

With AMD, you can still use build tools if that is how you like to roll in dev. Go for it. Almond is a great AMD one for that purpose. Here is an example of using the RequireJS optimizer for runtime HTTP-served builds. And since there are multiple AMD implementations, you can choose how big of an AMD lib you want.

I'm sure Tom could adjust his 50 line loader to process the define calls, since his built code probably has function wrappers. He could even adapt his "hydrate from strings" loader to use the AMD syntax.

By using that function wrapper, others could consume his individual modules and use their own choice of AMD loader to deal with their particular loading needs.

On the "hydrate from strings" build approach: I would prefer using appcache and using built layers of AMD code instead. This gives much better overall mobile app performance since more than just JS can be cached. But that is more of a side point. Feel free to use the "hydrate from strings" approach, AMD could be used in that fashion too.

My summary: it sounds like he just wants to avoid a small function wrapping and a level of indent. And to be fair, many simple AMD introductions use the array of dependencies syntax, not the sugared form above, so I can appreciate the confusion.

But trying to avoid that function wrap+indent comes with some code sharing costs, and the alternative he mentions does not handle the breadth of JS module consumption.


Joss Crowcroft said...

Yeah, I completely disagreed with Tom's post but appreciated that it was well-written and well-intentioned. Every point in there is easily addressed with a little more research and planning...

"If you are making a small application, then fine, I agree you don’t need build tools. But you probably don’t need AMD or a script loader, either. "

This, along with the 'too many HTTP requests' argument, is a red herring..

As you pointed out (perhaps not strongly enough) - when you're developing your application, often in a local environment, making 50 HTTP requests doesn't really matter when coupled with the ease of development it gives you. Then it's a one-hit command to build the project into a single bootstrap file for production.

The "Too Much Ceremony" argument is flawed too, unless you really hate indenting code (as you mentioned). Ceremony isn't always a bad thing - jQuery plugins follow a predictable skeleton format, enabling people to more easily gain experience in writing and understanding them... ditto WordPress plugins, entire projects (e.g. H5BP) etc.

It's a tiny amount of ceremony with a distinctive, recognisable and understandable architecture.

(FWIW, I think listing dependencies as an array and including them as anonymous function arguments is the more messy and unmaintanable way to do things - I use 'require()' calls for modules instead)

Most of this is probably a case of tomato/tomahto - obviously people can and will use whatever practices they like, but it seems unfair to claim that 'AMD is not the answer' when, for many people, it's a very solid answer..

Great response anyhow!

geddesign said...

Nice response James. Mine's similar: