Architecture, RevOps · · per Michael Wybraniec

Commercial architecture est une chaîne, pas une pricing page

La monetization est une chaîne gouvernée avec des actors clairs — product, GTM, platform, ops, billing, customer, FinOps, legal — pas une pricing page ni un “ajoute Stripe plus tard”.

La plupart des équipes publient une pricing page et appellent ça monetization. Puis elles ajoutent Stripe. Puis quelqu’un hardcode “Pro gets feature X” dans trois routes API. Puis ops a besoin d’un compte complimentary et quelqu’un ouvre un hotfix. Puis la facture vendor arrive et personne ne sait quel tenant a brûlé la marge.

Ce pattern est courant parce que le raccourci soft-launch — “pricing + FinOps + growth” — sonne complet. C’est nécessaire mais incomplet. Un vrai modèle commercial n’est pas un document, une page ou une intégration billing. C’est un system of capabilities avec des owners nommés, une séquence claire et un unique product catalog comme source of truth.

Cet article est project-agnostic : une commercial architecture portable qu’une équipe SaaS ou multi-tenant peut réutiliser. Même idée que l’architecture de domaine ailleurs sur ce blog — boundaries first, enforce at the edge, n’invente pas une seconde vérité dans le payment provider.

  • Monetization est une chain : catalog → agreement → provision → entitlements → meter → bill → cash (+ FinOps cost loop).
  • One catalog alimente pricing, admin, entitlements et billing — jamais une seconde plan list dans le MoR.
  • Entitlements ≠ feature flags ; deny avec un upgrade path clair.
  • Ship Encode (catalog + enforce + gates + admin) avant Charge ; legalize avant le premier paiement réel.
  • Cost ≠ revenue — FinOps et pricing sont des ledgers séparés.
  • Prends le cheat sheet et la liste Start Monday à la fin.

Les stacks leaders de billing et entitlements (Stripe Billing / Chargebee-class, couches entitlement + meter, pratique FinOps côté coût) traitent la monetization comme one governed chain. Le marketing peut commencer par une pricing page. L’architecture commence ici :

Catalog (what we sell)
  → Quote / order / subscription (commercial agreement)
    → Provisioning (tenant exists)
      → Entitlements (what runtime may do)
        → Metering (what was used)
          → Rating & billing (what we invoice)
            → Cash & revenue ops (collected, recognized)

Chaque flèche est un handoff. Si tu sautes un maillon — billing sans entitlements, ou entitlements sans catalog — l’équipe suivante invente sa propre vérité. Résultat : trois plan lists — site, folklore admin, Stripe.

Nuance : entitlements et metering ne sont pas seulement left-to-right. Les caps lisent souvent les meters (“allow if usage is under the limit”), donc les meters nourrissent entitlements, billing et FinOps. Garde la chain à sept étapes comme récit principal ; le cap check est un feedback edge, pas une excuse pour fusionner “entitle + meter + bill” en un seul blob.

Il y a aussi un parallel cost loop. Revenue demande “qu’ont-ils payé ?” Cost demande “qu’avons-nous payé pour les servir ?” Confondre les deux, c’est underprice pour toujours ou couper des features dans la panique sans data.

---
header: Commercial architecture - revenue chain and FinOps cost loop
legend:
  - color: "#3B82F6"
    text: "Revenue chain"
  - color: "#F59E0B"
    text: "Cost loop (FinOps)"
---
flowchart TB
  subgraph Revenue["Revenue chain"]
    direction LR
    A[Catalog] --> B[Agreement]
    B --> C[Provision]
    C --> D[Entitlements]
    D --> E[Metering]
    E --> F[Billing]
    F --> G[Cash / RevOps]
    E -.->|caps / fair use| D
  end
  subgraph Cost["Cost loop (FinOps)"]
    direction LR
    H[Allocate spend] --> I[Unit economics]
    I --> J[Budgets / kill-switches]
    J --> K[Price or package change]
  end
  E -.-> H
  K -.-> A
  classDef revenue fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
  classDef cost fill:#F59E0B,stroke:#D97706,stroke-width:2px,color:#fff
  class A,B,C,D,E,F,G revenue
  class H,I,J,K cost

Les meters alimentent entitlements (caps), invoices (plus tard) et FinOps (toujours). Tu peux meter pour caps et fair use bien avant de charger une carte. Les flèches en pointillés sont du feedback : usage informe allow/deny et cost ; cost informe packaging.


FinOps n’est qu’un actor — et seulement côté coût. La chain casse quand tout est déversé sur “pricing”, “billing” ou “engineering will add a flag”. Nomme les owners, même si une personne porte trois hats au début.

