Why Being the Programmer in Charge Still Matters

AI keeps getting better at writing code.

That’s good news.

It can inspect a problem, trace through existing code, propose a solution, write the implementation, run tests, find mistakes, and come back with a better version faster than most of us could do the same work by hand.

But every time it gets better, there’s a temptation hiding inside that improvement.

We start trusting the view.

The code looks good. The feature works. The tests pass. The documentation matches. The AI can explain exactly what it changed and why.

Everything looks right.

But sometimes the programmer still has to go back to being the programmer.

Not the person typing the code.

The person in charge of what the code is becoming.

Coding was never the whole job

There’s a lot of discussion about whether AI is eventually going to replace programmers.

I think that discussion often starts with the wrong definition of a programmer.

If programming means typing functions, remembering APIs, tracking down syntax errors, and turning a specification into executable code, then AI is already taking over an enormous amount of that work.

Good.

I’m happy to let it.

I’ve said before that I think of AI as something like a junior programmer who knows the entire internet and can type 3,000 words a minute.

I can’t compete with that.

I don’t need to.

But writing code and being responsible for a software system aren’t the same job.

Somebody still has to decide:

  • What are we actually building?
  • Has the product changed enough that the architecture needs to change with it?
  • Which part of the system owns a particular responsibility?
  • Are two different pieces of code doing what should really be one job?
  • Are we fixing the cause of a problem or just the place where we happened to notice it?
  • Are repeated little failures trying to tell us something bigger?
  • Is this code worth preserving?
  • Is this code worth replacing?
  • And sometimes, should we stop working toward release until we understand what’s going on?

Those aren’t syntax questions.

They’re programming questions.

Locally right can still be globally wrong

This becomes more important as an AI-assisted project gets larger.

An AI can do very good work inside the part of the system it’s examining.

That’s not the problem.

The problem is that a sufficiently large application eventually becomes bigger than the practical view of any one assignment.

Suppose the AI is working on one feature and finds that it needs a particular operation.

It looks through the nearby code.

It understands the problem.

It writes a helper that does exactly what’s needed.

The helper works.

Months later, another part of the application needs essentially the same operation. Another session looks at that area of the program, sees the need, and writes another perfectly reasonable implementation.

Now we have two pieces of code doing the same logical job.

Maybe they’re identical today.

They probably won’t stay that way.

Sooner or later, one gets improved. One gets a bug fix. One gains a special case. One persists something slightly differently.

Then a problem appears.

We find the problem and fix it.

Except we only fix one version of it.

That’s how you end up saying, “I thought we already fixed this.”

You did.

You fixed the one you were looking at.

The larger problem was that the system had more than one place claiming responsibility for the same job.

Scale changes what the programmer has to watch

There’s an important distinction here.

This isn’t necessarily bad code.

It’s not necessarily bad AI.

It can be the natural result of good local decisions accumulating inside a system that has become too large for each working session to see as a whole.

The AI is asked to solve a problem.

It solves the problem.

That’s what we asked it to do.

But the programmer in charge has to watch for something above that level.

We have to ask whether the same problem is being solved somewhere else.

We have to recognize when a feature that started as a small capability has grown into a major part of the product.

We have to recognize when the frequency of little glitches is becoming more important than any individual glitch.

One bug may say:

Fix me.

The fifth variation of the same kind of bug may be saying:

Stop fixing me one at a time and find out why I’m allowed to keep happening.

That’s a very different assignment.

Sometimes you have to stop the train

One of the hardest decisions in software development is stopping something that appears to be moving forward.

The product is close.

The next fix looks small.

The AI knows what it wants to change.

The tests will probably be green again shortly.

Everything encourages you to keep moving.

But somebody has to be the conductor.

And sometimes it’s the conductor’s job to stop the train.

Not because the train can’t move.

Because you’re no longer convinced it should keep moving until you know something about the track underneath it.

That can mean calling a halt to feature development.

It can mean freezing the current state long enough to study it.

It can mean deciding that another patch is exactly the wrong thing to do.

It can mean asking questions that have nothing to do with the bug currently sitting on the screen.

That’s not lost development time.

It may be the cheapest development time you ever spend.

A structural problem discovered before release is your problem.

The same structural problem discovered after thousands of customers have built valuable data on top of it becomes everybody’s problem.

