Making do with less in 2026
New year. New you. Or something like that. A time for resolutions at any rate. Dry January, a new exercise routine, a plant-based diet. Choose your poison.
I’ve got one for you. In 2026, try adopting a methodological minimalism around your code choices.
(Everything below this line is only available to paid subscribers)
This is part of the Choose Boring Technology approach. While we’re here, go re-read that — it’s been far too long:
Consider how you would solve your immediate problem without adding anything new.
The idea is simple: before you add anything new to your stack, try to solve your problem with what you’re already using.
Do you really need that extra queue, or will Postgres do you? Do you need that JavaScript-based solution, or is CSS enough? That Django package you just searched for…, is it really carrying its weight?
You might already do this. The suggestion is to try being a bit more determined about it. It’s amazing how far you can get.
§
A year ago now — how on earth can it be a year ago, I want to shout… — there were a couple of essays on Vendoring and Building things yourself. They’re of a piece here.
Especially with our new LLM tooling, far too many packages expose far too much API, and have far too questionable maintenance provenance.
Especially with our new LLM tooling, am I not just better off writing my own minimal version of exactly what I need?
Well, that’s an open question, but A) quite likely I am, and B) even if it turns out I’m not, I gain massively in the learning process of trying to to solve the problem myself. (The whole point with Use Boring Tech wasn’t never use anything new. Rather it was know why you need something new, which you can’t know if you jump immediately to the top chat bot suggested solution.)
§
Just as with a plant based diet, minimalism may not be something you want to adopt all the time. But even if not, a short reboot can do all kinds of good. And, who knows, maybe the idea grows on you.
§
I’m more militant. I default to less as a matter of principle. I experiment freely with pretty much everything that comes past. But it’s rare that a new package or tool will make the jump into production use for me.
A good example here is uv. I see it’s popular, and it’s OK, but none of the features stand out such that I’m in any rush to switch to it.
Folks struggle to accept this.
“Oh, but it’s the new de facto standard”, they say. Well, we’ll see. The same was said of pipenv and then poetry. I lost count of the number of “Why doesn’t your project use…” issues.
“But it’s faster”, they’ll say. I’m like Meh.
pip isn’t slow. I’ll grant those milliseconds are adding up. The reality is though, they’re not eating into the probably weeks of time I’ve saved over the last at least 15 years of not jumping on every new trend as it appears.
If uv becomes the actual standard, well, we’ll talk. Until then, I’ll keep my eye on it.
Now, I’m not saying you shouldn’t use uv. Likely you should. I’m well aware I work in a privileged environment. As I describe it on Django Chat every time Will questions me: I’m up my mountain herding goats. What works for me might not work for you.
Setting up a Python environment isn’t something I was confused about. venvs are not something I struggle with. uv’s project management features are something I actively don’t want.
And so on.
So, for me, adopting uv is something I’m just not going to do. It very successfully wraps up a whole set of issues that, for me, I want to — I’d go as far as to say, I need to — understand. I actively don’t want them covered up.
The feature that did appeal to me was the easy Python installation. I’ve been using pyenv for years to install multiple python versions. The uv approach is to use Python Standalone Builds, which are both quicker to install, and have better compile options than the default you get out of pyenv.
So digging in, I now have a simple pure Python script that does the curl and tar dance needed to install and manage Python installations in the same way as uv. I’m fully in control of that, and I understand it completely.
I much prefer that, over a dependency on another tool here. Again, YMMV, I’m not saying you shouldn’t use uv. Just, rather, why I don’t, and offering it as an example of a minimalist approach.
§
Those posts about building it yourself (that I linked above), part of the argument there is about LLMs making writing code so much easier that you don’t need to take on a dependency.
I’m not entirely convinced about this. There certainly will be an explosion of low quality packages that are essentially vibe coded, and are unlikely to be maintained. You’re clearly better off with your own than one of these. There’s still value in the bazaar though: code from well developed and maintained packages is still going to be better than anything you can come up with, machine assisted or not. That code is easier to produce isn’t an excuse to fall into not invented here.
Indeed, the argument for minimalism predates LLMs by an age.
Nonetheless, I think LLMs are relevant here because they make understanding all the more important.
Unless you think LLMs will reach the point where they’ll significantly go beyond their current short fallings — unless you really thing programmers will become redundant, in which case this probably isn’t the newsletter for you — it’s not the common cases — for which they demonstrably are good at, and getting better at — that matter. It’s the failure modes.
Building things yourself gives you an understanding of not only how things are put together but also of how they go wrong. It’s only this full conceptualisation that gives you any edge over the machines. In these latter days, if you want any career that’s more than babysitting a raft of coding agents, you need to dig in, get your hands dirty, and stay sharp. A coding minimalism may not be the only route there, but it’s a good one.
§
At a higher level, a methodological minimalism gives us a concrete answer to the central problem of complexity management.
An emergent theme of the Stack Report essays has been how we evolve our applications over time. I think the essence of our craft is not in particular tools, or languages, or techniques—which are all very interesting—but in how we respond to emerging requirements as our software grows.
We all agree that the big challenge is fighting complexity. Let me quote again from Greg Wilson’s Software Design by Example, which was the topic of the very first Stack Report:
As the number of components in a system grows, the complexity of the system increases rapidly. However, the number of things we can hold in working memory at any time is fixed and fairly small. If we want to build large programs that we can understand, we therefore need to construct them out of pieces that interact in a small number of ways. Figuring out what those pieces and interactions should be is the core of what we call “design”.
I’ve talked (repeatedly, it feels) about how Django is the _ right size for the web_: that it’s natural boundaries fit our problems very nicely, and that that explains its power and longevity (despite other, supposedly cleaner, patterns being available).
There comes a point where complexity grows, and you need to add more structure (more abstractions) to your application to keep your code comprehensible.
That structure, those abstractions, bring a certain complexity with them. They are themselves complex — otherwise we wouldn’t need giant textbooks about them. They’re not free.
The danger is that we bring in more abstraction than our application actually requires. That we make our code more complex (not less). That our more sophisticated code does more harm than good.
I’ll go out on a limb and say that, over my career, I think I’ve seen more projects go wrong by being too clever, than by being too dumb. I think the real danger is over-elaborate structures that obscure the essential problem, and eventually grind work to a halt.
There’s a kind of programming meme that programmers can’t program. I think, yeah they can; I see children programming. What they can’t do. What no-one can do is navigate a baroque labyrinth of indirection, whilst still holding a why of what’s going on in mind.
A methodological minimalism says No to premature abstractions.
Absolutely, Yes:
There comes a point where complexity grows, and you need to add more structure (more abstractions) to your application to keep your code comprehensible.
The question is when?
So many times, the answer given is often right at the beginning. ”We must start off right”. Combined with, “Or we’ll never scale”, perhaps?
I put it to you that that’s a mistake. You might be lucky, but you’re more likely to end up in the weeds.
The answer I’m suggesting, and suggesting you give a try, is “When we need to”. It’s not as glamorous. But it’s low tech and reliable. I’ve argued here before that, by allowing the deeper structures of your application time to solidify, it leads to net-simpler software. You can’t ask for more than that for a new year.
Give it a try. Let me know how you get on.