Developer Guided Code Splitting

Google Web Toolkit, which compiles Java to JavaScript for running code in the browser, now includes Code Splitting, for reducing application download time:

To split your code, simply insert calls to the method GWT.runAsync at the places where you want the program to be able to pause for downloading more code. These locations are called split points.

A call to GWT.runAsync is just like a call to register any other event handler. The only difference is that the event being handled is somewhat unusual. Instead of being a mouse-click event or key-press event, the event is that the necessary code has downloaded for execution to proceed.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

More doc, and a question

The above link is for the user manual. Some info on how it is implemented and why is here:

http://blog.lexspoon.org/search/label/code%20splitting

Question for the group: what other code-splitting or demand-loading techniques are being used *in practice* ?

Misguided understanding of code splitting and code on demand

To understand *in practice*, you must decouple the use of control partitioning techniques from the following notion, spread throughout Doloto and GWT-code-splitting's design docs or research proposals, i.e.:

The use of code-splitting separates the download impact over the entire user’s session
and thus is intended to reduce the number of download bottlenecks. An additional benefit
is that the user will download only the code they require for the features of the application
they use.

It is more likely that to reduce speed tracer sluggishness meter, or obtain a better YSlow! report, that you would want to anticipate what features the user will use, rather than wait for them to use it. Also, note, that the basic assumption is that this should be used for caching only.

I described on my blog back around May/June how/why to do this in .NET for Silverlight.

To be clear, I also prefer to think in terms of "control partitioning". If an AJAX application downloads a multi-step state machine, then it is not RESTful. Code splitting does not indicate any eye towards architecture considerations. But GWT does not seem to be geared toward allowing developers have a say in big choices like that...

Just 2 cents.

Prefetching

I agree that prefetching is important, Z-Bo, and GWT includes it. However, prefetching is an orthogonal issue. Any time you delay loading of resources, you'd like to prefetch them whenever there is idle bandwidth to do so.

My question about practicality was more about what competing approaches there are. Is Doloto, for example, used anywhere in production? The synchronous code downloads look problematic to me.

I still don't think your

I still don't think your question is clear, and feel you might be asking multiple questions.

I don't like the idea of referring to Doloto's code-on-demand strategy as synchronous. Doloto, after looking into it, appears to allow loading code transparently based upon what Doloto thinks is best for performance. James Hamilton has a nice blog post about Doloto for those interested in reading more about Doloto. James compares Doloto's loader architecture to repositioning error handling on a local machine so that frequently executed code hits the cache more often. However, this analogy is brittle, because a distributed network is not a local machine.

If you want a supporter that this behavior of Doloto's is wrong, then I am on your side.

However, I think there is something different I am arguing for, which you are not addressing. If you think Doloto is wrong, then you should also have a stance on my opinion as well, since it is different from what Doloto does. It is also different from how GWT models things.

I am arguing you should make loading asynchronous, but defined in terms of events. GWT.runAsync is not an event listener. I've read your blog posts were you note Bruce Johnson has decided its best to break from the Unix page loader model, but I think the solution doesn't reflect what I see as the lightest solution.

The programmer should think in terms of REST. That is to say that they should assume their application has multiple entry points. They should think about the roles their users may have, and what workflows those roles are exposed to. Thus, I think a better model would be to define separately from GWT some annotations that allow the developer to load based on events. For example, taken from my blog, if I add an item to my shopping cart, then the Checkout.dll assembly should be loaded. Most customers don't reach checkout, and they never take this step immediately. Even if they were to take this step immediately, they would be accustomed to the standard wait-time for any given entry point into the system!

As I said already, GWT.runAsync makes sense from the perspective of a programmatic abstraction, but it is not a declarative abstraction. Any declarative abstraction should be event-driven (or constraint-based). The Doloto model, where the declaration is simply "here is a web page, here is profiler.log, make me faster", is likely misleading.

