Presque toutes les réécritures SaaS qu'on nous a appelés à sauver remontent aux trois mêmes mots prononcés par l'équipe d'origine au premier mois : « on ajoutera ça plus tard ». La multi-location, les accès par rôles, la vraie facturation. Elles ressemblent à des choses qu'on visse une fois qu'on a des clients. C'est l'inverse — c'est la forme du bâtiment, et on ne change pas les fondations une fois les murs montés sans tout démolir.
Nous avons construit des plateformes qui tournent aujourd'hui dans le monde entier sur une seule base de code. La raison pour laquelle c'était possible est ennuyeuse et unanime : la tenancy, les accès et la facturation ont été décidés le premier jour, avant la première fonctionnalité. Voici à quoi ressemblent ces décisions — et ce que coûte leur report.
Pourquoi les équipes sautent l'étape (et pourquoi c'est un piège)
La pression est réelle. Vous avez un client pilote, une démo la semaine prochaine, et un backlog de fonctionnalités qui, elles, ressemblent au produit. La multi-location est invisible pour ce premier client — il ne peut pas la voir, donc elle a des airs de dorure. Alors l'équipe construit une app mono-locataire « pour l'instant » et promet de généraliser plus tard.
Le piège : « mono-locataire pour l'instant » laisse fuir une hypothèse dans chaque couche — qu'il existe exactement une organisation au monde. Cette hypothèse finit dans vos requêtes, votre auth, vos caches, vos tâches de fond, vos chemins de fichiers. La retirer plus tard n'est pas un refactoring — c'est une réécriture, parce que l'hypothèse n'est pas à un endroit. Elle est à chaque endroit.
L'asymétrie des coûts :
Ajouter la tenancy le premier jour coûte peut-être deux semaines d'architecture et de discipline. L'ajouter en deuxième année — avec de vrais clients, de vraies données et de vraies attentes de disponibilité — est une réécriture de plusieurs mois sous charge, avec une migration de données sans droit à l'erreur. Le travail ne disparaît pas en le reportant. Il capitalise.
Le locataire est la première colonne
Notre règle est simple : chaque ligne qui appartient à un client porte un tenant_id, et aucun chemin de code ne peut requêter sans lui. Pas une convention dont les gens se souviennent — une contrainte que la base de données et le framework imposent, pour que l'oubli soit impossible plutôt que simplement découragé.
La version la plus propre que nous utilisons s'appuie sur la base elle-même. Les politiques de row-level security placent la frontière de tenancy une couche sous votre code applicatif — là où un bug d'ORM ou une clause WHERE oubliée ne peut plus faire fuiter les données d'un client vers un autre. L'application fixe le locataire courant sur la connexion ; la base refuse de retourner quoi que ce soit d'autre.
postgres · row-level security
-- the boundary lives below the app, not inside it ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders USING (tenant_id = current_setting('app.tenant')::uuid);
-- app sets the tenant per request; a forgotten WHERE -- clause now returns zero rows instead of someone else's data SET app.tenant = 'a91f…'; SELECT * FROM orders; -- only this tenant's orders, always
Cette seule décision retire définitivement de votre liste d'inquiétudes une classe entière de bugs catastrophiques — la fuite de données entre clients. Ce sont les deux jours de travail au plus fort levier de tout le projet.
Le bug le plus cher du SaaS est celui qui montre à un client les données d'un autre. Architecturez pour qu'il ne puisse pas arriver — pas pour qu'il soit improbable.
— Sur l'isolation comme contrainte
Le RBAC n'est pas une page de réglages
Le deuxième « plus tard » qui devient une réécriture, c'est le contrôle d'accès. Les équipes livrent avec un monde implicite à deux rôles — admin et utilisateur — codé en dur dans des if éparpillés. Puis un client enterprise demande un rôle « facturation seule » ou un « auditeur en lecture seule », et vous découvrez de la logique de permissions à deux cents endroits.
Nous modélisons les permissions comme des données dès le départ : rôles, permissions et attributions vivent dans des tables, et chaque action protégée interroge une autorité centrale — « cet acteur peut-il faire cette chose sur cette ressource, dans ce tenant ? ». Ajouter un rôle devient une ligne, pas une release. C'est ce qui permet à une plateforme de dire oui à l'organigramme du client enterprise sans toucher au code applicatif.
- Les permissions sont des verbes sur des ressources —
invoice:read,invoice:void— pas des niveaux vagues comme « manager ». - Les rôles sont des paquets de permissions qu'un tenant peut composer — deux clients peuvent entendre des choses différentes par « admin ».
- Chaque vérification passe par une seule porte. Une logique d'autorisation en un lieu est auditable ; des
ifdispersés sont un passif.
La facturation est du produit, pas de la plomberie
C'est sur la facturation que « on l'ajoutera plus tard » fait le plus mal — parce qu'au moment de l'ajouter, vous avez des clients sur des accords de poignée de main, des données incohérentes sur qui doit quoi, et aucun concept propre d'abonnement. Retrofitter la facturation, c'est réconcilier de l'argent — le seul endroit où un bug n'est pas seulement gênant : c'est un remboursement, un litige ou un problème de conformité.
Nous concevons le modèle de facturation avec le modèle de tenant : plans, abonnements, usage, factures, et le cycle de vie entre eux. Même si les premiers clients sont facturés à la main, le modèle de données est là dès le premier jour — activer le libre-service plus tard est alors une fonctionnalité, pas une migration de tout votre historique de revenus.
Pays en production —
Une base de code, un modèle de déploiement, de nombreux tenants.
Base de code —
Aucun fork par client à maintenir ou laisser diverger.
Fuites entre clients —
Isolation imposée par la base de données, pas par convention.
Beaucoup de pays, une base de code
Atteindre beaucoup de pays sonne comme une histoire d'échelle. C'est en réalité une histoire de configuration. La plateforme n'a pas de branches de code par pays — elle a un modèle de tenant assez riche pour exprimer en données ce qui diffère par région : la devise, les règles fiscales, la langue, les formats de date, le texte légal d'une facture.
Quand la frontière entre « code » et « configuration » est tracée juste dès le premier jour, un nouveau pays est une tâche d'onboarding, pas un projet d'ingénierie. Quand elle est mal tracée, chaque nouveau marché est un fork — et en un an vous maintenez une douzaine de versions subtilement différentes de la même app, en livrant chaque correctif douze fois. Le résultat « une seule base de code » découle de décisions prises avant le premier client.
Le vrai gain :
Le bénéfice de le faire au premier jour n'est pas visible au premier mois — il est visible en troisième année, quand vous livrez une fonctionnalité une fois et que chaque marché la reçoit simultanément, pendant que votre concurrent mono-locataire la merge encore dans son septième fork client.
La checklist du premier jour
Si vous lancez une plateforme SaaS ce trimestre, voici les décisions à prendre avant d'écrire une fonctionnalité — non parce qu'elles sont urgentes, mais parce qu'elles sont irréversibles :
- Un
tenant_idsur tout, imposé sous l'application. Row-level security ou équivalent. Rendez la requête inter-clients impossible, pas improbable. - Des permissions modélisées en données. Rôles et attributions en tables, une porte d'autorisation centrale. Ajouter un rôle doit être une ligne.
- Le modèle de données de facturation tôt. Plans, abonnements, factures — même si vous facturez à la main au début. On ne retrofitte pas l'argent.
- Séparer le code de la configuration. Tout ce qui varie par client ou par région est de la donnée. Le prochain pays doit être un formulaire d'onboarding.
- Écrire d'abord le chemin de provisionnement d'un tenant. Créer proprement un nouveau tenant, avec des valeurs par défaut saines, est votre opération la plus fréquente. Automatisez-la dès le premier jour.
Rien de tout cela n'est exotique. C'est quelques semaines de discipline au départ en échange de ne jamais faire la réécriture. Les équipes qui atteignent de nombreux pays sur une seule base de code n'ont pas eu de chance — elles ont juste refusé de dire « plus tard » aux trois choses qui ne s'ajoutent pas plus tard.
Ces trois décisions « impossibles à ajouter plus tard » occupent la première semaine de chaque plateforme SaaS que nous construisons — dont une qui sert aujourd'hui des clients dans plusieurs pays depuis une seule base de code. Vous planifiez la vôtre ? Parlons avant que le schéma n'existe.

