My best PR last quarter deleted 800 lines and added 40. Nobody clapped. Production got faster. The slack channel moved on to lunch within four minutes of merge, which is the only standing ovation a deletion ever gets.
I keep thinking about that PR because it is the cleanest example I have of the kind of work I want to be doing more of in 2026. Not the kind where I add a clever new feature. The kind where I look at what is already there and notice that most of it does not need to exist.
The new craft
Here is the thing that has changed. AI can generate a thousand lines of working code in 90 seconds. I can sit in a chat window and watch a perfectly reasonable module appear on my screen faster than I can read it. The marginal cost of producing more code has cratered.
Which means the marginal value of restraint has gone up. When code was expensive to write, writing a lot of it felt like effort, and effort felt like progress. When code is cheap to write, writing a lot of it is just a habit, and the habit is now actively expensive on the other end of the lifecycle. Every line still has to be read, debugged, refactored, and explained to the next person. Every line is still a tiny liability with interest payments.
So the new craft, the one I am trying to get better at, is writing fewer lines than I could have. Not fewer than I need. Fewer than the lazy version of me would have produced if left alone with an autocomplete. That gap, between what I could have shipped and what I actually shipped, is where the work lives now.
Four habits that keep me honest
I have four little checks I run on myself. They are not glamorous. They feel almost embarrassing to write down because they are so simple. That is exactly why they work. They are cheap enough to actually do.
1. Before writing a function, search for one that already exists.
Half the time it does. Some past version of me, or some teammate, or some library I already depend on, has already solved this. Open the codebase, grep for the verb, look at what comes up. The first instinct should be to find, not to write. I cannot count the number of times I have caught myself two minutes into a new helper before realising the standard library has exactly that, with better edge case handling than I would have remembered to add.
The AI version of this trap is worse. Ask the model for a helper and it will cheerfully write you one, with no idea that the codebase already has three of them in different files. It will not search. You have to.
2. Before adding a config option, ask if the sensible default is enough.
Usually yes. Configurability is a tax. Every option you expose is a thing you have to document, test across combinations, deprecate carefully, and explain to confused users. Most of the config knobs I have shipped in my life have been used by exactly one person, which was me, exactly once, six months after I shipped them.
The honest move is to pick a sensible default, ship that, and wait. If a second person ever actually asks for the option, add it then. Configurability that nobody requested is a fancy way of writing code on speculation, and speculation is the most expensive kind of code there is.
3. Before adding an abstraction, count how many concrete cases you have.
If it is less than three, hardcode. This one is hard for me because abstraction feels like the smart, senior move. Naming the pattern, extracting the interface, generalising the shape. It feels like architecture. But premature abstraction is just code that solves problems you do not have yet, and the problems you do not have yet are usually not the problems you will turn out to have.
Two concrete cases is a coincidence. Three is a pattern. Wait for the third one to actually show up before you reach for the abstract noun. Until then, two copies of slightly similar code are honestly fine. Future me, when the third case arrives, will have far more information about what the abstraction should look like than present me does. Let future me do the abstracting.
4. Before keeping the dead code in case we need it later, delete it.
Git remembers. That is the whole answer. Every single time I have left commented-out code or an unused function or a feature flag for a feature that shipped two years ago, I have regretted it. Not once has anyone come back and said thank goodness we kept that commented block, it really saved us. What actually happens is someone reads it three months later, gets confused about whether it is current, asks in slack, wastes an hour confirming it is not, and then nobody deletes it because deleting things feels risky.
The deletion is the gift. Trust the version control. If you really do need that code back in six months, it is one git log away. The cost of looking it up later is tiny. The cost of leaving it there to confuse every future reader is paid every single time someone opens that file.
The temptation, named
I want to be honest about why this is harder than it used to be. AI tools make writing code cheap, which means the temptation to over-engineer is higher than ever. The friction that used to protect you from your own worst instincts is gone.
It used to be that if you wanted to add an abstraction layer, you had to actually type out the interface, the implementation, the wiring, and the tests. The sheer effort of all that typing was a tiny circuit breaker that gave your better judgement a chance to step in. Three minutes into the boilerplate you would think wait, do I actually need this, and most of the time the answer was no, and you would back out.
That circuit breaker is gone. The model will produce the abstraction layer, the wiring, and a plausible test suite, all before your better judgement has even woken up. By the time the rational part of your brain shows up, the code is already on screen and it looks fine, and the path of least resistance is to accept it.
So the discipline has to live somewhere new. It used to live in the friction of typing. Now it has to live in your head, as an explicit question you ask before you accept any block of generated code. The question is small and you can ask it in five seconds. What is the smallest thing that solves this? Then ship that. Not the elegant thing, not the future-proof thing, not the thing that handles cases nobody has asked for. The smallest thing. The thing that earns its keep on day one.
Code is the liability
This is the line I keep coming back to, and the one I want to leave you with.
Code is a liability. The asset is what the code does.
The asset is the user who got their job done, the bill that got paid, the document that got sent, the post that got published. The asset is the outcome. The code is the cost you paid to make that outcome possible, and like any cost, less of it is better at the same level of outcome.
Optimise for the asset, not the liability. Delete more than you add. Search before you write. Default before you configure. Wait for three before you abstract. Ship the smallest thing that works, then go for lunch.
The slack channel will not clap. Production will get a little faster. That is the whole job.