The Strangler Fig and Coexistence Architecture

The energy retailer had chosen the gradual route deliberately. The billing monolith would not be replaced in one release, which everyone agreed had failed twice at other companies they had worked for; it would be strangled, feature by feature, behind a façade, with the old system carrying whatever had not yet moved. Three years in, the numbers looked like progress. Sixty percent of the features ran in new services. The façade routed two-thirds of requests away from the monolith. And yet the monolith could not be switched off, or even scaled down, because twenty-three adapters, written one at a time to keep it fed, still wrote every tariff change, every meter reading, and every payment back into its database, where the month-end reporting job read them. Nobody could say which system owned a customer record: the new customer service accepted address changes, the old system accepted them too from the call-center screen that had never been migrated, and a nightly job reconciled the two with rules three people understood. When the program director asked for the retirement date of any of the twenty-three adapters, the answer was the one she had feared: they were built to be temporary, and nobody wrote down what temporary meant.

The retailer is fictional. Its problem illustrates a failure to design the intermediate states of replacement. The assessment and strategy choices and the program’s promises establish the goal; this article assumes that gradual replacement has been selected. That choice can spread delivery and limit exposure, but it also adds integration and operating work. The duration and value of coexistence depend on the estate, its constraints, and the alternatives. The work is to identify usable seams, assign authority, validate each transition, and decide which temporary components can be removed when their obligations end.

Why gradual, and what the fig actually is

Martin Fowler’s account of the strangler fig explains why incremental delivery can help: replacement takes time, business needs continue to change, and existing behavior is not always fully understood or worth reproducing. Microsoft illustrates a request-routing implementation. A façade first directs work to the old system, then sends selected capabilities to new services. The legacy system can be retired once its required responsibilities and dependencies have been addressed. The façade may then be removed, or retained for clients that still need its interface. The retailer had introduced routing and moved some capabilities; it had not completed dependency removal or planned the remaining adapters’ exits.

Evaluate the available seams and the cost of keeping both paths operable. Microsoft identifies request interception and access to legacy code as constraints on its illustrated approach, and notes that a small, easily replaced system or an urgent shutdown may favor another approach. Those are suitability considerations, not universal exclusions. Source restrictions can prevent changing internal calls while still allowing a proxy, message route, database change stream, or redesigned user workflow. If no permissible seam supports the required behavior and ownership transfer, choose another migration design. A useful façade is one implementation of gradual replacement, not a requirement that every migration must route all work through one proxy.

Seams: where the two systems connect

A seam is a boundary at which behavior can be redirected or replaced. The Event Interception pattern uses existing APIs, messages, database interactions, and user activity to reduce changes to legacy components. Sometimes an internal abstraction or a change to the caller creates a better boundary. Choose a seam by the capability being separated and the contracts that must survive, not just by where traffic is easiest to observe. A copy of an event can support a comparison; transferring responsibility requires additional decisions about who may act on it.

SeamWhat is interceptedMechanismWhat it lets the program doThe retailer
Request pathCalls from clients to the legacy interfaceA reverse proxy or an API gateway routing by path, header, or payload; the strangler façade itselfMove one capability’s requests to the new service while clients see one interfaceThe façade in front of billing, routing by tariff type
Message pathEvents flowing between legacy components over existing messagingSelectors, routers, and wire taps on the broker; a routing component that re-enqueues by contentSend a copy or a subset of events to new consumers “with or without duplication”Meter readings tapped at the queue and fed to the new consumption service
Code seamA call inside the legacy system to one of its own layersBranch by abstraction: keep the interface, swap the implementation behind itRedirect reads, then writes, of one domain without touching callersThe persistence layer for the customer domain
Data pathChanges recorded by the legacy databaseA transactional event record delivered after commit, or change data capture from the log; trigger behavior depends on the databaseMaintain a derived copy or notify consumers under an explicit delivery and application contractA possible feed after write authority is settled; not a replacement for conflict handling or reconciliation
User interfaceUser actions in a legacy web applicationA script added to the template, released independently, that intercepts actions and changes behaviorDecouple the new work’s release cycle from the old application’sNot used; the call-center screen was left untouched, which was the mistake

The Event Interception authors describe one domain extraction sequence. They first loaded a new store and used CDC to compare it with legacy data. They then redirected reads while legacy remained the business writer, followed by writes to the new service and finally domain logic. In that case, legacy data updates stopped at the write transition and downstream consumers also moved. The table below is an adaptation that permits a temporary feed back to legacy consumers; it is not that case study unchanged or a mandatory order for every domain. The retailer differed at a critical point: both systems accepted independent address changes for the same customers. Replacing its nightly job with CDC would not decide which conflicting update should win. First define authority by record or customer group, synchronization direction, ordering, duplicate and delete handling, and the initial-load boundary. A stream and periodic reconciliation can complement each other. Trigger terminology also needs a concrete database: PostgreSQL AFTER triggers run within the transaction, not after commit. Recording an event transactionally and delivering committed records later avoids publishing an update that subsequently rolls back, but delivery still needs retry and idempotency handling. Where technical interception is costly, a supported change to the user workflow may provide a simpler temporary seam.

