books / clean-code / ch-06SHEET 6 / 12 · REV ASIGN IN
MODULE 6 · CLEAN CODE CH 8

Taming the Edges: Boundaries

A boundary is only clean if you can rip out what's behind it and nothing in front of it notices.

The idea

Third-party code serves two masters, and you are the second one. Library authors make things general because they serve everybody; you want one narrow job done for your app. SQLite will cheerfully store the string "hello" in a column you declared INTEGER — flexibility you never asked for, and now a bug you can hit.

One call to a library is cheap. The damage comes when its imports, types, and calling conventions spread across file after file. Its vocabulary becomes your vocabulary, an upgrade touches nine files, and switching to something better gets so invasive that you never do it. You never decided to marry the library; it happened one convenient import at a time.

The fix isn't avoiding libraries, it's writing the interface you wish existed. Name the five or six things your app actually wants from storage, then write one adapter that translates those into library calls. Nothing else imports the library, and nothing passes its rows, handles, or error classes through the wall. Before you build that wall, interrogate the library with small tests that encode what you believe it does — learning tests you keep, so the next version's surprises show up in your test runner instead of production.

The same seam works when the other side doesn't exist yet. Define the interface, write a fake, keep building; when the real SDK ships you write one adapter and the feature you already built stays untouched. And the proof of any of this is the swap: replace what's behind the wall and show a diff that never leaves the adapters directory.

The bench — 4 exercises

EX 01

Interrogate SQLite

Write six learning tests against the raw SQLite driver that pin down its quirks, so what you'd otherwise learn in production is executable and permanent.

  1. Create a tests/learning/sqlite test file that talks to the raw driver only, with no app code involved.
  2. Before running anything, write a one-line EXPECTED comment for each behavior you're about to probe.
  3. Cover at least: inserting a string into an INTEGER column, the type returned after a float goes into a TEXT column, what SELECT 1/2 returns, NULL under = versus IS, what date('now') actually gives you, and last_insert_rowid after a failed insert.
  4. Assert the observed behavior, and note next to each expectation whether you were right.
hint

Stuck on what to assert? Run the statement in the sqlite3 REPL first, be surprised there, then capture the surprise as a test.

hint

sqlite.org/quirks.html is the official confession list — but write your expectations before you peek.

DONE WHEN

· Six tests exist, import the driver directly, and all pass

· Each test carries an EXPECTED comment written before the run

· At least two of your expectations turned out wrong

EX 02

Build the Wall

Collapse SQLite from nine files down to one adapter behind an interface you designed, with none of the driver's types crossing the boundary.

  1. Grep the repo for driver imports and list what the call sites actually do with storage — the verbs, not SQLite's API.
  2. Define a StorageGateway interface of at most six methods named in domain language (saveInvoice, findInvoicesByCustomer — not query or execute).
  3. Write one adapter implementing it, converting driver rows into your domain objects and driver exceptions into your own error types.
  4. Rewrite every call site to use the gateway, then confirm the driver is imported from exactly one non-test module.
hint

Don't design the interface by staring at what SQLite offers — you'll rebuild SQLite in miniature.

hint

An interface method that returns a raw driver row is a wall with a hole in it.

DONE WHEN

· The existing test suite is still green

· A repo-wide search finds the driver imported only in the adapter (and the learning tests)

· No driver-defined type appears in any signature or thrown error outside the adapter

EX 03

The Swap Test

Reimplement StorageGateway on top of plain JSON files and flip one wiring line — the finale that proves the boundary is real, not aspirational.

  1. Commit or tag your state at the end of the previous exercise so you can diff against it.
  2. Write a second adapter backed by JSON files in a data directory, using no library at all.
  3. Change the single line that wires storage, then run the full suite on both adapters.
  4. Diff against your tag and read where, if anywhere, the change spilled out of adapters/.
hint

If the flat-file adapter forces you to change a call site, that's an interface problem — fix the gateway and come back.

hint

This exercise should feel boring. Pain here is a map of exactly where the wall leaks.

DONE WHEN

· The suite is green wired to SQLite and green wired to flat files

· The diff since your tag touches only adapters/ plus one wiring line

EX 04

The Seam Before the SDK

Build a payment-submission feature against an interface and a fake, then adapt a deliberately awkward vendor SDK to it without touching the feature.

  1. Define a PaymentGateway of two or three methods expressed purely in your own types, e.g. submit(invoice) returning a Receipt.
  2. Write a fake implementation that records submissions in memory and returns canned receipts.
  3. Build the app's submit command and its tests against the fake, and get them green.
  4. Now write a stand-in vendor SDK with different verb names, its own money type, and a required session handshake — then write the adapter that maps it to your interface.
hint

Design from the caller's need, not from guessing what the vendor provides; their handshake is the adapter's problem.

hint

The fake isn't scaffolding — keep it as the test double for every future payments test.

DONE WHEN

· The submit command and its tests were green before the SDK existed

· After adapting the SDK, the submit command and its tests are unchanged

· The SDK is imported only from the new adapter

Go deeper (after the bench)

Read Clean Code ch. 8 now — it's short, and the learning-tests argument lands very differently once SQLite has already embarrassed you. Then skim the maintainers' own "SQLite Quirks" page (sqlite.org/quirks.html, free) and count how many of their surprises your six tests caught.