Back to blog

Published

Reviewing code you could not have written

How senior engineers can review AI-generated code for both correctness and comprehension, spot hidden risks, and protect trust in their technical judgment at work.

November 1, 2026·9 min read

A pull request lands with a clean description, passing tests, and code that looks plausible at a glance. You recognize the framework, the names are reasonable, and the diff is larger than you would have written by hand. The author says an AI assistant drafted most of it.

You are still the person approving it.

That is the uncomfortable part of AI-generated code for experienced engineers. A junior developer can copy a bad answer from a chatbot. A senior engineer can also approve an implementation for a production system after verifying only the surface.

You may catch syntax errors, missing null checks, and an obvious query in a loop. But can you explain why a cache key includes those fields, what happens when a retry arrives after a timeout, or which database constraint makes an apparent race harmless? If the answer is no, the review is less reliable than it looks.

Generated code often fails in the boring parts

AI assistants are good at producing familiar shapes. A request for an endpoint gets a controller, validation, service method, database call, tests, and error handling. That shape gives reviewers a lot to skim.

The missed problems tend to sit between those pieces.

A generated authorization check may confirm that a user is signed in while failing to confirm that they own the record being changed. A helper may serialize an object correctly for a normal request while quietly dropping a field needed by a downstream worker. A retry wrapper may look cautious, yet repeat a non-idempotent payment request. A migration may add a column and an index, but lock a busy table at the wrong time.

None of these mistakes are exotic. They are failures to connect implementation details to the actual behavior of your system.

This is also why a green test suite can be falsely reassuring. Imagine a generated endpoint that treats status = active as enough to grant access, followed by tests that use the same assumption. The suite proves that the code follows that rule. It does not prove the rule matches the product or permission model.

GitClear reports aggregate trends in code changes during growing AI adoption, including more copy-pasted code and less reuse. Its analysis does not establish that AI caused those changes or tell you whether a particular pull request is poor. It is still a useful warning for reviewers: code that resembles the surrounding system may bypass abstractions carrying years of bug fixes and operational knowledge. GitClear's report is worth reading with your own repository in mind.

Correctness review and comprehension review are different jobs

Correctness review asks for evidence that the change does what the system requires. That includes the expected result, invariants, permissions, concurrency, failure paths, effects on existing callers, and tests at useful boundaries.

For a small and isolated change, the evidence may be easy to gather. A reviewer can trace the input and output, inspect the error behavior, and confirm that the tests cover the rule that matters.

Comprehension review asks something harder: could you explain this code, defend the tradeoffs, and safely change it six months from now?

Comprehension does not replace correctness work. It makes correctness work possible when the behavior depends on several layers. You need to understand why a transaction boundary is where it is, name the invariant the code preserves, and know which failure mode a retry handles and which one it does not. You should be able to remove a generated helper without guessing about hidden behavior inside it.

A reviewer does not need to memorize every line of a large diff. But approving code while unable to state the assumptions at the center of the change creates a gap between accountability and fluency. That gap gets expensive during an incident, when the original prompt, chat history, and model output do not answer the pager.

The senior engineer trap

Senior engineers are especially exposed because review work expands as hands-on coding time shrinks. You may be setting direction, helping with incidents, writing plans, interviewing, and unblocking several people. A detailed, polished AI-drafted pull request can make a shallow review feel sufficient.

The natural response is to review at a higher altitude. Read the description. Scan the diff. Ask for tests. Approve the change that seems aligned with the design.

That works until the code crosses a boundary you no longer inspect instinctively. Maybe you have not written a Postgres migration in a year. Maybe your old TypeScript instincts have gone dull after moving into a Go service. Maybe a framework feature looks familiar enough that you assume it behaves like the version you used before.

Skills can decay when AI writes much of the code: tracing data through unfamiliar code, reading an API contract closely, and spotting the small implementation detail that changes the outcome. Those are often the exact skills a review needs.

Repeatedly approving changes you cannot defend also changes how others assess your technical judgment. When a teammate has to untangle an approval during an incident, or finds that a supposedly reviewed path has no clear owner, future design discussions and review approvals carry less weight. Trust is built partly through being able to explain the decisions attached to your name.

