Production-ready: what stands between a working demo and a launched product
A demo proves an idea can work. Production means it keeps working for strangers, on bad connections, with data you did not anticipate, while you sleep. The gap between them is smaller than people fear and larger than a demo suggests.
What the demo left out
Demos are built along the happy path with cooperative data and a friendly audience. Production is the sum of every path you did not walk.
- Other people's data: empty accounts, enormous accounts, names that break your layout, timezones that are not yours.
- Other people's networks: the request that takes eleven seconds, or fails after the user pressed submit.
- Other people's intentions: someone will edit the URL and try to read a record that is not theirs.
- Other people's devices: three-year-old phones, small screens, zoomed text, screen readers.
- Time: sessions expiring, tokens rotating, a background job failing quietly at 3am.
Production readiness is mostly imagination applied to failure. The code is not harder, the list is just longer.
Auth and permissions, done properly
This is the one area where nearly right is the same as broken, and where prototypes are almost always wrong because access was faked to make a demo flow.
- Enforce access on the server, at the data layer, for every table. Hiding a button is presentation, not security.
- Store roles separately from user-editable profile data, and check them server-side. A role a client can write is not a role.
- Assume every identifier is guessable and every endpoint will be called directly.
- Cover the whole account lifecycle: sign up, verify, reset, change email, sign out everywhere, delete.
- Decide what happens to a person's data when they leave, before anyone asks.
Real data behaviour
- 01
Design for zero and for too many
The first screen every user sees is empty. The screen that breaks first is the one with two thousand rows. Both need a designed answer: onboarding for one, pagination or virtualisation for the other.
- 02
Validate on the server as well
Client validation is a courtesy for the user. Server validation is what protects the data. Both, with the same rules, or the two drift.
- 03
Make writes safe to retry
Networks duplicate requests. Any create or payment path should be safe to call twice without producing two of something.
- 04
Have a story for migrations
Schema changes against live data are the highest-risk routine act in software. Additive first, backfill separately, and rehearse it before running it.
- 05
Know that backups restore
An unrestored backup is a belief, not a backup. Try one once.
Performance that people actually feel
Perceived speed is not the same as measured speed, and a small number of things account for most of the difference.
| Thing | Why it matters | Cheap fix |
|---|---|---|
| Images | Usually the heaviest asset on any page by a wide margin | Correct dimensions, modern formats, lazy loading below the fold, explicit sizes to stop layout shift |
| Fonts | Cause invisible or shifting text on first load | Preload the display face, sensible fallbacks, avoid loading weights nothing uses |
| First data request | Decides whether the page is useful or a spinner | Fetch on the server where possible, and render the shape of the result while waiting |
| Interaction latency | The clearest signal of cheapness | Acknowledge within about 100ms, update optimistically where success is near certain |
Measure on a mid-range phone over a throttled connection. Everything looks fast on a laptop next to a router.
Failing well
- Every request has a failure branch that a person can read and act on.
- Never lose typed work. Preserve form state through a failed submission, always.
- One error boundary per major surface, so one broken widget does not blank the page.
- Real errors reach you: capture them with enough context to reproduce, and look at them.
- Log what happened, never what was private. No tokens, no personal data, no card details in logs.
- Have a way to turn a risky feature off without a deploy.
Accessibility and the basics that get skipped
- Keyboard operable end to end, with visible focus. This alone catches a large share of issues.
- Contrast that passes at real sizes, including muted secondary text.
- Labels on every input, and error messages tied to the field they concern.
- Meaningful alt text on images that carry information, and none on decoration.
- One h1 per page and headings in order, which helps assistive technology and search equally.
- Reduced-motion support: keep the state change, drop the travel.
Also in this category, because they are equally easy to omit and equally visible: page titles and descriptions, a favicon, share previews, a sitemap, and correct behaviour on a shared link.
Launch day and week one
- 01
Walk the whole thing as a stranger
New account, new browser, real email address, on a phone. Sign up, do the core job, invite someone, pay if there is payment, log out and back in.
- 02
Check the plumbing you forget
Custom domain and certificate, transactional email actually arriving, redirects from old URLs, analytics recording, error reporting connected.
- 03
Watch the first fifty people
Not vanity numbers. Where they stop, what they never find, which errors repeat. The first week of real behaviour is worth more than a quarter of speculation.
- 04
Keep one fast lane
The ability to ship a small fix within the hour matters more in week one than any feature on the roadmap.