Delivery fork: bolt-on app vs. first-class platform citizen¶
Generated from a canonical source
This page is a read-only projection of docs/strategy/delivery-fork.md.
Edit the canonical file, then run npm --prefix tools/project-knowledge-derive run derive.
Status: Open question — not yet decided Filed: 2026-05-22 Trigger: Slack thread, jordan.sim ↔ nino.chavez, 2026-05-22 — Jordan raised concern that bc-subscriptions is currently shaped as a "bolt-on app" rather than a "first-class citizen," citing B2B Edition's bolt-on/sidecar architecture as the cautionary reference.
The fork¶
bc-subscriptions can ship one of two ways. They are not points on a continuum; they are different runtime substrates with different teams, languages, and operational shapes. The current codebase commits to neither yet — it's a marketplace-app shape that could be graduated to a marketplace-app product, or used as the executable spec for a native first-class build.
Path A — Marketplace app (current default shape):
- BigCommerce marketplace app, App Extensions for control-panel integration
- Owns its own runtime (Cloudflare Workers today, GCP/Cloud Run in Phase 2 per ADR-0030)
- Consumes BC public APIs (/v3 REST, GraphQL Storefront API)
- Owns its own data store (D1 today, Cloud SQL in Phase 2)
- Owns its own auth identity (OAuth client install per merchant)
- Owns its own payment-method vault wrapping BC Payments (ADR-0037)
- Shipped, billed, and supported as a marketplace product
Path B — Native first-class platform citizen:
- A BC domain service owned by a Pod (likely Payments or an Orders-adjacent pod, paralleling Billing Pod's ownership of merchant-subscription-manager)
- Runtime: Nomad-scheduled containers in BC's CDE or platform cluster, deployed via Launchbay
- Language: Ruby (BigPay, msms) or Scala (api-proxy-java) or Java — never TypeScript on the backend
- Internal interface: gRPC; proto definitions land in bigcommerce/interfaces
- Public interface: composed into REST/GraphQL by api-proxy-java (we don't expose /v3 ourselves)
- Data: tables in the shared store DB, partitioned by bounded context; never JOINs across context boundaries
- Auth: BC's internal request-context propagation (x-bc-request-context-bin, x-audit-principal-bin, OT-trace IDs via gRPC trailing metadata)
- Eventing: RabbitMQ via the Domain Eventing system with strongly-typed contracts
- Payments: direct gRPC into BigPay — no separate stored-instrument vault on our side
- Observability: Lightstep traces, logstash app_name, Sentry per-habitat, Prometheus auto-wiring
Why this is not a SWOT¶
A SWOT collapses orthogonal dimensions (velocity vs. correctness vs. scope vs. team capacity) into a 2×2. The actual decision has at least three independent axes, and the dominant constraint (who staffs the native service if Path B is chosen) lives outside the artifact entirely. SWOT is the wrong instrument.
The right instruments:
1. native-shape-gap.md — dimension-by-dimension gap between current shape and the two target shapes, with per-dimension rework cost.
2. portability-inventory.md — every artifact (ADRs, PRD sections, prototype behaviors) tagged survives-both | bolt-on-only | first-class-only | requires-rework.
3. This document — narrative wrapper + decision criteria.
4. A [Decision] Hive proposal referencing all three.
Decision criteria¶
The fork is not "which path is better?" It is "what would have to be true for path X?"
For Path A (marketplace app) to be correct: - The product is durably a marketplace offering — not a stepping stone to native. - Revenue model assumes marketplace economics (rev share or platform fee). - Multi-runtime portability has real value (some merchants will want self-hosted variants). - Native graduation is explicitly an option, not a commitment (ADR-0029 already encodes this posture). - BC is comfortable with a paid bolt-on for subscription functionality long-term.
For Path B (native first-class) to be correct: - BC strategy treats subscription billing as a platform-native capability, not a partner-app concern. - A Pod exists or will be formed to own it (Billing Pod, Payments Pod, or a new Subscriptions Pod). - The bounded-context boundary against Orders / Payments / Customers / BigPay is identifiable. - A staff engineer is available to shepherd the gRPC interface design and the bigcommerce/interfaces proposal. - The product team accepts BC's release cadence, deploy infrastructure, and SLO obligations rather than marketplace-product cadence.
What's true today: neither column is fully checked. Path A has the most existing investment (codebase, ADRs, deployment pipeline). Path B has the strongest architectural correctness argument per Jordan's framing, but requires organizational decisions outside the project's scope.
What survives both paths¶
The existing investment is not lost regardless of which fork is chosen. Specifically:
- Behavioral specifications: BRD personas, PRD flows, AC. The what is invariant.
- Rail decisions documented as ADRs: capture timing (ADR-0038), eligibility chokepoint (ADR-0028), idempotency rules (ADR-0011), delivery-instance entity (ADR-0024). These are behaviors, not mechanisms — they describe properties any subscription system must have.
- Prototype as executable spec: the running storefront/admin/portal triad is design oracle.
- Methodology + Hive substrate: process is portable; it's been exercised on a marketplace-app shape but the proposal/synthesis/ADR pattern works for any delivery model.
What's fork-dependent¶
- Substrate mechanics: Cloudflare bindings, D1 schemas, Worker cron, KV usage, R2 — bolt-on-only.
- API consumption strategy: our REST
/v3consumption + OAuth client model — bolt-on-only. - Payment vault façade (ADR-0037): bolt-on-only; native uses BigPay direct.
- Storefront integration mechanism: Scripts API + widget injection — bolt-on-only; native ships with the storefront.
- Phase 2 GCP migration plan: bolt-on-only; if Path B is chosen, GCP is moot.
Recommendation¶
Continue prototype-track work (Jordan explicitly endorsed this as the engineering handoff artifact). Freeze new platform-substrate ADRs until the fork is called. Surface the question through a [Decision] Hive proposal so it can be synthesized rather than living in Slack. Use the gap matrix and portability inventory as the substrate for that conversation.
Trigger¶
The Slack exchange that surfaced this fork (paraphrased):
jordan: this is a separate 'bolt-on' app, as opposed to it being a first-class citizen. For me that's an issue because of the desperation of the execs/company vs how to architect it correctly. It's a bigger concern for me, because B2B Edition is a bolt-on/sidecar to the main app, and we're paying for it today. If we technically say we will do Subscriptions right, using your work, then velocity drops significantly.
nino: I'm going to pause any further work as to not waste tokens until that becomes clearer.
jordan: Totally fair... fwiw I could see you get to a very good prototype end to end which eng then takes over to systematize in the existing architecture.
nino: "waste" was the wrong framing. I was thinking of it more like avoiding rework or refactoring if there are functional capability and/or technical architecture directional changes once we decide how we want this to actually get delivered.
jordan: Totally fair.
References¶
- native-shape-gap.md — dimension-by-dimension gap matrix
- portability-inventory.md — artifact-level portability tagging
docs/rag/bc-internal-docs/services/merchant-subscription-manager-service.md— the structural precedentdocs/rag/bc-internal-docs/services/bigpay.md— canonical payments platform a native subscription service would integrate withdocs/rag/bc-internal-docs/technical/standards/bounded-contexts.md— the governing principledocs/rag/sdlc-frameworks/bigcommerce-bigeng.md— DoD gates and where we already diverge- ADR-0029: Marketplace-first, native-ready — current posture
- ADR-0030: BigEng pattern alignment