Even though I have only been doing web development for four years, it appears to have been the most tumultuous four years in the history of the discipline. When I started out, all-in-one frameworks like Angular and Ember were all the rage. Developers were eschewing the pattern of stitching together various libraries, jQuery plugins, and lightweight modular solutions like Backbone. The promise of these frameworks was that they would create their own ecosystems of compatible components, encompassing all possible functionality.
At the same time, the proliferation of node.js catalyzed an explosion in the complexity of web development. Web developers began writing developer tools in JavaScript. Dissatisfied with the limitations of the archaic language, these developer tools were often ways to “compile” newer or alternative syntaxes to “vanilla” JavaScript. New “flavors” of JS emerged like CoffeeScript, TypeScript, and JSX, as well as the emerging ECMAScript standard referred to as “ES6” or “ES2015”. CSS got its own set of new flavors as well, with SASS/SCSS and Less becoming standard in many projects. In addition, more pre-processors became standard like minification, polyfills for older browsers, and automatic insertion of prefixes for browser-compatible styles. Specialized development servers and task runners were created in JavaScript to perform these transformations as needed during development, and update the browser.
Each layer of complexity creates the opportunity for more problems to arise. Generated code now needs a “source map” file along with it, so that when errors occur at runtime, the actual user code causing them can be pinpointed. Libraries like React chain so many function calls internally, that when the developer makes an error, they may see a stack trace 30 levels deep in the browser that does not even include any code they themselves have written. The already confusing errors that JavaScript produces become exponentially more confusing when the running code is not actually the same as what the developer is writing.
The JavaScript ecosystem as it stands now encourages a “cargo cult” mindset. It is so easy to include a library, that developers can ignore the performance or complexity concerns in doing so. A popular authentication-as-a-service provider, who will remain nameless, publishes a library that displays a login modal tied to their service. Even though the modal only contains a handful of inputs and buttons, it includes the entire React 15 library with it. If your project does not use React, you must include the entire framework anyway. If your project does use React, in a version other than 15, your dependency chain will break when installing it via NPM. In this case you are enforced to include two entire versions of React in your project, one of which is bundled with their published library and one via the NPM method.
React provides templating and data binding functionality with an HTML-like syntax and an object-oriented development style, just like custom elements. Alternative CSS syntaxes provide the use of variables and new features without vendor prefixing, just like CSS3. These features are proposed in the web browser standard, but they have only been partially available in unstable release channels, and with too much divergence between browser vendors. Until now.
ES2015 and custom elements are now supported in most major release channels (even the notorious IE and iOS). Many CSS transitions and transforms can be used without vendor prefixing in all major browsers. New features like the fetch API, localStorage and IndexedDB are reliably present as well. The features that are relevant to modern application developers are finally making it to the average user’s devices, and it is beautiful.
For me, the experience of writing an application with web components was like coming up for air after years of suffocation. There were no pre-processors. Class syntax, promises, lamdba functions, CSS variables — it was all just there. There was no build task manager, because there were no build tasks to be performed. Exceptions that were thrown pointed directly at my own screw-ups, not deep in some compiled library. Code could be tested in the browser console, because I was using the same namespace and syntax to develop as was actually running in the browser. And the few libraries I did import and use, I did so through — gasp — global script tags. How perfectly barbaric. I found myself falling in love with web development again.
Certainly it will take a few more years before large companies are comfortable taking the plunge and ending support for users that have not updated their web browsers. But a truly wonderful world awaits when they do. I hope I am correct in predicting that the complexity and code proliferation in web development, after increasing for many years, is about to take a nose dive. The features that these libraries and frameworks provide have arrived in the “operating system” itself — the web browser. No pre-processors now. Just code.