My bottleneck as a programmer is not writing code.
I've been writing software for over twenty-five years and I'm not sure it ever was. The code itself — the actual typing, the syntax, the boilerplate — that was always the easy part. What took time was everything else. Understanding what a system actually does. Knowing what will break when you change something. Figuring out what the business actually needs, which is rarely what anyone wrote down.
AI tools are getting remarkably good at writing code. I use them, they're useful, and they're getting better fast. But watching the enthusiasm around them I keep thinking: we're optimising the wrong thing.
The clearest way I know to see this is to think about what the first weeks in a new codebase actually look like. Not a greenfield project — a real system, built by people who have left, full of decisions nobody documented because at the time they seemed obvious.
You are not slow because the code is hard to write. You are slow because you don't know what you're allowed to break. The system has years of accumulated decisions baked in — workarounds, implicit contracts, dependencies that exist for reasons nobody remembers. Your first task takes ten times longer than it should, and the extra time is almost entirely spent on understanding, not writing.
What I've learned to do in those situations is to allow myself to be slow. The approach depends on what I need to learn first. Sometimes I start bottom-up — a small contained task, a bug fix, a minor feature — and use it as a thread to pull. Find the entry point, understand the structure of that part of the codebase, learn what tools and frameworks are in play. Other times I start top-down — setting up or improving the CI/CD pipeline, mapping the deployment assumptions, understanding how the parts connect before touching any of them. That sounds like infrastructure work but it's really archaeology. Most of the time it ends up being both, switching between levels as the picture fills in.
Either way, my constraint is never the typing. It's the understanding of the system.
And patience. Just because something looks strange doesn't mean it is strange. There is accumulated intent in every codebase. Decisions that made sense in a context you don't have yet. Two engineers with different backgrounds — one thinking in object hierarchies, one in functions and immutability — will read the same codebase differently and reach for different rewrites. Neither necessarily wrong, just different. AI makes both of them faster. Without the judgement to know what should be preserved, faster is not obviously better.
This is not just an onboarding problem. It's the job. The experienced engineer's day is mostly this — holding a large, partially understood system in your head, reasoning about what will happen when you change something, maintaining the invariants that keep it from collapsing under its own complexity. Jason Heeris captured it in a comic that's made the rounds for a decade — a developer slowly builds up an enormous mental scaffold, someone taps them on the shoulder to mention a trivial email, and the whole thing collapses. Every experienced engineer has felt that. The code you write at the end of that process is almost incidental.
Which brings me back to the AI tools. They are genuinely useful. I reach for them regularly. But they are fast at the part that was never slow. Ask an AI to find your 1+N query, understand why the network call is blocking, tell you which of the twelve services will break if you change this interface — and you'll quickly find the limits. Not because the tools are bad, but because that kind of understanding requires context that lives outside the code. In conversations that happened in meetings. In decisions that were made and never written down. In the heads of people who left two years ago.
There is something else worth naming. The friction of writing code manually was never pure waste. Writing ten constructor parameters told you the class was doing too much. The awkwardness of wiring dependencies by hand told you the design needed rethinking. The ugliness was the system talking back. AI removes that signal along with the friction — and experienced engineers lose one of the ways they knew something was wrong. That deserves its own exploration, but it belongs in this picture.
AI is an amplifier. It makes good engineers more effective and it makes poor judgement faster. Which means the thing being amplified — system understanding, patience, knowing what to preserve — matters more than ever, not less.
Peter Naur argued something similar in 1985, in a paper most programmers haven't read but should. Programming, he wrote, is not primarily about producing programs — it is about building a theory of how problems are solved by program execution. The code is a residue of that understanding. Lose the understanding, and the code becomes archaeology. That was true before AI. It is more consequential now.
Software engineering, at its core, is a cognition problem. It always was. The next abstraction layer won't change that — but it will need to take it seriously in a way current tools don't.
Johan Aludden has been building software since the late nineties. He is currently thinking about what comes next.