6 min read

SaaS MVP architecture: what to get right before you scale

Most MVP advice says ship fast and fix it later, and it is mostly right. But four decisions are effectively permanent once you have paying customers. Get those right and cut corners everywhere else.

The standard MVP advice — ship fast, do things that do not scale, rewrite later — is mostly correct, and following it literally has killed a lot of promising products.

The reason is that "rewrite later" is not uniformly priced. Some decisions are genuinely cheap to reverse at any point. Others become effectively permanent the moment you have paying customers, because reversing them means migrating live data, coordinating downtime, or asking users to accept something breaking.

This article is about telling the two apart, so you can be reckless about the first group with a clear conscience.

The four that are permanent

1. Tenancy — how you separate one customer's data from another's

The single most expensive thing to change later, and the one most often decided by accident.

The question is: where does the boundary between customers live? Usually one of:

  • A tenant_id column on every table, in one shared database.
  • A schema per tenant.
  • A database per tenant.

For nearly every early-stage B2B SaaS, the first is right. It is simplest, cheapest, and migrations touch one place.

But the decision that actually matters is not which one — it is whether the boundary is enforced somewhere it cannot be forgotten. A tenant_id column that every query is supposed to filter by is not a boundary. It is a convention, and conventions fail at 2am during an incident, in the one query somebody wrote in a hurry.

Put it in a layer that cannot be skipped: row-level security in the database, or a repository layer that has no method for querying without a tenant scope. The cost is a day at the start. The cost of retrofitting it is a security incident, and the cost of the incident is not the fix — it is the disclosure email.

Also: decide early whether a user can ever belong to more than one tenant. If the answer might be yes, model users and memberships separately from day one. Collapsing them is easy; splitting them later is a data migration touching every session, invite and permission in the system.

2. Identity — what a "user" is

Two specific traps, both cheap now and brutal later.

Do not use email as the primary key. People change email addresses. Companies get acquired and rename their domain. Two people share an address. If email is what everything foreign-keys to, every one of those becomes a data migration. Use an opaque internal ID; treat email as a mutable attribute with a uniqueness constraint.

Separate authentication from identity. The user is one thing; the ways they can prove they are that user — password, Google, Microsoft, SAML, magic link — are a list attached to it. If you model "user has a password" rather than "user has credentials", then the first enterprise customer asking for SSO is a rewrite of your auth system rather than a new row type. That request will come, and it will come attached to your largest deal.

3. The money model

Not your pricing — pricing should change often. The shape of it.

The question that reaches furthest into your schema: who is billed, and for what unit? Per user, per tenant, per usage, per seat with tiers. Changing the unit later means recomputing history, and the finance conversation that follows is worse than the engineering one.

Two things worth doing before your first paying customer:

  • Record events, not just state. If billing depends on usage, store an immutable log of the usage events. A counter tells you the number now; an event log lets you answer "why was this invoice this amount" eight months later, which is the question that actually gets asked.
  • Keep subscription state out of your own tables where you can. Stripe and its peers already model trials, proration, dunning and tax. A hand-rolled subscription state machine is a large amount of work to arrive at a worse version of something you can rent.

4. What you can see when it breaks

Not architecture in the diagram sense, but it belongs on this list because retrofitting it is unusually painful — you cannot go back and observe something that already happened.

The minimum, and it is genuinely small:

  • Structured logs with a request ID threaded through, so one user's report becomes one query rather than an afternoon.
  • Error tracking with a real service. Not a log file nobody opens.
  • One dashboard with request rate, error rate and p95 latency.

P95, not average. Averages hide the experience of the users most likely to churn. This is also why the performance work worth doing is usually on the tail: median latency is what you feel in development, and tail latency is what a customer on a bad connection actually lives with.

What to cut, guilt-free

Everything here is cheap to fix later, and doing it early is the most common way MVPs run out of runway building infrastructure for load they never get:

  • Microservices. A monolith with clean module boundaries is faster to build, easier to debug, and splits cleanly if you ever need it. Distributed systems problems are real and you do not want them at ten customers.
  • Kubernetes. A container on a managed platform, or one well-configured VPS, will carry you further than you think. Provisioning is not product.
  • Caching. Add it when you have measured something being slow. Cache invalidation bugs are expensive and premature caches hide the query problems you should be fixing.
  • Horizontal scaling. A single decent database instance handles far more than most products ever see. Get to the ceiling before designing for it.
  • Perfect test coverage. Test the money, the auth and the data integrity. The settings page can wait.
  • Your own component library. Use something off the shelf. Nobody has ever churned over your button styles.

A test for which list something belongs on

When you are unsure, ask:

If I change this in eighteen months with a thousand paying customers, do I have to migrate live data, take downtime, or break something a customer relies on?

Yes to any of them: it is in the first list, spend the day now. No to all three: ship the crude version and stop thinking about it.

Almost everything is in the second group. That is the actual point of this article — the first list is short precisely so you can be ruthless everywhere else.

The one that is not technical

Write down why you decided things. Not comprehensive documentation — a short file in the repository saying what you chose, what you rejected and what would make you change your mind.

Six months later, the constraint that made a decision obvious is gone from everyone's memory, and what remains is a design nobody can defend and nobody dares change. That is how systems become haunted. Ten minutes of writing at the time is the cheapest thing on this list.

Read next

Web & Product

More notes

Have a version of this problem?

Thirty minutes on a call and you'll have a fixed price and a timeline — or an honest no.