The Subscription Trap: Why “Just Add Billing” Fails

Digital products and SaaS models have made recurring billing the default. It’s stable, it’s predictable, and from the outside, it looks solved. We have incredible payment infrastructure now—Stripe, Paddle, Adyen—so the assumption is that the hard work is done. You have a sales channel (app, landing page, or B2B sales team), an engineering team building the thing, and finance counting the money.
At first glance, the architecture seems straightforward. Just hook them up, right?
But I’ve been noticing that the devil is almost always in the details of how these groups interact. They might be working on the same product, but they care about fundamentally different things, and they operate on different timelines:

  • Sales lives in a world of negotiation and flexibility. They need to bundle things, change monetization strategies on the fly, and close deals by Friday. If a customer wants a custom package, Sales needs to say “yes” without waiting three weeks for a deployment.
  • Product & Engineering want to build a coherent system and innovate. They want to ship features that solve user problems, not build custom billing logic or if/else statements for every new deal structure that Sales invents.
  • Finance demands accuracy and compliance. They need clean data for dunning, revenue recognition, and controlling. They don’t care about the feature; they care about the SKU and the General Ledger code. If Sales sells a bundle, Finance needs to know exactly how to allocate that revenue—and they need it to be auditable.

The Bottleneck

The conflict usually manifests here: The agility that Sales expects to evolve the business model ends up consuming significant capacity in Product Development.
Every time Sales wants to try a new bundle, a summer promotion, or a new pricing tier, Engineering has to hard-code it. Suddenly, your best developers are building pricing tables and permission checks instead of core features.
The technical debt accumulates rapidly. You end up with a codebase riddled with “temporary” logic for specific enterprise deals. Finance gets frustrated because the data structure changes with every hack, making reporting a nightmare. The result is an organization where Engineering becomes the “Department of No” simply because they are buried in billing logic.

Decoupling as a Survival Strategy

I think the only way out of this is strict decoupling. We have to separate how the product is sold from what is sold, and isolate how both of those affect the financials.
It’s not enough to just “buy a billing tool.” You need an architecture that supports this separation. Currently, I’m seeing these four abstractions as the key to a modern subscription architecture:

  1. Product Definition: The basic definition of the product required for it to be sold. This isn’t the Git repository or the deployable artifact; it is the accounting and finance backbone of the selling motion. It defines the SKUs and the revenue rules.
  2. Entitlements: The connection to the actual product. This is the interface between Sales (how it is sold) and Engineering (what the product can entitle you to). It translates a purchase into a set of permissions.
  3. Usage: Decoupled meters and metrics. An “add-on” to the product definition that reports raw activity, independent of billing logic.
  4. Prices: What do we charge for that access or usage? (The sales layer).

This decoupling isn’t just about prices and product definitions; it’s about isolating ‘Entitlements’ and ‘Usage’. By keeping these layers distinct, Sales can change the price or the bundle composition without Engineering having to touch the code, and without breaking the ‘Product Definition’ that Finance relies on.

The Agnostic Meter

I’m realizing that for this to work, the application must remain completely ignorant of the commercial deal. This is harder than it sounds because it requires engineers to resist the urge to put business logic in the app.
Usage should be modeled purely as meters or metrics—an “add-on” to the Product Definition. The application shouldn’t care if a metric is being used for billing, or if the customer is on a basic plan, a pay-as-you-go model, or a “Pro” plan with included usage and overage fees.
The application’s job is just to report “X happened.”

  • “Storage used: 5GB”
  • “API calls: 1500”
  • “Active Users: 12”

Whether that usage costs $0.05, is free, triggers an upgrade, or is part of a “Enterprise Flat Rate” is a problem for the pricing layer, not the codebase. If the app knows the price, you have to redeploy the app to change the price. That is the definition of tight coupling.

The Decision Belongs in Billing

The paradox of “Seats” (is it a billable metric or a hard limit?) is solved by recognizing that the decision about limits doesn’t belong in the product at all.
The product must support both mechanisms:

  1. The Metric: A way to count usage (e.g., counting active user accounts).
  2. The Limit: A way to check an entitlement (e.g., blocking creation of the N+1 user).

However, the product should never decide if they are connected. That connection happens exclusively in the Billing / Subscription Management layer.

  • If the customer buys a “15 User Pack”, the Subscription system tells the product: “This Metric is now bound to a Limit of 15.”
  • If the customer switches to “Pay-As-You-Go”, the Subscription system tells the product: “This Metric is unbound (or infinite).”

The application logic remains dumb and stable. It just exposes the knobs (Metrics) and the gates (Limits). It is the Subscription Management layer that decides to wire a specific knob to a specific gate based on the commercial contract. This allows Sales to invent entirely new business models (like hybrid prepaid/overage models) without ever asking Engineering to change the code.

Future Explorations

This architecture solves the immediate friction, but it opens up a new set of questions that I’m currently chewing on:

The Aggregation Headache

How complex does the usage reporting need to be?

  • Raw Data: Should the app fire an event every time something happens? (e.g., “User Login”, “API Request”). This keeps the app pure but creates a massive data volume problem (“Chattiness”) and pushes all aggregation logic into the billing system.
  • Pre-aggregated: Or should the app report “Daily Active Users: 15” once a day? This is cleaner for the infrastructure but re-introduces business logic (what defines “Active”?) back into the application code. Finding the balance between “Chatty” and “Coupled” is the next hurdle.

The Bundle Paradox

How do we model bundles without creating a combinatorial explosion of SKUs?
If a bundle is just a marketing wrapper in the Sales layer, how does that flow down to Finance? If I sell a “Gold Bundle,” does Finance see a single line item, or does it need to see the revenue recognized against the three underlying products inside it? The mapping here is non-trivial.

The “Enterprise Custom” Reality

The ultimate stress test for this abstraction is the complex B2B deal.
Enterprise contracts are rarely standard. They look like: “Pay for storage, get 50 seats free, but pay double for API calls after 10k requests, except on weekends.”
Can our “dumb app, smart billing” architecture handle this? It suggests the Subscription Management layer needs to be less of a static database and more of a flexible Rule Engine that can evaluate complex conditions dynamically.