Architecting a SaaS Platform That Survives Its Own Growth
Rewrites are rarely caused by the language or the framework. They are caused by three decisions taken in the first month and never revisited: how tenants are isolated, how slow work is executed, and how billing state is derived. Get those right and the rest of the system can be refactored gradually.
Choose a tenancy model deliberately
A shared schema with a tenant identifier on every row is the cheapest to operate and the easiest to migrate, provided the isolation is enforced at the database layer through row-level security rather than by remembering to add a filter in application code.
Schema-per-tenant makes sense when customers demand data separation contractually or when per-tenant customisation is unavoidable. It is significantly more expensive to migrate once you cross a few hundred tenants, so decide before, not after.
- Enforce isolation in the database, never only in the ORM query
- Put the tenant identifier in the primary index of every large table
- Write an automated test that asserts one tenant cannot read another
Move slow work off the request path
Report generation, exports, third-party sync, email and webhook delivery all belong in a queue with retries, dead-letter handling and idempotency keys. A request handler that calls three external APIs will eventually be the reason your platform looks slow.
Make every background job safe to run twice. At-least-once delivery is the norm, and idempotency is far cheaper to build in early than to retrofit after duplicate invoices reach customers.
Treat billing as derived state
Store usage events immutably and compute invoices from them. When a customer disputes a charge you need to replay exactly what happened, and a mutable counter cannot tell that story.
Plan for the awkward cases up front: mid-cycle plan changes, proration, GST on Indian invoices, failed payments and grace periods. These are business rules, so keep them in one module rather than scattered through the checkout flow.
Make observability a feature
Structured logs with a tenant identifier, request tracing across services, and dashboards for the handful of metrics that predict churn — error rate, p95 latency, queue depth, failed payments — are what let a small team support a large customer base.
Keep the schema migration path open
Expand, migrate, contract: add the new column, backfill it, write to both, switch reads, then drop the old one. Migrations that require downtime are acceptable for the first ten customers and unacceptable for the next thousand.
Key takeaway
Tenancy, queues and billing are the decisions worth arguing about early. Everything else can be improved release by release.
Want this applied to your business?
Makandaax Private Limited builds AI, software and SaaS systems for Indian and global teams, on-site and through hybrid delivery models.
Talk to our team