Why I still write code as a product leader
I still ship production code, which people tell me is a poor use of a product leader's time. It means I can answer 'is that hard?' myself in ten minutes, instead of asking three people and waiting a day.
Writing production code as a product leader shortens the time between a question and an answer you can trust. Most of what makes product decisions slow is the relay between the person who has the question and the person who can answer it.
The claim is narrower than “leaders should code”, and worth being precise about.
Faster iteration loops
The loop that matters is question, answer, next question. Every step you can’t take yourself adds a queue.
“Would it feel better if the result streamed in instead of landing all at once?” is a five-minute question if you can change it and look. It’s a two-day question if it has to become a ticket, get prioritised, get built, and come back for you to react to, by which point you’ve lost the thread that made you ask. What you lose is momentum, plus the quiet way a slow loop trains you to ask fewer questions.
When you can close the loop yourself, you ask more of them and you ask sharper ones, because being curious got cheaper.
Feasibility you can check before it hits a roadmap
A roadmap is a set of promises, and the expensive mistake is promising something whose hard part you haven’t seen.
Plenty of features look like a week and turn out to be a quarter, and the difference is usually invisible from outside: the third-party API that rate-limits in a way that breaks the whole flow, the data that doesn’t exist in the shape the design assumes, the latency you only notice once real volume hits it. You find these by building a thin version and watching where it strains. Doing that yourself means feasibility is something you checked rather than something you were told, and the roadmap gets more honest because the person making promises has touched the part that would break them.
The best version of this happens live, in the room. A client or a non-technical stakeholder describes what they want, and because I can see roughly how it would be built, I can think through a couple of implementations on the spot, pick the leanest one that would actually test the idea, and put a rough timeline on it before the meeting ends. That turns a one-way feature request into a decision we make together. Here’s what it would take, here’s what you think it’s worth, is it worth doing? Having that exchange with the person who knows the value, while the cost is still a cheap estimate, beats carrying a vague ask back to a team and returning weeks later with a number that reopens the whole question. Value for effort is the call that matters most, and technical depth is what lets you make it in real time.
AI behaviour you can debug directly
This is the one that’s changed most, and it’s where a relay does the most damage.
When an LLM does something wrong, the bug is rarely in the code in the ordinary sense. The model invents a field that isn’t in the schema, or ignores half the context, or behaves perfectly in the demo and falls apart on the input a real user gives it. Diagnosing that means reading the actual prompt that went over the wire, the actual context window, the actual tokens that came back. It’s empirical work and it doesn’t survive translation.
Through a relay it goes like this. “It’s giving weird answers” becomes a ticket, becomes an engineer’s best guess at reproducing a vague complaint, becomes a fix for a problem that may not be the real one, comes back, still wrong. Each hop strips detail out of a problem made entirely of detail. Debugging it yourself collapses all of that into one person looking at a trace. On the VicRoads number-plate pricing work, the front end was an LLM and the engine behind it was deterministic, and the entire art was in where the model was allowed to reason and where it had to defer. You can’t specify that boundary from a distance. You find it by watching the model fail and moving the line.
Skipping the organisational telephone
Intent degrades at every handoff, the same way the playground game does. What you meant becomes what you wrote, becomes what they read, becomes what they built. Nobody involved is incompetent. Meaning just leaks at each crossing, and it leaks most where the nuance matters.
Code skips the relay, because the intent and the artefact are the same object. There’s no version of “what I meant” separate from “what shipped”. For decisions where the nuance is the entire point, like the pricing edge case, the model boundary, or the interaction that lives or dies on fifty milliseconds, removing the telephone decides whether the decision arrives intact.
Understand the layer beneath you
There’s a general principle under those four reasons. To lead a domain you have to understand, in detail, the layer directly beneath the one you operate at. Not the bottom of the stack, just the next layer down. That holds going up a hierarchy and going down a stack of abstractions.
Take the abstractions first. A product manager’s decisions get expressed in code, so code is the layer beneath you and you have to be able to read it. A programmer doesn’t need to know the binary, but they do need mechanical sympathy, meaning a feel for how their code compiles and runs against the machine, enough to write something performant rather than merely correct. Go one layer below the layer beneath you and it’s noise. Nobody needs the binary. The hierarchy works the same way. A CEO doesn’t need to know what each individual is doing, and does need to understand in detail what each department is doing. A department head tracking exactly how each person does their work is micromanaging, and one who can’t say which tasks are being done, by whom, and how they ladder up to the goal isn’t leading. One layer down, in detail. Anything below that is either noise or micromanagement.
That’s the argument that convinces me. It lets me step into a technical conversation with people far more technical than I am and hold my end of it. Engineers have a depth I don’t, and what they often lack is the business context and the strategy that decide which trade-off at their layer is the right one. Understanding their layer is what lets the conversation ladder up to mine. I’ve worked hard at being able to pivot mid-conversation between design, technology, strategy and the market side, because each of those rests on the one next to it.
I’m watching the same rule play out from the other side now, as non-technical people start shipping real code with a model’s help. They can describe the business behaviour they want, which gets them much further than anyone expected. What they’re missing is the layer beneath: branches and rebasing, linting, the tests that tell you something broke, the mental model of how it all fits together. So when something goes wrong they’re stuck, at the mercy of the model that wrote it. It’s remarkably powerful and it’s capped, because there’s a ceiling on the complexity they can handle until they learn the domain. The domain’s own language is exactly what unlocks the next level of performance from these models. The layer beneath still has to be understood, even when the machine is doing the typing.
The limits
This isn’t always the right use of a leader’s time, and pretending otherwise is its own failure.
The job is leverage, and your hands in the codebase are a narrow lever. Write the code when the decision quality genuinely depends on you having touched the thing: the risky feasibility question, the AI behaviour nobody else can characterise, the prototype that settles an argument faster than a meeting would. Don’t write it when an engineer would do it better and faster and the only thing your involvement adds is your name in the commit history. A leader head-down in a feature someone else should own has created a bottleneck with their own name on it. The skill is telling those apart, and the test I use is whether I’m the cheapest path to a trustworthy answer here.
Code is the shortest path between a question and an answer you can trust, for the narrow class of questions where the relay would have wrecked the answer on the way back. Use it there. Everywhere else your job is still to make other people’s loops faster rather than to live inside your own.