The pull request looked good at first. The code compiled, the visible tests passed, and the diff was short enough to review without losing the plot.
Then my senior engineer asked a simple question: why does this branch exist?
I had an answer for the surface of the code. I could describe what the function did. I could point to the test that turned green. But I could not explain the design decision behind the branch, the failure mode it protected against, or what would happen if the input arrived in a slightly different shape.
The uncomfortable truth was that AI had helped me produce code I could not defend.
That moment changed how I think about AI-assisted programming. The problem is not that a model can write code faster than I can type it. The problem is that a plausible implementation can arrive before understanding does.
The Code Passed. I Still Failed the Review.
The feature was small: accept a request, normalize a few fields, call an existing service, and return a response. I asked an AI assistant to suggest an implementation, adapted the result to fit the repository, and added tests for the expected path.
Nothing about the pull request looked reckless. That was part of the problem.
The review did not fail because of a syntax error. It failed because I had treated a working draft as a finished explanation. I had not checked whether the normalization rules matched the rest of the system. I had not asked why one error was being swallowed while another was being returned. I had not considered whether the new branch made an existing contract ambiguous.
The senior engineer was not asking me to memorize every line. They were asking whether I understood what I was asking the system to promise.
Plausible Code Is Not Understood Code
AI-generated code is often persuasive for the same reason a polished answer is persuasive: it arrives with the shape of confidence already built in.
Names look reasonable. Functions are split into tidy pieces. Comments explain the obvious. The code follows familiar patterns, which makes it easy to mistake familiarity for correctness.
But software is mostly made of conditions. It has contracts between services, assumptions about data, rules about time, and behaviors that only appear when something goes wrong. A generated function can be locally elegant and still be wrong for the system around it.
That distinction is easy to miss when the first draft appears in seconds. Before AI-assisted workflows, confusion created friction. You had to search documentation, inspect similar code, or ask someone to walk through the system. Now the tool can carry you across a gap in your knowledge without making the gap obvious.
Speed is useful. Invisible uncertainty is not.
What the Review Actually Exposed
The review exposed three gaps in my process.
First, I had described the task too narrowly. I asked for an implementation before I had written down the behavior the system needed to preserve.
Second, I had tested the happy path before testing the boundaries. The code worked when every field was present and valid. I had not checked missing values, repeated requests, partial failures, or a response that was technically valid but semantically incomplete.
Third, I had outsourced the hardest part of the work: choosing what the code should mean.
An AI assistant can offer a pattern. It cannot take responsibility for the contract between that pattern and a real product. That responsibility stays with the engineer who opens the pull request.
AI Is Good at Compression. Ownership Is Different.
One reason AI coding tools feel so powerful is that they compress several steps into one interaction. They can summarize a codebase, draft a function, suggest tests, and explain an error without making you move between as many tabs.
Compression is valuable when you already know what you are looking for. It is risky when it hides the steps you still need to learn.
There is a difference between reducing repetition and removing contact with the underlying idea. If I use AI to generate a test fixture, I can still inspect the fixture and ask what behavior it represents. If I use AI to replace a module I do not understand, I may only be moving uncertainty to a later, more expensive moment.
The goal is not to preserve every manual step. The goal is to keep the reasoning visible enough that another person, including future me, can continue the work safely.
Focused Tools Teach the Same Lesson
I notice the same pattern in creative software. A focused tool can make one decision easier without pretending to replace the creator's judgment.
For example, a best automatic bpm detector can help identify the tempo of a track before someone edits, remixes, or arranges it. An audio to midi converter ai can turn an audio idea into a more editable representation.
Those tools reduce the cost of getting to a useful starting point. They do not decide whether the tempo fits the arrangement or whether the converted notes capture the musical idea accurately enough for the next step.
That is the healthier model for AI in general: narrow assistance around a decision, followed by a person who understands what changed and why it matters.
Code generation should work the same way. Let the tool shorten the distance to a draft. Then inspect the draft as evidence, not as authority.
A Workflow for Defending AI-Assisted Code
After that review, I changed the order of my work. The following process takes a little longer at the beginning and saves more time once the code reaches review.
State the contract first. Write down inputs, outputs, invariants, and failure behavior before asking for an implementation.
Ask for alternatives. A single answer encourages acceptance. Two or three approaches make tradeoffs visible.
Make the tool explain its assumptions. Ask what it inferred from the prompt and what information would change the design.
Test the edges. Probe missing data, duplicate requests, partial failures, unexpected ordering, and invalid state transitions.
Change the result yourself. Make a small modification without asking for a full rewrite. This exposes whether the structure is actually understood.
Write the review note. Explain why the chosen approach fits the current system and what would make it worth revisiting.
Reviews Should Test Understanding
A good review is not a ceremony performed after the real work. It is one of the places where the real work becomes visible.
For AI-assisted code, reviewers may need to ask questions that are less about style and more about ownership:
What assumption is this implementation making about the input?
Which behavior is required by the product, and which behavior is just a convenient default?
What alternative did we consider, and why did we reject it?
What happens when the dependency is slow, unavailable, or returns an unexpected success?
Could the next engineer change this safely without reconstructing the entire prompt?
What AI Should Not Decide
There are decisions I am happy to delegate when the cost of being wrong is low: a first-pass test stub, a repetitive mapping, a list of possible names, or a rough explanation of an unfamiliar API.
There are other decisions that need a human owner because they shape the system's future:
Whether a data boundary is safe to cross.
Which user behavior the product should encourage.
What level of complexity the team can maintain.
Whether a workaround should become a permanent contract.
Which risks are acceptable for the people who will depend on the system.
The Engineer's Job Moved Upstream
AI has not removed the need for engineering judgment. It has moved more of that judgment earlier in the process.
The important questions now appear before the code is generated: What are we actually trying to preserve? Which constraints matter? What would failure look like? What must remain understandable to someone who did not write the prompt?
This is why prompt quality matters, but not in the shallow sense of finding magical wording. A useful prompt is often a compact design note. It includes the context, the boundaries, the existing contract, the unacceptable behaviors, and the way the result will be checked.
The best AI-assisted developers are not necessarily the people who know how to produce the longest answer. They are the people who can give the tool a meaningful problem and recognize when the answer is only plausible.
The Code We Can Defend
I still use AI to write code. I use it to explore unfamiliar APIs, generate alternatives, draft repetitive pieces, and turn a rough idea into something concrete enough to inspect.
The difference is that I no longer treat the first working result as proof that I understand the problem.
Before I open a pull request, I want to be able to explain the behavior in plain language. I want to name the assumptions. I want to say which edge cases I tested and which ones remain open. I want to know what I would change if the product requirement moved one inch.
That is what it means to defend the code. Not to claim that it is perfect, and not to pretend I wrote every character by hand. It means I understand the decision well enough to own its consequences.
AI can write code I could not have written as quickly. It should not write code I cannot explain.
Top comments (0)