Finally, pre-fetching can be done many ways. Event-driven prefetching is the solution that makes the most sense to me (

@LoadOn(eventName="ItemAddedToCart")

). The one that makes the second most sense to me is not represented by GWT.runAsync, either. Why? Because it encourages tightly coupling MyModule to logic about how to handle linker and loader-related resolution issues. I would rather encourage developers to think about things completely asynchronously. We could add further annotations to stress to the loader what to do in the event of network disruption. Right now, GWT.runAsync does not seem to provide any hooks into the GWT environment to let the developer know, hey, the network is down. The developer in turn must tell the environment, "Here is what we do when offline while the user is still shopping." Another advantage would be potentially improved support for delayed binding. It would also be trivial to incorporate rule-based code analysis tools with checks on GWT projects to ensure that every @LoadOn annotation specified must have a disruption handler. Additionally, with @LoadOn, Story-Of-Your-Compile (SOYC) could tell the developers exactly how large the payload of code-on-demand will be. Developers can then be empowered by their refactoring IDEs to plow code mountains around and terraform the design. GWT.runAsync seems instead to encourage developers to traverse into the depths of Callback Hell, and GWT mailing list member Ian Petersen has previously expressed reservation over this. I might be wrong, though. I don't use GWT professionally and would not consider myself a user. I do however know what research projects stuff like GWT grew out of in the 1990s. Fun fact: Guido van Rossum worked on one of these projects as a grad student, before becoming Benevolent Dictator for Life!

The last option I would prefer to see would be some programmatic way to wrap some dead code so that it is loaded later on. GWT.runAsync might work there. Doloto's model might also work. I could see anotating a package (or "assembly") with @LoadOptions(...) and having that annotation allow Doloto-like loading. I would probably worry about doing the Doloto approach last. [Edit: One subtelty of @LoadOptions(...) I overlooked mentioning was the potential for specifying how to do disruption tolerance, even if in a very suboptimal manner. Doloto does not appear to have any mechanisms for disruption tolerance handling. In my books, this makes Doloto completely worthless and a result of complete misunderstanding of the Web and the Internet. Most people just misunderstand the Web, i.e., GWT being inherently RPC-based and seemingly having poor modeling for disruption tolerance and fault tolerance. As I've said before elsewhere on the Internets, a GUI application is effectively a dynamically distributed, dynamically federated system. Such higher-level problem solving is, or, should be, the raison d'être of building a source-to-source compiler like GWT. GWT was originally created to solve The Over-Constrained Implementation Problem; the architects should now focus on helping with The Networked Computing Problem.]

But GWT does not seem to be

[Oops: intended in reply to Z-Bo.]

But GWT does not seem to be geared toward allowing developers have a say in big choices like that...

Could you elaborate? I don't see how GWT restricts developers in "big choices", compared to directly using JavaScript.

Granted, GWT provides somewhat "un-webby" default solutions, like RPC based on serialized Java objects, but these can always be replaced with "web-native" solutions.

If an AJAX application downloads a multi-step state machine, then it is not RESTful.

I'll have to think about this some more, but I don't see a priori which of the REST constraints is violated by code splitting.

It's possible GWT's runAsync

It's possible GWT's runAsync feature has no limitations, but it seems very imperative and I don't quite understand why you would implement it that way. I could see it as perhaps a programmatic interface, but a declarative abstraction just makes much more sense.

What do you really need to know? You need to partition your application according to events that will be raised. In an event-driven system, you don't know when those events will be raised. However, you might be able to define a positive correlation between two events, such that if one is raised, another will most likely be raised.

The first step of code splitting is minimizing the initial download size of the hypermedia. GWT does this well by basically re-using its liveness analysis.

The second half of the program, which it seems to me Lex wants to know how to design, is: How should users specify loading the program? Otherwise, I don't get the point of his question, unless it is to build a tutorial for the GWT website... and define use cases/user stories.

For a user story, my blog already has a simple shopping cart metaphor that is pretty standard (I saw it at least four times prior in my career when other developers were discussing control partitioning, so I just re-used the example).

For how to do it, it should be event-driven. It might also be possible for GWT to profile (track events) in a hidden iframe or if Google Gears is there then use that... or in Silverlight, you can write to the local storage.

Edit: Here is another user story. Google Wave on-demand plugin loading. Google Wave right now is remarkably fast for me, but (a) does it make sense for Google to use up lots of bandwidth on very large conversations? It doesn't seem there is support right now for "paging" in the conversation view.

Why developer controls?

I think a little more interesting is the machinery in Doloto and AjaxScope for an optimizer to automatically decide when to load what. The last time I looked, there were analyses for understanding the reactive access structure and then a stub approach for loading large chunks: this was sufficient for its policy that is a mixture of just-in-time and prefetching techniques.

I didn't like the particular policy Doloto had -- it seemed too demand driven (lazy, just in time, whatever). Furthermore, the motivation is *large* web applications: it's cool Z-Bo found use for this technique in his shopping cart, but I'm guessing Ben Livshits is more concerned about, say, MS's transition to web versions of their huge office suite. Messing with linking policies, esp. in GWT's approach and some of those proposed here, would essentially be full-time jobs for a full team! Treating it like a mixed-initiative interface problem, using statistical/AI techniques to figure out what a user is doing and loading components in spare cycles automatically based on that, seems much better.