ActorOwns in the chain
Product / packagingCatalog: plans, SKUs, limits, trials
GTM / growthWho may join; invite vs open; soft-launch vs paywall
Platform / engineeringProvisioning, entitlements at request time, metering events
Ops / adminLive overrides, caps, complimentary access, kill-switches + audit
Billing / financeSubscriptions, invoices, renewals, revenue recognition
Customer (buyer)Plan, usage, invoices (portal)
FinOpsWhat we pay; margin; budgets; cost kill-switches
Legal / complianceTax, merchant-of-record vs Connect, entity — before “charge” go-live
RevOps / CS (later)Activate, expand, churn, support load

RevOps aligne sales, marketing et customer success autour du revenue lifecycle — qui possède activation, expansion, renewal et signaux de churn. Ça compte surtout après Charge. Au début, ce sont souvent des runbooks founder + CS, pas une équipe dédiée ni une surface produit.

Deux chemins, même système — ledgers distincts :

---
header: Actors on revenue vs cost ledgers
legend:
  - color: "#3B82F6"
    text: "Revenue ledger"
  - color: "#F59E0B"
    text: "Cost ledger"
---
flowchart LR
  subgraph Rev["Revenue ledger"]
    direction LR
    P[Product] --> GTM[GTM] --> PL[Platform] --> OPS[Ops] --> BIL[Billing] --> CU[Customer]
  end
  subgraph Cost["Cost ledger"]
    direction LR
    U[Usage / infra] --> FO[FinOps] --> FB[Package / price / gates]
  end
  PL -.-> U
  FB -.-> P
  classDef revenue fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
  classDef cost fill:#F59E0B,stroke:#D97706,stroke-width:2px,color:#fff
  class P,GTM,PL,OPS,BIL,CU revenue
  class U,FO,FB cost

Pricing / catalog / billing vivent sur le revenue ledger. FinOps sur le cost ledger. Ni l’un ni l’autre ne remplace l’autre. Quand la margin casse, FinOps informe Product ; quand un tenant dépasse un limit, Platform enforce et Ops peut override — avec audit trail, pas un DM Slack et un deploy oublié.


Ces rules font la différence entre “on a du pricing” et “on a une commercial architecture”. Les sauter, c’est reconstruire le même chaos tous les six mois. Sous chaque rule : une strategy shippable sans attendre la billing suite parfaite.

3.1 One product catalog

Plans, SKUs, limits et trial rules vivent dans une source of truth. Pricing page, admin et billing la lisent tous.

Strategy : mets le catalog en code versionné ou petite table DB — plan_id, display name, limits, trial days, module defaults. La /pricing publique et le copy i18n importent ce modèle ; elles ne redéfinissent pas les tiers. Quand le billing arrive, crée les produits MoR depuis les catalog IDs (map 1:1), jamais l’inverse.

---
header: One catalog as source of truth
legend:
  - color: "#10B981"
    text: "Catalog SoT"
  - color: "#3B82F6"
    text: "Surfaces that read the catalog"
---
flowchart TB
  CAT[(Catalog SoT)]
  CAT --> WEB[Pricing page]
  CAT --> ADM[Admin console]
  CAT --> RES[Entitlement resolver]
  CAT --> MOR[Billing MoR 1:1 SKUs]
  classDef sot fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
  classDef consumer fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
  class CAT sot
  class WEB,ADM,RES,MOR consumer

3.2 Entitlements ≠ feature flags

Flags = rollout. Entitlements = vérité commercial + security au request time.

Strategy : un resolver — tenant → plan + overrides → { modules, caps }. Appelle-le depuis l’API middleware ou une policy layer partagée sur chaque path payant/coûteux. Garde les flags type LaunchDarkly pour “ce code path est-il live ?”, pas pour “ce tenant est-il sur Pro ?”.

---
header: Feature flags vs entitlement resolver
legend:
  - color: "#3B82F6"
    text: "Request / resolve inputs"
  - color: "#8B5CF6"
    text: "Decision"
  - color: "#10B981"
    text: "Allow"
  - color: "#EF4444"
    text: "Deny / not shipped"
---
flowchart TD
  R[API request] --> F{Feature flag: code live?}
  F -->|No| X[Not shipped]
  F -->|Yes| V[Entitlement resolver]
  V --> C[Catalog defaults]
  V --> O[Tenant overrides]
  V --> M[Monetization / kill-switch mode]
  C --> D{Allowed?}
  O --> D
  M --> D
  D -->|Yes| OK[Proceed + meter if needed]
  D -->|No| DENY[Structured deny + upgrade CTA]
  classDef ok fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
  classDef deny fill:#EF4444,stroke:#DC2626,stroke-width:2px,color:#fff
  classDef decide fill:#8B5CF6,stroke:#7C3AED,stroke-width:2px,color:#fff
  classDef input fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
  class OK ok
  class DENY,X deny
  class F,D decide
  class R,V,C,O,M input

