Chapter 8 Flashcards — Boundaries

flashcards clean-code boundaries third-party adapter-pattern learning-tests

What is the “boundaries” problem in software?
?
We seldom control all the software we use. We integrate third-party packages, internal APIs from other teams, and modules that don’t exist yet. A boundary is the seam where our code meets foreign code. Poorly managed boundaries couple our code to third-party decisions, making upgrades painful and testing difficult.

Why is exposing a raw Map<String, Sensor> across boundaries a problem?
?
Map has a full interface — clear(), put(), remove(), entrySet(), etc. Any consumer receiving the map can mutate or misuse it. The full interface leaks implementation details. When you decide to switch from HashMap to a different data structure, every usage site must change. Wrap it in a focused class that exposes only the operations callers actually need.

What is the core principle of the Sensors / wrapper example from Clean Code?
?
Create a wrapper class that owns the collection internally and exposes a narrow interface tailored to the application’s needs. For example, Sensors with getById(String id) and add(Sensor s) hides the underlying Map. Callers get only what they need; the implementation is free to change without touching callers.

What are learning tests?
?
Learning tests are unit tests written to explore and verify the behavior of a third-party library. Instead of throwaway scratch code, you write formal tests that call the library and assert its behavior. They serve as executable documentation and run against every new library version to detect breaking changes.

What two benefits do learning tests provide that documentation does not?
?
(1) Validation: learning tests prove the library behaves the way you believe it does — docs can be wrong or outdated. (2) Regression detection: when a new version of the library ships, run the learning tests. A failing test tells you exactly which behavior changed before you integrate the upgrade.

Why are learning tests “better than free”?
?
You must learn the API anyway — the cost of exploration is unavoidable. Making that exploration into formal tests gives you the same knowledge plus a regression suite you can run on every future version. The incremental cost of writing tests instead of scratch code is near zero; the benefit compounds across every future upgrade.

What does “using code that doesn’t exist yet” mean and what pattern solves it?
?
Sometimes you must write code that depends on a module another team hasn’t built yet. The solution: define your own interface that represents what you need, write and test against that interface, and bridge to the real API with an Adapter when it arrives. Your code is unblocked; Team B’s timeline doesn’t block Team A.

What is the Adapter pattern and what problem does it solve at a boundary?
?
The Adapter pattern (GoF) wraps a class with an incompatible interface and makes it conform to the interface your code expects. At a boundary, it translates your domain interface calls into the third-party API. When the provider changes or you switch providers, only the adapter changes. Your domain code is unaffected.

Name three concrete benefits of the Adapter pattern at a system boundary.
?
(1) Isolation: your domain code never imports the third-party package — only the adapter does. (2) Replaceability: switching providers (e.g., Stripe → PayPal) means writing one new adapter class; zero domain changes. (3) Testability: domain code is tested with a mock that implements your interface; the adapter is tested separately with an integration test.

Where should third-party library types appear in a well-structured codebase?
?
Only in the adapter or wrapper — never in domain class method signatures, service interfaces, or entity definitions. A stripe.Charge in a service method means the service is coupled to Stripe. A PaymentResult (your type) in the service method means the service works with any payment provider.

What is the relationship between the Adapter pattern and the Open/Closed Principle?
?
The Adapter pattern keeps domain code closed to modification when third-party APIs change. New providers are supported by adding new adapter classes (open for extension) without modifying the domain (closed to modification). Without adapters, adding a new provider requires touching every call site in the domain.

What is a “seam” in the context of boundaries?
?
A seam (Feathers, Working Effectively with Legacy Code) is a place in the code where you can change behavior without editing that code — by injecting a different implementation. Boundaries are seams: an adapter or wrapper is how you exploit the seam. Seams are essential for testing legacy code and for isolating third-party dependencies.

What is the Hexagonal Architecture (Ports and Adapters) pattern?
?
Hexagonal Architecture (Cockburn) is the formal architectural expression of the boundary principle. The application core defines ports (interfaces). Adapters connect ports to external systems (databases, HTTP, payment gateways). The core has zero knowledge of external systems. Each external dependency has one adapter. Clean Code’s boundary principle is this idea applied at the class level.

