Index

Design Before the Pattern Name

Design Becomes Valuable When the Next Change Arrives

One migration removed four vendor packages. Two payment drivers, their client interfaces, and their factory interfaces did not change; the concrete clients changed substantially.

Two application controllers were less fortunate. They had constructed a vendor connection directly to manage webhooks, so the replacement had to add a small webhook-management role and move the remote protocol behind it.

The same migration produced both results: some earlier seams absorbed the change, while other paths showed exactly where the vendor had leaked through. That tells us more than declaring the design “clean.” A design makes a prediction about future change, and the next real change tells us which parts of that prediction were right.

Design claims are predictions

An interface, adapter, factory, or repository is often defended in the present tense:

This decouples the application from the vendor.
This makes the transport replaceable.
This follows dependency inversion.

Those statements are too broad to verify. A narrower claim can be tested:

If the HTTP library changes, payment decisions will not change.
If the vendor SDK disappears, the driver will retain its client contract.
If webhook framing changes, the connect workflow will not learn the wire
protocol.

The design is valuable to the extent that the predicted change lands where the system said it would. We don’t need to replace every production dependency before trusting a seam, but a real replacement is much stronger evidence than an orderly diagram.

Define the change before scoring the seam

“We replaced the SDK” can hide several independent changes:

  • remove an unused package;
  • replace outbound request construction;
  • replace authentication and signature code;
  • decode callbacks without the SDK;
  • translate provider errors;
  • manage a remote resource lifecycle; and
  • preserve application behavior while all of that moves.

The migration did all of these across several integrations. Calling that one change would make the analysis useless, so start with an inventory:

Change pressureExpected owner
request URL, method, and bodytransport adapter
authentication placementtransport adapter
callback extraction and verificationprotocol boundary
charge and refund decisionspayment driver
webhook registration policyapplication capability
dependency constructionfactory or composition root

Now the diff can answer a meaningful question: did each change reach the owner we expected?

Unchanged callers are evidence

For two payment integrations, the application already spoke through client and factory interfaces. The replacement changed concrete client internals and introduced explicit request objects, but it left the payment drivers and their interfaces alone.

That is direct evidence for a bounded claim:

transport replacement
    changed adapter
    changed request construction
    preserved driver
    preserved client role
    preserved factory role

The value is not the interface count. It is the absence of transport-driven edits in code which owns payment decisions, backed by tests which exercise the unchanged caller against its declared role and the new adapter separately. A quiet diff without behavioral coverage can mean the seam worked; it can also mean the replacement is incomplete. The unchanged file becomes evidence only after both paths run.

Changed callers reveal the leak map

The commerce integration had client and factory interfaces too, but they did not protect every workflow. Its connect and disconnect controllers stepped around those seams: they constructed the vendor connection, selected a dynamic event-listener resource, used remote event and field names, and caught package exceptions.

When the SDK left, those controllers had to change. The diff revealed the actual dependency graph:

connect workflow
    knows vendor connection
    knows remote resource grammar
    knows remote event vocabulary
    knows package failures

The replacement added a two-operation webhook-management role. The earlier design had not failed everywhere; its client seam still protected other commerce operations, but it stopped short of this lifecycle. Calling the whole adapter a failure would throw away the useful part of the evidence. “The adapter protected order operations but not webhook lifecycle” tells us where the next boundary belongs.

A stable interface can preserve the wrong thing

The payment seams survived, but their shapes were not provider-free. One interface returned a framework HTTP response. Another returned mixed and arrays. Callback parsing produced arrays. One factory retained a credential parameter which the replacement client no longer needed because changing the factory contract would have disturbed callers.

The replacement therefore preserved both useful roles and old leakage:

useful stability
    driver operations remain unchanged
    construction remains substitutable
    transport internals move outward

preserved cost
    caller can still see transport response semantics
    arrays still carry provider-shaped data
    obsolete construction input remains in the role

Compatibility is not the same as quality. A stable interface can make a bad type durable, but that does not mean the seam should have been removed. Replacement evidence simply has two axes:

  1. how much unrelated caller change the seam prevented; and
  2. how much provider or transport meaning the seam still allowed through.

A useful seam can score well on the first and poorly on the second. Pretending those are the same question makes both answers worse.

Count change distribution, not changed files

File counts are tempting because Git gives them to us for free. They are also easy to misuse. A replacement may change forty adapter and test files because the remote API is large and still be well-contained. Another may change only four files, all of them central business workflows. The smaller diff can carry the larger architectural cost.

Classify changed files by responsibility:

expected
    dependency manifest
    connector
    request objects
    protocol parser
    adapter tests

boundary repair
    new application capability
    composition binding
    caller changed to receive the capability

unexpected propagation
    business decision changed for transport reasons
    domain result gained vendor fields
    unrelated workflow learned new authentication

The migration contained expected adapter work and a visible boundary repair. It did not require the two payment drivers to learn the replacement transport. That distribution says more than the aggregate diff size.

Tests show whether the seam has an owner

The strongest replacement tests sit on both sides of the boundary.

Caller tests should prove:

  • the same charge and refund decisions still hold;
  • application workflows persist or remove local webhook state correctly; and
  • provider failures become outcomes the caller understands.

Adapter tests should prove:

  • request method, path, body, and authentication;
  • callback verification and malformed input;
  • response extraction; and
  • remote failure translation.

Two previously skipped controller tests became practical after the webhook capability could be substituted. That is evidence of a boundary becoming testable, but mocked transport tests still do not prove that a live provider accepts the request. A seam can make local behavior easy to test while the remote contract remains wrong.

Keep a next-change scorecard

The public reproduction applies one synthetic SDK replacement to four consumers:

  • two callers behind existing roles;
  • one caller with direct vendor grammar; and
  • one caller behind a stable but provider-shaped result.

It produces a scorecard rather than a pass/fail verdict:

callers unchanged
callers changed for boundary repair
transport details contained
provider meanings still leaked
new tests made possible
live-provider behavior unproved

The fixture does not reproduce the private migration or its framework. It models the evaluation method and asserts that an unchanged caller can coexist with a preserved semantic leak.

Use the same scorecard after a real change:

  1. state what the old design predicted;
  2. inventory the actual change pressures;
  3. classify changed callers and adapters;
  4. record which seams held;
  5. record which leaks forced propagation;
  6. record which old leaks remained stable; and
  7. decide whether the newly observed pressure deserves another boundary.

That scorecard turns “the architecture helped” into an explanation someone else can challenge.

Let the change revise the principle

Before the migration, “depend on interfaces” might have sounded sufficient. Afterward, the evidence was more precise:

  • an application-facing role can absorb a transport replacement;
  • a factory can preserve runtime-specific construction;
  • an interface does not help a workflow which bypasses it;
  • stable arrays and response objects can preserve provider coupling; and
  • a new capability should follow an observed caller need, not a desire for more interfaces.

That revised principle is more valuable because it carries its failure conditions. Design is not proven when the diagram looks orderly. It becomes useful when a change arrives, most edits land where predicted, and the exceptions tell us exactly what the earlier model missed.