3.3 Denied UX fait partie du engine

Un échec d’entitlement renvoie un message upgrade / denied clair, jamais un silent fail.

Strategy : deny payload standard — code, limit, current, upgrade_to, cta_url optionnel. L’UI mappe les codes vers le copy (“Storage full — upgrade to Plus”). Log les denials avec tenant + capability pour product/FinOps. Jamais un 403 brut avec body vide.

3.4 Meter before you bill

Même les produits seat/subscription font du metering pour caps, fair use et FinOps.

Strategy : émets des usage events aux edges qui coûtent (email, storage, AI tokens, maps, outbound sync). Stocke tenant_id, metric, quantity, at. Commence append-only + totaux admin — pas de math d’invoice encore. Les caps liront les mêmes meters plus tard ; le billing les rate plus tard.

3.5 Overrides sont first-class

Complimentary access, custom caps, extend trial — avec audit.

Strategy : modèle les overrides comme data sur le tenant — plan_override, cap_overrides, complimentary_until, notes. L’admin UI écrit ; chaque write append who / when / what / before / after. Le resolver merge catalog defaults ← overrides. Pas de hotfix branch pour “donne-leur Plus”.

3.6 Growth is gated

Invite vs open, monetization mode, cost kill-switches — sans redeploy.

Strategy : table de global settings (ou config service) — registration_mode (invite / open), monetization_mode (soft_launch / trial_enforced / paid_enforced), kill-switches par capability coûteuse. Enforce à la création de tenant et sur les features chères. Defaults = soft-launch d’aujourd’hui ; passer en paid est un settings change, pas un release train.

3.7 Cost et revenue sont des ledgers séparés

FinOps (ce que nous payons) ne remplace jamais Pricing (ce qu’ils paient).

Strategy : deux vues — revenue (plan, MRR, invoices) et cost (vendor invoices + usage alloué). Unit economics = cost per tenant vs price per tenant. Quand la margin casse, change package/price ou kill-switch — ne “fixe” pas en prétendant que le cost est une plan feature.

3.8 Legalize before charge

Entity, tax/VAT, MoR vs Connect (ou équivalent) en policy écrite avant le billing go-live.

Strategy : un go-live memo court — qui vend, qui facture, traitement VAT, MoR vs marketplace Connect, quand les books commencent à reconnaître le revenue. Bloque le checkout production tant que le memo n’existe pas. Product peut finir Encode en parallèle ; Charge attend le memo, pas un théâtre légal parfait pour toujours.

Commercial object (général) : souvent le tenant / account / workspace est le SKU primaire. Les seats peuvent venir plus tard. Forcer “per user” comme seul modèle tôt verrouille le packaging dans une forme qui peut ne pas matcher value (ou cost). Strategy : price et entitle le tenant d’abord ; ajoute des seat SKUs quand le cost ou value par seat est prouvé.


Tu n’as pas besoin de dix product epics le jour un. Tu as besoin de trois layers distincts en design, même si le delivery est compressé.

LayerCapabilitiesQuestion
DefineProduct catalog & packagingWhat SKUs/plans exist? Limits? Trial?
EnforceEntitlements, growth/policy gates, admin overridesWhat may this tenant do right now? Who may join? Can ops change it live?
Monetize & operateMetering, billing, customer portal, FinOps, money-flow/compliance, RevOpsHow do we charge, show usage, protect margin, stay legal?
---
header: Capability layers - Define, Enforce, Monetize
---
flowchart TB
  D["1. Define - catalog and packaging"]
  E["2. Enforce - entitlements, gates, admin"]
  M["3. Monetize - meter, bill, portal, FinOps"]
  D --> E --> M

Define est volontairement ennuyeux : plans et limits versionnables. Si /pricing est un tableau hand-written qui diverge du runtime, tu n’as pas de catalog — tu as du marketing.

Define — solution : un module Catalog exporté vers web, API et (plus tard) jobs de sync billing. Snapshot ou version ID à chaque entitlement resolution pour que l’audit sache quel catalog s’applique.

Enforce est où la vérité commercial rencontre le request path. Resolve plan → modules/caps allowed → allow ou deny. Soft-launch peut encore vouloir dire “pilots get broad access”, mais via override ou mode explicite — pas “enforcement doesn’t exist yet”.

