AI can get you to a convincing first version surprisingly fast.
That changes where the risk sits.
The problem is no longer only getting something to work. It is noticing when “working” is hiding a system you can no longer explain.
As a product designer, I’ve spent the last few months building a side project: Simple Screen Recorder, a local-first Chrome extension for browser walkthroughs and product demos. Recently, while building a camera overlay feature, I ran into a trap that I see teams falling into everywhere.
1. The First Version Looked "Close Enough"
The camera overlay feature seemed simple on paper: let users record their webcam in a circular frame placed in the corner of their browser window while capturing a tab.
I prompted my AI coding assistant. Within minutes, the UI appeared. The camera feed rendered. Drag-and-drop worked. I could see myself in a circular bubble in the corner.
It worked just well enough to make the worst next step feel entirely reasonable: keep fixing the edge cases as they appear.
But when I started recording, the edge cases multiplied:
- Closing the camera overlay during a recording left the screen layout distorted.
- Changing recording quality settings occasionally reset the camera stream state.
- The preview in the browser looked perfect, but the exported MP4 file sometimes had the webcam out of sync.
Each time I noticed an issue, I sent a new prompt: "Fix the layout distortion when the camera is closed." Or "Ensure the export timing matches the preview."
The model solved each localized task. But with every fix, the code grew more complex. Event listeners were patched on top of state variables, which were patched on top of DOM manipulation hacks. We were resolving local symptoms while introducing system-level fragility.
For a recording tool, visual correctness is not enough.
A user needs to know whether the camera is active, what will appear in the final export, and what happens when they change something halfway through a recording.
Otherwise, the feature may work technically — but still feel unsafe to use.
2. The Trap Was Not Bad Code. It Was Local Optimization.
We often blame LLMs for "hallucinating" or writing messy code when a codebase gets tangled. But the model wasn't failing randomly. It was doing exactly what I asked: solving the micro-problem of the moment, inside a system neither of us had fully mapped.
In AI-assisted building, the dangerous phase is not when code fails to compile. It’s when it almost works.
At that point, each local improvement made the system harder to reason about. The feature looked better on the surface. But the cost of changing it safely kept rising.
3. I Stopped Patching and Asked for a Map
When the cost of explaining the current broken implementation to the AI became higher than the cost of rebuilding it, I stopped.
Before changing the code again, I asked the model to help me map the feature:
- What states existed;
- Which component owned each one;
- How the camera stream started and stopped;
- What preview and export shared;
- What had to remain true when the overlay was removed, settings changed, or recording ended.
Writing a specification is often viewed as a slow, pre-AI development process. But in the era of AI, a specification is the ultimate lever. Without it, you are feeding the model ambiguity. And when ambiguity is expensive, the code it produces will be fragile.
4. The Rollback Was the Productive Move
Once I had a clearer map of the state flow, I rolled the feature back. I deleted the nested event listeners and state patches, returning the code to the last clean, predictable state I could reason about.
I rolled back to the last version I could explain.
A rollback isn't a regression. It’s a removal of uncertainty. If you cannot explain why a piece of code works, you cannot safely prompt an AI to modify it.
The rebuild took less time than I expected. More importantly, the result was easier to explain, easier to test, and less fragile when I changed adjacent parts of the recording flow.
Pseudo-State Lifecycle Flow:
Overlay mounted
→ request camera stream
→ attach stream to preview
→ attach stream to recording pipeline
→ recording ends or overlay closes
→ stop tracks
→ clear references
→ restore previous layout state
5. Three Rules I Now Use When Building with AI
This experience forced me to shift how I interact with code generation. If you use AI assistants, these three heuristics will save you days of debug cycles:
Rule 1: Describe what must not break, not just what should happen
AI models are highly optimistic—they build for the happy path. When describing a feature, define the boundaries:
- “Closing the camera overlay must restore the prior page grid layout exactly.”
- “Changing the mic input setting must not reset the webcam stream state.”
Rule 2: Define the invariant before asking for the fix
When you spot a bug, don’t just paste the error. Explain the rule that was violated:
- Weak prompt: "The camera export timing is off. Fix it."
- Stronger prompt: "Preview and export must derive timing from the same source of truth. Inspect the current timing flow, identify where they diverge, and propose the smallest change that preserves this invariant."
Rule 3: Use stronger reasoning where ambiguity is expensive
Use your strongest model for system mapping, architecture decisions, failure analysis, and difficult debugging. Use faster or cheaper models only after the task is bounded and the acceptance criteria are explicit.
6. The Product Lesson: Users Only See "Trust"
An early tester once described re-recording the same clip repeatedly in another tool because they could not tell where its zoom would begin or end until after export.
That changed my definition of “done.”
A feature can technically work and still be too opaque for people to trust. If a setting is active but its status is invisible, the user experiences it as randomness.
AI can generate a functional feature in minutes.
It cannot decide which uncertainty is acceptable for the person using it.
That still requires product judgment: what must remain stable, what users need to see, and where the system is allowed to fail.
Product quality doesn’t come from the volume of code you generate. It comes from the constraints you design.