Michal Pietraszko

Michal Pietraszko

Web apps are too complex. This is how we can simplify them.

Web apps are too complex. This is how we can simplify them.

I believe that we can do a better job of managing the complexity of our apps.

Not many of us realize how many second-order effects our decisions have caused.

Let's see how complexity had grown over time.

The Static era

Simple times. We had a MySQL database, business logic and HTML + CSS views.

All content was static, the browser's job was to display content, navigate and submit forms.


I like to think about test effort as a benchmark for simplicity. There were 3 layers.

Business logic and persistence layer can be easily integrated and view layer can be browser tested.

You may need a tester, developer, and a designer to maintain something like this. It is realistic to have one person responsible for all of this.

The AJAX era

JavaScript opened a door for more considerations in user experience. Adding a dynamic menu, forms, or calendar to a WordPress website was the coolest thing you could do.


We have a complexity spike on the client-side.

Many browsers differed in JS implementation, which required jQuery to come into existence.

This gave a lot of power to designers and has moved more engineering effort into the front end. JavaScript made the browser extensible.

Did the testing complexity increase? Yes. Each new JavaScript bit could only be tested in a browser.

This requires testing, backend programming, JavaScript, and design expertise in your team. Jumping between server-side and client-side languages became frustrating. There was a trend to have different people responsible for each side.

The Single-page era

Remember the first example of the Angular.js app? The input field that automatically updated the content of the div? Good times.

Welcome to the single-page era where front-end development became even more complex than back-end development - mostly due to relevant logic moving to the client. As a result, the divide has increased and JavaScript fatigue became a thing.


We have ended up with two apps that are tightly coupled.

To maintain this, you need at least someone experienced in testing, backend, frontend development (extensive framework, tooling, and browser knowledge), and design.

Now, two apps have to be maintained, and there is much more code than ever. You have to maintain unit, integration, and end to end tests on both sides. Now business logic is not directly accessible due to security concerns. Frontend and backend now have to maintain layers that are responsible for communication.

Client code needs lots of API mocks to be tested on lower levels - DOM tests are resource-heavy.

Orchestration becomes difficult because you have to make sure that deployments are synchronized. It is even more difficult if you have separate teams for the backend and frontend.

Don't forget about browser testing that also can have a lot of overlap with client-side integration tests. Even more, things to consider in terms of complexity and trade-offs.

That resulted in more code, which contributed to - again - increased complexity.

SEO became problematic, but thankfully this problem has been addressed by the ecosystem through server-side rendering and hydration.

Good patterns have emerged too. UX became better and more creative. We are finally capable of defining client-side logic in a manageable and scalable way.

We all know now that we want to have components and avoid excessive side effects, together with uncontrollable state mutation.

React de facto became a standard.

Simplicity renaissance

The remedy to complexity is embracing the coupling and making the developer experience unified.


Simplicity through innovation in older frameworks.

Ruby on Rails and Laravel are relevant.

Consider them. Their maturity will allow you to move very fast.

They have recently innovated in many interesting ways.

Take a look at Laravel's components or RoR's Hotwire!

Next generation of JavaScript frameworks

People who want to stay in JavaScript land should consider the following.

Next.js started a good trend by putting React and server logic next to each other.

Blitz.js, which is based on Next, is a good ruby on rails equivalent. It brings the right amount of abstraction that makes you treat your app as a unified whole. Using it sometimes feels like cheating - in a good way. It inspired me to talk about the complexity issue in our ecosystem.

Remix with a fresh take on the problem domain and bringing a lot of good and forgotten patterns.

React's Server Components to make everything even better.

Recently, the React team has presented a new idea that can make our component-driven world better.

Consider reading the article and watching their presentation.

When they are released, then we will end up in the best-case scenario where web apps are only dynamic in places that require it without having to jump between server-side and client-side paradigms.

All of the frameworks above will benefit from them.

In conclusion

We should start asking ourselves if our standard approach is something we still want to maintain.

Suggested frameworks reduce complexity and allow us to experience the simplicity of older approaches while having the benefits of the modern approach.

They embrace the fact that both backend and frontend are tightly coupled and make the developer experience unified.

This is an opportunity to write less code, spend less time testing, simplify orchestration, spend less money on more people having to maintain the complexity, and put more effort into products we are trying to create.

Share this