Last weekend I did a bit of "fullstack" development (i.e. a bit of frontend, backend and deployment) using supposedly modern tools in JavaScript (namely NextJS, Prisma for the DB backend and Docker for the deployment). These tools for this project were somewhat thrown at me without a lot of discussion, so I just shrugged and said OK. It was a longtime I hadn't touched that stack and now I remember why. It's still the unpleasant developer experience I recalled.
I have strong opinions about this stack, it's important for me to write them down so at least I will warn my future self to avoid it in the future if possible.
§ What's wrong with "fullstack" development?
I believe that JavaScript - taken as a mere programming language - has its "sweet spot" for scripting stuff running in the browser. The language itself has its quirks (luckily I rarely stomp on them, besides the occasional !== operator, which still irks me) but it's not bad compared to others I have encountered. I see it more like a technology to support web sites, not to be used to create monsters (you all know what they say about hammers and nails, right?).
JavaScript is easy to learn so here we are with people forcing a square peg into a round hole, pushing JavaScript to do more than it was thought of. Namely, complex web applications and (ugh) backends. I am not complaining about things like scalability or performances (don't have data points to support such claims), I will just look at the overall developer experience. The basic issue is that when I see a technological stack that grows forever, adding layers over layers of abstractions, complicating the developer workflow - all this is for me a strong sign that something's wrong. That things just do not work so workarounds and hacks are needed to make them work in an acceptable way.
There are many memes about npm and NodeJS and probably many people have a laugh and accept this ecosystem as a fact of life. Personally I don't find anything funny about that: I see instead a stack that has grown well over its intended scope and we have a bus that has hit a wall at full speed and we are at the incident site trying to help survivors.
More specifically, this is a non-exhaustive list of observations that are telling about things being so bad.
-
Opaque developer workflow: why do I have to do X and Y steps to go from my source code to the final artifact? Why do I have to compile JavaScript? Why do I have to transpile JavaScript? Why is tree-shaking needed? Details of these step are convoluted that I am sure most of the frontend developers have no idea either. They just read the documentation, follow the steps and believe all this cargo-culting is a good thing.
-
Opaque help on internet: "I solved X doing Y, period." without any clear explanation on the why that worked. I especially see answers like this from developers in areas of the world where these technologies have most adopters and where - I assume by reading the conversations - the priority is to make things work somehow and fast. Comically, some replies are completely unrelated to the issue (example: "I solved by deleting
node_modulesand running npm install again"). I have the feeling that this ecosystem encourages being a sloppy developer. -
Complex stack: many opaque layers. Slow and fragile. Things break and it is unclear why. Bad maintainability. All these thoughts crossed my mind during the usual iteration of writing, running and testing my code.
-
Update available!: why in less of a week of development do I already see that message for some random package I am using? Or even from the package manager itself? Unless meant to close critical bugs or vulnerabilities, I don't see a high-frequency release cycle as a good thing on a project for which I've installed a "9.x" version. Rather, I see an unstable platform or a project maintained by a trigger-happy developer. And, no, I am not one of those asking "is the project dead?" when there are no new commits for 6 months.
-
A library-first ecosystem: do you need something? Typical answer is "just install library X". Then I wonder why my
node_modulesdirectory is around ~700mb? What's exactly in there? Why things break when I delete and rebuild that directory after 5 months? Is the code I have installed safe? To be fair, this is a concern I have also for other ecosystems (Python, Rust, Ruby come to mind) so the JavaScript ecosystem is not alone but it feels like the worst offender. -
JavaScript abuse: I see virtually everyone using a JavaScript web framework, also using it even for basic things like form validation. Forms can be validated using the browser just as well (see MDN link). CSS animations can often replace JavaScript code. All this stuff is free and supported by any browser. Before injecting JavaScript, I would first be sure the browser is not enough.
-
Online-first development: apparently without a fast internet connection you're bust. You need to download over and over again gigabytes of stuff. This is not sustainable, both environmentally and also from the perspective of countries without a fast internet connection (or arbitrary restricted). Or even a bad day in Europe when your provider is down.
-
Simple vs. easy: this all reminds of the great talk by Rich Hickey (link to my comment) about the difference between simple and easy things. Simple things are things you can grasp and create a mental model of them; things you can reason about. Easy things just work because you press a few buttons in the correct sequence but you don't understand what's happening under the hood. Easy things can be inscrutable and hard to debug. And people using these easy tools like to call themselves "software engineers". This complexity and churn is falsely taken as a distinction point: if you can make these things work, if you know that framework and can cobble up a Docker Compose file: you're an engineer. Yeah. An engineer that doesn't have knowledge of their tools is a magician at best.
§ The icing on the cake: Docker
Oh, almost forgot. Arm in arm with the JavaScript ecosystem we see glorious Docker containers.
Even basic system administration is not cool anymore. Now we ship these monsters on a cloud server under the assumption that sysadmin work is obsolete.
Docker is also another clear sign that complex systems are unmanageable. I can't forget this meme:
I had a local image of Postgres with a script to initialize the database, in the directory it should be so it is automatically picked up on the first server startup. Works on my machine but on the server the initdb file is ignored (here where it happens). Why? No fucking clue.
Solution? Create a custom Postgres image and inject the initdb script:
FROM postgres:12
ADD ./scripts/init.sql /docker-entrypoint-initdb.d/
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 5432
CMD ["postgres"]§ Conclusions
I'm not updated on the new iterations of the frontend ecosystem (Svelte? Deno?) so I can't say how the future will be. But one thing is sure: next time I will have something to complaint about Rust, everyone please remind me of this weekend doing NextJS with Docker.