Going back to the original question... This isn't just about AJAX apps. If you look at something like Chrome OS, web accelerators, and launching large desktop applications (e.g., I bet MS Office applications use some automation here while Photoshop does not), you see very similar problems. MSR has some cool stuff in this area (beyond Doloto). Eric Horvitz's work in this area is coming at it from the other end (usage model extraction and analysis), with his more recent (unpublished?) stuff is starting to incorporate these ideas into the OS akin to what Doloto does for JavaScript (though for different optimizations).

Strong disagreement

Furthermore, the motivation is *large* web applications: it's cool Z-Bo found use for this technique in his shopping cart, but I'm guessing Ben Livshits is more concerned about, say, MS's transition to web versions of their huge office suite.

I looked into Doloto, and their FAQ explicitly states that their clustering analysis basically always aligns along the path of application features.

So, in short, Doloto clustering analysis is using statistics to derive something you should be able to get simply by looking at some UML. I love statistics, but this strikes me as a merely academically interesting use of statistics and not suitable to "Fortune 500"-style controls software engineering. Statistics should instead be used to verify quality assurance measures. [Edit: It is basic process control theory that you can't use statistics to improve a broken system design, without re-engineering the system. Dr. W. Edwards Deming's "Red Bead Game Experiment" is the classic example of this.]

I don't think Microsoft should throw away its clustering analysis. I think it is a vital component of any modern compiler that uses Javascript as its backend in a distributed computing environment. I disagree, however, with the idea that you can get performance for free. There is no such thing as a free lunch. If Microsoft expects to automatically deploy desktop applications as if they are web applications, then they will get any reputation for poor quality in web apps they deserve. It doesn't matter how 'cool' the idea is; quality of implementation and matching to the problem at hand must be the first things you judge as an engineer.

Messing with linking policies, esp. in GWT's approach and some of those proposed here, would essentially be full-time jobs for a full team! Treating it like a mixed-initiative interface problem, using statistical/AI techniques to figure out what a user is doing and loading components in spare cycles automatically based on that, seems much better.

