Architecture, RevOps · · por Michael Wybraniec

Commercial architecture es una cadena, no una pricing page

La monetización es una cadena gobernada con actores claros — product, GTM, platform, ops, billing, customer, FinOps, legal — no una pricing page ni un “añade Stripe después”.

La mayoría de equipos publica una pricing page y lo llama monetization. Luego añaden Stripe. Luego hardcodean “Pro gets feature X” en tres rutas de API. Luego ops necesita una cuenta complimentary y alguien abre un hotfix. Luego llega la factura del vendor y nadie sabe qué tenant quemó el margen.

Ese patrón es habitual porque el atajo de soft-launch — “pricing + FinOps + growth” — suena completo. Es necesario pero incompleto. Un modelo commercial serio no es un documento, una página o una integración de billing. Es un system of capabilities con owners nombrados, una secuencia clara y un único product catalog como source of truth.

Este artículo es project-agnostic: una commercial architecture portable que cualquier equipo SaaS o multi-tenant puede reutilizar. Misma idea que en architecture de dominio en este blog — boundaries first, enforce at the edge, no inventes una segunda verdad en el payment provider.

  • Monetization es una chain: catalog → agreement → provision → entitlements → meter → bill → cash (+ FinOps cost loop).
  • One catalog alimenta pricing, admin, entitlements y billing — nunca una segunda lista de plans en el MoR.
  • Entitlements ≠ feature flags; deny con un upgrade path claro.
  • Ship Encode (catalog + enforce + gates + admin) antes de Charge; legalize antes del primer pago real.
  • Cost ≠ revenue — FinOps y pricing son ledgers separados.
  • Quédate con el cheat sheet y la lista Start Monday al final.

Los stacks líderes de billing y entitlements (Stripe Billing / Chargebee-class, capas entitlement + meter, práctica FinOps en el lado del coste) tratan la monetization como one governed chain. Marketing puede empezar con una pricing page. Architecture empieza aquí:

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)

Cada flecha es un handoff. Si saltas un eslabón — por ejemplo billing sin entitlements, o entitlements sin catalog — el siguiente equipo inventa su propia verdad. Así acabas con tres listas de plans: una en la web, una en el folklore de admin, una en Stripe.

Matiz: entitlements y metering no son solo left-to-right. Los caps a menudo leen meters (“allow if usage is under the limit”), así que los meters alimentan entitlements, billing y FinOps. Mantén la chain de siete pasos como historia principal; el cap check es un feedback edge, no una excusa para fusionar “entitle + meter + bill” en un solo blob.

También hay un parallel cost loop. Revenue pregunta “¿qué pagaron?” Cost pregunta “¿qué pagamos nosotros por servirles?” Mezclar ambas es cómo los equipos underprice para siempre o cortan features en pánico sin datos.

---
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

Los meters alimentan entitlements (caps), invoices (más tarde) y FinOps (siempre). Puedes meter para caps y fair use mucho antes de cobrar una tarjeta. Las flechas discontinuas son feedback: usage informa allow/deny y cost; cost informa packaging.


FinOps es solo un actor — y solo en el lado del coste. La chain falla cuando todo se vuelca en “pricing”, “billing” o “engineering will add a flag”. Nombra a los owners, aunque al principio una persona lleve tres hats.

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 alinea sales, marketing y customer success alrededor del revenue lifecycle — quién posee activation, expansion, renewal y señales de churn. Suele importar después de Charge. Al principio suelen ser runbooks de founder + CS, no un equipo dedicado ni una superficie de producto.

Dos caminos, mismo sistema — ledgers distintos:

---
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 viven en el revenue ledger. FinOps vive en el cost ledger. Ninguno sustituye al otro. Cuando el margin falla, FinOps informa a Product; cuando un tenant supera el limit, Platform enforce y Ops puede override — con audit trail, no un DM de Slack y un deploy olvidado.


Estas rules son la diferencia entre “tenemos pricing” y “tenemos commercial architecture”. Si las saltas, reconstruirás el mismo lío cada seis meses. Bajo cada rule: una strategy que puedes shippear sin esperar al billing suite perfecto.

3.1 One product catalog

Plans, SKUs, limits y trial rules viven en una source of truth. Pricing page, admin y billing la leen todos.

Strategy: Pon el catalog en código versionado o una tabla DB pequeña — plan_id, display name, limits, trial days, module defaults. La /pricing pública y el copy i18n importan ese modelo; no redefinen tiers. Cuando llegue billing, crea productos MoR desde los catalog IDs (map 1:1), nunca al revés.

---
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 = verdad commercial + security en request time.

