Early in a SaaS product, speed matters more than perfection, and most technical decisions can be revisited later without much pain. A small number can't. Getting these right at the start costs days. Getting them wrong costs months later on.
How tenants are isolated
Shared tables with a tenant column, a schema per tenant, or a database per tenant: each has different trade-offs for cost, isolation and operational complexity. For most early products we choose shared tables with row-level security enforced in the database, not only in application code.
alter table projects enable row level security;
create policy tenant_isolation on projects
using (tenant_id = current_setting('app.tenant_id')::uuid);With the policy in the database, a missing filter in one query can't leak another customer's data. That is a mistake you only want to make on paper.
Identity and roles
Decide early whether a user can belong to more than one organisation, and model roles per membership rather than per user. Retrofitting this after launch touches every permission check in the product.
Billing as a domain, not a plugin
Plans, limits, trials and usage belong to your domain model even if a payment provider handles the money. Keep an internal record of what each tenant is entitled to, and treat the provider's webhooks as events that update it.
What can wait
- Microservices. A well-structured monolith is easier to change while the product is still finding its shape.
- Multi-region deployment, until customers or regulation require it.
- A custom design system. Start with consistent components and extract a system once patterns repeat.
The skill is not in making every decision perfectly. It is knowing which ones deserve the extra care now, and which ones can safely stay simple until the product has earned the complexity.