06 — Prototype vs Production
The biggest waste in engineering isn’t bad code, it’s production code written at prototype speed or prototype code written at production speed.
Know which you’re building. Tell the reviewer.
What’s OK to skip in a prototype / POC
- UI polish. Ugly is fine. It’s a prototype.
- Edge-case error handling. Happy path only.
- Full auth. A hardcoded allowlist of emails or a single shared password is fine for a demo.
- Horizontal scaling. One box. One DB.
- Deep observability. Console logs + Datadog / Application Insights free tier is fine.
- DB migrations. Dropping and recreating the DB is fine. Use
drizzle-kit pushorprisma db push, not migrations. - CI perfection. Typecheck + test is enough; skip the 5-stage deploy pipeline.
- Documentation beyond the README.
What’s NOT OK to skip, even in a prototype
No exceptions. These are cheap and prevent real damage:
- Secrets out of the repo.
.env.local+.env.example. Even in a throwaway. - One smoke test. Proves the thing boots. See 04-testing.
- README with run steps. “Clone,
pnpm install,pnpm dev, visit X.” Three lines. - Error tracking. Datadog free tier, or Application Insights on Azure-hosted apps. You need to know when it’s broken during the demo.
- Some kind of auth — even “enter your email and get a magic link” is enough. Never ship
/adminopen. - HTTPS in any deployed env. Vercel gives this for free. Azure/VPS: Caddy for free. No excuses.
Harden-for-production checklist
When a prototype becomes “we’re shipping this to clients,” walk this list before flipping the switch. Don’t skip items — write “N/A + reason” next to any that genuinely don’t apply.
Security
- Real auth (Clerk / Auth.js / Entra), not a hardcoded list.
- Input validation on every boundary (Zod / Pydantic). No
body: any. - Rate limiting on auth endpoints and any expensive action.
- CSRF protection on any cookie-authed mutation.
- SQL via ORM / parameterized queries — no string concatenation into SQL, ever.
- Secrets rotated; anything that touched a dev
.envgets a new value in prod. - Dependency audit —
pnpm audit/pip-audit. Fix criticals.
Data
- Migrations in place (Drizzle Kit / Prisma Migrate), no more
db push. - Backup configured + one successful restore tested.
- PII identified and handled (redacted in logs, encrypted at rest where needed).
- Data retention policy written down, even if “indefinite.”
Reliability
- Structured logging (JSON), no stray
console.log. - Error boundaries on every top-level React tree.
- Every external call has a timeout + retry.
- Health check endpoint + an uptime monitor hitting it.
- Graceful shutdown on services (handle SIGTERM, finish in-flight requests).
- Rollback path documented and tested once.
Performance
- Load test on the critical path — even a quick
k6orbombardierrun. Know your p95 under expected load. - N+1 queries audited (turn on query logging, look at the dev request).
- Images via
next/image; fonts vianext/font. No 2MB hero images. - Bundle analyzed (
@next/bundle-analyzer) — nothing absurdly large slipped in.
Ops
- Datadog (and Application Insights for Azure-hosted apps) wired and receiving events.
- On-call knows this app exists. Runbook written — 1 page, covers “it’s down, what do I do.”
- Owner named. Not a team — a person.
- Cost reviewed: estimated monthly spend, alert if spend > 2x estimate.
- Legal / compliance check if it touches payments, PII, or regulated data.
Testing
- All test files required by 04-testing exist and pass.
- Critical flows covered end-to-end.
- At least one test reproduces each fixed bug from the prototype phase.
Harden or rewrite?
Heuristic: if the prototype’s data model survives a senior’s review, harden it. If the data model is wrong, rewrite — you’ll lose more time fighting it than rebuilding.
Harden when:
- Data model is sane.
- Framework choice fits the prod use case.
- The bugs you see are in edge cases, not in the core flow.
Rewrite when:
- The prototype used a stack that doesn’t fit prod (e.g., a Streamlit POC becoming a client app).
- The data model assumed single-tenant and the prod requires multi-tenant.
- There’s hidden state everywhere and nobody can explain where a value comes from.
In doubt, harden. Rewriting from scratch is almost always more expensive than the current team remembers.
Tell the reviewer
Every PR on a prototype: mention it.
“This is still prototype-grade — skipping full auth and migrations. Harden list tracked in #123.”
Every PR post-hardening: note which checklist items the PR addresses.
“Hardens auth (Clerk) + input validation on
/api/invoices. Closes harden-list items 1, 2, 7.”
Keeps everyone on the same page about what the code is supposed to be.