Enforce — solution : middleware / policy helper sur chaque route coûteuse ; global growth settings + per-tenant overrides ; pilot = complimentary ou monetization_mode = soft_launch, pas l’absence de checks.

Monetize & operate est où vivent money et margin. Ça vient après Define + Enforce sauf contrainte légale de billing d’abord — et même alors, catalog + entitlements restent le product SoT.

Monetize — solution : produits MoR clonés depuis catalog IDs ; webhooks mettent à jour tenant.plan / paid flags ; portal lit le même catalog + meters. FinOps commence en spreadsheet + vendor dashboards ; budgets in-app seulement quand les meters existent.

Sequence rule : n’implémente pas billing / portal avant Define + Enforce. La legal / money-flow policy vient avant le billing go-live. Billing ne doit pas inventer une seconde plan list.

After you charge : les subscription webhooks (ou équivalent) sont le SoT du paid state — ils mettent à jour plan / entitlements. Le billing provider mappe 1:1 au catalog. SoT du “ont-ils payé” ; pas SoT de ce qu’un plan signifie.

---
header: Checkout - catalog meaning vs MoR paid state
---
sequenceDiagram
  participant Buyer
  participant App
  participant MoR as Billing MoR
  participant Cat as Catalog
  participant Ent as Entitlements
  Buyer->>App: Checkout
  App->>Cat: Resolve plan SKU
  App->>MoR: Create or confirm subscription
  MoR-->>App: Webhook paid / renewed / canceled
  App->>Ent: Update tenant plan from webhook
  Note over Cat,Ent: MoR equals paid state - Catalog equals plan meaning

Si la chain est la cible, ces raccourcis la cassent. Chaque anti-pattern a un recovery move :

Anti-patternFix
Pricing page as the productDrive the page from the catalog; delete duplicate hard-coded tiers
Payment provider as second catalogMoR SKUs = catalog IDs only; regenerate from catalog, never edit live in the dashboard as SoT
Feature flags as entitlementsSplit: flags for rollout, entitlement resolver for commercial allow/deny
Hard-coded limits in random routesCentral caps table + one assertCap() / assertModule() helper
Billing before catalog + entitlementsFreeze checkout; finish Encode; then map MoR 1:1
Silent denialStructured deny codes + upgrade CTA on API and UI
Productizing everything earlyKeep FinOps/RevOps as runbooks until meters and charge path exist

Ordre de recovery quand c’est déjà le bordel :

---
header: Recovery order when commercial architecture is a mess
---
flowchart LR
  A[Extract catalog] --> B[One resolver] --> C[Centralize checks] --> D[Admin overrides] --> E[Webhooks = paid SoT]

Extract depuis pricing page et dashboards MoR → wire one resolver → centralise les hard-coded checks → admin overrides → seulement alors fais confiance aux webhooks comme paid-state SoT.


L’architecture reste abstraite tant qu’ops ne peut pas bouger sans engineering.

World-class, ça ressemble à ça : un pilot a besoin de complimentary Plus trente jours — ops flip un flag, pose une expiry, audit log who/why. Spike de vendor cost — quelqu’un toggle un kill-switch sur une intégration chère sans deploy du vendredi soir. Soft-launch se ferme — registration_mode et monetization_mode changent, et create-tenant / upgrade paths suivent tout de suite.

Si l’un de ces gestes exige encore un hotfix branch, la chain est incomplète. Admin n’est pas un UI nice-to-have ; c’est comment les overrides deviennent first-class au lieu d’exceptions tribales.


La capacité est finie. Tu peux quand même garder des boundaries en combinant le delivery. L’erreur n’est pas de ship Encode dans un milestone — c’est de ship Encode + Stripe comme un blob indifférencié.

Delivery sliceKeep separate in design (even if shipped together)
EncodeCatalog + entitlements + growth gates + admin overrides
ChargeMoney-flow / compliance policy then billing (catalog ↔ subscriptions ↔ entitlements)
ScaleMetering depth + customer portal (+ optional in-app FinOps)
---
header: Encode, Charge, Scale delivery path
legend:
  - color: "#3B82F6"
    text: "Encode"
  - color: "#10B981"
    text: "Charge"
  - color: "#8B5CF6"
    text: "Scale"
---
flowchart LR
  subgraph Encode
    A1[Catalog] --- A2[Entitlements] --- A3[Gates] --- A4[Admin]
  end
  subgraph Charge
    B1[Legalize] --> B2[Subscribe] --> B3[Webhooks update plan]
  end
  subgraph Scale
    C1[Meters] --> C2[Portal] --> C3[Optional FinOps UI]
  end
  Encode --> Charge --> Scale
  classDef encode fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
  classDef charge fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
  classDef scale fill:#8B5CF6,stroke:#7C3AED,stroke-width:2px,color:#fff
  class A1,A2,A3,A4 encode
  class B1,B2,B3 charge
  class C1,C2,C3 scale