No, it provides very direct implementation guidance to teams: Load on triggered by events, not on hardwired timing. Finally, the prefetch part of the GWT kernel can prefetch 'assemblies' prior to any load on trigger. [Edit: This is because the server controls the caching policy here, and all that matters from the client is that it has a block of executable code that is valid according to the server's caching policies.] LoadOn simply says, "if you can't start downloading once this event passes through the kernel event handler, then you've got a disruption problem or a bug in the loader." This strictly defines QA in the application environment.

Here is the QA analogy, once again: Selenium tests are based on events in the application, not based on hardwired timing. Basing tests on things such as hardwired timing is susceptible to all distributed computing fallacies. Instead, you build an event-driven application, refactor your code, and make it less brittle.

Really, if MS's code is such hodge-podge it needs clustering analysis as the deployment mechanism, then Ray Ozzie is not doing his job.

Bottom line: I very strongly agree with Lex Spoon that using clustering analysis to allow for synchronous code loads seems like a bad idea, and have explained the many levels at which it looks wrong to me. I think it tightly couples many concerns, and encourages bad practices. On the other hand, clustering analysis is very important for performance profiling and verifying loader bottlenecks are mitigated. It is a log analysis that can objectively measure your cache hit rate. When you think about loading in terms of also pre-fetching just-in-time, then all loading operations are defined in terms of a cache hit rate.

[Edit: Essential question for Ben Livshits to answer in his FAQ: If clusters correspond to features, then why bother with clustering at all?]

I think you missed my

I think you missed my point.

1. I agree - I don't like Ben's clustering policy.
2. His work, including AjaxScope, has the basic infrastructure to separate certain policies from mechanism.
3. When to load what is based on (individualized) usage patterns, not just static structure.

I agree that developer controls are nice (it'd help assuage point 1). Adding them to Ben's framework is not too big of a deal (point 2). However, most of it *should not* be handled by the developer if possible. How to expose manual overrides is an important question... but, to me, dwarfed by the question of automation (and controls for that).

Getting back to the PL side: I'd claim this is a global, non-composable optimization / transformation, so it should be handled by the language (or an API that looks like it). A feature might not just be a UML notion -- the whole point of stuff like MashMaker and CoScripter is that features are user dependent! Some new OS schedulers predict decisions, like memory allocation and cache prefetching, based on user behavior! Most interesting to me, if the application was to expose tweakable controls, perhaps it should be of more than just triggers / stop points: it should be of values / data that can be tweaked. I think of this as a mixed-initiative interface problem: the user wants to do something quickly, like look at a particular meeting in GCal -- how should Google specify the web app so the system can learn how to arrange its loading structure around the user? What should the system manipulate and optimize? What user actions should it listen for?

There was basic work at UW in Dan Weld's group about generating/learning context-dependent UIs; judging by it and the above cited work, I think mixed-initiative interfaces are an under-served area. The HCI/AI people have motivated it... but the SE/PL people haven't sufficiently worked on it -- the infrastructure just isn't there. That's why Doloto+AjaxScope, as opposed to just GWT, is interesting to me (even if not its particular policy).

Thanks for the clarification

I now understand your points, and mostly agree with them. I think I stated your points in my reply, but you are less critical of research prototypes than I am.

Features are indeed user dependent, and whether the user uses those features is (or should be) determined by their role. In other words, different users see different features. However, you're extending this argument by allowing for tools likee MashMaker and CoScripter to allow for user composable interfaces. The phrase mashup is just a buzzword for that age-old concept. Note this is not a different model! Why? We've already defined the user's capabilities -- there's just an added level of accessibility. We deal with that accessibility problem in our Software Product Line / Software Factory, where we do fairly deep and extensive customization while still supporting high code reusability. Some clients of ours do not want to train their users to use certain features, so we have a way to turn off/turn on certain features. In this way, we are the users composing interfaces for installation on client machines. Likewise, clients of ours may not want to pay for certain features that we've learned over time have more support requests, and in turn have decided to bundle separately (this has nothing to do with the complexity of the software or bugs, but rather additional feature requests or help using really powerful features; alternatively, it could simply be a hard module to implement and customize due to significant business process re-engineering).

how should Google specify the web app so the system can learn how to arrange its loading structure around the user? What should the system manipulate and optimize? What user actions should it listen for?

None of these questions are disallowed by what I've suggested. The change is that the kernel must be subverted so that it has a manifest of the services. Links should have temporal and flow properties (while still decoupling). As I said above, Guido van Rossum worked on a system like this in grad school. It was called CMIFed. Other related projects from the early '90s would include OOHDM (Object-Oriented Hypermedia Design Methodology) and HyTime -- major drawbacks of projects like HyTime included dependency on complicated data serialization specifications such as SGML.

The key question is flipped into, How can GWT be more mashup friendly? I would say that it has to start by supporting multiple entry points better.

There was basic work at UW in Dan Weld's group about generating/learning context-dependent UIs;

I've never really paid attention to Dan Weld's work. I'll look into it now. However, if you please could, send me links to the papers by him you like the most.

I think mixed-initiative interfaces are an under-served area.

Is mixed-initiative interface different from user composable interface? I suppose a machine could handle mixed-initiative and thus we would not give user control, but this is a very light trust condition that if we lift, we seem to get the same goals for both projects?

I've never really paid

I've never really paid attention to Dan Weld's work. I'll look into it now. However, if you please could, send me links to the papers by him you like the most.

For Dan Weld's work, I really should have cited his student Krzystof Gajos, now at Harvard: http://www.eecs.harvard.edu/~kgajos/ . Orthogonal but also interesting: Dan Weld and Pedro Domingo collaborated on some cool ML projects with PL flavor (version spaces and I believe a language over learned relations). Mira Dontcheva and Tesse Tau seem to have also been circling around this bubble of thought.

Is mixed-initiative interface different from user composable interface? ...The key question is flipped into, How can GWT be more mashup friendly? I would say that it has to start by supporting multiple entry points better.

"User composable", to me, sounds like the user shows how to compose things. I've run a small user study (~8 people) on CoScripter, MashMaker, etc. which essentially shows they fail in practice -- I don't know if the idea is flawed, the execution, the domain, or what, but there are many hurdles.

I liked "Principles of mixed-initiative user interfaces" by Horvitz for the intuition. Unlike the above, the user might be oblivious to what's going on. E.g., Google's personalization of search results, which you interact with by using. However, hooks still need to be exposed (or extracted) somehow.

In the mixed initiative work, performance isn't just loading features, but getting the task done, which might involve skipping features. It's hard for me to imagine future software not having flavors of this work.

Finally, again, for many optimizations, prefetching style optimizations are not static. E.g., when starting a customized photo application program, the memory manager might remember previous initialization sequences and usage patterns and allocate based on it. Instead of just doing it on application load, we might try to do it everywhere. Profile-guided optimizations are called that for a reason :)

In the mixed initiative

In the mixed initiative work, performance isn't just loading features, but getting the task done, which might involve skipping features.

I'm not sure I understand what "skipping features" means, exactly. Skipping a UI affordance? Yes. Skipping a feature... well, perhaps if the system is really complex then I could see it. If the user is skipping a UI feature, then they are not skipping features defined in terms of the core capabilities the UI depends upon. (Stuff like mashups based on screen scraping the Web is only a degenerative case where all the features are only exposed via a UI; the bulk of the problem there is that the mashup is based on fragile screen scraping and not reliable services defined by the Web site.)

Here's an example of skipping something that meaningfully changes things, in my eyes. Amazon ClickOnce totally obviates the need for an event such as ItemAddedToCart as a good heuristic for defining when the latest load time for loading the checkout feature might be. In this sense, the task is to buy an item. I'm not sure if this example is what you had in mind by "skipping features". If it is, then you are definitely on the right track and are forming the right picture about the problem domain.

I'm familiar with Mira Dontcheva's work on mashups based on screen scraping the Web. I see screen scraping as useful to search engines trying to find latent semantic meaning in documents. I don't see it as a good way to do user composable interfaces. Mira's counter to this appears to be that users can more easily visual "ah, I want this piece of this web page, and this piece of that web page." Such programming by example certainly can certainly make some kinds of mashups easier. However, if a web page can't tell the mashup tool what back-end service it wiggled to get that slice of data, then the mashup will be brittle, since nothing is more fungible than the presentation layer and relying on the UI for fake service definitions isn't reliable.

As for user composable interfaces... there are limited mainstream examples of this sort of thing. Probably the closest example would be "compound documents" a'la Word documents that utilize COM OLE (Object-Linking-and-Embedding) automations. But even this is not very deep.

One last nit

Instead of just doing it on application load, we might try to do it everywhere. Profile-guided optimizations are called that for a reason :)

