Why Modern Web Development is a Mess

This isn’t yet another article about the bad parts of JavaScript, the heaviness of your node_modules folder, crazy webpack build times, or thousands of trivial leftpad-like imported modules. I‘d like to look at the modern web from a different perspective.

From the perspective of UI automation.

I was working on UI automation for a while. The task is seemingly simple — simulate user interactions with a web page. It’s useful for end-to-end or performance testing of your application or just pure automation of certain activities. A typical tool for web UI automation is Selenium or any frameworks that use it, e.g. Serenity BDD.

When you write UI automation, you don’t really care about how the web application is written, whether it’s a single page application in React or Angular or just plain old HTML with JQuery. All you care about is, you want to interact with some elements and observe some expected behavior.

Imagine a simple case of clicking two elements in succession, e.g. triggering a combobox to open and then selecting something inside the dropdown that appears.

Seems trivial. Just look at your web page as an API:

But if you did UI automation, you know it’s never that simple.

You quickly come to an understanding that modern web UI is crazy brittle. If you start working with it as with an API, it just falls apart.

Why would it matter? I mean, you interact with a machine, right? The machine should work in a deterministic way. If you click on two elements in succession, they should always work the same, regardless of whether you click on them with a 1-second delay or with a 1-millisecond delay. Right?

Wrong.

There’s a lot of asynchronous stuff under the hood that you can’t influence or observe in any way:

There are so many asynchronous races going on here that your automation may fail 30% of the time, or 50% of the time, or just once in 1000 runs.

Before any interaction, you have to make sure the previous interaction is finished. But how do you make sure the interaction is finished? Just observing an element in the UI is not enough. Tricky modern frameworks and badly written JavaScript code may cause the DOM elements to be refreshed multiple times, and you end up with a reference to a stale element instead of the one that’s actually interactable.

Moreover, some of the things are not even observable at all. How can you make sure all proper asynchronous code related to the element being displayed and interactable is finished?

The practice of UI automation says you can’t. You just add some large enough delay to make sure the JavaScript finished executing, or simply retry your action until it succeeds.

What bothers me is nobody ever fixes those races or is rarely even aware of them. Why? Because no human could possibly click elements so quickly. The problem is not reproducible. If the problem is not reproducible, you don’t need to fix it. Or do you?

Now imagine you’re a backend developer, and you write a backend API with the same assumptions:

“Oh, nobody will call this API faster than once every millisecond, so I can just rely on that and hack together some asynchronous thread-unsafe fire-and-forget spagetti code. Well, probably it will randomly overlap and fail every 1000 calls, but who cares.”

Sounds crazy, but not in the frontend world.

Asynchronicity of the interface is not the issue here. It’s the absence of determinism. The absence of understanding how your code really executes, and what are dependencies between various parts.

Can you deterministically say what will happen if a user invokes two calls in your application with a 1-millisecond delay? If not, there’s something fundamentally wrong with your code.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Forketyfork

Software developer @ JetBrains Space. I mostly write about Java and microservice architecture. Occasional rants on software development in general.