Most SaaS rewrites are not caused by bad code. They are caused by a handful of decisions made in week two, when the product had zero customers and every option still looked equally fine, then discovered in month fourteen when changing them means migrating live customer data.
These are the six that matter most, in roughly the order they become irreversible.
1. Tenant isolation: how separate is separate?
Every multi-tenant product picks one of three models, and the choice shapes everything downstream — backups, compliance answers, per-customer performance, and how hard it is to onboard an enterprise client who insists on data residency.
Shared database, shared schema
One database, one set of tables, a tenant_id column on every tenant-owned row. Cheapest to run, simplest to deploy, easiest to query across tenants for analytics. The risk is entirely in application code: one query missing its tenant filter is a cross-tenant data leak.
Shared database, schema per tenant
Structural isolation with moderate operational cost. Migrations must now run across N schemas, which becomes a real engineering concern somewhere past a few hundred tenants.
Database per tenant
Strongest isolation, easiest per-customer backup and restore, the answer that satisfies cautious enterprise buyers. Also the most expensive to operate and the slowest to iterate on, because every schema change is a fleet operation.
Our default: shared schema with tenant scoping enforced at the framework level, plus the ability to promote an individual large customer to a dedicated database later. Start cheap, keep the door open.
2. Enforce tenant scoping in one place, on day one
If tenant filtering is something developers remember to write, it will eventually be something a developer forgets to write. The fix is architectural: a global query scope applied automatically to every tenant-owned model, so the default query is already scoped and bypassing it requires deliberate, visible, reviewable code.
Pair it with a test that fails if any tenant-owned model lacks the scope. This is perhaps thirty lines of infrastructure that prevents the single most damaging class of SaaS bug — and retrofitting it across a mature codebase is a multi-week audit instead.
A cross-tenant data leak is not a bug you recover from with an apology email. Design so that the leak requires deliberate effort, not merely a moment of inattention.
3. Decide the billing model before you write billing code
Billing is where product strategy becomes database schema. Per-seat, per-usage, tiered, or hybrid — each implies a different data model, and switching later means migrating live subscriptions with money attached.
Whatever you choose, these are not optional, and each is far cheaper to design in than to bolt on:
- Proration. What happens when someone upgrades on day 17 of a 30-day cycle?
- Trials and grace periods. What is accessible after a trial ends but before payment lands?
- Dunning. Cards fail constantly. Without automated retries and notifications, involuntary churn quietly becomes your largest churn category.
- An audit trail. Every plan change, every invoice, every refund, immutable. You will need it for a dispute, an accountant, or a customer who is certain they were overcharged.
Use a billing provider such as Stripe rather than building this. But model the subscription state in your own database too — you need to answer "what can this tenant do right now?" without a network call to a third party.
4. Roles and permissions, per tenant
Authentication is straightforward. Authorisation is where products get painted into corners. Two questions decide the architecture:
Can a single user belong to multiple tenants? If yes — and for anything B2B, the answer eventually becomes yes when agencies and consultants show up — then permissions belong on the user-tenant relationship, not on the user. Retrofitting this later touches every authorisation check in the codebase.
Can tenants define their own roles? Fixed roles (owner, admin, member) are simple and fine for a long time. Custom roles are a genuine enterprise requirement and a substantially larger build. Do not build custom roles speculatively — but do keep the permission check abstracted behind a single interface so adding them later is contained.
Design the invitation flow properly at the same time: invite by email, accept, revoke, transfer ownership, and remove-user-without-deleting-their-work. Every one of these gets asked for within the first ten customers.
5. Background jobs and the noisy neighbour problem
One customer imports a 400 MB CSV. Your queue fills with their work. Every other tenant's exports, emails, and webhooks stall behind it, and your support inbox fills with people whose product just appeared broken.
Two mitigations, both cheap to add early and awkward to add late: separate queues by job class so fast work never queues behind slow work, and record the tenant on every job so you can measure, rate-limit, and isolate per-tenant load. The second one also makes your bills explicable — you will eventually want to know which customer costs you the most to serve.
6. Observability with a tenant dimension
Aggregate metrics hide exactly the problems that matter in SaaS. A p95 response time of 400ms across all tenants can conceal one customer consistently experiencing eight seconds because their dataset is twenty times larger than everyone else's — and they are the one about to churn.
Tag logs, traces, and error reports with the tenant identifier from the first deploy. Then build the internal admin view that answers, for any single customer: what is their usage, when did they last log in, what errors have they hit this week. Your support team will use it every day and your sales team will use it in renewal conversations.
What to deliberately skip in the MVP
Being early does not mean building everything early. Safe to defer, because each can be added without restructuring what exists:
- SSO and SAML — until an enterprise deal actually requires it
- Custom roles — fixed roles carry you a long way
- White-labelling and custom domains — real work, near-zero early demand
- A public API — unless the API is the product
- Multi-region deployment — solve it when latency or residency is a signed-contract requirement
The distinction is simple: defer anything additive, get right anything that touches the shape of your data.
The pattern underneath all six
Each of these is cheap to decide correctly at the start and expensive to change once real customers hold real data. That asymmetry is what should drive your architectural attention early — not scale, not performance, not the technology choices that generate the most discussion.
If you are scoping a SaaS build and want these decisions pressure-tested against your specific model before you commit, that is what the architecture phase of our SaaS Product Development service exists to do.