The essence of my argument is that this unbounded optimization would be a SOFTWARE DEFECT. Why? In a distributed system, where failure and latency is to be expected, it doesn't make sense to transform-away from the initial payload any of the offline mode functionality, and stuff like fault tolerance and disruption tolerance should be baked into the core.

Bottom line: "Do it everywhere" is a misunderstanding of both the Web and the Internet. Once you take this stance, you see why I am also not overly impressed with GWT's solution, and don't really like Lex's categorization as "asynchronous" versus "synchronous". It is a false dichotomy and there are bigger engineering issues to think about that we can ascribe real ISO/IEEE defined qualities to.

In a distributed system,

In a distributed system, where failure and latency is to be expected, it doesn't make sense to transform-away from the initial payload any of the offline mode functionality.

I'm not sure how this is relevant to using modern web applications.

Perhaps you assume that resources are lazily loaded by policy and are concerned resources won't download if the connection is dropped somewhere between starting the application and using a particular feature. However, we already accept this can happen (e.g., Google search is a web service, you don't download all their data!). Furthermore, perhaps an assuaging policy is to dynamically order the loading of resources based on user interactions, pipelining the process but not stalling it. This would consume excessive bandwidth -- perhaps prohibitively so if done niavely -- but such a policy does not seem contrary to what I've been talking about.

Finally... I'm 100% for transformations etc. handled by the language to take care of concerns in distributed systems. That code is hairy so developers can use all the help they can get. My general position in designing PL features is to expose hooks only if there's a compelling reason -- which seems premature in this domain.

However, we already accept

