Asher Cohen
Back to posts

The Cost of What I Don't Know

Generative AI makes the first iteration of software feel almost magical. But there is a cost that is easy to miss: the cost of the second iteration.

Generative AI makes the first iteration of software feel almost magical.

You describe what you want, get a plausible implementation in seconds, fix a few things, and suddenly you have something that works. The feedback loop is dramatically shorter. Prototypes that used to take hours or days can take minutes.

That part is real.

But there is a cost that is easy to miss: the cost of the second iteration.

The first iteration is not the whole job

The first version of AI-generated code often looks like an incredible productivity gain.

You ask for a feature. It generates the code. You run it. It works.

The temptation is to conclude that the implementation cost has collapsed.

It hasn't.

Some of the cost has simply moved.

The problem becomes apparent when the requirements change.

Now you need to modify the code. Something is subtly wrong. An edge case appears. A dependency behaves differently than expected. The architecture doesn't quite fit the next requirement.

And suddenly you're working with code you didn't really understand when it was created.

The second iteration is where the bill arrives.

The first implementation may have been cheap, but every subsequent change has to pay for the assumptions embedded in it.

The cost of what I don't know

There is a particular kind of technical debt that AI makes easier to accumulate:

the cost of what I don't know.

When I write code myself, I accumulate understanding while implementing it.

I know why a particular abstraction exists. I know which edge cases I considered. I know which parts are deliberately awkward because the underlying system is awkward.

The code may not be perfect, but I have a mental model of it.

With AI-generated code, it's possible to skip that process entirely.

The code exists without the corresponding understanding.

That can be perfectly fine for disposable prototypes.

It becomes expensive when the code survives.

The longer it survives, the more important that missing understanding becomes.

From bad to worse

This is where the second iteration can become particularly dangerous.

The first AI-generated implementation contains an assumption I don't notice.

The second request builds on that assumption.

The third request builds on the first two.

Each iteration makes the next one slightly harder because the system's actual behavior and my mental model of the system drift further apart.

Eventually, the AI is generating changes to code that neither of us fully understands.

This is how something that initially felt like acceleration can turn into deceleration.

Not because the AI suddenly became worse.

Because the complexity of the context increased faster than my understanding of it.

The best AI-generated code looks suspiciously familiar

I've found one approach that changes this completely:

I want AI to generate code that actually reflects what I would write.

Not merely code that works.

Not code that happens to pass the happy path.

Code that fits my conventions, abstractions, architecture, naming, error handling, and understanding of the problem.

That requirement sounds slower.

It isn't.

It is due diligence.

If I ask AI to produce code I would never have written myself, I have created another problem to solve: understanding someone else's solution before I can safely modify it.

If instead the generated code looks like something I could have written, reviewed, and maintained myself, the AI becomes an implementation multiplier rather than an abstraction generator.

The distinction matters.

I don't want AI to replace my understanding. I want it to accelerate the expression of my understanding.

Verification is part of the implementation

The other important realization is that generating the code is only one step.

The surrounding verification is not bureaucracy. It is part of the work.

For me, that means things like:

  • linting
  • type checking
  • tests
  • running the application
  • checking the actual behavior
  • inspecting the diff
  • making small, atomic commits
  • reviewing the generated code
  • verifying assumptions against the actual APIs and documentation
  • checking edge cases
  • understanding the parts that aren't obvious

These steps establish confidence.

Without them, generated code has essentially no confidence to ship.

It doesn't matter that it compiles.

It doesn't matter that the demo works.

It doesn't matter that the AI confidently explains why the implementation is correct.

Plausibility is not confidence.

Confidence comes from verification.

The uncomfortable trade-off

This creates an interesting paradox.

AI can make me dramatically faster at producing code I don't understand.

And that can be genuinely useful.

If I'm exploring an unfamiliar domain, AI can help me get from zero to something working incredibly quickly. I can use the result to learn, explore the problem space, and discover what I actually need.

But I'm much more uncomfortable using the same shortcut in an area I understand deeply.

Because now I can see the problems.

I notice the unnecessary abstraction.

I see the leaky boundary.

I recognize the incorrect assumption.

I know that the error handling is wrong.

I can see the future maintenance problem hiding inside a perfectly functional implementation.

And that's perhaps the strangest effect of generative AI:

I'm often happier shipping code I don't know much about than code I know well enough to recognize is bad.

That isn't necessarily an argument against AI.

It's an argument against confusing speed of generation with speed of engineering.

The real productivity metric

The useful question isn't:

«How quickly did AI produce the first version?»

It's:

«How cheaply can I understand, verify, modify, and safely ship the next version?»

The first iteration is where generative AI looks transformative.

The second iteration tells you whether it actually was.

If every subsequent change requires more archaeology, more prompting, more context reconstruction, and more manual verification, the initial speed was partly borrowed time.

If the generated code fits the architecture, passes the usual engineering checks, survives review, and remains easy to change, then the speed is real.

That's the standard I want to optimize for.

AI should make the first iteration faster without making the second iteration more expensive.

And when it does, that's when it starts feeling less like code generation and more like actual leverage.

#ai #software #engineering