Multi-tenancy means every customer uses the same application while each sees only their own data. It sounds like any other requirement. It is the one you effectively cannot retrofit without rewriting the database.
Why retrofitting gets expensive
Start without a tenancy model and you end up filtering in the application. Every query gains an extra condition on the organisation. That works as long as every query remembers. At two hundred queries one will not, and then customer A sees customer B's data.
What separates this from an ordinary defect is the class of damage. A miscalculated discount is a ticket. A leak between two customers is a reportable GDPR incident, a loss of trust, and depending on the sector a contractual problem.
Where the separation belongs
In the database. Row level security in PostgreSQL enforces the separation where the data lives: a query without a valid tenant context returns no rows, whatever the application code forgot. The application can no longer bypass the rule, because it no longer knows it.
- A tenant column on every table holding customer data — no exceptions
- Row level security enabled, not merely defined
- The tenant context comes from the session, never from a request parameter
- A test that queries without context and proves nothing comes back
What that looks like in practice
For Enclessa, confidentiality was the precondition of the product: direct messages are end-to-end encrypted with MLS (RFC 9420), hosted in the EU. For TixFin it is organisers sharing seat maps and payments in real time without two organisers ever seeing the same row. Both settled their tenancy model before the first migration, because we knew the alternative was building it twice.
Any decision you can change later is one you may take later. Data separation is not one of them.
The check
Taking over an existing platform, this is the first question: what does the separation hang on? If the answer lives in the application, the honest estimate for changing it is not a sprint but a migration with a downtime window. That is why we read the code before writing a quote.