Your job isn't programming
Dec 12, 2025We’ve been doing software development wrong. When trying to improve a poor codebase, the solutions we often reach to are generic and technical. Move the frontend to React. Split the backend into microservices. Rewrite everything in Rust. Some of these approaches can have real benefits in the right situation, but none of them are going to solve the core problems with bad codebases.
From John Ousterhout’s A Philosophy of Software Design:
The greatest limitation in writing software is our ability to understand the systems we are creating.
The core problem with bad codebases is that they’ve grown too complex to understand. Rust or React can’t fix that problem. So what can?
Making things simpler
The solution is abstractions. Abstractions are ideas that hide unimportant details and emphasize important ones. Note that abstractions are ideas and not the code that they happen to be expressed in. These ideas are simplified models of the real complexity they conceal, and therefore make it easier to understand the systems you’re working with. You can think in generalizations and only look under the covers when you have to.
Remember: abstractions are ideas, so a good abstraction should change the way you think about part of your codebase. If you introduce an abstraction and it doesn’t change the way you think, you haven’t made an abstraction, you’ve made a layer of indirection.
Designing
How do you design an abstraction anyway?
Some of the time, good abstractions will be practically screaming at you. They’re ideas that already exist in your business that just need some ironing out and to be properly expressed in code. These are things like an invoice, a product, a customer, or a subscription. But note that when your business talks about a customer, they’re not talking about the English dictionary definition of customer, they’re talking about one of their customers, and their customers have specific rules and structured ways of interacting with them. That’s already an abstraction! You just need to express all of that in code in the same way your business expresses it in words.
Some abstractions are going to be a little bit tougher to find, and you’re going to have to get creative. There’s no silver bullet to this kind of work; you just need find complexity and experiment with ideas to manage it. Often this sort of work doesn’t even need code, and writing things out is good enough to evaluate how good your possible abstraction is. This might sound hard at first, but it gets easier the more you do it.
There’s a pattern in the abstractions I mentioned earlier. They’re all types of data. If you’re looking for abstractions, you’re going to find a lot of them in your data. This should make sense! Data is never just data. Data almost always has rules around how to modify it, and these rules can often be hidden away in an idea if they’re not already.
Redesigning
Sometimes finding a good abstraction is hard because the code already has many abstractions but they’re ones that don’t work well anymore. In Sandi Metz’s “The Wrong Abstraction”, she mentions a great technique to help with this: get rid of an abstraction you think doesn’t work anymore and re-introduce the duplication. It’s easier to find good abstractions if you can see the duplication than trying to find a good abstraction in a badly abstracted codebase.
It’s important to note that you shouldn’t be afraid to design abstractions because you might get it wrong. You will get it wrong! Designing abstractions is a creative endeavor, and creative endeavors requires trial and error. Instead of being afraid, you should design abstractions and be ready to re-design them when they start to hurt more than help. Just like editing is an essential part of writing, refactoring is an essential part of software development, and what often needs refactoring the most as time goes on are the abstractions you use.
Programming
Your job isn’t programming. Your job is managing complexity by designing, refining, and redesigning abstractions. If you’re doing that, then the programming is easy.
If you’re not doing that, then your codebase is going to become harder and harder to work with over time. You’re not going to be able to effectively onboard new developers. Simple features are going to become complex. Complex features are going to become impossible.
But it doesn’t have to be that way, and if you’re already drowning, then there’s a way out: find complexity in your application, figure out what’s important and what’s not, wrap everything up in an idea, and then repeat. You can dig your way out one abstraction at a time.