However, we already accept this can happen (e.g., Google search is a web service, you don't download all their data!).

Yes, and web search engines are not an offline-capable application. You are not addressing my objection with this counter-example. Why give me this counter-example? It's not a useful one. Reaching out for data has nothing to do with the application being able to handle many failure modes. At this point, the client already knows all the outbound links from the application. This is not an interesting question. The interesting question is, How do we respond when the transfer agent gives the client back non-standard answers? Potentially trashing the user's data that we may have a way to save is not a good answer!

Let's come up with a user story. Let's say we're develoing a Web application in Silverlight/Moonlight for Blackbaud and it's going to run on a smart phone. Blackbaud is the largest player in the "Development" industry (fundraising software). Let's say the World Wildlife Foundation comes to Blackbaud and says, "Hey, we need a way that we can solicit donations and get signatures that is attractive to our high-end donors. Can you make us a Palm/Blackberry/iPhone solution that does that?" What happens if that smart phone is in an area where there is poor reception? Maybe a volcano erupted nearby and the sky is ashen and all cellphone reception is suddenly dead due to the thick, heavy ash in the sky -- a perfect opportunity to solicit a donation for saving wildlife near volcanoes! How do we record that donation (name, stylus signature + donation amount) on our web app? How do we "package" the software so solicitors who buy Blackbaud's development software are not left cursing us when the app is down when they click submit? Why can't we just detect when an internet connection is available, and put that donation into a offline queue?

My whole point is that "do it everywhere" means there is no model, that everything is open. My opinion is that this is a bad idea, and I explain why.

Finally... I'm 100% for transformations etc. handled by the language to take care of concerns in distributed systems. That code is hairy so developers can use all the help they can get.

How do you propose the compiler infer what fault tolerance measures the programmer wants to take? Likewise for disruption tolerance. Good systems should strive to limit programmer input to the beginning and end of automated processes, and not ask for programmer input where it's not necessary. At the same time, it should not be like RPC and prohibit the programmer from expressing the application's natural protocol. Very important design heuristic: Your API (and therefore language) must be at least as expressive as your application protocol. As Roy Fielding handles a similar question on a mailing list in 1998, he puts it a slightly different way:

>So, if we decide to wrap this interface programmatically (ala libwww), have
>we lost any information? I don't think so. They're both equally
>expressive, and express the same info.

Going in that direction, yes, but only if your programmatic wrapping is
as expressive as the interface. libwww is not. Going in the other
direction (a network protocol based on a programmatic API), we end up
with a protocol that reflects the API rather than being efficient for
the application.

I think I'm being fairly rigorous here, even admitting that heuristics such as event-driven prefetching have known scenarios where it does not work out well (i.e., Amazon ClickOne). Designing an all-encompassing solution is not trivial. Erik Meijer has been working on his distributed computing for the masses solution for at least three years now.

How do we record that

How do we record that donation (name, stylus signature + donation amount) on our web app? How do we "package" the software so solicitors who buy Blackbaud's development software are not left cursing us when the app is down when they click submit? Why can't we just detect when an internet connection is available, and put that donation into a offline queue?

When in tunnels and trying to access an online resource, I do not curse at web services / applications I'm using but the bad wifi / wimax support down there. Ok, not true: I wish it was easier to have an offline mode and my data prefetched. However, I don't see what these systems are doing as making the situation worse (assuming it compiles into REST and maintains appropriate stub info) -- you're asking for them to solve a different existing problem!

Surprising to me, by bringing it up, you seem to be suggesting these systems are in a tantalizingly close position to making the shakey connection / offline-mode part easier. I don't know of *any* satisfying solution to this problem, so I'm not crossing my fingers for a robust solution. I think such infrastructure has the potential to make it easier to work with offline mode, but there's a lot more going on.

For more general distributed systems... we already have things like MapReduce that are essentially fully automated. I've seen some crazy ideas in the same vein (using machine learning to pick nodes, online /adaptive queries, interactive modes, hierarchical task abstractions, etc.). They pick a side of the CAP theorem and remove the rest of your robustness concerns from your boilerplate code. I just don't view modern web applications as belonging to this class of computations (which are insulated as web services or whatnot from the rest of the application) -- maybe handling it is web 4.0 or something.

Surprising to me, by

[Edit: The Blackbaud hyperbole was done intentionally so the user story was moderately entertaining in a ridiculous sort of way.]

Surprising to me, by bringing it up, you seem to be suggesting these systems are in a tantalizingly close position to making the shakey connection / offline-mode part easier.

Absolutely. And PLT would be a prime vehicle to study this... but from a software engineering perspective, the kernel hooks provided by a hypermedia design environment like CMIFed or GWT could allow for better supporting this.

Side note: Using machine learning to pick nodes is difficult, because any machine learning will likely be bias towards picking machines with the fastest processor. Thus, the machine learning algorithm is likely dominated by having better data about your environment's set-up; a good MLA effectively seems to learn this information about the environment. Some pretty smart guys have done Ph.D. theses on "farm configuration" algorithms, but since you are currently a Ph.D. student I would not recommend spend time delving into this right now and instead focus on your current research. ;-)

maybe handling it is web 4.0 or something.

There are actually other neat complementary ways to reinvent the web that are not being discussed in this thread... I don't like version numbers on the Web, though. Web 2.0 is the most unwebbiest solution proposed in most cases. The idea of a Web application (a'la GWT RPC or John K. Ousterhout's Fiz) is in many ways the antithesis to the Web.

Thus, the machine learning

Thus, the machine learning algorithm is likely dominated by having better data about your environment's set-up; a good MLA effectively seems to learn this information about the environment

There's a *lot* of work on using ML in the data center (my favorites have to deal with predicting node failure and single-job scheduling); I'm more into the personal computing space. Very little stunning use of statistics and learning theory there (though I like the trend of using statistics to get the most out of a single bit of dynamic tracing information, pushing tracing costs to the post-mortem analysis).

automatic fetch priorities

lmeyerov writes: "I think a little more interesting is the machinery in Doloto and AjaxScope for an optimizer to automatically decide when to load what. The last time I looked, there were analyses for understanding the reactive access structure and then a stub approach for loading large chunks: this was sufficient for its policy that is a mixture of just-in-time and prefetching techniques."

I agree there's a difference in focus. GWT has focussed more on supporting different kinds of chunking and on diagnostic tools. It's focused much less on automatic prioritization of fetches, though that looks helpful, too.

The reason is that performance-tuned web applications don't have that many resources to prioritize. For apps as small as the mobile-phone version of Maps and as large as Wave and AdWords, the maintainers have gotten their initial app startup to take on the order of 3-10 resource fetches and on the order of 2-3 round trips to get them. With numbers at this magnitude, automatic prioritization doesn't have much room to help.

Teams that don't chunk up their resources have bigger problems than fetch priorities. If an app needs 100 fetches to get started, then it will start sluggishly even with an ideal ordering of the fetches. The first order of business for such an app is to chunk up the resources.

Based on this reasoning, GWT has focused more on supporting different kinds of resource chunking and on diagnostic tools. The code splitter is a new tool in the first category: it divides an overly large chunk of code into smaller chunks. In the second category, GWT provides a "compile report" for a static view and a "speed tracer" for dynamic profiles.

Readers are invited to try the speed tracer on web sites of their choice and see just how well a sluggish startup correlates to a large number of resource fetches.

A quick comment on minimalism

First, let me admit that I haven't followed all of this exchange in excruciating detail. But I just want to say a word or two about what GWT.runAsync() is, and what it is *not* (just my thoughts, of course; Lex did all the actual work).

The point, IMHO, of runAsync() is simply to give the developer the opportunity to provide information to the compiler about potential split points. Its particular structure (the fact that it works through callbacks, and provides an explicit failure notification) is driven by fundamental constraints: there's no way to fetch code synchronously from a web browser (sync XHR simply doesn't work sensibly), and those fetches can fail. The compiler then separates the code into fragments based upon where the developer is willing to deal with (a) a pause, and (b) potential failure.

