The crumminess of web tech

We make things for the web because the web is free. We want our stuff to be accessible to everyone. We don't want, for instance, the Apple police dictating whether we can update our app or not, or when, or how, or taking an enormous cut of our income.

But the default web tech has always been very bad for making web applications. It was originally invented for hypertext, not apps. It has been a long "evolution", but until yesterday, it didn't even offer native popovers, so everyone had to create their own. It is safe to say that a popover component should be part of every basic app toolkit.

Here are a few ways in which web tech is bad:

1. JavaScript

JavaScript is a language that makes kittens cry every day. It was created in a week and now we have to tolerate it forever!? WAT. The best book about it is called "JavaScript - the good parts"... It is the only language I know with so many evil parts, other than INTERCAL.

2. Standards schmandards

Web browsers implement standards differently, and devs suffer with browser support. This will never improve: the new Popover API, which everyone wants to use, already has been implemented differently in each browser. In 2024! My God, the story never changes!!!

Therefore your layout breaks through no fault of your own. It may work today and break tomorrow – not in theory, but in practice.

Further, when your web app runs out there in weird browsers on weird phones, or just in Safari (which is always lagging like an Internet Explorer in adoption of web standards), you keep seeing very weird error messages in your Sentry. Hard to believe they are real.

3. CSS

CSS is always in flux. At first, one had to learn how to write semantic CSS. Then CSS frameworks appeared, solving new problems and creating new ones. Now everyone uses Tailwind, which is again better, but again poses the old problem of how to use CSS well. In fact, Tailwind requires the development of very good taste about "where to put this code", because there are 4 different layers of abstraction, all of them valuable:

  1. global CSS (such as theme variables),
  2. components (where Tailwind defers to Bootstrap style),
  3. usage of actual Tailwind classes (with the danger of too much repetition), and
  4. inline styles, which should be very rare, but still do happen.

4. Build pipeline hell

Typescript and Flow are better than JS, but using them requires compilation. Tailwind does, too. Build tools such as vite are hard to understand and configure, but occasionally that responsibility falls on you. At that moment, you wish you had chosen straight JS, no build pipeline.

For instance, this article teaches you how to have hot reload if you use Vite in the frontend and Flask in the backend. It doesn't mention that you can do the opposite: Vite has a reverse proxy. Maybe the article was written before Vite added the reverse proxy. Anyway, the section named "A word of caution" states:

In my 7 years of building for the web, I've used Grunt, Gulp, Webpack, esbuild, and Parcel. Snowpack and Rome came-and-went before I ever had a chance to try them. Bun is vying for the spot of The New Hotness in bundling, Rome has been forked into Biome, and Vercel is building a Rust-based Webpack alternative.

5. Overactive ecosystem

The above paragraph is just one instance of a tremendous problem:

The JavaScript ecosystem is overactive, oriented to excitement, with lots of packages being created all the time, and then dying in less than 5 years. In fact, you will have a library die before you have a chance to use it. Happened to him, happened to me. A normal developer needs something durable, stable. But we are unable to tell which tools are going to be responsible and stay maintained. And the considerable force of the FOMO makes us try many alternatives.

6. Unruly npm dependencies

Controlling the dependencies of your project can be pretty impossible. Every medium project out there depends on an unreasonable number of npm packages, and you as app developer have a very faint idea of what most of them do. This is dangerous. But it is also a consequence of the overactive ecosystem.

7. Legacy code

Because libraries and frameworks are so short-lived, they put a ceiling on how much devs can accomplish before they have to reimplement their own "legacy code". Witness Angular 2.0, which infamously gave devs no upgrade path from AngularJS 1, leaving thousands of projects orphaned.

8. Boiled frogs

Because the web is necessary, programmers learn JavaScript first and specialize in it too early. These inexperienced developers easily become the proverbial boiled frog – they have no idea of what a sane environment actually feels like, let alone a good programming language. They think that world is normal.

Boiled frogs will disagree with what I am saying. Perhaps even jump to the cheap accusation of a "skill issue".

A subset of the boiled frogs shrug and declare "I did my job". They are happy to learn something new, jump ship, and leave the hand that fed them with legacy code that is only 4 years old. That is an irresponsible and immoral thing to do. Those people have no right to complain about planned obsolescence, or they would be hypocrites.

On the other hand, developers who have a clue may dismay in the face of adversity, sometimes optimizing for ease of implementation rather than awesomeness of UI design. Today many a developer says they prefer the backend to the frontend.

If HTML, CSS and JS weren't so awful, then we wouldn't be seeing every other language compile to JS or WebAssembly or both. The impetus of WebAssembly is proof that many, many people agree with what I am saying here.

9. Too much learning

And now the worst part: the amount of learning that a programmer has to constantly go through. This is something that the boiled frogs don't see anymore, but they have been wasting their lives and brains on HTML, CSS and JS, not to mention JS frameworks in general, especially React, Vue etc. which are always imposing new concepts and new ways.

FOMO makes you try new frameworks. For instance, SolidJS suddenly offers much higher performance through a smarter observable. But only when you use it, do you realize limitations such as... SolidJS only likes arrays, you can't use Maps or Sets, which by the way are collections in JS that should be much more popular.

99% of JS frameworks are full of leaky abstractions like that. They seem to solve a problem but create many others by hurting all the other dreams you had for your code.

Tiny history of the crumminess

In 1995, Netscape told Brendan Eich to quickly create a little language they wanted to add to their browser. So JavaScript was created in 10 days. Nobody expected it to become the most used language...

