The commit still has my name on it

For almost a year now, I have barely typed code in the way I used to.

I describe what I want. A clanker produces a patch. I read it. Most of the time, it is fine.

At first this felt disorienting. Then it felt normal. Then it started to worry me, because if the clanker can write the code, what exactly am I being paid for?

The answer took me longer to find than I would like to admit.

It is not that I write code. It probably never was, although I did love writing code and flexing my Neovim muscles.

The thing I do, the part that is still hard, is deciding which code I am willing to stand behind.

The clanker can generate the diff.

The commit still has my name on it.

That sounds like a small distinction.

It is not.

Writing got cheap. Judgement did not.

I used to treat writing code as the work.

Not intellectually, maybe. I knew design mattered. I knew tradeoffs mattered. But day to day, the thing that felt like work was sitting in my editor (neovim btw), building the function, changing the shape of it, swearing at a test when it fails in CI, finding the stupid edge case I should have seen earlier, then finally getting the thing into a state I could defend and ask for other people to review my code.

That friction did something useful.

Because writing code cost time, I was forced to choose carefully. I did not casually ask for five versions of the same module. I did not generate three abstractions and see which one felt nicest. The cost of producing code acted as a filter.

That filter is mostly gone now.

An agent will happily produce three implementations before I have finished deciding whether the first one belongs in the codebase at all.

That sounds like a productivity win, and sometimes it is. But it also means the old bottleneck has moved up a layer.

The hard part is no longer getting code to exist.

The hard part is deciding whether this particular code should exist, in this particular shape, in this particular codebase, with my name on the commit.

Ownership does not transfer

Owning a change means taking it from “we have a problem” to “we do not have to think about this anymore.”

That is not the same as writing the code.

Writing the code is one step. Sometimes it is not even the hardest one.

Owning the change means figuring out what the problem actually is. “Migrate from X to Y” is not a problem. It is a proposed solution. The problem is that X is slow, or expensive, or unreliable, or painful for a customer. Maybe migrating is right. Maybe it is not.

Owning the change means thinking about edge cases, including the ones you choose not to handle.

Networks fail. Queues back up. Retries retry the wrong thing. Timeouts hide the real issue. A user presses the button twice. A customer has data from 2019 that violates an assumption nobody remembers making.

Owning the change also means knowing how you will prove it worked.

Not just “the tests pass.”

What data changed? What did production do? Did the metric move? Did the errors get quieter? Did the customer stop complaining?

It means landing it, watching it, telling the people who need to know, and checking back later.

An agent can help with almost every piece of that. It can write the retry logic. It can generate the test. It can produce the migration. It can even suggest the rollout plan.

What it cannot do is own the arc.

It has no stake in the outcome. It will not remember the decision tomorrow. It will not get paged. It will not feel embarrassed when someone opens the file next year and screams WTF.

Ownership is the wrapper around all the steps.

That wrapper is still human.

Before I merge something, I often ask myself a stupid but useful question:

Would I show this to John Carmack and say, “Here is what I built. Here are the constraints. Here are the tradeoffs”?

The clanker never asks itself that.

Pride is not in its loss function.

It is in mine.

Taste is compressed pain

I have local rules that I enforce almost without thinking.

One file, one responsibility. Types go in type files. Hooks go with hooks. If a thing deserves a name, give it a name. If a thing deserves a file, make the file. Do not sneak it into the nearest convenient place because creating a file feels annoying.

Could I justify every one of these rules from first principles, every time?

Probably not. Not quickly.

But I know where they came from.

They came from codebases that rotted slowly. They came from files that became dumping grounds. They came from harmless exceptions that multiplied. They came from the day you open a module and realize nobody knows where anything belongs anymore.

That is what taste is.

Taste is not magic. It is not vibes. It is not being precious about code.

Taste is experience compressed into a fast judgement. It is the cache of mistakes you do not want to make again.

A model has something that looks like taste, but it is not the same thing.

It has the average of a lot of code.

The problem is that the average of a lot of code is not good code. Most code is mediocre. So left alone, generated code tends to drift toward mediocrity with excellent syntax.

My job, increasingly, is to pull it away from the mean.

Not because I enjoy being difficult.

Because I know what the mean looks like after many years in production.

Reviewing is the new writing

My day has changed shape.

I used to spend most of it writing code and some of it reviewing code. Now I spend most of it reviewing code that was generated for me.

Reviewing is harder than writing in a very specific way.

When you write a line yourself, you know roughly why it exists. You may be wrong, but the intent is still in your head.

When you read generated code, you have to reconstruct the intent from the artifact. You have to ask what problem it thought it was solving, compare that to the problem you actually have, and notice the mismatch.

That mismatch is often small.

Generated code is plausible. That is what makes it dangerous.

Bad human code often looks bad. It has a smell. It is awkward, inconsistent, obviously confused.

Clanker code is fluent. It has the shape of correctness. It uses the right names. It imports the right library. It writes the test you expected to see.

And sometimes it is still wrong.

Not dramatically wrong.

Worse.

Quietly wrong.

Wrong in the way that passes review when you are tired.

I have caught myself approving things because they looked clean.

That is the trap.

Fluency is not correctness. Confidence is not understanding. A green build is not a design review.

The author field lies. Blame does not.

The clanker wrote the line.

Git blame points at me.

That is the deal. And I think it is the right deal.

When the code breaks at 2am, nobody pages the model. When someone opens the file in a year and says “who thought this was a good idea?”, the answer is me.

That is the part that matters.

The agent produces a draft. Signing off on the draft is an act of judgement. You cannot outsource that judgement to something with no memory, no stake, and no consequences.

The blame is mine because the decision was mine.

What I would tell someone starting now

If you are early in your career and worried that clankers will make your skills obsolete, I think you are worried about the wrong skill.

Typing code was never the moat.

The moat is knowing what good looks like.

The moat is being able to take a vague problem, turn it into a real solution, ship it, verify it, and make it disappear from everyone’s list of things to worry about.

You still only get that the slow way.

You build things. You watch them break. You fix them. You remember the shape of the failure. You build a little differently next time.

The agents made the cheap part free.

They did not make the expensive part cheap.

They made it more visible.