Strategy: Un resolver: tenant → plan + overrides → { modules, caps }. Llámalo desde API middleware o una policy layer compartida en cada path de pago/coste. Reserva flags estilo LaunchDarkly para “¿está live este code path?”, no para “¿este tenant está en 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 forma parte del engine

Un fallo de entitlement devuelve un mensaje claro de upgrade / denied, nunca un silent fail.

Strategy: Deny payload estándar: code, limit, current, upgrade_to, cta_url opcional. La UI mapea codes a copy (“Storage full — upgrade to Plus”). Loguea denials con tenant + capability para product/FinOps. Nunca un 403 crudo con body vacío.

3.4 Meter before you bill

Incluso productos seat/subscription hacen metering para caps, fair use y FinOps.

Strategy: Emite usage events en los edges que cuestan dinero (email, storage, AI tokens, maps, outbound sync). Guarda tenant_id, metric, quantity, at. Empieza con events append-only + totales en admin — sin math de invoice todavía. Los caps leen los mismos meters después; billing los ratea después.

3.5 Overrides son first-class

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

Strategy: Modela overrides como data en el tenant: plan_override, cap_overrides, complimentary_until, notes. Admin UI escribe; cada write añade who / when / what / before / after. El resolver mergea catalog defaults ← overrides. Sin hotfix branches para “dales Plus”.

3.6 Growth is gated

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

Strategy: Tabla de global settings (o config service): registration_mode (invite / open), monetization_mode (soft_launch / trial_enforced / paid_enforced), kill-switches por capability cara. Enforce en create-tenant y en features costosas. Defaults = soft-launch de hoy; pasar a paid es un settings change, no un release train.

3.7 Cost y revenue son ledgers separados

FinOps (lo que pagamos) nunca sustituye Pricing (lo que pagan ellos).

Strategy: Dos vistas: revenue (plan, MRR, invoices) y cost (vendor invoices + usage allocated). Unit economics = cost per tenant vs price per tenant. Cuando el margin se rompe, cambia package/price o kill-switch — no lo “arregles” fingiendo que el cost es una plan feature.

3.8 Legalize before charge

Entity, tax/VAT, MoR vs Connect (o equivalente) como policy escrita antes del billing go-live.

Strategy: Un go-live memo corto: quién vende, quién factura, tratamiento VAT, MoR vs marketplace Connect, cuándo empiezan los books a reconocer revenue. Bloquea checkout de production hasta que exista el memo. Product puede terminar Encode en paralelo; Charge espera al memo, no a un teatro legal perfecto para siempre.

Commercial object (general): a menudo el tenant / account / workspace es el SKU primario. Los seats pueden llegar después. Forzar “per user” como único modelo temprano encierra el packaging en una forma que puede no encajar con value (o cost). Strategy: price y entitle el tenant primero; añade seat SKUs cuando el cost o value por seat esté probado.


No necesitas diez product epics el día uno. Sí necesitas tres layers que sigan distintos en design, aunque el delivery se comprima.

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 es aburrido a propósito: plans y limits versionables. Si /pricing es una tabla hecha a mano que discrepa del runtime, no tienes catalog — tienes marketing.

Define — solution: un módulo Catalog exportado a web, API y (luego) jobs de sync de billing. Snapshot o version ID en cada entitlement resolution para que el audit sepa qué catalog aplicó.

Enforce es donde la verdad commercial encuentra el request path. Resolve plan → modules/caps allowed → allow o deny. Soft-launch puede seguir siendo “pilots get broad access”, pero como override o mode explícito — no como “enforcement doesn’t exist yet”.

Enforce — solution: middleware / policy helper en cada ruta costosa; global growth settings + per-tenant overrides; pilot = complimentary o monetization_mode = soft_launch, no checks ausentes.

Monetize & operate es donde viven money y margin. Viene después de Define + Enforce salvo que la ley fuerce billing primero — y aun así, catalog + entitlements siguen siendo el product SoT.

Monetize — solution: productos MoR clonados desde catalog IDs; webhooks actualizan tenant.plan / paid flags; portal lee el mismo catalog + meters. FinOps empieza como spreadsheet + vendor dashboards; budgets in-app solo cuando existan meters.

Sequence rule: no implementes billing / portal antes de Define + Enforce. La legal / money-flow policy viene antes del billing go-live. Billing no debe inventar una segunda plan list.

After you charge: los subscription webhooks (o equivalente) son el SoT del paid state — actualizan plan / entitlements. El billing provider mapea 1:1 al catalog. Es SoT de si pagaron; no es SoT de lo que un plan significa.