Transitional architecture: built to be removed

The retailer’s adapters are examples of transitional architecture: software introduced to enable replacement, with an intended end to its role. Cartwright, Horn, and Lewis compare this work to scaffolding. An intermediate integration can be worth building even though it is absent from the target design. Their Legacy Mimic pattern describes a new implementation exposed through an old interface, or a new component using an old system’s interface. The retailer’s adapters used the latter approach to supply legacy reporting. Whether each adapter can be removed depends on all of its consumers and recovery obligations, not on its name or the age of the code.

An anti-corruption layer translates between different models so that one subsystem need not adopt another’s semantics. Microsoft allows this layer to be either an internal component or a separate service, and asks whether a migration layer will remain or be retired. The Legacy Mimic example retains an adapter to an external partner in its target design. That is a design decision, not proof that every adapter to a long-lived partner must survive: a later interface change might remove the need for translation. Record which responsibilities remain in the target and which are temporary. A retained façade may keep its client contract while losing legacy-routing rules. Give temporary responsibilities an owner, removal conditions, and a review or target date; complete dependency and recovery checks before removing them.

Temporary integrations can deliver value earlier or allow smaller exposures while a replacement is tested. The Transitional Architecture authors explain both benefits, while recommending comparison with construction and operating costs. Interception can also make changing the route easier. It does not automatically restore state: after the new service accepts a payment, routing back to an older store may lose the accepted change or repeat a real action. A recovery plan must establish whether the old path can process the current committed data and schema, handle in-flight requests and retries, and avoid duplicate side effects. Define the period in which rollback is supported, test synchronization or replay needed for that return, and choose controlled repair or forward recovery when those conditions no longer hold.

Parallel running, and who is allowed to decide

Coexistence needs an explicit distinction between the path serving reads, the authority accepting business writes, and processes maintaining derived copies. Record those roles by data group and enforce them through routing and access controls. A single business writer per group is a useful design; a design permitting independent writes on both sides needs a tested conflict-resolution contract. A replica write is not a second independent business decision. For parallel comparison, agree on the data position, scope, expected meaning, and approved differences. The Divert the Flow authors recommend worked examples and investigation of discrepancies because legacy outputs can contain defects; matching them is not the sole definition of correctness. Measure freshness separately from equality. Shadow testing supplies selected live inputs to a candidate without letting it perform production side effects. Isolate those effects and account for added load and sensitive-data handling. Before reading from a new copy while writes remain on legacy, establish the required consistency: for example, wait for a relevant commit position or route a session to the authoritative path when immediate read-after-write behavior is required.

Illustrative stage for one data groupRead-serving pathBusiness write authorityIntermediate operationEvidence required to advance
Dark launchLegacyLegacyNew store receives committed changes for comparison; production side effects isolatedAgreed-position reconciliation, approved differences, capture coverage, and freshness checks
Reads redirectedNew service, with a defined fallback when requiredLegacySynchronization continues; legacy remains authoritative for accepted changesRead-after-write and consistency requirements met; lag, failure handling, performance, and read-path recovery tested
Writes redirectedNew serviceNew service for the migrated groupIn this adaptation, a one-way mimic maintains legacy-derived data for remaining consumers; independent legacy writes are blocked or redirectedAll writers accounted for, in-flight work and replay handled, consumers validated, and rollback or forward-recovery conditions established
Logic migrated and dependencies retiredNew serviceNew serviceAny still-required consumer feed or recovery path remains until its own exit conditions holdApproved behavior and consumer migration verified; support/recovery windows closed before legacy objects and temporary components are removed

Expand and contract separates adding a compatible form, migrating its users, and removing the old form. Danilo Sato applies this idea to interfaces and deployments. A canary exposes a limited portion of real work, while an appropriately implemented runtime feature flag can change a route without redeployment. Neither reverses committed data or external side effects. In the table’s read-migration stage, a call-center screen writing to legacy is valid if legacy remains the sole business write authority and the new read path meets its consistency contract. The retailer’s error was allowing the new service to accept independent writes to the same customer records as well. Each authority transfer needs a cutover plan with evidence, a decision owner, stop criteria, and a tested recovery approach. Advancing the calendar or moving business logic does not itself authorize removal of remaining feeds.

Duplicate responsibility and transition debt

Duplicated rules create coordination work: a business change must be reflected consistently wherever that rule is still evaluated. The legacy displacement authors describe how ongoing business work and replacement projects can drift apart. Possible responses include delegating both entry points to a shared implementation, maintaining both with tests against approved examples, or accelerating the relevant migration. Freezing the old implementation is viable only after every affected path, including batches, stored procedures, and administrative tools, stops relying on it for changed behavior. A façade routing interactive requests is not enough. Shared implementation also introduces a dependency and operating cost; choose it only where its contract fits both paths. Agreement between two implementations is useful evidence, but both can share the same mistake.