AI assistance can still be useful. The approval bar should reflect the size and risk of the generated change. A large AI-drafted change deserves more than visual familiarity.

Read the diff as a set of promises

Before commenting on line-level details, write down what the change promises to do. Use plain language, not the ticket's title.

For example: “A workspace admin can export only records from their workspace, and the export should complete once even if the client retries.” That sentence gives you concrete things to trace.

Then find where each promise is enforced. Which query scopes the workspace? Where is the role checked? What makes repeated requests safe? What happens if export creation succeeds but enqueuing the worker fails?

If you can't point to the answer in the diff or a linked existing contract, ask the author. “Where do we prevent a retry from creating two exports?” is a stronger review comment than “Could we add more tests?” It asks for an explanation that can reveal a missing design decision.

This approach also catches code that does too much. Picture a requested export endpoint that arrives with a configuration switch, a generic retry layer, and fallback behavior for an error case the product has not defined. Every extra branch becomes behavior your team has to understand, test, and maintain.

A checklist for reviewing AI-generated code

Use this before approving a substantial generated diff. It is deliberately slower than a quick style pass.

  • Can I state the user-visible behavior and the main failure behavior in my own words?

  • Can I trace untrusted input from entry point to storage, network call, or rendered output?

  • Where are authentication, authorization, tenant boundaries, and sensitive fields enforced?

  • What invariant must remain true after this change? For example, one invoice can't be paid twice, or an archived project can't receive new tasks.

  • What happens on retry, timeout, partial failure, duplicate event, and concurrent request?

  • Do the tests exercise the rule, or do they merely repeat the structure of the implementation?

  • Did the change bypass an existing helper, domain service, query scope, or error convention? If yes, why?

  • Is every new abstraction needed for the requested behavior? Can any generated layer be removed without losing a real requirement?

  • Could I modify this code during an incident without reopening the AI conversation that produced it?

An unresolved answer does not mean the pull request must be rejected. It means the review has found work that should happen before approval: an explanation, a test at the right boundary, a smaller diff, or a design discussion.

Ask the author to explain the risky line

“AI wrote it” should never be the final answer to a review question.

The author may have used AI as a drafting tool, but they own the patch. Ask them to explain the non-obvious parts in the pull request: why this lock is needed, why this query is safe, why this fallback exists, why this library option matches the system's delivery semantics.

An explanation can expose a missing assumption, reveal an approach neither reviewer nor author would have chosen by hand, or leave useful context for the next person who changes the code. It can also confirm that a strange-looking section is correct for a reason the diff did not make obvious.

If an author can't explain a critical section, reduce the scope. Pair on it. Replace the generated block with a smaller implementation. Add a focused experiment. The answer depends on the risk of the code, not on a rule that every line must be typed manually.

Keep a small amount of code fluency on purpose

Review quality depends on the ability to simulate code in your head. That ability fades when most of your contact with a language becomes reading polished generated output.

The fix is not another large course or a weekend of interview questions. Write small pieces yourself often enough that the common operations in your stack still feel legible. Implement a parser edge case. Trace a transaction. Rebuild a small API boundary without autocomplete. Read a bug fix and predict the failing test before opening it.

Unrot is built for that kind of maintenance work. The tracks are short daily exercises for keeping a stack warm, especially if your job has moved you away from the code you still need to review. Five minutes will not make you an expert in a new distributed system. It can keep familiar failure modes from becoming invisible.

Approval is a technical decision

A code review approval says more than “the diff looks reasonable.” In practice, it says the team can operate this change after it merges.

AI can make implementation faster. It can also make a diff appear more complete than the understanding behind it. The useful habit is to notice when you are recognizing code rather than comprehending it, then slow down at the boundary where the system can hurt users or the business.

If you want a quick way to rebuild the fluency that makes those reviews safer, try Unrot. Keep the practice small, regular, and close to the code you are accountable for merging.

Keep reading

Reading about it is not the same as doing it.

Start a free session