Interface-First SaaS. How to Swap Infrastructure Without Rewriting Product Logic
How to keep product logic stable when vendors shift
Interface-first SaaS means product logic depends on domain interfaces, while vendor SDKs stay behind adapters.
This is not abstraction theater. It is a way to keep billing, RBAC, and delivery paths stable when vendors change terms.
I am not asking you to abstract everything. I am asking you to isolate volatile dependencies before they handcuff your release calendar.
What broke when we integrated vendors directly
Direct integration feels fast in sprint planning. You call provider SDKs from resolvers or handlers. You ship. Everyone moves on.
Then a provider changes pricing, limits, or behavior. Migration does not stay local. It spreads through API edge code, worker flows, and operational scripts. What looked like speed becomes quicksand.
The bill is not just engineering time. It is delayed roadmap learning and delayed monetization because your team is now doing emergency rewrites instead of shipping planned outcomes.
If you want evidence that contract and behavior drift is normal, check public changelogs from major providers such as Stripe, Twilio, and GitHub REST API breaking changes.
What conviction came from that scar
If volatile dependencies cross domain boundaries without interfaces, every vendor change becomes a product rewrite risk.
What is interface-first SaaS in one sentence
Interface-first SaaS means domain logic depends on explicit contracts, and providers are swapped through adapters that implement those contracts.
Where should this be mandatory first
Start where external changes can hit revenue, access, or reliability.
-
Billing and checkout execution.
-
Access control and identity providers.
-
Queue and orchestration backplanes.
-
Email and notification delivery.
-
Analytics and event routing.
If a dependency can change your unit economics or release pacing, isolate it.
When is direct coupling still acceptable
Use direct coupling only when all three are true.
-
Dependency volatility is low.
-
Capability is non-differentiating for your product.
-
Failure impact is bounded and reversible.
If one condition is false, add an interface boundary now instead of paying a larger migration tax later.
How does interface-first compare to direct SDK coupling
DimensionDirect SDK couplingInterface-first boundaryChange scopeSpreads across product logicStays inside adapter layerMigration speedSlow under incident pressureFaster with staged cutoverTest strategyProvider behavior mixed with domain testsContract tests per adapterRollback pathOften unclear and manualExplicit via adapter selectionOwnership clarityDiffuse across teamsClear adapter and contract owners
What should a minimal contract look like
Keep the interface small and domain named. Do not leak provider terms into the contract surface.
// Domain-facing contract.
interface Notifier {
sendTemplate(input: {
template: "BILLING_PLAN_CHANGED" | "PAYMENT_FAILED";
to: string;
variables: Record<string, string>;
}): Promise<void>;
}
Adapters implement this contract for each provider. Domain utilities receive the interface, not a vendor client.
async function sendPlanChangedEmail(
notifier: Notifier,
input: { to: string; previousPlan: string; newPlan: string },
) {
await notifier.sendTemplate({
template: "BILLING_PLAN_CHANGED",
to: input.to,
variables: {
previousPlan: input.previousPlan,
newPlan: input.newPlan,
},
});
}
The point is cost containment. Swap work stays in adapter packages, not across product logic.
How do you migrate from hard coupling without freezing delivery
-
Define the contract from domain language first.
-
Wrap the current provider with an adapter that preserves behavior.
-
Redirect all new call sites to the interface entrypoint.
-
Backfill old call sites in controlled slices.
-
Add contract tests that run against every adapter.
-
Implement a second adapter behind the same contract.
-
Roll traffic gradually and monitor error parity.
-
Remove the old adapter after stability criteria are met.
This sequence avoids heart transplant migrations. You constrain blast radius while still shipping.
Progressive rollout and canarying are standard reliability practices for change risk reduction, which is why stepwise cutovers beat big-bang swaps in production (Google SRE Book. Reliable Product Launches).
Which anti-patterns create hidden debt taxes
Why is interface inflation dangerous
Teams design broad contracts for future flexibility. This creates handcuffs because every extra method becomes a stability promise you never needed.
Why does provider naming leak coupling
If contract methods include provider terms, you import coupling through vocabulary. The domain should ask for outcomes, not vendor operations.
Why should resolvers and handlers stay thin
Thin boundaries matter. Keep orchestration in integration layers and business rules in backend utilities. Do not embed provider decisions inside GraphQL edge code.
Why does configuration sprawl break swappability
Swappability fails when provider selection is hardcoded in random call paths. Keep provider routing centralized and typed.
What quality gates prove this is truly swappable
-
Contract surface is small and domain named.
-
At least two adapter implementations compile against the same contract.
-
Contract tests run for every adapter in CI, following a consumer-provider contract testing model (Pact documentation).
-
Cutover can be traffic gated and reversed.
-
No direct SDK imports remain in domain utility modules.
-
Benchmark. You can switch primary adapters in one release with rollback under 15 minutes, and with no edits to domain utility modules.
If one gate is missing, you do not have optionality. You have a migration promise with no enforcement.
How does this align with Refract architecture
Refract documents this operating model clearly. Keep GraphQL resolvers thin, keep business logic in backend utilities, route external integrations through the tooling system, and centralize typed configuration for swappable tool implementations in billing and RBAC critical paths. Reference architecture docs: Refract Architecture Overview, GraphQL Architecture, Tooling System, Configuration System, RBAC, and Billing.
That model is not about purity. It is about preserving sovereignty over billing, RBAC, and roadmap velocity when vendors shift.
What should you review before adding any new dependency
-
What event could force migration inside 12 months.
-
Which contract will isolate this dependency.
-
Where adapter ownership lives.
-
How rollback works during cutover.
-
What rewrite liability looks like if we skip the boundary now.
If you cannot answer these in one review document, speed is an illusion.
FAQ. Interface-first SaaS and adapter boundaries
Do I need interfaces for every dependency on day one
No. Start with dependencies that can impact revenue, access control, or delivery reliability. Add boundaries where volatility risk is highest.
How many adapter implementations should exist before production cutover
At least two. One current adapter and one alternative adapter prove the contract is real and expose leakage early.
Where should provider selection logic live
Keep it in centralized typed configuration and integration composition code. Do not scatter selection in resolvers, handlers, or domain utilities.
What tests matter most for safe vendor swaps
Contract tests that run the same assertions against every adapter. This gives parity checks before traffic cutover.
How do I avoid over-abstracting the contract
Start from domain outcomes and keep method count minimal. Add methods only when a concrete use case exists in product logic.
Final note
I built this pattern into Refract because I am done paying vendor tax through hidden rewrites.
If your revenue path depends on a vendor SDK call inside product logic, you do not own your roadmap.
You do not need to agree with every implementation choice. You do need a boundary strategy before external terms decide your roadmap.
Sources and further reading
-
Refract architecture references: Architecture Overview, GraphQL, Tooling System, Configuration System, RBAC, Billing.
-
Vendor change evidence: Stripe Changelog, Twilio Changelog, GitHub REST API Breaking Changes.
-
Architecture foundations: Hexagonal Architecture by Alistair Cockburn, Inversion of Control Containers and the Dependency Injection Pattern by Martin Fowler.
FAQ
- What is interface-first SaaS architecture?
It means domain logic depends on explicit contracts, and vendor providers are swapped through adapters that implement those contracts.
- Do I need interfaces for every dependency from day one?
No. Start with dependencies that can affect revenue, access control, or delivery reliability, and add boundaries where volatility risk is highest.
- No. Start with dependencies that can affect revenue, access control, or delivery reliability, and add boundaries where volatility risk is highest.
At least two. A current adapter plus one alternative expose contract leakage before production cutover.
- Where should provider selection logic live?
In centralized, typed configuration and integration composition code, not scattered across resolvers, handlers, or domain utilities.
- What's the biggest risk of skipping interface boundaries?
Provider changes stop staying local and instead force rewrites across API code, worker flows, and operational scripts, turning planned work into emergency migrations.