---
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 es el target, estos atajos la rompen. Cada uno con un recovery move concreto:

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

Orden de recovery cuando ya tienes un lío:

---
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 desde la pricing page y dashboards MoR → wire one resolver → centraliza hard-coded checks → admin overrides → solo entonces confía en webhooks como paid-state SoT.


Architecture es abstracta hasta que ops puede moverse sin engineering.

World-class se siente así: un pilot necesita complimentary Plus treinta días — ops flips a flag, pone expiry, audit log who/why. Sube el vendor cost — alguien togglea un kill-switch en una integración cara sin deploy de viernes noche. Soft-launch cierra — cambian registration_mode y monetization_mode, y create-tenant / upgrade paths siguen al momento.

Si cualquiera de eso sigue pidiendo un hotfix branch, la chain está incompleta. Admin no es un UI nice-to-have; es cómo los overrides se vuelven first-class en lugar de excepciones tribales.


La capacidad es finita. Aun así puedes mantener boundaries aunque combines delivery. El error no es shippear Encode en un milestone — es shippear Encode + Stripe como un blob indiferenciado.

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 es la barra de soft-launch que aún se ve industry-shaped: one catalog, enforce en el request path, gate growth, ops override con audit. Puede que aún no cobres — y está bien.

Encode — playbook: (1) catalog model + /pricing cableada, (2) entitlement resolver + tests Free vs Paid vs pilot, (3) global registration/monetization settings, (4) admin plan/override/audit UI. Cuatro workstreams en un milestone si hace falta — nunca un blob FR único.

Charge order: legalize → subscribe / renew / cancel → webhook updates plan. Nunca “crea products en el MoR primero y reverse-engineer el catalog después”.

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

Scale es cuando customers ven usage e invoices in-product, los meters se enriquecen y FinOps puede pasar de spreadsheets a alerts. Optional — no es prerequisite para cobrar limpio.

Scale — playbook: deepen meters (admin first) → customer usage + invoices → optional budget alerts ligados a kill-switches.

Parallel ops track (not product): review mensual de vendor bills, disciplina de margin en el first customer, y playbooks RevOps pueden vivir como runbooks mientras Encode ships. No esperes a un FinOps UI in-app para practicar cost hygiene — y no confundas esos rituales con la commercial chain.


Úsalo como barra, no como vanity checklist. La commercial architecture es “real” cuando:

  • Cambiar un plan limit no exige cazar hard-coded checks en rutas random
  • Entitlements denied muestran upgrade / denied path claro — nunca silent fail
  • Ops puede dar complimentary access o freeze a costly capability sin deploy
  • Pricing page, admin y (luego) billing muestran el mismo catalog
  • Registration / monetization mode / kill-switches son platform settings, no memoria tribal
  • Money-flow policy (entity, tax, MoR) existe antes del primer charge real
  • Paid state fluye billing events → plan / entitlements (sin segunda plan list en el MoR)
  • Usage que cuesta dinero es visible antes de que la invoice te sorprenda
  • Un FinOps soft ceiling puede disparar un kill-switch, no solo un pánico en Slack

Haz screenshot. Es el artículo entero en una 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

Una pricing page vende la historia. Commercial architecture la hace enforceable.


Cinco moves para empezar Encode sin esperar a Stripe:

  1. Lista cada hard-coded limit — busca en routes nombres de plan, caps y checks “if Pro…”; ponlos en una página.
  2. Nombra el commercial object — tenant / account / workspace (seats después, salvo prueba).
  3. Draft catalog IDs — Free / Trial / Paid… con limits y trial rules; cablea /pricing solo a ese modelo.
  4. Elige los knobs — registration_mode, monetization_mode, y qué features caras llevan kill-switches.
  5. Nombra quién puede override — un ops owner para complimentary / cap overrides + hábito de audit log (aunque sea un spreadsheet al inicio).

Cuando esas cinco son verdad, tienes el skeleton de Encode. Charge es una decisión posterior — no un sustituto de este trabajo.


Una pricing page vende la historia. Stripe (o cualquier MoR) mueve el dinero. Ninguno es la architecture.

Commercial architecture es la chain — catalog → agreement → provision → entitlements → meter → bill → cash — más el cost loop, más los actors que poseen cada eslabón. Construye Define y Enforce antes de celebrar Charge. Mantén one catalog. Trata overrides y growth gates como product, no como folklore.

Esa forma funciona en cualquier project. Los nombres de plans y providers cambiarán. La chain no debería.

Michael Wybraniec

Michael Wybraniec

Diseño de sistemas, automatización GenAI y arquitectura