What software production readiness actually means beyond passing CI

The gap between working and ready is where most production incidents live. A checklist of the questions a green build does not answer, and how to answer them.

A green pipeline means the tests you wrote passed. That is genuinely useful and it is a much narrower claim than “ready for production.”

The gap between the two is where nearly every production incident lives. Not in the code paths someone thought to test, but in the questions nobody asked because the build was green and the demo worked.

This is the list we work through, roughly in the order that the answers tend to be uncomfortable.

Access control

Can you enumerate who can do what?

Not “we have roles.” An actual answer: which roles exist, what each can reach, and who currently holds them.

The common failure is authorization checked at the interface rather than at the operation. The button is hidden from users who should not press it, and the endpoint behind it checks only that someone is logged in. Anyone who can construct the request can perform the action.

# The blunt test: authenticate as your lowest-privilege user,
# then call the endpoints you assume are protected.
curl -H "Authorization: Bearer $LOW_PRIV_TOKEN" -X DELETE https://api.example.com/v1/users/42

If that succeeds, you do not have access control. You have a user interface.

Are object-level permissions checked?

A logged-in user requesting /orders/8412 should get their order or a 404, not somebody else’s order. This class of bug is consistently one of the most common in real applications, because the authentication check passes and nobody wrote the ownership check.

What happens when someone leaves?

If revoking access means remembering which of eleven systems hold an account, it will not be done completely.

Recovery

Have you restored a backup?

Not “do you have backups.” Backups that have never been restored are a hypothesis. The failure modes we see regularly: the dump was empty, it excluded the database, the encryption key was on the machine that died, or the restore takes eleven hours nobody had budgeted for.

Restore to a throwaway environment, confirm the data is complete, confirm the application starts, and write down how long it took. That number is the one everybody wants during an incident and almost nobody has beforehand.

Do backups survive the thing you are recovering from?

A backup on the same server does not survive that server being compromised or destroyed. A backup in the same account does not survive that account being compromised.

Can you roll back a deployment?

Including a deployment that changed the database schema. That is the case people discover is impossible at the worst moment, because a rollback of the code against a migrated database fails in a new and interesting way.

Observability

When it breaks at 2am, what do you look at?

If the answer is “SSH in and check,” you have a diagnosis process that requires your most senior person to be awake.

The practical minimum:

  • Application errors reaching somewhere a human sees, with a stack trace and enough context to reproduce
  • Request logs with timing, separating your application’s time from the infrastructure’s
  • Availability monitoring at the level a customer experiences, not just whether the host pings
  • A small number of business signals: orders per hour, signups per day. These detect a broken checkout faster than any technical alert, because a store can serve pages perfectly while taking no money.

Do your logs survive an incident?

Logs held only on the affected machine are logs an attacker with root can edit. Ship them somewhere else.

Is anyone actually reading the alerts?

An alert routed to an unmonitored mailbox, or a channel so noisy the real one is lost, produces the confidence of monitoring without the benefit. That is worse than no alerting, because it removes the worry that would otherwise prompt someone to check.

Dependency health

This is the most-neglected item on the list, and usually the largest share of the code actually running.

What are you running, and is any of it known-vulnerable?

npm audit --production
pip-audit
composer audit

Is anything unmaintained?

A dependency whose last release was four years ago will not be patched when something is found in it. This is not hypothetical risk; it is a scheduled problem with an unknown date.

Could you update if you had to?

If a critical vulnerability were disclosed in your framework tomorrow, could you take the patch? If you are three major versions behind, the answer is “not this week,” which is not the answer you want while a public exploit exists.

Do you know what got installed?

The dependency you chose brought others. Most teams have reviewed the code they wrote and none of the several hundred packages underneath it. That imbalance shows up constantly in real assessments.

Configuration and secrets

Are secrets out of the repository?

Including the git history, which is where they usually still are after being removed from the current tree.

# Look through history, not just the working tree
git log -p --all -S 'password' -- . | head -60

A secret committed and later removed is still a secret that was published, and it should be treated as rotated rather than deleted.

Does the application fail loudly on missing configuration?

An application that starts with a missing environment variable and falls back to a default is an application that will run in production with a development setting. It should refuse to start.

Are environments genuinely separate?

Staging pointing at the production database is more common than it should be, and it is discovered when someone tests a destructive operation.

Data handling

What personal data do you hold, where, and for how long?

If nobody can answer this, you cannot honestly respond to a deletion request, and you cannot scope the impact of a breach.

Is it encrypted in transit and at rest?

In transit is usually handled. At rest, particularly for backups, frequently is not.

Do errors leak it?

Stack traces in production responses, personal data in log lines, and full request bodies captured by an error tracker are three routine ways that data ends up somewhere it was never meant to be.

Operational maturity

Can you deploy without a specific person?

If deployment is a sequence of manual steps one person knows, that is a single point of failure and they cannot take a holiday.

Is there a runbook?

Even a short one. What the system consists of, how to restart it, who to contact, what the known failure modes are. Written for someone who has not seen it before, because that is who will read it.

Has anyone rehearsed an incident?

Not a full exercise. Just once: pick a plausible failure, walk through who does what, and find out which access nobody has and which contact detail is out of date. It takes an hour and always finds something.

How to use this

Do not attempt all of it at once. Work down in this order, because it is roughly the order of consequence:

  1. Restore a backup. It is the highest-value single action on this list, and the most commonly skipped.
  2. Check authorization at the API, not the interface.
  3. Make errors visible to a human.
  4. Audit your dependencies and establish whether you could update under pressure.
  5. Get secrets out of the repository and its history.
  6. Write the runbook, then have someone else follow it.

Each of these produces a specific answer. Where the answer is uncomfortable, that is a finding rather than a failure, and finding it now is considerably cheaper than finding it during an incident.

Why standards are useful here

OWASP’s Application Security Verification Standard and NIST’s Secure Software Development Framework both exist because these questions recur across every organization, and having someone else’s exhaustive list beats reconstructing your own from memory.

Neither is a certification you pass. They are structured lists of things to check, and the value is in the structure. Use them as a source of questions rather than as a compliance exercise, and the answers will tell you where you actually are.

The honest framing

“Production ready” is not a state you reach and hold. It is a set of properties that decay: dependencies age, access grants accumulate, the recovery procedure stops matching the architecture, and the person who knew how it worked moves on.

The useful question is not whether you are ready. It is when you last checked, and whether the answers have been written down anywhere other than in one person’s head.

Sources and further reading

Third-party facts in this article come from the primary sources below. Anything not cited here is a TechSteps observation from our own work, and should be read as such.

  1. OWASP Application Security Verification Standard (opens in a new tab) OWASP
  2. NIST Secure Software Development Framework (SSDF) (opens in a new tab) NIST
  3. NIST SP 800-61 Rev. 2, Computer Security Incident Handling Guide (opens in a new tab) NIST

Dealing with this yourself?

If this describes a system you are responsible for, we can look at the specific case rather than the general one.