A transition register can make these obligations visible alongside ordinary technical debt. This is the author’s proposed management tool, not a claim that all temporary code is debt or that all its cost is interest. Record necessary coexistence costs separately from avoidable burden caused by delayed removal, and compare both with the benefit and risk of keeping the integration. Microsoft highlights façade capacity and failure risks and the latency and maintenance of an anti-corruption layer. Those costs depend on implementation, including whether the layer is a separate service. An entry should explain the responsibility, its consumers, what must happen before removal, and who reviews changes to that plan.

ColumnWhat it recordsWhy the program needs itThe retailer’s adapter 14
Component or duplicationThe seam, feed, façade rule, or duplicated behaviorMake its role and dependency coverage visiblePayment-event adapter writing a legacy-derived ledger table
Purpose and consumersEvery consumer and any remaining support or recovery obligationOne consumer’s migration may not satisfy every exit conditionMonth-end reporting; confirm whether any other consumer or recovery path needs the feed
OwnerWho operates the component and who approves removalResponsibility continues while the component remains necessaryPayments team
Cost and remaining benefitNecessary run cost, change work, incidents, avoidable delay cost, and removal effort/riskPrioritize using net benefit and risk, not the total bill treated as debt interestTwo attributed incidents and duplicated schema work; estimates reviewed with the team
Removal condition and targetVerified prerequisites and a planned date, revised when assumptions changeMake the intended exit actionable without confusing a date with permission to deleteReporting and any other dependents migrated; recovery window closed; target quarter agreed
Actual removal and evidenceCompleted date, deployment/configuration/resource changes, and consumer verificationNo writes observed is supporting evidence, not proof that code or dependencies are goneRecord the actual removal date after checks; retain monitoring and rollback/repair evidence appropriate to the change

The register changed the retailer’s sequencing decision. All twenty-three adapters fed month-end reporting, but four also supplied other legacy consumers. Nineteen could therefore end when reporting moved and their support and recovery obligations closed; the other four required further work. Divert the Flow describes why moving a critical aggregator earlier can reduce the need to keep supplying legacy. It is an option to evaluate, not an instruction always to move the largest consumer next. The retailer compared continued adapter cost with the effort to recover report meaning, connect to original sources, validate the new report, and support remaining domains. In this case those conditions favored bringing reporting forward. The four additional consumers stayed visible in the plan.

Judging a coexistence architecture

Review whether authority, transition contracts, and remaining responsibilities are explicit and enforced. For each temporary component, check the owner, current need, removal conditions, and the cost or risk of delay. A shared adapter may legitimately outlive an already migrated domain. Verify duplicated rules against approved behavior and inspect consistency, freshness, and recovery evidence. Routing and feature-migration percentages can show progress within a defined scope, but they do not prove retirement readiness. Review removal opportunities at an appropriate cadence; do not delete something merely to meet a quarterly quota. Record both completed removals and justified retention decisions.

What a reviewer should ask

  1. Which permitted seams can support gradual replacement, what changes do they require, and are their costs and constraints acceptable?
  2. For each data group, which path serves reads, which authority accepts business writes, and which processes maintain derived copies?
  3. Which seam carries each capability, and what does it cost the legacy system to have it there?
  4. Which components are transitional and which are permanent, and does each transitional one have a removal condition and an owner?
  5. At what data position and scope are results reconciled, which differences are approved, how is freshness checked, and what happens on divergence?
  6. Which rules are implemented twice, and what is the strategy for each?
  7. Which consumers keep temporary components necessary, and what cost, risk, and prerequisites justify the current migration order?
  8. What has been safely removed, what remains necessary, and what evidence supports each decision?

The retailer’s fourth-year plan brought reporting forward after that comparison. The replacement used new services and committed change streams from the two domains still in legacy. The team verified the initial-data handoff, processing progress, deletion and duplicate handling, and report meaning. It compared three month-end closes at agreed positions; one discrepancy was traced to a legacy defect and its corrected treatment approved by the report owner. After all relevant consumers and recovery obligations were checked, the nineteen reporting-only adapters were removed in the following quarter. Deployment and configuration checks, observation across relevant business cycles, and consumer validation established removal; write counts were only part of the evidence. For the customer domain, the team reconciled conflicting accepted updates, tested the handoff, redirected the call-center writes, and blocked independent legacy writes for migrated customer groups. The nightly job was retired after the new ownership and synchronization contracts had been validated. Four adapters still served named legacy consumers, each with an owner, removal conditions, and a target or review date. The monthly review tracked delivered capabilities, remaining risk, and completed or deferred removals together.

References:


Discover more from Insightful Data Lab

Subscribe to get the latest posts sent to your email.

Similar Posts

Questions, corrections, or additional insights?

This site uses Akismet to reduce spam. Learn how your comment data is processed.