I have built several multi-tenant SaaS platforms in Laravel — including a WhatsApp business messaging platform serving organizations from a single codebase. Multi-tenancy decisions are the hardest to reverse later, so here is what I wish every founder knew before the first migration runs.
With a shared database, one missing WHERE tenant_id is a data breach. I enforce scoping with a global scope applied through a BelongsToTenant trait on every tenant model, set from the authenticated context — never from request input. Add tests that try to read another tenant's data and assert failure; they are the cheapest security audit you will ever run.
A queued job runs outside the request, so it must carry its tenant explicitly. Serialize the tenant id into every job and rehydrate context in the handler. Also rate-limit per tenant: one customer's bulk import must never starve everyone else's jobs — a lesson I learned in production, the hard way.
Per-tenant feature flags and plan limits enforced in one place; usage metering written as events; billing through Cashier with webhooks handled idempotently; and an admin 'impersonate tenant' tool for support. None of this is exciting, and all of it is what separates a demo from a product.
Multi-tenancy is a security model first and a database layout second.