The first question you always ask now when writing a new webapp is hey, what front-end framework will we use — Ember, Angular, or React?
But having to choose which framework to use is like deciding between being shot and poisoned. Instead of frameworks, we should call them “scaffolds” — because they’re where we hang ourselves.
When did we go framework-crazy? When did we decide that the answer to all our problems was a framework? This insane fixation on frameworks appears to be a millenial phenomenon — brought to you by the generation who never really learned software architecture. Their solution to any problem is a framework which dictates not just what you do, but also what you think. In their view, the developer’s role is merely to hang things like Christmas ornaments on the Christmas tree of their framework.
What we need is not frameworks, but un-frameworks, or anti-frameworks, or no frameworks at all. I make a proposal at the end of this essay; read on.
The framework-centric approach is especially surprising in an America still characterized by an individualistic, don’t tread on me, libertarian, cowboy mentality. It’s odd in the extreme that American developers would take the top-down, paternalistic approach to architecting computer systems that we see in frameworks.
But what actually is the problem with JS frameworks? After all, don’t they do the heavy lifting for you? Don’t they let you concentrate on the problem at hand? Don’t they do away with unnecessary boilerplate? Don’t they provide the conceptual structure for your app? Don’t they make the hard decisions for you?
Yes, but they also run out of gas. They box you in. They take over your life. They allow you to do things that they thought of, but nothing else. They are not extensible. The decisions they make for you to make things easier eventually become a millstone around your neck. These are very real problems when you’re building a real app, as opposed to a toy app.
In addition, because it’s a ”framework”, whose goal is to do as much of your work for you as possible, including work you didn’t know you wanted it to do, there is a inevitable tendency for it to grow beyond all reasonable bounds, both in terms of size (remember, we have to download the JS for these frameworks), as well as footprint — with hundreds or even thousands of classes and objects and interfaces and APIs.
In the below, I’ll talk about a specific framework called Ember.js, because that’s what I’m familiar with. Judge for yourself whether my comments pertain to other frameworks.
When initial architectural design decisions are flawed, and require changes in the fundamental architecture, how do we roll out the changes to developers who have already built and shipped apps using the previous version? We can let developers with running apps based on an old version stay in the legacy ghetto, or we can ask them to expend their precious resources on updating their apps to the latest, greatest version of our framework instead of building new features. Yes, this problem exists with other platforms as well. But it’s especially acute in the area of JS frameworks, where in many cases the early design decisions were especially bad and thus require especially major changes.
Ember has adopted the policy of six-week release cycles. This may seem unreasonably fast to you — because it is in fact unreasonably fast. Part of the reason for releases being so frequent is to fix bugs that arise from the fact that releases are so frequent. Another reason is just that hey, the framework designers had some cool new ideas. Developers may choose to skip the six-week releases — but that means skipping fixes to bugs which are breaking their apps. Developers can wait and upgrade every three months instead, but by that point the volume of intervening changes is so great that the upgrade can be very painful, and introduce bugs and risks. Similar upgrade issues can be seen with other JS frameworks.
A key problem with frameworks is that when I reach the point where it can’t do what I need, I fall off a cliff. At that point, I have to hack the framework, or rewrite the parts of it that I need. But wait: can’t all these issues be solved via the notion of add-ons to the framework? Actually, not at all. Leaving aside the issue of whether I can actually find the add-on I might want, or whether the add-ons are well designed, or supported, or maintained, or kept up-to-date with the framework, which is all too often not the case, add-on interfaces are often complex and buggy; I may end up spending more time debugging the add-on than I save. The entire notion of the framework add-on is the ultimate cop-out by the framework author: “let them use add-ons”.
The dirty little secret about JS frameworks, not just Ember, is their abysmal performance on mobile devices. First, we have to download the huge runtime on a slow connection. Then, we have massive amounts of framework code running on a device which has a slower processor to start with, but may also have an inferior JS engine. The result can be latencies in seconds or even tens of seconds merely to display a page. There is no simple solution to this problem.
A massive problem with frameworks is that they cannot keep up with changes in the surrounding technologies. Often, a key element of frameworks is “filling in the holes” — providing solutions that core underlying technologies cannot provide. However, the frameworks usually end up filling the holes in ways that are incompatible with how the technologies are likely to evolve or eventually do so; or, they are built in ways that make it hard, or impossible, to take advantage of the new technologies. Ember will have a very hard time adapting its architecture to take advantage of web workers, to take just one example. It will also require massive re-architecting to take advantage of JS proxies. In the case that the framework is updated to take advantage of the new technologies, it will usually require intrusive architectural changes that give rise to the versioning issues discussed above.
We can see this in the case of CSS and the immensely popular Bootstrap framework, the use of which is so widespread that it now appears in job descriptions. A key aspect of Bootstrap was to “fill in the holes” of the lack of ability to arrange things on pages, a problem that is now largely solved by flexbox, which is available in all browser worth supporting. Yet people remain stuck in the Bootstrap ghetto, with an uncertain upgrade path to an alpha version of 4.0 which supports flexbox. Although we may not want to call them frameworks, CSS preprocessors such as SASS are also classical “fill in the holes” frameworks, which promote poor CSS practices and introduce additional cumbersome build steps. The framework mania has now reached the point that the Wikipedia page for CSS frameworks lists more than two dozen; we also have frameworks which are built on top of other frameworks.
On the topic of building, perhaps we should not call grunt or gulp or broccoli “frameworks”, but they are indeed a form of framework for building projects. Once again, they take a father-knows-best approach in deciding for you how your project should be built and give you little places to hang your ornaments. Most or all of this “new generation” of build frameworks created by millennials ignore decades of solid work done on build systems such as GNU make — most of them have probably never even heard of it — and are basically reinventing the wheel, except these wheels often are not round and do not actually roll along the ground.
Just say no to frameworks.
JS frameworks have actually played a useful, if unintended role: they have taught us more about how to think about how to architect our JS apps. Now that they have done that, we are done with them. Write your webapp the old-fashioned way, from the ground up. Find the best, reliable, robust libraries for the key aspects of your app, whether it be routing, or data modeling, or templating, or control flow. The future lies in such well-designed libraries.
Your app is not something you hang off a framework; it is something you compose.