Through the decades, many, many alternatives were created, so developers wouldn't have to suffer the crummy JavaScript. One of the most important ones was CoffeeScript (2009). Microsoft hired the designer of Delphi to create their own in 2012: TypeScript, or "C# in the browser". TypeScript basically has won. However, all these languages introduce a compilation step, because only JavaScript ran in the browser. Now developers have to wait before seeing changes on the page, which is crummy.

The formatting language, CSS, was considered crummy, so they did the same thing: they invented CSS improvements which needed a compilation step down to CSS, making the total compilation time even longer and crummier.

With compilation, debugging suddenly became crummy, because when an error occurred, the browser reported the error was in line 934 of a JavaScript program that you hadn't written and looked hideous. So Chrome invented source maps, solving that problem: Now the browser would map lines of code and report the error on your CoffeScript source, not the translated JavaScript source. But source maps are a heavy download and they take even more time to compile, making them crummy. For being heavy, they are never used in production.

Trying to control the problem of web apps being heavier and heavier downloads in production, they added "tree shaking", which means, the compiler is smart enough to omit functions that were written but aren't actually currently used in the app. Feeling smart, they forget this is one more complicated thing that every web developer needs to know exists, and takes more compilation time, being, therefore, crummy.

To give back to developers the immediacy of saving code and seeing the changes in the browser without having to wait, sophisticated build tools such as gulp, webpack, vite etc. make complex decisions about which of those things to do during development and which to do in a production build. That's not all they do, it's more complex. Anyway, they have become yet another essential tool in the toolkit, they last even shorter than web frameworks, they are hard to understand, they have hundreds of configuration options... but they solve the immediacy problem – yet again, in a crummy way.

And in this manner, web development is ever more complex, and the crazy mob continues to think themselves smart after patching fundamental wounds with more and more tools.

The real solution always was something like Dart (2011) or WebAssembly (2017): a hard break with crummy web tech, as long as the replacement tech were as open as the web, and designed for applications from the start. So what did the boiled frogs do? They basically ignored these. The initial plan for Dart was to include it in Chrome as the good brother of JavaScript. This was criticized for fragmenting the web, so they gave up this idea in 2015.

Instead, Node.js (2009) brought JavaScript to the server, and now boiled frogs write their backend and frontend in the same language: the worst one. Someone help them!

Searching for a solution

You thought you would develop your product, be done, sit back, let the profits come in and never work again. Then you learned Chacon's lesson: software is a tamagotchi. Like a virtual pet, software has its own needs that must be tended to, over time, constantly. That's normal in software. What is abnormal is the furious intensity of the tamagotchiness if you use web tech: if you don't update it, in only 4 years it is legacy code that nobody wants to maintain.

In short, you had a problem, so you decided to write a web app. Now you have 9 problems, with more to be expected in the future.

We want to make web apps, yes. But with decent tools!

Through the years I have tried many alternatives to JS, especially those that promised I could write my web apps in Python, the most legible language. But I never felt they were mature enough to actually use. They always came with serious drawbacks, such as more difficulty debugging, due to translation to JS.

Still within web tech: Mithril

The best I could do was use basic JavaScript as much as possible, avoiding frameworks that interfere in my data. Something like Mithril.js, a reactive library with limited scope, is the best you can choose. When I use Mithril, my state management library is {}. Do not let a framework dictate how your data should be organized!

You must isolate your business logic from JS frameworks. Write the core of your system with one principle: importing the JS framework is not allowed! Keep the code that uses the framework very thin. This is the only way some of your code can survive these frameworks, which usually last only 4 years.

Avoiding the situation in which your entire frontend is suddenly a pile of legacy code is so important that it trumps other considerations, such as the availability of ready-made widgets for your framework. There are only one or two widget libraries for Mithril out there. To avoid reinventing the wheel we switch to web components (custom elements) for very complex things such as editable sortable filterable data grids.

Beyond web tech: Flutter

The crumminess of web tech is a good reason to adopt something like Flutter. Since 2021, it can render on the web using Canvas. Like a game engine, it paints the screen before it hands the pixels over to some dumb surface or canvas of the platform it is currently sitting on. Therefore, my layout will never break. I will no longer care whether browsers agree on most things. I won't have to use HTML or CSS or web frameworks. The Dart language has been getting great features, such as null safety (2021). Much better than JS and very similar to TypeScript, so learning Dart is easy for most. My Sentry will certainly contain fewer weird errors from weird browsers. And no need to ever configure a bundler.

CSS? Hahahahahaah... CSS... Good one!

Flutter is an open source cross-platform application development kit created by Google. From one codebase it generates the same application for Android, iOS, web, Windows, OS X and Linux. One million applications have been created with it.

Flutter has an initial learning curve, for sure. But in the long run, that is much less effort than keeping up with crummy web tech!

What about beginning developers? What do you think is easier, to learn HTML, then CSS, then Javascript, then Typescript, then Vue, then vite (and alternatives)... or to learn Dart and then Flutter?

But one must know when to use this kind of thing on the web. Flutter is for applications, not for content websites. A Flutter app running on the web is a heavier and slower download, does not expose its content to search engines, and does not easily integrate with the accessibility features of the web platform. Keep your blog in HTML, because HTML was created for hypertext. But it wasn't created for web apps! We have been forcing web apps onto it for years, defacing it, turning it into a monster.

And the only problem is, Flutter's performance is great on every platform, except the web, and Google is not doing anything about that – not in 2024, at least. The web seems to be a second-class platform for them.

Conclusion

Although I am only getting started with Flutter, it is already easy to see how developer productivity should be higher by using it instead of HTML, CSS and TypeScript.

My interest is not necessarily to make iOS or Android apps, no. Just make my life easier. Make a bloody app that will even run on the bloody web, without requiring that I use crummy web tech.

Further thoughts by other people