Encode est la barre soft-launch qui reste industry-shaped : one catalog, enforce sur le request path, gate growth, ops override avec audit. Tu peux ne pas encore charger — et c’est ok.

Encode — playbook : (1) catalog model + /pricing branchée, (2) entitlement resolver + tests Free vs Paid vs pilot, (3) global registration/monetization settings, (4) admin plan/override/audit UI. Quatre workstreams dans un milestone si besoin — jamais un blob FR unique.

Charge order : legalize → subscribe / renew / cancel → webhook updates plan. Jamais “crée les products MoR d’abord et reverse-engineer le catalog après”.

Charge — playbook : go-live memo → clone catalog into MoR → checkout path → webhooks → UX thin “pay / manage”. Le portal polished attend Scale.

Scale : customers voient usage et invoices in-product, meters plus riches, FinOps peut passer des spreadsheets aux alerts. Optional — pas un prerequisite pour encaisser proprement.

Scale — playbook : deepen meters (admin first) → customer usage + invoices → optional budget alerts liés aux kill-switches.

Parallel ops track (not product) : revue mensuelle des vendor bills, discipline de margin sur le first customer, playbooks RevOps en runbooks pendant qu’Encode ships. N’attends pas un FinOps UI in-app pour pratiquer la cost hygiene — et ne confonds pas ces rituels avec la commercial chain.


Utilise ça comme barre, pas comme vanity checklist. La commercial architecture est “réelle” quand :

  • Changer un plan limit n’exige pas de chasser des hard-coded checks dans des routes random
  • Entitlements denied exposent un upgrade / denied path clair — jamais silent fail
  • Ops peut donner complimentary access ou freeze une costly capability sans deploy
  • Pricing page, admin et (plus tard) billing montrent le même catalog
  • Registration / monetization mode / kill-switches sont des platform settings, pas de la mémoire tribale
  • Money-flow policy (entity, tax, MoR) existe avant le premier charge réel
  • Paid state circule billing events → plan / entitlements (pas de seconde plan list dans le MoR)
  • Usage qui coûte de l’argent est visible avant que l’invoice te surprenne
  • Un FinOps soft ceiling peut déclencher un kill-switch, pas seulement une panique Slack

Screenshot ça. C’est l’article entier sur une card.

CHAIN
  Catalog → Agreement → Provision → Entitlements → Meter → Bill → Cash
  (+ meters → caps / FinOps → package or price)

LAYERS
  1 Define   catalog & packaging
  2 Enforce  entitlements · growth gates · admin overrides
  3 Monetize meter · bill · portal · FinOps · compliance · RevOps

DELIVERY
  Encode → Charge → Scale
  (legalize before Charge; meters before fancy FinOps UI)

NEVER
  · Second catalog in the payment provider
  · Silent 403 with no upgrade path
  · Billing before catalog + entitlements
  · Soft-launch vs paid as tribal memory

Une pricing page vend l’histoire. Commercial architecture la rend enforceable.


Cinq moves pour démarrer Encode sans attendre Stripe :

  1. Liste chaque hard-coded limit — cherche dans les routes les noms de plan, caps et checks “if Pro…” ; mets-les sur une page.
  2. Nomme le commercial object — tenant / account / workspace (seats plus tard, sauf preuve).
  3. Draft catalog IDs — Free / Trial / Paid… avec limits et trial rules ; branche /pricing uniquement sur ce modèle.
  4. Choisis les knobs — registration_mode, monetization_mode, et quelles features chères ont des kill-switches.
  5. Nomme qui peut override — un ops owner pour complimentary / cap overrides + habitude d’audit log (même un spreadsheet au début).

Quand ces cinq points sont vrais, tu as le skeleton d’Encode. Charge est une décision plus tard — pas un substitut à ce travail.


Une pricing page vend l’histoire. Stripe (ou tout MoR) déplace l’argent. Aucun des deux n’est l’architecture.

Commercial architecture est la chain — catalog → agreement → provision → entitlements → meter → bill → cash — plus le cost loop, plus les actors qui possèdent chaque maillon. Construis Define et Enforce avant de célébrer Charge. Garde one catalog. Traite overrides et growth gates comme product, pas comme folklore.

Cette forme marche sur n’importe quel project. Les noms de plans et de providers changeront. La chain ne devrait pas.

Michael Wybraniec

Michael Wybraniec

Conception système, automatisation GenAI & architecture