How to Vibe Code Without Degrading Your Codebase
Vibe coding becomes dangerous when generation speed is mistaken for verified progress. Keep the speed; change the control loop.
Vibe coding is productive because it reduces the distance between an idea and a running thing. Describe the behavior, watch an agent edit, try the result, and keep moving. The feedback feels immediate.
The risk is that visual or test success becomes the definition of done. The agent may have added unnecessary layers, weakened a failure path, invented a dependency, or built a path nobody on the team can explain. The product works in the demo while the codebase absorbs the search process.
Vibe coding needs a faster quality loop, not an exemption from one.
Rule 1: one session, one behavioral contract
Define the user-visible or system-visible outcome before generation. Include one success path, the important failure path, and the behavior that must not change. A narrow contract gives the agent a stopping condition and gives you a review standard.
Weak: “Add a better upload flow.”
Useful: “A signed-in user can upload one PNG under 5 MB, sees progress and a recoverable error, and cannot access another user's file.”
Rule 2: inspect before the agent edits
Ask the agent to find the current owner of the behavior, a recent similar change, the relevant tests, and the commands CI runs. Require a short plan naming the files it expects to change and why. This reduces duplicate implementations and invented architecture.
If the plan needs ten files for a small behavior, stop and rescope before generation makes the diff expensive to unwind.
Rule 3: build a vertical slice, then checkpoint
Let the agent connect the smallest real path from boundary to outcome. Run it. Check the diff. Commit or checkpoint only after the slice is understood. Then add failure behavior, accessibility, observability, and refinement in separate steps.
This keeps the agent's trajectory visible. If an approach is abandoned, revert its checkpoint instead of leaving remnants for a final cleanup that rarely happens.
Rule 4: make the fast path executable
Put format, types, focused tests, security checks, dependency policy, and generated-code quality rules in one fast command. Run it after each meaningful edit and before the agent declares completion. The full clean-environment suite still runs in CI.
When a check fails, ask the agent to repair the cause. Do not let it delete tests, broaden types, swallow errors, or disable a rule simply to restore green output.
Rule 5: test against the mistake, not the implementation
Generated tests often mirror generated assumptions. Write an assertion at the public boundary and prove it fails when the implementation is reverted. Add an adversarial case: invalid input, duplicate action, denied permission, timeout, partial failure, or concurrency.
For UI work, interact with the rendered flow at phone and desktop widths. Type into fields, use the keyboard, trigger errors, reload intermediate state, and confirm what a real user sees.
Rule 6: perform a minimum-patch review
After the behavior works, ask which changed lines are actually required. Remove unused helpers, speculative options, duplicated validation, narrative comments, compatibility branches without a caller, and drive-by refactors.
The TRIM research connects this residue to the agent's trajectory and reports meaningful reductions when that trajectory is minimized. You do not need the paper's algorithm to adopt the review question: what can be reverted without violating the contract?
Rule 7: finish with ownership, not output
The human owner should be able to answer:
- What invariant does this change introduce or preserve?
- Where is untrusted data accepted and validated?
- What happens on the worst credible failure?
- Which new dependency or API claim was independently verified?
- How would we detect, contain, and roll back a bad deployment?
If nobody can answer without reopening the entire chat, the code is not operationally owned yet.
The stop conditions
Pause generation when:
- The agent edits outside the planned ownership boundary.
- The diff grows faster than your ability to explain it.
- A fix weakens a test, type, permission, scanner, or error path.
- New packages or configuration appear without an explicit decision.
- The same failure returns after two attempted fixes and the agent is guessing.
At that point, reduce scope, restore the last known-good checkpoint, and diagnose. Continuing to generate usually compounds the trajectory instead of resolving it.
A deterministic guardrail for the loop
aislop can run beside the agent to catch repeatable residue before commit: swallowed exceptions, unsafe assertions, narrative comments, debug leftovers, dead code, unresolved stubs, generic naming, and complexity growth. It does not prove the feature is correct or understood.
npx aislop@latest scan --changesThat boundary is the value: let automation protect the repeatable floor while you spend attention on the contract, user experience, security, and system model.
Sources
Frequently asked questions
Can vibe coding produce production-quality software?
Yes, when the workflow supplies the controls that free-form generation lacks: clear acceptance criteria, small changes, repository context, tests, security and quality gates, human review, and operational ownership. The model is an accelerator, not the evidence.
What is a safe vibe-coding workflow?
Start from a clean baseline, define one observable contract, let the agent inspect before editing, build one vertical slice, run checks after each step, review the minimum patch, verify the real user path, and keep a human owner who can explain and operate the result.
How do I prevent AI slop while vibe coding?
Automate repeatable standards close to the agent, keep diffs small, remove abandoned edits, prohibit silent error handling and unsafe shortcuts, verify dependencies, and convert recurring review feedback into tests or deterministic rules.