How do learning tests protect you during a library upgrade?
?
Before integrating a new library version, run the learning tests against the new version. Failing tests identify the exact behavior that changed. You know precisely what to fix before integrating the upgrade into the rest of the application. Without learning tests, you discover behavior changes through production bugs.

What is wrong with using third-party exception types in domain code?
?
If a service method’s signature declares throws StripeException, or a catch block catches com.stripe.exception.CardException, the domain is coupled to Stripe’s exception hierarchy. Switching payment providers requires updating all those catch sites. The fix: catch third-party exceptions at the adapter boundary and rethrow as domain-owned exceptions (see ch07-error-handling Principle 5).

What is the difference between a Wrapper and an Adapter?
?
A Wrapper (or Facade) exposes a simpler or narrower interface over a broad third-party interface (e.g., wrapping Map<String, Sensor> into Sensors). An Adapter bridges an interface mismatch — your code expects one interface, the third party provides a different one (e.g., your PaymentProcessor interface vs. the Stripe SDK’s Charge.create() API). Both isolate third-party knowledge; adapters additionally translate the interface.

What is a Fake or Stub in the context of boundaries and testing?
?
A Fake is a simple, in-memory implementation of your domain interface used in tests. A Stub returns hardcoded values. Neither calls the real third-party API. Because domain code depends on your interface (not the SDK), tests inject the fake. Tests run fast, in memory, with no network or database. This is only possible because the boundary is clean.

Why should the number of “entry points” into a third-party library be minimized?
?
Each entry point is a place that must change if the API changes. If Stripe SDK calls are in 20 service files, a Stripe API upgrade touches 20 files. If all Stripe calls live in one StripePaymentAdapter, the upgrade touches one file. Minimizing entry points is the practical application of “don’t let too much of your code know about third-party particulars.”

What is the risk of calling a third-party API directly from a domain service?
?
(1) Testability drops — you cannot test the service without the real API available. (2) Replaceability drops — switching providers requires editing the service. (3) Coupling rises — the service imports the SDK, so its compilation depends on the SDK. (4) Upgrade cost rises — any breaking SDK change requires immediate changes to business logic.

What should you do when you depend on a module that hasn’t been designed yet?
?
(1) Define the interface you wish you had in your own terms. (2) Write your code against that interface. (3) Write tests using a stub or fake implementation. (4) When the real module is delivered, write a single Adapter that bridges your interface to the real API. This unblocks progress and keeps both teams independent.

What is the Python Protocol type and why is it useful at boundaries?
?
Protocol (from typing, Python 3.8+) defines a structural interface — any class that has the right methods matches the Protocol without explicitly inheriting from it. This is ideal for defining boundaries: you describe the operations you need from a dependency (e.g., def transmit(self, payload: str) -> None), and any class that implements those methods satisfies the Protocol. Third-party classes can satisfy your Protocol without any subclassing.

What is the difference between mypy enforcing Optional[T] and the Adapter pattern enforcing boundaries?
?
mypy Optional[T] is a static type system enforcement — it detects null-related bugs at type-check time without runtime cost. The Adapter pattern is an architectural enforcement — it restricts which code can access which API at the class/module level. They operate at different levels: mypy catches individual call-site mistakes; adapters prevent entire categories of coupling.

What are the four columns of the boundary strategies comparison table?
?
Strategy (what technique), When to Use (the situation that justifies it), Coupling to Third-Party (how much of your code knows the SDK), and Testability (how easy it is to test code on either side of the boundary). The pattern with lowest coupling and highest testability for general use is the Adapter pattern combined with interface definition.

Why is the “direct use of third-party API” approach only acceptable for throwaway code?
?
In throwaway prototypes or one-off scripts, the cost of architecture is not justified by the lifespan of the code. But in production code with expected longevity, direct use creates: high coupling (SDK changes break business logic), low testability (must stand up the real service), and high maintenance cost (upgrade the SDK = audit the entire codebase). The breakeven point is reached very quickly for production systems.

Total Cards: 24
Review Time: ~16 minutes
Priority: MEDIUM
Last Updated: 2026-04-14