I suspect everyone reading this already understands these points. The reason I reiterate them is to make the case that runAsync() is an essentially reductionist approach to the problem. I.e., I am unaware of any reasonable way to achieve the same thing with simpler abstractions. Now that's not to say *easier to use* abstractions, just more fundamental ones. In my mind, that's the way you have to build a complex system -- from the fundamental abstractions upwards. The back and forth in this thread over the kinds of declarative abstractions one might wish to apply to the problem is a perfect example of why I don't think you should *start* there. There's nothing stopping you from building something "nicer" for a particular problem *on top* of runAsync(). For example, see Matt Mastracci's "GWT Website" structure, here: http://code.google.com/p/gwt-website/

Side note: I don't think it's fair to say that Lex's characterization of "synchronous" vs. "asynchronous" code loading is a false dichotomy. I believe he's simply pointing out that if you attempt to load code synchronously and transparently, you run into two problems: First, you can't do it in the background, so you're forced to lock the UI thread. Second, you have to deal with failure somehow, and a synchronous XHR makes that very awkward.

What about promises/futures

What about promises/futures or continuations? Most JS interpreters don't support them (only Rhino does?), and I don't know how GWT compiles down, but those are traditional primitives for relaxing and manipulating control flow.

I don't think there's any

I don't think there's any sensible way to implement continuations in a stock javascript vm. You could have the compiler transform the code after the load into a callback automatically, but the stack would be in an entirely different state. I suspect that would do more harm than good, because you'd end up with all kinds of subtle state bugs. You also need a way to catch network errors, and while those *could* be caught elsewhere (i.e., some kind of global "code loading failed" handler), I think it's better to have the context available for such failures.

It might not be as bad in

It might not be as bad in the case of GWT: you're compiling anyway, and CPS'ing is understood.

There are runtime source to source implementations of continuations and promises/futures (descendants of narrativejs), but tweaking the GWT compiler seems to make more sense.

My rebuttal

I would argue that the fact you are using a callback as a fundamental abstraction seems to indicate that it is not a fundamental abstraction. I'm unfamiliar with thinking of callbacks as fundamental abstractions, and more familiar with thinking of callbacks as a way for lower-levels of a system to call back to higher-levels of a system.

