Guides

Production Readiness Checklist: What to Verify Before You Ship

A working checklist for the gap between "it runs on my machine" and "it can take real users": security, reliability, scalability, observability, deployment and discoverability.

Updated

An app is production ready when it keeps working, and keeps your users' data safe, under conditions you did not test by hand: real traffic, bad input, partial outages and the next deploy. This checklist groups the checks by the kind of failure they prevent. Treat each item as a question you must be able to answer with evidence, such as a file, a config value or a test, not with "probably".

How to use it: go section by section, and for every item write down where the proof lives. An item you cannot point to is an open risk, not a pass.

1. Security

  • No secrets in the repository or its history. Search for API keys, tokens and connection strings, and rotate anything that was ever committed.
  • Secrets come from the runtime environment or a secrets manager, never from source files. Keep a committed .env.example with names only.
  • Every route that returns or changes user data checks who is asking and whether they may access that specific record, not just whether they are logged in.
  • All input is validated on the server: types, lengths, allowed values. Parameterize every database query; never build SQL by string concatenation.
  • Output that includes user content is escaped by default, and any raw HTML rendering is explicit and sanitized.
  • State-changing requests are protected against cross-site request forgery (same-site cookies, tokens or origin checks).
  • Security headers are set: Strict-Transport-Security, X-Content-Type-Options, a Content-Security-Policy, Referrer-Policy and clickjacking protection.
  • Dependencies are scanned, lockfiles are committed, and known-vulnerable packages are upgraded or replaced.

2. Reliability

  • Errors are caught at boundaries and return a safe message to the user while the detail goes to your logs. A raw stack trace in a response is a finding.
  • Every outbound call (database, third-party API, AI provider) has a timeout. A call with no timeout can hold a request open indefinitely.
  • Retries use backoff and only wrap idempotent operations. Retrying a payment or an email send without an idempotency key creates duplicates.
  • There is a health endpoint that checks real dependencies, and your platform uses it to decide whether an instance is ready.
  • The app shuts down gracefully: it stops accepting work, finishes in-flight requests and closes connections.
  • Failure of a non-essential dependency degrades the feature instead of taking down the page.

3. Scalability

  • Queries that run on every request have supporting indexes. Check the query plan for the slowest ones instead of guessing.
  • N+1 query patterns are removed: loading a list and then querying once per row does not survive real data volumes.
  • Database connections are pooled and the pool size fits the database's connection limit across all instances.
  • Expensive or repeated reads have a caching strategy with a clear invalidation rule.
  • Public and authenticated endpoints have rate limits, with tighter limits on login, password reset and anything that costs money to serve.
  • List endpoints are paginated. No endpoint returns an unbounded result set.
  • Long-running work runs in a background job, not inside a web request.

4. Observability

  • Logs are structured, include a request or correlation ID, and never contain passwords, tokens or full payment details.
  • Unhandled errors reach an error tracker or alert, so you learn about them before a user tells you.
  • You can answer "is it up, how fast is it, and what is failing" from a dashboard: availability, latency percentiles and error rate.
  • Alerts exist for the conditions that need a human, with a named owner and a runbook, and are quiet otherwise.

5. Deployment and data

  • Configuration is environment-based. The same build runs in staging and production with different variables.
  • Database migrations are versioned, reviewed and run as part of deploy. You know how to roll one back.
  • Backups exist, and someone has actually restored one. An untested backup is a hope.
  • A deploy can be rolled back quickly, and the previous version remains available until the new one is healthy.
  • CI runs typecheck, lint, tests and a production build on every change, and a failing check blocks the merge.
  • No mock, placeholder or demo data paths remain in code that production can reach.

6. Discoverability and performance

  • Every public page has a unique title, meta description and canonical URL.
  • robots.txt and sitemap.xml exist and agree with each other. Private areas are marked noindex and the sitemap lists only pages that return content.
  • Social sharing tags (Open Graph and Twitter card) include an image.
  • Core Web Vitals are measured on real pages: Largest Contentful Paint, Interaction to Next Paint and Cumulative Layout Shift.
  • Images are sized and compressed, and scripts that block rendering are deferred.

Scoring your result

Count the items you can prove. Anything in the security section that you cannot prove blocks launch. Reliability and scalability gaps are ranked by blast radius: how many users a failure would affect and how long recovery would take. Observability and SEO gaps rarely block launch but make every later problem more expensive.

This is the same four-part structure Higharch 5 uses when it audits a repository: security, scalability, SEO and production readiness. Each finding points to a file and line so the fix can be verified.

Run this audit on your own repo

Connect a GitHub repository and Higharch 5 checks it across security, scalability, SEO and production readiness.

Get started

Related guides