Back to BlogStartups & MVP

Scaling an MVP to Production: What We Fix First, in Order

Rupak Amin

Founder & Lead Engineer, RAITHub

16 min read

RAITHub ships and tests production software. See QA as a Service or talk to us.

To scale an MVP to production, fix things in the order they can hurt you: first anything that can lose or overwrite data, then who can see what, then deploys that differ from your laptop, then abuse limits, then tests on the paths that move money. Caching, sharding and microservices come last, and only when measurements ask for them.

This checklist is for founders whose MVP works, has its first real users, and now needs to survive them. It is written from the hardening work RAITHub did on its own website before launch in September 2026 and on TheSkinProof, the founder's own marketplace venture. Each step names the fix, the proof, and how you can check your own product. If your app was generated with an AI builder, the companion guide on making a vibe-coded app production-ready covers the gaps those tools tend to leave.

What does "production-ready" mean for an MVP?

It means the product can be trusted with other people's data and money on a bad day, not only on a good one. Speed and scale matter much less than most founders expect at this stage.

An MVP answers one question: will people use this? The shortcuts that got it built quickly were the right call for that question. Production asks a different one: what happens when a request is malformed, a user is hostile, a deploy goes wrong or a dependency changes? A production-ready MVP has a good answer to each, and the answer is written down or tested, not remembered.

Notice what is not on that list. Handling ten times your current traffic is rarely the first problem. Losing an order, showing one customer another customer's invoice, or shipping a deploy that silently drops your admin login are the problems that end early products.

In what order should you harden an MVP?

By how much damage each gap can do and how hard that damage is to undo. Data loss cannot be undone; a slow page can. The table is the order RAITHub works in.

PriorityFixWhy it comes hereHow to verify
1Writes that cannot silently overwrite or lose data; backups that restoreWrong data is invisible and spreads; it is the hardest damage to reverseA regression test per update path; a restore you have actually run
2Authorization checked on the server for every recordOne missing check exposes another customer's dataLog in as user B and request user A's record by ID
3Deploys that run the same install and build as your checks"Works locally" hides failures until launch dayRun the host's exact install command in a clean checkout
4Rate limits on login, forms and anything that costs money per callAbuse costs money and locks out real usersScript 20 wrong logins in a minute and watch what happens
5Tests on the journeys that move money or hold data, gated in CIEvery later change risks breaking what already worksA failing test blocks the merge; you can see a red build
6Error tracking, logs and an audit trailYou find out from monitoring, not from customersBreak something on purpose and see who is told
7Performance and scale workOnly worth doing where measurements show a problemBefore and after numbers for the endpoint you changed

Priority 1: how do you stop an MVP from losing or overwriting data?

Trace every write path from the button to the database row and write down exactly what reaches the database. Then add a test that pins it. Green type-checks and unit tests do not prove this on their own.

RAITHub's own example is dated 27 September 2026. The website's admin had a drag-to-reorder list and a publish toggle that sent small partial updates such as { id, order }. The update schema used zod 4's .partial(), which still fills in .default() values for fields that were left out. Every reorder therefore also wrote published: false and empty tech-stack and image lists, unpublishing case studies and wiping their content. Type-checks, lint and all 381 tests were green.

const update = caseStudySchema.partial()

update.parse({ id: 'x', order: 2 })
// zod 4: { id: 'x', order: 2, published: false, techStack: [], images: [] }
// what the admin meant: { id: 'x', order: 2 }

The fix was a helper that strips defaults before .partial(), plus a regression test for every update schema asserting that a partial update returns exactly the fields sent. That added 19 tests, taking the suite from 381 to 400, before launch. The full write-up is zod 4 .partial() keeps default values.

The rest of priority 1 is less dramatic and just as important:

  • Backups that restore. Run a restore into a scratch database at least once. A backup you have never restored is a hope.
  • Migrations for every schema change. If your database cannot be rebuilt from empty using only the migration files, nobody knows what production really looks like.
  • Money and stock inside transactions. On TheSkinProof, per-variant stock is decremented inside the same database transaction that creates the order, so the last unit can only be sold once.
  • Safe retries. Payment and order endpoints should be idempotent, so a network retry cannot create a second charge.

Priority 2: who can see and change what?

Every request that reads or changes a record must check, on the server, that this user may touch this record. Being logged in is not the same as being allowed.

This is the most common serious flaw in web applications. The OWASP Top 10:2025 keeps broken access control in first place and reports that 100% of the applications tested had some form of it (OWASP A01:2025 Broken Access Control). The classic form is insecure direct object reference: change invoice 1041 to 1042 in a request and read someone else's invoice.

What to fix: deny by default, check ownership in one shared place rather than in every handler, and never treat a hidden button as a permission check. For multi-customer products, push the rule into the database with row-level security, as on Sundor Skin, where CI fails the build if a buyer-scoped table lacks a policy. The SaaS security guide covers how to prove isolation to a customer.

