When not to vibe code is a software engineering decision that separates low-consequence experimentation from work that requires verified behavior, in the context of building software with AI. The decision exists because generated code can look convincing while hiding security flaws, data loss, false assumptions, and maintenance traps. People searching for vibe coding risks, AI-generated code safety, or whether vibe coding is suitable for production code are asking the same practical question: if the model is wrong here, can a person detect the error before anyone is harmed?
What vibe coding actually is
Vibe coding is a way of making software by describing a desired result to an AI system, accepting much of the generated code, and steering through visible outcomes instead of understanding every implementation detail. Its defining feature is delegated judgment, not simply AI use.
An AI coding tool predicts code that fits the prompt, the surrounding files, and patterns learned during training. It does not inspect the future, prove that the program is correct, or automatically know every rule of the business. A person may ask for a login page, run the result, notice that the button works, and ask for a prettier layout. The interaction feels more like directing than programming.
The page loads, the button responds, and the demonstration follows the expected path.
The system also handles invalid input, failed dependencies, hostile requests, retries, permissions, and recovery without breaking its promises.
The gap between those panels explains both the appeal and the danger. Visible success gives fast feedback. Verified behavior requires examining cases that a demonstration may never show. Better instructions can improve an answer, but no wording turns a probabilistic model into a proof system.
Vibe coding is most defensible when the result is disposable, the consequences are small, and failure is obvious. A private script that renames copies of test files may fit. A script that renames the only copies of family photos does not. The code can be identical in complexity while the decision changes because the assets and recovery options changed.
How the risk test works
The risk test works by estimating the harm a defect could cause, how easily the defect would be detected, and how completely the system could be restored. Vibe coding becomes a poor choice as consequences rise, detection weakens, or recovery becomes uncertain.
Start with a defect, such as a permission check that trusts a user ID supplied by the browser. Exposure asks who can reach that code. Harm asks what an attacker gains. Detection asks whether logs or tests reveal the attempt. Recovery asks whether stolen information can be made secret again. A password can be reset. A disclosed medical record cannot be undisclosed.
A useful classroom model is to rate four factors on a small scale and multiply them. Let impact, exposure, and uncertainty range from 1 to 5. Let recoverability also range from 1 to 5, where 1 means easy recovery and 5 means effectively irreversible. The score is not an industry standard. It is a transparent thinking tool.
A private throwaway mockup rated 1, 1, 2, 1 scores 2. A public payment handler rated 5, 5, 3, 4 scores 300.
The arithmetic does not certify safety. It makes assumptions discussable. If two people disagree about the score, they can identify the disagreement: perhaps one assumes that a backup exists while the other knows it has never been restored. That conversation is more useful than calling a project “simple.”
How vibe coding works under the surface
Vibe coding works through a loop in which a person supplies a goal and context, a model predicts a code change, tools run or display it, and the person reacts. Each loop can improve visible behavior while still preserving an incorrect hidden assumption.
The prompt names a feature, such as “add password reset,” but may omit rate limits, token expiry, audit records, and the required identity check.
The model selects patterns that resemble relevant code. Plausibility means the tokens fit learned patterns. It does not mean the design matches the local threat model.
The developer runs a happy path. If the reset email arrives, the feature appears complete even if one token can be reused forever.
An error message or failed build causes another prompt. Hidden faults survive because they never become feedback.
This is why context matters. The model sees only what the tool sends: selected files, prompt text, tool output, and perhaps repository instructions. It may not see the data retention policy, an agreement with a customer, or a production setting stored elsewhere. The ideas behind choosing and structuring AI context help reduce missing information, but a larger context window still cannot supply facts nobody recorded.
Passing output is evidence about the path that ran. It is not evidence about every path that could run, every input an attacker could send, or every failure a dependency could return.
The loop also creates automation bias. Once a polished diff appears, the easiest action is acceptance. The generated names and comments make the code feel deliberate, which can discourage the slow work of tracing data and checking invariants. A reviewer must reverse that pressure by asking what evidence would make the implementation false.
Vibe coding versus AI-assisted engineering
Vibe coding delegates understanding to the model, while AI-assisted engineering uses the model as a fast contributor inside a process controlled by people. The same generated function can belong to either approach, depending on specification, review, testing, and ownership.
Imagine an AI tool generates a function that calculates a delivery fee. In a vibe-coded workflow, the team tries two addresses, sees plausible totals, and ships. In an engineering workflow, the team states the fee rules, writes boundary examples, checks currency rounding, reviews the implementation, and monitors production discrepancies. AI wrote both functions. Only one process established why the function should be trusted.
| Decision | Vibe-coded approach | AI-assisted engineering approach |
|---|---|---|
| Requirement | Describe the desired screen or behavior | State behavior, constraints, and unacceptable outcomes |
| Review | Scan until the change looks reasonable | Trace inputs, state changes, permissions, and failure paths |
| Testing | Try the common path | Derive tests from rules and known risks |
| Ownership | Ask the model to repair surprises | Keep a person accountable for the design and operation |
The dividing line is not how many lines the model produced. It is whether the people shipping the change can explain the system’s promises and present evidence that the code keeps them. Careful review methods for AI-generated changes turn unexplained output into something a team can challenge, test, and own.
High-consequence systems require evidence before speed
High-consequence systems should not be vibe coded because a defect can affect safety, liberty, access to care, major financial decisions, or essential services. In these settings, an attractive demonstration is weak evidence compared with traceable requirements, independent review, and controlled testing.
Consider software that calculates a medication dose. The arithmetic might be short, yet the full problem includes units, weight ranges, decimal entry, drug concentration, duplicate orders, allergies, overrides, and the behavior of connected systems. A result can look reasonable while being wrong by a factor caused by a unit mismatch. The main danger is not complex syntax. It is an incomplete model of the real process.
A school uses a generated script to rank applications for a limited program. The script silently treats a missing score as zero. The code runs without an error, but students with incomplete imported records move down the list. The defect is a policy decision disguised as a default value.
Similar boundaries appear in hiring, insurance, public benefits, legal filing, industrial control, and accessibility systems. The developer needs domain knowledge and a named approval process. Some fields also impose recordkeeping, testing, privacy, or validation duties. A model cannot decide which duties apply merely by recognizing the type of code.
AI can still help in high-consequence work. It can propose tests, explain an unfamiliar module, draft documentation, or generate a prototype against artificial data. The output remains a candidate contribution. Qualified people must verify the requirement and the implementation through the process appropriate to that field.
Security and identity code fail outside the happy path
Security and identity code should not be vibe coded because its job is to resist inputs and sequences chosen by an adversary. A normal demonstration shows that an authorized person can enter; it rarely proves that every unauthorized person stays out.
A common access-control error checks whether a request contains a valid login session but fails to check whether that user owns the requested record. If the route is /invoice/4821, changing the number might reveal another customer’s invoice. The page renders correctly for the developer, the database query succeeds, and no exception signals the breach.
Can a signed-in customer download an invoice?
Can this customer download only invoices that policy allows this customer to see, through every route and identifier form?
Authentication, authorization, session handling, cryptography, secret storage, file upload, and input parsing all have this adversarial shape. Small mistakes compose. A safe upload check based only on a filename extension can be bypassed. A reset token stored in logs can defeat an otherwise sound password flow. A cryptographic function used with the wrong mode can compile and still expose information.
The right response is not to ban code generation. It is to use reviewed libraries, define the threat model, test authorization boundaries, scan dependencies and secrets, and obtain specialist review when the potential harm justifies it. Generated security code begins the investigation. It does not end it.
Data, money, and irreversible operations need controlled failure
Code that changes valuable records should not be vibe coded unless deletion, duplication, partial completion, and retries are explicitly controlled. Databases and payment systems preserve consequences after a process ends, so a brief mistake can outlive the code that caused it.
Suppose a checkout service charges a card and then writes an order record. The charge succeeds, the database write times out, and the browser retries. A naive implementation charges again. The visible code may contain sensible calls in sensible order, but it lacks an idempotency rule that lets repeated requests produce one intended effect.
A database migration has another failure shape. An AI might generate a command that adds a required column to a populated table. Existing rows have no value for it, old application instances do not know it exists, and a long table rewrite may block traffic. Sound migration design considers compatibility, backfill, deployment order, locks, rollback, and verified backups. The broader techniques for using AI around database changes begin with those state and recovery questions.
A backup is only a recovery option after a restore has been tested. A file that exists but cannot be read, is missing recent records, or takes too long to restore does not meet the operational need.
Destructive file operations, bulk email, account suspension, infrastructure changes, and automated purchases belong in the same family. The implementation needs a dry-run mode, narrow permissions, limits on batch size, audit records, confirmation for broad actions, and a recovery plan. The exact controls differ, but they all reduce the blast radius of a mistaken instruction.
Private data and hidden rules change the answer
Vibe coding becomes unsafe when prompts or generated systems expose confidential data, or when the model lacks rules that cannot be inferred from code. Privacy and business constraints live across contracts, policies, settings, conversations, and law, not only inside a repository.
A developer debugging a support tool might paste a production record into a coding assistant. That record can contain a name, contact details, account history, or an internal note. The immediate task is technical, but the disclosure decision is organizational. The safe path depends on the approved tool, its data handling terms, access controls, retention settings, and the organization’s obligations.
Hidden business rules cause a second kind of failure. Imagine a warehouse system where returned items must be quarantined until inspection. A model sees an inventory table and generates a convenient “return to stock” action. The code is coherent, and the count is mathematically correct, yet the action violates the process that keeps damaged goods away from customers.
Use synthetic or carefully anonymized examples where possible. Give the tool the least access needed. Keep secrets out of prompts and source files. For a consequential requirement, name its source in a ticket, test, or design record so the next developer can distinguish a deliberate rule from an accidental pattern.
Prototypes are safer only when they stay prototypes
Vibe coding is often suitable for prototypes because prototypes answer questions cheaply, but the safety depends on containment. A prototype becomes dangerous when real users, sensitive data, financial effects, or operational dependence arrive without a corresponding change in engineering discipline.
A prototype might test whether students understand a new timetable interface. It can use invented names, a fixed sample schedule, and a temporary deployment behind access control. If it fails, the team records what it learned and discards the code. The asset is the evidence about the interface, not the generated implementation.
Prototype drift happens when the temporary tool is useful. Someone shares the link, real records replace samples, and other work starts depending on it. No single step feels like a launch. The team should therefore define an exit gate in advance: before real use, identify an owner, document requirements, replace unsafe shortcuts, review dependencies, add tests, establish monitoring, and decide how to recover.
Some generated work can survive the transition after review. Layout code and isolated formatting logic may be straightforward to validate. Identity, storage, permissions, and external side effects deserve fresh design. The category “prototype” describes how a system is used, not a magic property inside its files.
Five mistakes people make with the boundary
Most mistakes about the vibe-coding boundary come from judging code by appearance, size, or short demonstrations instead of consequences and evidence. Five recurring errors make experimental software look safer than it is or make careful AI use seem riskier than it is.
1. “The code is small, so the risk is small”
Risk follows authority and impact, not line count. A one-line command can delete a directory, publish a secret, or grant a permission. A large animation module may affect nothing beyond pixels. Ask what resources the code can read or change and under whose identity it runs.
2. “The tests passed, so the feature is safe”
Tests establish only the cases they specify. AI-generated tests can repeat the same mistaken assumption as AI-generated implementation, especially when one prompt produces both. Derive important cases from external requirements, threat models, past incidents, and boundary analysis. Then inspect whether each test would fail for the defect it claims to detect.
3. “A human reviewed it, so it was understood”
A fast approval is not proof of comprehension. Review quality depends on time, domain knowledge, change size, supporting tests, and a clear statement of intent. Large generated diffs overload attention. Split changes, explain invariants, and require the author to trace sensitive paths before asking someone else to approve them.
4. “The model can fix any bug it created”
The model may repair a symptom while preserving the mistaken design. Repeated prompting can also add layers that nobody understands. When a fix fails twice or changes unrelated behavior, stop the loop. Reduce the problem, reproduce it deterministically, inspect state, read the relevant documentation, and reason from the violated requirement.
5. “Avoiding vibe coding means avoiding AI”
AI use and vibe coding are not synonyms. A developer can ask for alternatives, generate test data, translate an error, or draft a pure function while retaining control of the specification and evidence. The boundary rejects unverified delegation in the wrong setting, not useful automation.
Can vibe-coded software be made safe later?
Vibe-coded software can sometimes be turned into maintainable software, but safety comes from re-establishing understanding and evidence, not from polishing the interface. The team must treat current behavior as an untrusted candidate and recover the requirements, design, tests, and operating controls.
Begin by freezing new features and listing what the system can affect. Map entry points, data stores, third-party services, permissions, scheduled jobs, and destructive actions. Remove exposed secrets and unsupported dependencies immediately. For each important behavior, identify the source of truth and a person who can answer policy questions.
Restrict access, reduce permissions, stop automatic side effects, and preserve evidence needed to understand current behavior.
Write tests around observed behavior, then separate behavior that is required from behavior that is merely present.
Rewrite or verify sensitive modules, document decisions, and add independent tests for failure and abuse cases.
Add monitoring, recovery procedures, ownership, controlled releases, and a process for learning from incidents.
Rewriting everything is not automatically safer. A rewrite can introduce fresh defects and erase behavior that users rely on. Choose module boundaries based on risk. A well-tested parser may remain, while a home-made authentication layer is replaced by a maintained system with a clear security model.
When should a beginner stop prompting and start inspecting?
A beginner should stop prompting when they cannot predict what the next change may affect, cannot explain an error after reproducing it, or are about to use real data or irreversible actions. That pause is part of learning, not evidence of failure.
Inspection starts with small questions. Which function receives the input? What type and range does it expect? Where is state stored? What happens if a dependency returns an error? Which identity authorizes the change? Use a debugger, logs, focused tests, and documentation to answer one question at a time.
You ask the model to add a study timer. After several patches, pausing creates two countdowns. Stop requesting fixes. Reproduce the problem, locate every place that starts an interval, record each interval identifier, and verify that pause clears exactly the active one. The bug becomes a lesson about state and lifecycle.
Beginners can keep experiments safe by working in version control, using test data, reviewing each small diff, and running code without broad machine or account permissions. Before copying a command, look up each option and identify its target. If the command changes a database, cloud account, or important files, create a recoverable practice environment first.
A good stopping rule is simple: if you would be unable to explain the change to the person affected by its failure, do not ship it yet. Ask for help, reduce the scope, or build a smaller model of the problem until the explanation becomes concrete.
What should a team require before production?
A team should require a named owner, testable requirements, reviewed changes, controlled access, recovery procedures, and observable production behavior before generated code serves real users. The exact depth should match the consequences, but every control must produce evidence rather than ceremony.
The owner answers for the change after the AI session ends. Testable requirements define what correct means, including limits and failure behavior. Review challenges assumptions. Controlled access restricts the possible damage. Recovery procedures prepare for faults that escape. Logs, metrics, and alerts reveal whether the deployed system keeps its promises.
Production is a relationship, not a server name. A spreadsheet script used every morning for payroll is production software even if it runs on one laptop. A public demonstration using invented data may still be an experiment.
Teams should also record the model, tool, or exact transcript when that information helps reproduce a generated change, but provenance does not substitute for review. The maintained artifact is the source code plus its requirements, tests, dependency records, deployment configuration, and operating knowledge.
The takeaway: Vibe code where failure is cheap, visible, contained, and reversible. Engineer with explicit evidence where software can expose secrets, move money, alter durable data, deny access, or affect another person’s rights and safety.
Good computer science makes the boundary visible
Good computer science turns vague confidence into models that can be checked: data flow, state transitions, access rules, failure modes, complexity, and tests. Knowing when not to vibe code is one application of that larger habit of making software behavior explainable.
The subject supplies practical tools for doing this. Types constrain values. Algorithms expose assumptions about inputs. Databases make consistency questions precise. Operating systems reveal permissions and concurrency. Networks explain partial failure. Security asks what an adversary can do. Software engineering connects these ideas to review, testing, deployment, and maintenance. You can see how these ideas fit across the computer science guides as you meet each tool in a different setting.
On the next AI-generated change you see, do not begin by asking if the code looks professional. Name one promise the software must keep, one way that promise could fail, and one piece of evidence that would reveal the failure. If those answers are missing, the next useful action is inspection, not another prompt.
