skip to content

A protocol detail has leaked into a shared business action's signature in a layered automation suite — what does that cost when the adapter beneath it is replaced?

level: seniorimportance: should knowfreq 44%

answer

  1. The signature is the real boundary
  2. Vocabulary from below appears above
  3. Replacing the adapter now edits every caller
  4. Own the types the boundary speaks
  5. Translate at the lowest layer's edge

basics

~20 s

A leaked detail in an action's signature — a raw response, an element identifier — puts the transport into every caller. Replacing the adapter then changes the signature and every case using it, so the boundary protected nothing.

solid answer

~50 s

The leak turns a one-module change into a suite-wide one. A shared action whose parameter or return value is defined by the layer below — a raw transport response, an element identifier, a connection handle, a protocol status number — has published that vocabulary to every caller. Replace the adapter and the action's signature must change, so every case that called it changes too, including cases that only used the flow as arrangement. The layer was paid for and then bypassed: the change cost is the same as having no boundary. The repair is to define a small set of suite-owned domain types the boundary speaks, translate at the adapter edge so every adapter for one flow produces the same type, and review exported signatures by asking whether any name in them belongs to only one way of reaching the product.

code

pseudocode · 17 lines
pseudocode
# leaked: the action's parameters and return belong to one way of reaching the product
action sign_in(user_field_id, secret_field_id, submit_id) -> raw_response

# owned: the action speaks the suite's own vocabulary
action sign_in(credentials) -> signed_in_customer

adapter screen_driver:
    sign_in(credentials):
        enter(user_field_id, credentials.name)
        enter(secret_field_id, credentials.secret)
        activate(submit_id)
        return signed_in_customer(display_name = read_greeting())

adapter service_caller:
    sign_in(credentials):
        answer = transport.send(login_request(credentials))
        return signed_in_customer(display_name = answer.field("name"))

go deeper

for a junior

Recall that a shared step should take and return values a domain reader recognises, and that a value belonging to one way of reaching the product should not appear in it.

for a middle

Explain the mechanics: which signature shapes count as leaks, why the change reaches every caller when the layer below is replaced, and what translating at the lowest layer's edge does for two adapters behind one flow.

for a senior

Show the production judgement: how you find leaks by reading exported signatures on an inherited suite, how you repay them without a rewrite, and where you decided a wrapper was ceremony rather than isolation.

for a principal

Own the economics. Argue how much translation a suite should buy, given how likely a second way of reaching the product really is, and be ready to defend leaving a leak in place where the isolation would never be used.

## What leakage looks like A boundary exists so that one side can change without the other. A **leak** is any place where the lower side's vocabulary appears in the upper side's signature, because from then on the upper side cannot compile, read or reason without knowing what is below it. In a layered suite the leaks are recognisable by shape. | Leaked signature | Why it is a leak | Owned alternative | | --- | --- | --- | | An action returning the raw transport response | Callers must know the response layout | A small result type the suite defines | | An action taking element identifiers as parameters | The identifier vocabulary belongs to one way of reaching the product | Take the domain values the flow needs | | An action taking a live connection or driver handle | Callers now manage the transport's lifetime | Let the adapter own its own lifetime | | An action returning a numeric protocol status | The number's meaning is the transport's, not the domain's | Return an outcome the domain names | | An action taking a serialized payload string | Callers build transport-shaped data | Take the domain object, serialize below | The shape is always the same: a value whose **meaning is defined by the layer below** has become part of the contract of the layer above. ## What the leak costs when the adapter is replaced The whole point of the adapter layer is that replacing it — a second way of reaching the same flow, a rewritten transport, a different stand-in for a collaborator — is an edit inside one module. A leaked signature converts that into a spreading change. 1. **The signature changes.** The new adapter does not produce the old raw response, so the action's parameter or return type has to change. 2. **Every caller changes with it.** The action is shared, so the edit reaches every case that used it, including cases that only ever wanted the flow arranged. 3. **The cases were never supposed to know.** The edits happen in code whose whole job was to state intent, so a transport migration shows up as churn in files that should not have moved. 4. **The boundary bought nothing.** The suite paid for a layer, kept the folders, and still got the change cost of no layering at all. This is the outcome worth naming in an interview: the layer was not wrong, it was *not enforced*. A second, quieter cost: the leaked type spreads by copying. Once one action returns a raw response, the next author writes a second action the same way, because the pattern is what the codebase shows them. ## How it is repaid Repayment is a design move, not a heroic rewrite, and it has three parts. **Define the vocabulary the boundary speaks.** The suite owns a handful of small types — an order, a signed-in customer, a submission outcome — with names from the domain, not from the transport. They are yours, so no adapter change can alter them. Keep them small: a result type that mirrors the response field-for-field is the leak wearing a different name. **Translate at the adapter edge.** The adapter is the only place allowed to hold both vocabularies, and translation is its job, not an inconvenience. Every adapter for the same flow produces the same suite-owned type, which is exactly what makes two adapters interchangeable beneath one action. **Make the boundary checkable.** Ask of each exported action signature: does any parameter or return name a concept only one way of reaching the product has? If a reader in a team that reaches the product differently would have to ask what the value is, it leaks. This question is worth more than a style rule, because it can be answered by reading the signatures alone, without running anything. ## The judgement call: how much translation is worth it Not every leak is worth repaying, and pretending otherwise is how a suite ends up with a mapping layer larger than the suite. - **Repay it** where the value is used by many callers, where a second way of reaching the flow is plausible, or where the leaked type is large and the caller reads only two fields of it. - **Leave it** where the adapter is one throwaway probe with one caller, or where the suite-owned type would exist only to be copied field-for-field from the transport shape and would change whenever it changed. - **Watch the middle case**: an identifier the transport minted — an order number, a correlation value — is usually a domain value that merely arrived over a transport, not a leak. The test is whether the *meaning* is transport-specific, not where the value came from. ## What to say in an interview The strong answer names the mechanism rather than the rule: a leaked signature makes the boundary decorative, because the cost of change is measured across callers rather than at the seam. Then it names the repair — a suite-owned vocabulary, translation at the adapter edge — and finishes with the limit, that translation earns its keep only where more than one caller or more than one adapter is realistic. A candidate who says only "always wrap everything" has learned the rule without the tradeoff, and a candidate who says "it is only a test suite" has not yet maintained one at size.

  • Is an identifier the transport minted, such as an order number, also a leak?
    Usually not. The test is whether the meaning is transport-specific, not where the value came from. An order number means the same thing however the order was placed, so it is a domain value that happened to arrive over a transport. A numeric protocol status, an element identifier or a session handle mean something only to one way of reaching the product.
  • When is wrapping the value in a suite-owned type not worth it?
    When the adapter has one caller and no plausible replacement, or when the owned type would mirror the transport shape field-for-field and change whenever it changed. That is a mapping layer with no isolation. Spend the translation where several callers depend on the value or a second adapter for the same flow is realistic.
  • How would you spot leaks across a suite you have just inherited?
    Read the exported signatures of the action modules, nothing else. For each parameter and return, ask whether a colleague who reaches the product a different way would need to ask what the value is. That review needs no run and no build, and it usually finds the leaks in an afternoon because they cluster in the actions written first.

saying these in an interview costs you the question

  • Returns the raw transport response and calls it a business action
  • Thinks a leak is fine because the code still compiles
  • Wraps every value in a type that mirrors the response exactly
  • Says a test suite is too small to have real boundaries
  • Blames the replacement adapter for the wave of caller edits