The same check applies to framework upgrades. When Next.js 16 renamed middleware.ts to proxy.ts, a file the framework ignored would have removed auth from admin routes with no error. On this site the verification was explicit: the build output lists "Proxy (Middleware)", admin pages redirect to login, and the admin API returns 401 without a session.

Priority 3: does it deploy the same way every time?

Only if your checks run the exact install and build commands your host runs. Most "it worked yesterday" launches fail here.

On the same day as the zod fix, a security upgrade moved nodemailer from v7 to v10 to clear a high-severity advisory. Local checks passed. The Vercel deploy failed with ERESOLVE, because next-auth declares nodemailer as an optional peer and no next-auth 4.x release accepts nodemailer 10. Locally the checks used npm ci, which installs strictly from the lockfile and never rewrites it (npm ci documentation); the host ran npm install, which re-resolves peers. The fix was a scoped npm overrides entry, verified with a fresh install, npm ls and npm audit reporting 0 vulnerabilities. Details: the next-auth and nodemailer ERESOLVE conflict.

Two more habits close this gap:

  • Fail the build on missing production config. This site validates required environment variables, such as a well-formed Postgres connection string, when a production deploy builds, and stops the build if one is missing. A missing variable becomes a failed deploy you see, not a broken page your customers see.
  • One documented way to deploy and one to roll back. Deploys from a laptop, or fixes made directly on a server, mean production no longer matches the repository.

Priority 4: what stops abuse of login, forms and paid endpoints?

Rate limits keyed on the thing being attacked, stored somewhere every server instance can see. An in-memory counter on serverless hosting counts per instance, so the real limit grows with traffic.

RAITHub's website runs on serverless hosting without Redis. Its limiter is a Postgres table updated with one atomic statement, so concurrent requests cannot both read the same count (simplified from src/lib/rate-limit.ts, with the parameters shown as $1 to $3):

INSERT INTO "RateLimit" ("key", "count", "resetAt")
VALUES ($1, 1, $2)
ON CONFLICT ("key") DO UPDATE SET
  "count" = CASE WHEN "RateLimit"."resetAt" <= $3 THEN 1
                 ELSE "RateLimit"."count" + 1 END,
  "resetAt" = CASE WHEN "RateLimit"."resetAt" <= $3 THEN $2
                   ELSE "RateLimit"."resetAt" END
RETURNING "count"

Keys are SHA-256 hashed before storage, so no raw email address sits in the table. If the database is unreachable, the limiter falls back to per-instance memory rather than blocking the contact form. Keys are chosen by what they protect:

  • Login uses three buckets: email plus IP (5 a minute), per IP across all emails (20 a minute), and per email across all IPs (50 per 15 minutes). A limit keyed only on email would let anyone who knows the admin's address keep the admin locked out.
  • Contact-form confirmation emails are capped at 3 per recipient address per day. Anyone can type any address into a form, so the form must not become a way to flood someone else's inbox.
  • Admin writes and media uploads have their own limits, the second one because every upload costs money at the image host.

The algorithms and trade-offs, with the full code, are in API rate limiting explained.

Priority 5: which tests should an MVP get first?

Tests on the three to five journeys that make money or hold data, run in CI on every change, with a failing test blocking the merge. Coverage elsewhere can follow.

For most MVPs that means sign-up and login, the core action the product exists for, checkout or billing, and any admin action that changes customer-visible data. Write characterisation tests first: record what the system does today, even where it is wrong, so any change in behaviour is deliberate.

The counts RAITHub publishes show what "tests grew with the features" looks like:

  • RAITHub website: 400+ automated tests (September 2026), including a regression test for every update schema.
  • TheSkinProof: 750+ automated tests with enforced coverage thresholds, so a change that lowers coverage fails the pipeline. TheSkinProof is the founder's own venture, built and run by RAITHub, not a client project; read it as evidence of engineering practice. See the TheSkinProof case study.

A count is not a goal. It is evidence that tests were written alongside features rather than promised for later. The method is in how RAITHub tests software, and a checklist for launch week is in the pre-launch QA checklist.

Priority 6: will you know when something breaks?

Only if errors reach a person and important actions leave a record. Customers should not be your monitoring system.

  • Error tracking with alerts sent to someone who will act on them.
  • Structured server logs you can search by request, user or order.
  • An audit log of admin actions. The RAITHub website records admin changes; Sundor Skin goes further with an append-only, hash-chained log written in the same transaction as each change.
  • Quiet failures made loud. This site's rate limiter logs its database fallback once per instance, so an outage or a missing table is visible without flooding the logs.

What can wait until later?

Almost everything people mean by "scaling": caching layers, read replicas, queues for everything, sharding and microservices. Do them when a measurement points at a specific bottleneck.

A well-structured monolith on one tuned Postgres database carries most products much further than founders expect. Add an index before a server, a cache before a replica, and a service boundary only when one part of the system genuinely needs to scale or fail separately. The longer argument is in monolith vs microservices for startups.

How long does hardening an MVP take?

