When code is written to match a "vibe" rather than a specification, the result often runs—until it doesn't. This page catalogs the failure modes that emerge when intuition substitutes for design, with concrete examples you can recognize and avoid. It matters whenever an AI-assisted workflow, a hackathon prototype, or a rapid-iteration cycle bypasses review, testing, or documentation steps. The anti-patterns below are not language-specific; they apply wherever code is produced faster than it is understood.
Prototyping Without a Safety Net
A reader tries to spin up a working prototype in hours rather than days. The intent is speed, but the anti-pattern is leaving the prototype as production code.
Exact command
curl -s -X POST http://localhost:8080/run \
-H "Content-Type: application/json" \
-d '{"prompt":"build a user service with auth and api"}'
What silently fails
The server starts and responds 200 OK, but the generated service binds to 0.0.0.0, exposes every endpoint, and writes session tokens to plaintext files in /tmp. No rate limiting, no input validation, no audit logging. The prototype functions as a demo, but each of those omissions becomes a vector the first time it reaches a public network.
Exact error text (produced in production after deployment)
2025/03/12 08:14:23 http: server reaching connection limit due to unbounded connection pool
2025/03/12 08:14:25 security: token leaked in /tmp/session_192.168.1.42.json
What you only learn after it breaks
When the prototype is promoted to staging, the absence of a Dockerfile USER directive means the container runs as root. A single path traversal in the generated API yields root-level file read. The fix requires rebuilding the image with a non-root user, adding RUN useradd -m app and USER app, and regenerating secrets. If the original prompt included “use environment variables for secrets,” that instruction was likely ignored by the generator.
Debugging AI-Generated Code You Did Not Write
A reader encounters a stack trace in a service they did not author. The intent is diagnosis, but the anti-pattern is reading intent into code that was synthesized from a prompt.
Exact command
git log --oneline --all | head -20
Sharp edge: commit messages that say “fix” or “update”
Every commit message in the repository is feat: add login or fix: runtime error. No diff is attached, no ticket reference, no description of what changed. When git show <sha> is run, the patch adds a try/catch that swallows the original exception:
try:
result = db.query(sql)
except Exception as e:
logger.error("query failed") # original traceback lost
raise
The reader sees query failed in the log and has no way to determine whether the failure was a timeout, a deadlock, a schema mismatch, or a syntax error. The only recovery path is to enable log_error_verbosity = verbose in the database configuration and restart the service.
Exact configuration key ( PostgreSQL )
# postgresql.conf
log_min_duration_statement = 200
log_error_verbosity = verbose
Setting these two keys before restarting systemctl restart postgresql causes every slow query to emit its full text, parameters, and the query plan node that exceeded the threshold. Without them, the only signal is statement_timeout exceeded.
Modifying Vibe-Coded Components
A reader needs to add a field to a data model generated by a vibe-coding workflow. The intent is incremental change, but the anti-pattern is treating generated code as hand-written.
Exact command
psql -c "\d users"
Result
Table "public.users"
Column | Type | Collation | Nullable | Default
----------+---------+-----------+----------+---------
id | integer | | not null | nextval('users_id_seq'::regclass)
email | text | | |
created_at| timestamp with time zone | | not null | now()
The table was created by a prompt that specified “users table with email and timestamps.” Adding phone_number requires either editing the migration file manually or re-running the generator. If the generator is rerun, any hand-written changes are overwritten. If the migration is edited manually, the next generator run may produce a conflicting schema definition, and the migration tool will refuse to apply both.
Exact error text ( migration tool )
ERROR: duplicate column phone_number exists
HINT: Add IF NOT EXISTS or use ALTER TABLE ... DROP COLUMN first.
The reader must choose between keeping the hand-edited migration (and risking future conflicts) or accepting a new generator output (and losing prior modifications). There is no metadata tag that marks a file as “generator-produced” versus “hand-written,” so the decision is manual and permanent.
Deploying Vibe-Coded Containers
A reader pushes a container to a registry, intending to deploy it to a cluster. The anti-pattern is assuming the image is complete because it starts without error.
Exact command
docker build -t myapp:latest .
What silently fails
The Dockerfile was generated with FROM node:20 and COPY . /app, but the prompt did not include a CMD or ENTRYPOINT. The image builds successfully, and docker run myapp:latest starts a shell with the working directory set to /app. No process runs, no port is exposed, and the container exits immediately after the shell command finishes.
Exact command to reveal the issue
docker run -d myapp:latest
docker ps -a
Result
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 myapp:latest "/bin/sh" 2 seconds ago Exited (0) 2 seconds ago nostalgic_mcnulty
The reader must add a CMD ["node", "index.js"] or equivalent to the Dockerfile and rebuild. If the original prompt specified “use port 3000,” that instruction may have been recorded only in a README or a comment, not in the exposed EXPOSE directive.
Exact Dockerfile fragment to add
EXPOSE 3000
CMD ["node", "index.js"]
After docker build -t myapp:latest . and docker run -p 3000:3000 myapp:latest, the application listens on the expected port. Without the EXPOSE flag, docker inspect will show HostPort as 0 unless -p is used at runtime.
Integrating Vibe-Coded APIs
A reader needs to connect a frontend application to an API generated by a vibe-coding tool. The intent is integration, but the anti-pattern is assuming the API surface is stable.
Exact command
curl -i http://localhost:8080/api/users
Result
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
The response shape is a flat array of objects. The reader builds a TypeScript interface:
interface User {
id: number;
name: string;
}
Two weeks later, the generator is run again with a slightly different prompt, and the API now returns:
[{"userId":1,"fullName":"Alice","metadata":{"role":"admin"}}]
The userId field replaces id, fullName replaces name, and a nested metadata object appears. The frontend crashes with Cannot read properties of undefined (reading 'name').
Sharp edge: undocumented field renames
The generator does not emit a schema or changelog. The only way to detect the change is to inspect the raw response, compare it against the last known shape, or add runtime validation (e.g., zod or ajv) that tolerates unknown fields. Without validation, any field rename or addition is a breaking change.
Exact validation snippet (JavaScript, zod)
const userSchema = z.object({
id: z.number(),
name: z.string(),
});
app.get("/api/users", async (req, res) => {
const raw = await fetch("http://generator:8080/api/users").then(r => r.json());
const parsed = userSchema.safeParse(raw);
if (!parsed.success) {
return res.status(502).json({ error: "unexpected payload shape" });
}
res.json(parsed.data);
});
Adding this guard requires a deploy cycle, but it prevents the silent failure mode where malformed payloads propagate through the system.
Checklist: Before You Prompt
Before running any generator-assisted workflow, run through these concrete checks. Each one is a concrete action, not a principle.
- Run
git log --oneline --alland verify every commit message references a ticket or issue number. If messages are vague, treat the history as opaque and plan a manual audit before promotion.
- Inspect the generated
Dockerfilewithdocker build -t tmp .and thendocker run --rm tmp cat /app/Dockerfile. Confirm thatUSER,EXPOSE, andCMDare present and intentional. If any are missing, add them before the image is pushed.
- Check the API response shape with
curl -ior an HTTP client. Parse the JSON and compare it against the interface or model you intend to consume. If the shape differs from documentation, add runtime validation before the data enters your business logic.
- Enable database verbosity with
SET log_error_verbosity = verbose;andSET log_min_duration_statement = 0;for a single session, then run the critical query. Capture the output. If the log does not include the query text, parameters, and plan, the debugging window is narrower than expected.
- Never treat a generated migration as final unless the generator emits a checksum or migration ID that is stored in version control. If no such ID exists, plan to review the migration diff before
alembic upgrade headorsequelize db:migrate.
The common thread across all these anti-patterns is the same: code that is produced faster than it is examined will eventually be depended upon, and the cost of that dependency is paid in bugs, outages, and refactoring effort. The anti-patterns listed here are not inevitable; each one is avoidable with a concrete check, a second look, or a minimal validation layer. The reader who internalizes these checks spends less time recovering from failures and more time building features that survive contact with production.
This page was rewritten on 11 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
Join the Newsletter
Get the latest updates and deep insights shipped directly to your inbox.