In this regard, the question becomes: If the lower-level system must call back to a higher level system, then how should we design these two systems? First, I would say apply the Dependency Inversion Principle. Both systems should depend on abstractions. With this qualification, we can think of the callback system, GWT.runAsync and RunAsyncCallback, as the abstractions provided.

From that perspective, and using the examples provided by the GWT wiki, it looks to me like GWT is tightly coupling to a stack-based, procedural message-passing object-oriented programming language that does not decouple message from method, and is encouraging in its sample user stories to use the OOPL in a way that satisfies this tight coupling requirement.

For example, in the Code Splitting wiki page, one example is:

public void showContact(final String contactId) {
  GWT.runAsync(new RunAsyncCallback() {
      public void onFailure(Throwable caught) {
        cb.onFailure(caught);
      }

      public void onSuccess() {
        if (contactId == null) {
          // do nothing: just a prefetch
          return;
        }

        // Show contact contactId...
      }
  });
}

First, the interface to the outer message is messy, and looks like an RPC-based synchronous code loading interface. "showContact" sounds to me like a "DoIt()" method -- in effect, one of your primary user stories for this feature is encouraging no control partitioning whatsoever. That is the first design smell that tells me something is afoot, and lacking rigorous enough analysis. [Edit: Internally, I also don't see where the object reference "cb" is being instantiated or comes from.]

The documentation explaining the motivation for coding this way is messy, too:

The way to force prefetching is simply to call a runAsync in a way
that its callback doesn't actually do anything. When the application
later calls that runAsync for real, its code will be available. The precise
way to invoke a runAsync to have it do nothing will depend
on the specific case.

What you are in effect saying is that there is a contextual relationship that the programmer is responsible for maintaining. In other words, you disallow applying the Open-Closed Principle to showing profile contacts.

Then there are other tell-tale signs of tight coupling to a 3GL programming language's syntactic encapsulation features. The big one that jumps out at me is that a failure in the Loader is tightly coupled to a point in time. I mentioned this in my posts above: do not depend upon hardwired timing. Yet GWT is littered with hardwired timing warnings in its code splitting documentation:

The down side to specifying an initial load sequence is that if the split
points are reached in a different order than specified, then there will
be an even bigger delay than before before that code is run. For example,
if the third split point in the initial sequence is actually reached first,
then the code for that split point will not load until the code for the first
two split points finishes loading. Worse, if some non-initial split point
is actually reached first, then all of the code for the entire initial load
sequence, in addition to the leftovers fragment, must load before the requested
split point's code can load. Thus, think very carefully before putting anything
in the initial load sequence if the split points might be reached in a different
order at run time.

I am not a user of GWT and do not claim to use it professionally. However, this design does not appeal to me. In what way does runAsync allow me to have control over the definition of what "failure" is? Shouldn't failure be relative to the point in time where I need the module to be loaded?

All the documentation for RunAsyncCallback says is:

Called when, for some reason, the necessary code cannot be loaded.

What is "necessary code"? How rigorous can you be in identifying reasons? Later it says:

Called when, for some reason, the necessary code cannot be loaded. For
example, the web browser might no longer have network access.

So who controls network access? Obviously, the kernel should detect this, but how does that play with the loader? Are you just queueing up every module requested since the network went down? How do you know onFailure doesn't render that request moot? How do I tell the GWT kernel to cancel the load operation and shift to a different strategy?

These are all sorts of fundamental questions that should be answered before you call the code splitting abstraction of a callback fundamental. These are not addressed in the wiki...

Side note: I don't think it's fair to say that Lex's characterization of "synchronous" vs. "asynchronous" code loading is a false dichotomy. I believe he's simply pointing out that if you attempt to load code synchronously and transparently, you run into two problems: First, you can't do it in the background, so you're forced to lock the UI thread. Second, you have to deal with failure somehow, and a synchronous XHR makes that very awkward.

I think if you attempt to load code and have no quality assurance definition of what an acceptable load latency is, as well as a strategy for dealing with fault tolerance and network disruption, then you run into bigger problems. Furthermore, I think if you have an "open system" model where some compiler can arbitrarily pick where to split code, then you will run into serious transparency problems. Doloto doesn't actually have to be "synchronous", thus Lex Spoon's criticisms don't make sense. However, Doloto, as presently constituted, does have to use an "open system" model. It can't be any simpler than that. Your points about thread blocking are true and failure handling are true, but do not redefine the argument. In fact, you sort of indicate this: you say "synchronously and transparently", indicating that for both problems to be relevant, synchronous vs. asynchronous is not enough.

Bottom line: Synchronous vs. asynchronous is a false dichotomy when comparing GWT and Doloto code-on-demand strategies.