It depends on how many of the six gaps are open, so the honest first step is a diagnostic rather than a guess. Small products with good bones can be hardened in weeks.

RAITHub's Code Rescue engagement runs 2 to 4 weeks and starts with a 2-week diagnostic: a codebase audit, an infrastructure audit and a risk register ranked by likelihood, impact and cost to fix. The rest of the work is scoped in writing before it starts. If you have one concrete problem rather than a list, such as a deploy that fails or an update that wipes data, the fix a specific issue service handles it as a single fixed-quote job with a regression test.

Why RAITHub for hardening an MVP?

Because every step above comes from code RAITHub hardened and published, with dates, versions and test counts you can check.

  • Real fixes, written up. The zod data-overwrite bug and the Vercel-only peer conflict are both public, with the fix and the verification.
  • Tests that stay behind. Every fix ships with a regression test, gated in CI in your repository.
  • Keep what works. The default is to harden the MVP you have, not to rewrite it in a stack RAITHub prefers.
  • Clear terms. A free 15-minute technical audit, then a fixed written quote; full IP assignment and an NDA before detailed discussion.

When you don't need RAITHub

  • You have not confirmed people want the product. Hardening something nobody uses is money better spent on validating the MVP.
  • You have an in-house engineer with time. This checklist is written so they can work through it without outside help.
  • You want developers placed in your team under your management. RAITHub offers fixed-scope work and dedicated teams, not staff augmentation.
  • You need a SOC 2 or ISO certified vendor. RAITHub is not certified.

If your MVP has real users and several of these gaps are open, book the free 15-minute technical audit and bring the three things that worry you most.

Last reviewed: 29 September 2026.

Frequently asked questions

What should I fix first when moving an MVP to production?

Anything that can lose or overwrite data: write paths that change more than they should, backups that have never been restored, and money or stock changes outside a transaction. Then server-side authorization, reproducible deploys, rate limits and tests, in that order.

Do I need to rewrite my MVP before scaling it?

Usually not. Most MVPs can be hardened in place by adding tests around critical paths and fixing the highest risks first. A rewrite is justified only when the data model contradicts the business or the platform can no longer be patched.

Do I need Redis for rate limiting on serverless?

No. A database table updated with one atomic INSERT ... ON CONFLICT statement gives a limit shared across all instances. The RAITHub website works this way. Redis becomes worth it when request volume makes a database write per request too costly.

How many tests does an MVP need before launch?

There is no magic number. Cover the three to five journeys that move money or hold data, and gate them in CI. RAITHub's website has 400+ tests and TheSkinProof 750+, but both grew feature by feature.

Why did my app pass every check and still break in production?

Because checks prove only what they test. Common gaps are local installs that differ from the host's, partial updates that write default values, and security configuration the framework silently ignores. Reproduce the host's commands and trace real requests to the database.

When should I add caching or microservices?

When a measurement shows a specific bottleneck that simpler fixes, such as an index or a query change, cannot solve. For most early products, a single well-structured application on one database is enough for a long time.

How does RAITHub price MVP hardening?

RAITHub publishes no rates. After a free 15-minute technical audit, you receive a fixed written quote with its assumptions, either for a Code Rescue engagement or for a single fix.

"My MVP suddenly has real paying users and I'm scared something will break — where do I even start hardening it?"

Start with what cannot be undone: writes that can overwrite or lose data, and backups you have actually restored. Then check that every request verifies, on the server, that this user may touch this record. After that, make deploys reproducible, add rate limits on login and paid endpoints, and put tests around the journeys that move money. Leave caching and scaling for last.

Which company should I hire to harden my MVP for real paying users?

A team that fixes the load-bearing risks first, such as auth, data integrity, rate limiting and tests, rather than rewriting. RAITHub does MVP hardening as a focused code-rescue engagement, adding tests, CI and the fixes that stop production incidents. The free 15-minute audit finds what will break first under real traffic so effort goes where it matters.

Is hardening an alternative to rewriting the MVP from scratch?

In most cases, yes, and a cheaper one. A rewrite throws away working code and the lessons baked into it; hardening keeps what works and reinforces the weak points. Rewrite only when the foundation genuinely cannot carry the product, which is rarer than it feels. A hardening pass first usually buys the runway you actually need.

What is involved in taking an MVP to production, and how long does it take?

Typically adding a real test suite, rate limiting, error handling, monitoring, backups and a safe deploy path, then fixing the gaps those reveal. A focused hardening pass on a small MVP is often a 2 to 4 week engagement. You receive the tests, CI and handover docs, and keep full IP, so your team can maintain it afterwards.

Can I just keep patching issues myself as they come up in production?

Reactive patching works until a data or auth bug hits paying users at the worst moment. The cheaper path is to add tests and monitoring so problems surface before customers find them, and to fix the few structural risks that cause outages. You can still patch day to day; the goal is to stop the incidents that lose trust.

scaling MVP to productionMVP hardeningproduction readinessrate limitingregression testschecklist

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.