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
We have a complexity spike on the client-side.
Many browsers differed in JS implementation, which required jQuery to come into existence.
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.
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.
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.
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.
You can have an SPA experience while still writing a unified app!
Simplicity through new generation of React frameworks.
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.
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.
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.