I know which one I’d rather fix.

One job should have one owner

One rule I’ve become increasingly strict about is simple:

One logical job should have one semantic implementation.

There can be hundreds of callers.

There can be many workflows that need the operation.

There can be buttons, background processes, imports, exports, publishing paths, repair paths, and automated processes that all need it.

That’s fine.

But they should all know who owns the job.

Think of it like people in an organization.

If one person is responsible for issuing purchase orders, everybody who needs a purchase order goes to that person.

You don’t solve an urgent need in Accounting by quietly creating a second purchasing department behind a filing cabinet.

Software should work the same way.

When there is one semantic authority for an operation, we know where to fix it.

We know where to test it.

We know where its rules live.

We know that every caller gets the same behavior.

And, importantly for AI-assisted development, we can tell the AI that this is an architectural rule rather than hoping every future session independently rediscovers it.

Sometimes one AI isn’t enough either

There’s another interesting consequence when a project gets large enough.

You can’t necessarily solve the problem by saying:

Have the AI audit the architecture.

That sounds like a big-picture assignment.

It may still be too big.

A large repository can contain too many different kinds of questions to expect one agent to examine all of them with equal depth at the same time.

So the answer may be to divide the analysis deliberately.

Give one fresh agent a clean copy of the repository and ask it to examine one concern.

Give another agent its own clean copy and a different concern.

Have one look for multiple implementations of the same logical operations.

Have another look at module responsibilities and places where unrelated concerns have accumulated together.

Have another trace how data mutations move through the system.

Have another examine lifecycle or persistence assumptions.

Don’t ask all of them, “Is this code good?”

Ask each one a smaller, sharper question.

That’s another place where being the programmer in charge matters.

The programmer decides what questions need answering.

The programmer decides where to divide the problem.

The AI agents can do remarkably deep technical work inside those assignments.

Then somebody has to take their findings, compare them, resolve the contradictions, and decide what the architecture should actually become.

Sometimes scale doesn’t mean you need a smarter AI.

It means you need to ask smaller, better-defined questions of several very smart AIs.

Don’t throw away the good parts

An architectural review doesn’t automatically mean a rewrite.

In fact, that can be another mistake.

A large system that has developed some architectural problems can still contain an enormous amount of excellent, proven code.

The goal isn’t to declare everything contaminated and start over.

The goal is to determine what owns what.

If a component already does its job well, keep it.

If a piece of code is proven and belongs in the new architecture, use it.

If three implementations are doing almost the same thing, figure out which one should become authoritative and bring the callers to it.

A better architecture isn’t necessarily one with newer code.

It’s one where the responsibilities make sense.

That judgment is another thing the programmer has to own.

The better AI gets, the more important this becomes

There’s a strange inversion happening in software development.

As AI makes implementation cheaper and faster, bad architectural decisions can also become cheaper and faster to implement.

Years ago, a questionable idea had one accidental safety mechanism: somebody still had to spend a lot of time writing all the code.

Today, an AI can turn a questionable assumption into a substantial amount of polished, documented, tested software before lunch.

That doesn’t make AI dangerous.

It makes judgment valuable.

The important questions move upward.

Not:

Can you write this function?

But:

Should this function exist?

Not:

Can you fix this code?

But:

Is this the place where the problem should be fixed?

Not:

Does this feature work?

But:

Has this feature become important enough that our original architecture no longer describes the product we’re building?

Not:

Can one AI understand this repository?

But:

How should we divide the questions so each AI can investigate them deeply enough to give us useful answers?

Those are the jobs that remain.

Let the AI be the better coder

I have no problem with AI being better at writing code than I am.

I hope it gets dramatically better.

I hope the agents get better.

I hope they can inspect more, reason longer, make fewer mistakes, and carry even more of the implementation work.

That doesn’t threaten the part of programming I think matters most.

It makes that part clearer.

Somebody still has to understand what the product is becoming.

Somebody still has to notice when all the locally correct pieces no longer add up to a globally coherent system.

Somebody still has to decide which questions need to be asked.

Somebody still has to decide when the answers mean we should keep going.

And sometimes somebody has to pull the whistle and stop the train.

The AI can be the better coder.

You still have to be the programmer in charge.

Related Field Note

For the PageSnip experience that brought this lesson into focus for me, see Sometimes You Have to Stop the Train.