The year is 2024.
Youâre a software developer. You joined this industry a little more than 5 years ago and youâre doing quite well.
Youâre building a web-based application with a small team. You crank out features, one after another. Some of them are kinda useless, but youâre learning a lot and youâre having a pretty good time overall.
The team youâre working on is frontend-heavy. Your frontend is written in a JavaScript framework (itâs React, isnât it?). A while ago someone on your team introduced you to the magic of React Server Components and how they solve a whole slew of problems with traditional SPAs. Your team decided to adopt server components (youâre on Next.js now, arenât you?). You style your frontend using Tailwind, maybe you use shadcn/ui for some of their sweet UI components. All of this is pretty standard stuff, and itâs what the folks on Reddit and Hacker News keep talking about.
Hold on.
Maybe I got that wrong.
Youâre actually not one of those frontend weirdos. Youâre a backend engineer. Youâre not one to push pixels. You push data into databases and pull it back out using a plethora of CRUD endpoints. You sing the gospel of Go. You build a nice GraphQL API so those frontend teams donât bother you with new requirements to tailor your API to their needs all the damn time. On the rare chance that youâve actually got to serve a frontend from your backend application you decide to serve HTML with HTMX sprinkled on top for extra interactivity. Thereâs no way youâd touch any of that lousy JavaScript yourself.
You deploy and run your application on Vercel, or you host it with the friendly folks at Fly.io. As your organization grows, you start packing everything into Containers and start deploying to a managed Kubernetes cluster hosted on AWS. Youâve got a bunch of tireless tired SREs at your company who manage all that âk8sâ madness for you. On top of that they provide you with nice dashboards showing metrics, logs, and all sorts of events â Observability as they say. For some reason theyâre always grumpy and fighting fires, but luckily thatâs none of your business.
Whatâs your point, grumpy old fart?
How much of the above did I get right? I bet you found yourself at least somewhere in those ranty lines.
Look, Iâm not here to tell you that any of the technologies I listed above are bad. They absolutely arenât. Theyâre popular in 2024 for a good reason. They allow engineering teams to be effective and keep on building, shipping and running software reliably. Itâs none of my business to judge whether or not they make sense in your given circumstances.
The libraries, frameworks, languages, tools, patterns, platforms, and services I mentioned above give you a lot of practical abstractions so that you donât need to worry about whatâs beneath. You can focus on building the next AI-crypto-thing (Iâm getting cynical again, huh?) instead of getting lost in the weeds of the complexity of frontends, backends, servers, operations, and keeping things up and running.
Iâve been in this industry for a bit. Itâs more than 20 years since I built and published my first website. When I started, none of the tools and services I listed above existed.
This is not a nostalgia-heavy plea plea to return to building software like itâs 1999. Iâm not saying you should go back to building software like people did in the dark ages. Thatâd be silly.
âŚor would it?
Allow yourself to Under-Engineer for once
Iâd like to challenge you to deliberately under-engineer a small web application and get it all the way out there to production, with a public URL that actual people can visit. It doesnât need to be anything sophisticated. Pick an application that you can build relatively quickly, this whole adventure should take a few hours, tops.
If you need inspiration for an application you could build, how aboutâŚ
If you donât like the ideas above, pick one of your own. Donât overthink it, just have some fun.
The rules of the under-engineering challenge are simple:
- you build a web-app
- you keep the amount of frameworks, libraries, tools and services to an absolute minimum
- you deploy it to a web-server thatâs not your local machine
- you publish it so that people can access it on the open web (you share a URL, they open it and see the magic)
Under-Engineer like you mean it
When I say âunder-engineeringâ I mean that you should purposefully avoid anything you can cut out from your tech stack. Aim for radically simple. If you think you canât go simpler, try again. If you donât feel bad about how scrappy youâre building things, youâre probably not trying hard enough (or youâre more comfortable with scrappiness than most).
Write static HTML by hand, from scratch. No templating language. No static site generators. No frameworks.
If you want to add JavaScript, use vanilla JavaScript. No build tools. No bundlers. No TypeScript. Inlining JavaScript in <script>
blocks in your HTML is totally cool. No third-party dependencies like HTMX or jQuery - the DOM API is plenty.
Write CSS like your great-grandparents did. Or wouldâve done if it were around back then. No Tailwind. No Bootstrap. Just honest to god <style>
tags (or damn inline style
attributes for all I care!).
If you write a server application, try to use good old server-side rendered HTML instead of relying on single page applications or server-side components that you hydrate on the client. Try to use the smallest amount of frameworks and libraries possible. No third-party dependencies.
Get your own server. Maybe youâve got a spare laptop or raspberry sitting around that you could expose to the internet (via dynamic DNS, look at Cloudflare or ngrok). Or you rent a cheap VPS at Hetzner, DigitalOcean or grab a free AWS EC2 instance.
Donât bother to containerize your application. Donât mess with terraform
or kubernetes
or docker
. Donât rely on PaaS providers to do the heavy lifting for you. Provision your server manually via ssh
, install packages via apt
right there, copy your application over via scp
, rsync
or sftp
and start it on your server. Hook it up to a DNS record manually and youâre good to go.
To understand how your application is doing in production, you could ssh
into your production box and tail -f
some log files. If your app keeps crashing, bring it back up - maybe you start writing a cron
job or a systemd
service to keep it alive.
You hopefully get the idea at this point.
Scrap all the conventions and see what you learn
If youâve been around for long enough, youâll know what most of what Iâm suggesting above feels like. Youâve been there, youâve done that. You experienced the pain of not having comfortable tools first hand. You know that there are good reasons why we donât build software like that in this day and age.
But chances are youâve never been through this experience. All you know is the cushy and yet mind-blowingly complex world of modern software development. The layers upon layers of abstractions, services, âbest practicesâ that you they tell throw at you on YouTube and Reddit.
Or maybe itâs been ages since youâve last gone really really simple. You started using heavy JS frameworks and elaborate container orchestration even for the simplest setups because youâve grown so familiar with it. Thatâs smart, genuinely, but you might have forgotten what it was like to start with the bare essentials.
Look, Iâm not some purist, minimalist zealot telling you that youâll only achieve true programming zen if you start saying ânoâ to all the comforts the modern programming world is giving you. But if you truly go through this exercise, I promise that you will learn and experience a few insightful things:
- you get a chance to understand (and maybe appreciate) the primitives, the basic building blocks that hide underneath all the tools you usually use
- you start to appreciate the convenience that modern tooling truly offers
- equally, youâll discover that sometimes basic building blocks are actually be good enough
- you might learn that there are hardly any best practices in software development, only trade-offs that depend on circumstances
- you get a chance to challenge your own beliefs
- you might be surprised how far a radically simple way of building things gets you - and how can even apply this idea way beyond silly app ideas you got from a randoâs blog
- and of course you get the bragging rights of having shipped something in a completely unhinged way
Use modern tools but challenge your beliefs
Purposeful under-engineering is a fun exercise. Itâll help you challenge long-held beliefs and learn a few things while youâre having fun. However, itâs still just that: an exercise. Please donât go to work on Monday and tear down your production Kubernetes cluster because someone on the internet said itâs fun to deploy to your own VPC via ssh
. And if you do, donât blame me.
Iâm not here to say that modern software development is bad. Iâm not here to talk badly about frameworks, libraries, patterns and technologies that are widely used in the industry today - but I do challenge you to challenge your own beliefs.
While I think itâs healthy to reduce the complexity of your application stack that doesnât mean you shouldnât use modern tooling. Software is a fundamental pillar of modern life. Our users expect our systems to be responsive, reliable, accessible, and pleasant to use. Software development is more than hacking away by yourself - itâs a socio-technical problem that requires humans to collaborate closely and find creative solutions to meet ever-changing demands. Pick tools that make you and your team effective and donât reject modern tools to tickle a weird purist sense superiority.
I believe that going radically simple, under-engineering on purpose every now and then, can help you become a better software engineer, and I encourage you to give it a try.