If you’re not yet up to speed on the drama with the new FF4 nightly (aka “minefield”) and how it affects the performance functionality (“preloading”) of LABjs, I encourage you to read that previous post.
LABjs has a performance oriented feature (pretty much the 98% reason why LABjs even exists) called “preloading” which allows it to load all of a site’s scripts in parallel, but ensure their execution order (if needed/expressed) for dependency sake. Because the browsers all have different behavior on this topic, LABjs has a bunch of ugly gory stuff in its guts to smooth out the differences, using different tricks for different browsers.
The “trick” in question with the Mozilla line of browsers (FF, etc) is that <script> tags inserted dynamically into the browser (always have up to this point) preserved insertion-order for execution. As a matter of fact, that behavior is the most desirable behavior with respect to script loaders being used for improving site performance. But Mozilla has now dropped that behavior, and left us nothing in its place, meaning that LABjs’ performance functionality (again, the main and almost only feature) has been crippled for FF4 going forward.
So, I wrote that post a couple of days ago to try and surface within the community the importance of this issue and try to get Mozilla involved in a discussion about how to resolve this difficult turn of events. The primary developer from Mozilla who is responsible for that change in behavior has now responded with a pretty lengthy explanation/admonition. I encourage you to go read that comment in full (almost as long as my original blog post). But I’m writing this post here to respond to the things said there. It’s long and involved and so needs its own post to do it justice.
Before I go into specific detail to address the points raised in that comment, I want to just give a general response as a summary to my feelings on what was said.
The comment goes into exquisite detail to chastise me for improper behavior regarding “UA sniffing” (and similar approaches), basically suggesting that the turn of events that have left LABjs in turmoil are basically expected and warranted given the approach that I took with how LABjs does its magic. The comment does have some cordial language to suggest that Mozilla is willing to further discuss possible ways forward, but the clear overall tone is that LABjs should never have done things the way it did, and that the primary use case that LABjs was designed to serve is dubious/questionable at best.
So let me just respond to that as a general tone of the comment.
In nearly 100% of the cases, including comments even in LABjs’ own source code and documentation on the site, I’ve proclaimed and admitted that the biggest “problem” that the web community has in terms of performance-oriented script loading is the browser fragmentation on how dynamic, parallel-loading of resources works. Script loading is not why LABjs exists. Script loading in a performance-savvy way is why LABjs exists. It’s an exceedingly complex task to try and load multiple scripts (of any sort and location, not just those specially prepared) in parallel, and ensure execution order, and do so with both local and remote scripts, and do so on-demand (that is, either during page load or later), and normalize that behavior across all browsers. To my knowledge, no other script loader has ever achieved such a wide-spread task as that.
I don’t own the credit for recognizing this problem existed. That belongs much more to Steve Souders. Steve is recognized as probably the most well-known and influential advocate for web performance that the web has today (and may ever have). In both of his books, “High Performance Web Sites” (HPWS) and “Even Faster Web Sites” (EFWS), he identified dynamic, parallel script loading as a very important use case that was not being well served on the majority of the web sites out there (both big and small).
I read both of those books diligently (and many times), and concluded that a generalized solution to performance script loading must be developed. In many ways, Steve is why LABjs was born. In fact, it was Steve who documented all the different fragmented behavior between browsers, and explored the various tools and tricks we had available to us to solve them. Unfortunately, he, like I, readily recognized that no such trick worked in all browsers. He presented code in EFWS (chapter 4 and 5) for how to begin addressing dynamic script loading, but admitted that the code was not general purpose enough for the masses and that more needed to be done.
Steve is the one who deserves credit for raising the awareness on the issues of dynamic parallel script loading, and for codifying the specific general use case that LABjs was designed to solve. In fact, he was instrumental in helping LABjs come to be, through several lengthy email threads, phone calls, in-person collaborations and brain-storming, etc.
You see, I didn’t develop LABjs in isolation in my basement. I went to one of the foremost authorities on these issues, and I studied what he had to say, and I worked countless hours to try and find a way to bring all the pieces together. In that process (over 5 months of development), I found a very delicate balancing act of tricks that solved the use case. Steve and I explored several possible alternatives, but eventually I decided that the way LABjs does things is the only currently workable solution that meets the goals of the project.
And yes, this does require forking behavior between different browser families. But I’ve been a huge opponent of “UA sniffing” for years, including a rather lengthy disagreement comment-thread with Nicholas Zakas on the topic, as well as many others. So, I am fundamentally opposed to UA sniffing, and I absolutely detest anything that even smells like it. Of course I agree that feature-testing is the right way to go.
But there is no feature-test for the quirky behavior of different browsers with respect to how they load external resources dynamically, cache them, fire load events, etc. It’s one of those rare niche cases where feature-testing does not fit the bill. So the next best thing, in my opinion, is to use browser inference to fork the behavior between browsers. Essentially, this involves an indirect feature-test, by feature-testing for something known to belong only to one (or one set of) browser family(ies), and then based on that inference, relying on the previously observed behavior.
Is this foolproof? Hardly. It is a good idea? No, it’s a terrible idea. But in this case, no other even remotely acceptable alternative exists. It’s not an option, in my opinion, to simply not solve this use case now, as the other comment suggests. It’s not practical to have entirely different loaders for each browser, and force the web site to some how figure out (through server-side UA sniffing no doubt) which loader to use. UA sniffing AND browser-inference suck, but for LABjs, inference was the only option.
So, all this is to say, I understand the admonitions of the other comment against UA sniffing and related ilk. I don’t like them either. But I’m not some inexperienced script kiddie that has no clue. I’ve spent over a year trying to specialize in this area, and this is the best that I (and other far greater minds than me) could come up with. So while I appreciate the wisdom of avoiding such techniques when possible, I’d submit to you that it’s rather condescending to suggest that I simply was foolhardy and ignorant and that LABjs was destined for such failure as a result of my ill-advised approach. For the last 15 months, LABjs has been radically improving the web performance of hundreds or thousands of sites, and doing so carefully balanced on the tenuous tight rope of distinct browser quirks.
Call it what it is
The problem is not that LABjs relies on less-than-ideal practices to exploit quirks in various browsers. The problem is that the various browsers have persistently, for years, failed to find an agreement on how such resources can be loaded with performance optimization and flexibility as the primary concern. Even now, in that comment, the admission is clear: we can’t solve this use case with the current state of standards nor with the current state of web browser technology.
LABjs is a gap technology designed to fill the holes that browsers have carelessly left open. LABjs is designed to bring web performance optimization to web sites and scripts of all kinds right now, while we wait for browsers to catch up to that trend. The key assumption is of course that browsers will take note of that trend, and of the validity of the need, and will participate in the process. And I’ve done everything I can humanly do to advocate for that, in the shadows of greats like Steve Souders.
OK, now that that’s off my chest, I need to address many other specific points in the Mozilla developer’s comment. But this post is already too long. So, I’m going to cut it here, and follow up with a second post (“part 2”), right away. So stay tuned.
[UPDATE] Please continue down the rabbit hole with Part 2 of Mozilla & LABjs.