AI can write code that looks finished
One of the most dangerous moments in AI-assisted development is not the obvious error.
It is the moment when the app runs, the screen looks polished, and you assume the code underneath must be sound.
I have learned to separate two questions:
- Can this code produce the screen or behavior I asked for?
- Does this code preserve the rules my app is supposed to follow?
The first question is a demo. The second is engineering.
AI is very good at producing a plausible first answer. It is not automatically good at preserving your product’s boundaries while changing one part of the system.
That is why I review AI-generated code before I trust it, even when the feature appears to work.
Start with the user contract, not the code
Before opening the changed files, write down what the feature is supposed to do in one sentence.
For example:
> A signed-in user can save a private note and see only their own saved notes after refreshing the page.
That sentence gives you more to review than a vague request such as “add notes.” It contains a user, an action, ownership, and a persistence expectation.
Then ask AI to show where each part of that contract is implemented:
- Where is the user identified?
- Where is ownership enforced?
- Where is the note saved?
- Where is the saved note loaded again?
- What happens when the user is signed out?
- What happens when the request fails?
If the answer cannot point to those boundaries clearly, the feature is not ready for trust.
The five checks I use before trusting a change
1. Check the data path
Follow the data from the input field to the database or storage layer and back to the screen.
Do not stop at the component that looks correct.
Ask:
- Is the input normalized or validated?
- Is the data shape the same at every layer?
- Is a missing value treated differently from an empty value?
- Is the saved record loaded from the real source, or only held in local screen state?
- Could an old field name silently produce an empty result?
AI can update a type, a form, and a query in three different ways. Each change can look reasonable in isolation while the path between them is broken.
I want to see the contract between layers, not just a collection of green-looking files.
2. Check who is allowed to do what
A button being hidden is not the same as an action being protected.
If an app has accounts, private data, paid features, admin actions, or shared workspaces, find the actual authorization check.
Ask:
- Does the server or database verify ownership?
- Can a user change an ID in a request and read somebody else’s record?
- Are create, read, update, and delete rules consistent?
- What happens when the session expires halfway through an action?
The beginner-friendly way to review this is to imagine two test accounts. If account A can change an identifier and see account B’s data, the feature is not secure just because the UI shows the right menu.
3. Check the failure paths
AI often spends most of its attention on the happy path: valid input, working network, existing record, expected response.
Review the opposite cases deliberately.
- What does the user see when the request times out?
- Is a failed save presented as a success?
- Can the user retry without creating duplicates?
- What happens when the response is empty?
- Does an error leave stale data on screen?
- Is a loading state cleared on every exit path?
A feature is not finished when it works once. It is finished when the user can understand what happened when it does not work.
4. Check the change surface
Ask AI to list every file, schema, route, dependency, permission, and environment variable it changed.
Then compare that list with the request.
This catches a common failure mode: a small feature quietly changes shared code used by unrelated screens.
I also look for:
- duplicated business rules,
- new dependencies that solve a problem the project already solved,
- broad refactors mixed into a narrow feature,
- renamed fields without a migration path,
- fallback behavior that hides a configuration error.
The more unrelated surface area a change touches, the more proof it needs. “The new screen works” is not proof that the old screens survived the change.
5. Check whether the code explains itself
Readable code is a QA tool. If you cannot tell what a function is allowed to change, you cannot review it confidently.
Look for clear names, small responsibilities, predictable error handling, and comments that explain decisions rather than narrate syntax.
When AI generates a large function, I ask it to explain:
- the inputs it expects,
- the side effects it performs,
- the errors it can return,
- the data it is allowed to read or write,
- the assumptions that would make it unsafe.
If that explanation reveals hidden assumptions, I fix the boundary before polishing the implementation.
A review prompt I would give AI
You can use this as a starting point:
> Review the changed code against this user contract: [write one sentence]. First map the data path from input to persistence to display. Then list every authorization check and identify any action protected only by the UI. Then inspect timeout, empty, invalid-input, duplicate-submit, and expired-session behavior. List every changed file, dependency, schema, route, environment variable, and shared component. Identify regressions this change could cause outside the requested feature. Do not rewrite the code yet. Report evidence by file and function, then give me the smallest safe correction for each issue.
The important instruction is “do not rewrite the code yet.” I want the review before the next burst of generated code. Otherwise AI may confidently patch an assumption that nobody has inspected.
What I would not use as proof
These are useful signals, but none is enough by itself:
- The app compiles.
- The page looks right.
- The happy-path demo passed.
- AI says it fixed the bug.
- There are no visible errors in the browser console.
- The code is long and detailed.
The proof has to connect the implementation to the user contract. It has to include the boundaries where data, permissions, failures, and unrelated features can leak.
My rule for beginners
Do not ask, “Did AI write good code?”
Ask, “What rule is this code responsible for preserving, and what evidence shows that it still preserves it?”
That question changes your role. You are no longer grading code by how impressive it looks. You are checking whether the system still behaves like the product you intended to build.
If you want a guided starting point, I made the AI App Builder Starter Prompts, and they are free. They help turn a blank prompt box into a more specific conversation about scope, architecture, implementation, and proof.
For the deeper, end-to-end path from idea through prompting, building, QA, deployment, and launch, the next step is AI App Builder From Zero.
The goal is not to stop using AI. It is to make AI’s speed answer to your standards.
You can also find me here:
Medium: https://medium.com/@marcusykim DEV.to: https://dev.to/marcusykim Website: https://marcusykim.com/blog/ X: https://x.com/marcusykim LinkedIn: https://www.linkedin.com/in/marcusykim/