skip to content

As a principal engineer setting integration standards across many teams, when would you actively tell a team NOT to build an Anti-Corruption Layer for a given integration, and how do you decide when an existing one should be retired?

level: principalimportance: should knowfreq 40%

answer

  1. skip when single consumer + short lifespan + stable model
  2. internal bounded context may fit shared-kernel/conformist better than ACL
  3. retire once strangler-fig cutover completes
  4. track real traffic to trigger retirement
  5. publish a lightweight decision checklist org-wide

basics

~20 s

Skip it when the outside system's model is already close to yours, the integration is small, short-lived, or has one consumer, or you're about to fully replace the outside system anyway. Retire an existing ACL once nothing is left using the system it protects against.

solid answer

~50 s

Skip an ACL when the cost isn't justified: a single consumer, a short expected lifespan, a well-governed and stable external model that's already close to the new system's own domain language, or a case where the team plans a fast, one-shot cutover rather than a long coexistence period. It also doesn't make sense when the 'foreign' system is itself another well-designed bounded context within the same organization that the team could instead influence directly through collaboration (a shared kernel or a conformist relationship may fit better than a translation layer). To retire one, track what's still routed through it - once a strangler-fig migration completes and nothing calls the legacy system anymore, the ACL's translation logic has no purpose and should be decommissioned, with its facade contract either removed or pointed straight at the new system's native contract.

go deeper

for a junior

Should be able to say, in simple terms, that not every integration needs this much protection, without needing to name the alternative DDD relationship patterns.

for a middle

Should name at least one concrete reason to skip an ACL, such as a short-lived or single-consumer integration.

for a senior

Should give a fuller decision framework (consumer count, lifespan, model stability) and describe a plan for eventually retiring an ACL once its legacy target is gone.

for a principal

Should reason about this as an organizational standard-setting problem - how to keep the decision from becoming either a reflexive default or a neglected one across many teams - and connect it to the broader DDD menu of context-mapping relationships as alternatives.

## Why a blanket rule fails Setting integration standards across many teams means resisting the temptation to make 'always add an Anti-Corruption Layer' a blanket rule, because a rule applied without judgment produces the same failure mode as skipping the pattern entirely: either unnecessary indirection that slows every team down, or missing protection exactly where it was needed. The principal-level judgment call is knowing, integration by integration, which side of that line a given case falls on - and knowing when a previously-justified ACL has outlived its usefulness. ## When to recommend against one There are several concrete situations where recommending against an ACL is the right call. 1. **The external model is already close and governed by a stable contract.** The clearest is when the external model is already close to the new system's own domain language and is governed by a stable, versioned contract with real backward-compatibility discipline - a well-run internal platform team's API, or a major cloud provider's stable SDK, rarely needs a translation boundary because the risk of corruption or breaking drift is already low, and a thin typed client wrapper captures most of the value at a fraction of the cost. 2. **One consumer, a short lifespan.** A second is when the integration has exactly one consumer and a short expected lifespan - if a team is calling an external system for a feature that's explicitly scoped to be replaced or retired within a quarter or two, the cost of building and maintaining a full facade/adapter/translator stack likely exceeds the corruption risk it would have prevented in that short window. 3. **The 'foreign' system is another team's bounded context.** A third, more nuanced case is when the 'foreign' system is actually another team's well-designed bounded context inside the same organization rather than a genuinely legacy or third-party system - in that situation, Domain-Driven Design's broader menu of context-mapping relationships offers alternatives that may fit better than an ACL. | Alternative | When it fits | |---|---| | a shared kernel | if the teams are willing to co-own a small shared model | | a conformist relationship | if it's cheaper for the downstream team to simply adopt the upstream team's model as-is | | an open-host service with a published language | if the upstream team is willing to publish a stable contract for many consumers | Reaching for an ACL by default in these cases skips a genuinely useful conversation about which relationship actually fits, and a principal engineer's job is to make sure that conversation happens rather than letting 'add an ACL' become a reflexive answer. ## When to retire an existing one The flip side of this judgment is recognizing when an existing ACL should be retired, which is a decision teams are often slower to make than the decision to build one, because a working piece of infrastructure has organizational inertia even after its purpose has expired. - **The clearest trigger is the completion of a strangler-fig migration.** Once traffic tracking shows that nothing is still being routed to the legacy system the ACL was built to isolate, the translation logic has no remaining purpose, and keeping it around only adds an unnecessary hop and an ongoing maintenance burden for code that no longer does meaningful work. - **Retiring it well means more than deleting the translator.** It means deciding what happens to the facade contract that consumers were built against: either every consumer is migrated to call the new system's native contract directly (removing the indirection entirely, which is usually the cleaner long-term outcome), or, if the facade's shape has become a genuinely useful abstraction independent of the legacy system it originally protected against, it can be kept as a plain internal API with the now-pointless translation logic simply removed. - **A second, subtler trigger** for retirement is when the 'legacy' system on the other side has itself been modernized or replaced with something whose model now matches the new system closely enough that the translation is doing negligible work - at that point the ACL has quietly become dead weight even though the system it talks to hasn't been formally retired. ## Building the signals in Operationally, making both of these calls well as a standard-setter means building in the signals to detect them rather than relying on any individual team to remember. - **Tracking real traffic volume through each ACL** (and specifically volume still hitting the legacy path versus the new path during a migration) gives an objective, low-effort trigger for retirement conversations, rather than leaving it to someone's memory that 'we were going to sunset that eventually.' - **Publishing a lightweight decision guide** - a short checklist covering consumer count, expected integration lifespan, and stability of the external model - gives teams a shared, fast way to decide whether a given integration warrants the pattern at all, which does more for consistency across an organization than mandating the pattern everywhere and hoping teams apply it with restraint.

  • How would you convince a team that's emotionally attached to an ACL they built (because it protected them from a bad legacy system for years) to actually retire it?
    Show them the traffic data proving nothing routes through the legacy path anymore, and reframe retirement as removing dead code and an unnecessary hop rather than undoing their earlier good decision - the ACL did its job precisely by making this eventual removal cheap and low-risk, which is worth calling out explicitly as the payoff of having built it well in the first place.
  • What's the risk of publishing a rule like 'always use an ACL for third-party integrations' as an org-wide standard?
    It removes judgment from a decision that genuinely needs it, leading teams to build unnecessary translation layers for small, stable, single-consumer integrations where the overhead isn't justified - and worse, it can create a false sense of security where teams assume any integration with an ACL label is automatically well-protected, without checking whether the translator actually fails loudly on drift.
  • If two teams disagree about whether their integration needs an ACL, how do you arbitrate?
    Walk through the concrete decision factors together - expected lifespan, number of consumers, and stability/governance of the external model - rather than appealing to the pattern's general reputation, and default toward the lighter-weight option when the factors are ambiguous, since it's cheaper to add a translation layer later if drift or corruption actually materializes than to carry unnecessary indirection from day one.

Like deciding whether to build a permanent customs checkpoint for a border crossing that only ever sees a handful of trusted, well-known travelers versus one that handles unpredictable, high-volume traffic from an unfamiliar country - and knowing to tear the checkpoint down once that border itself closes for good.

saying these in an interview costs you the question

  • Treats 'always use an ACL' or 'never use an ACL' as a fixed rule rather than a judgment call
  • Can't name a concrete case where a lighter-weight alternative (typed wrapper, conformist relationship) beats an ACL
  • Has no plan for detecting when an ACL's purpose has expired
  • Assumes retiring an ACL just means deleting code with no consumer migration plan
  • Doesn't distinguish 'foreign legacy system' integrations from 'another team's well-designed bounded context' when recommending a pattern

context