A seam is a controlled separation point
A seam is a place where part of the behavior can be changed or replaced without rewriting the entire legacy module at the same time. It can be an interface, adapter, gateway, function boundary, event, or routing decision. A good seam reduces blast radius and makes replacement observable.
A seam does not create safety automatically. If the boundary merely renames a direct call but does not isolate side effects, configuration, or failure paths, the risk remains.
How to find a seam
| Signal | What to investigate | Risk |
|---|---|---|
| Stable input/output | Is there a clear format at the boundary? | hidden fields or state coupling |
| Repeated dependency | Is the same client or utility repeated? | changing one caller affects many flows |
| Side-effect boundary | Where does a write, event, or external call happen? | partial migration and duplicate side effects |
| Routing decision | Who chooses the legacy versus new path? | flags, dates, or rollout diverge |
| Test boundary | Can the seam be checked separately? | impossible to distinguish regression from integration failure |
Dependency direction and adapters
During decomposition, record who owns the contract and which direction dependencies flow. An adapter can hide a legacy API from new code, but it must not silently change semantics, error formats, or retry behavior.
The interface should be narrow enough to describe the required behavior rather than the entire internal surface of the legacy class. If the seam repeats every implementation detail, it does not create an independent replacement point.
- mark the producer, consumer, and contract owner;
- record data transformations and possible information loss;
- describe errors, timeouts, retries, and idempotency separately;
- check whether the seam can be disabled or bypassed during rollback.
A seam map as a decision record
- Choose one flow and find its entry point and side effects.
- Draw the actual dependency path without desired components.
- Mark candidate seams and the evidence for each.
- State which part can be replaced independently.
- Record unknowns, the owner, and the focused verification method.
- Choose one seam for the first slice and record why the others are deferred.
Seam decomposition anti-patterns
- creating an abstraction only to increase the number of interfaces;
- hiding a business rule in an adapter without a separate owner;
- mixing routing, persistence, and formatting in one new boundary;
- ignoring legacy callers that bypass the new seam;
- treating a seam as safe before checking its failure path.
Canonical lesson source · JavaRush
Open JavaRush material