BXUP Track progress & test yourself — free
Software Engineering · Playbook · Advanced · ~5 min read
SOFTWARE ENGINEERING · PLAYBOOK · ADVANCED

Strangler-Fig Extraction Planning: Map Seams and Draft the Anti-Corruption Layer with AI

Pulling a capability out of a monolith fails in planning more often than in execution: the seam is drawn through shared data, the extraction order maximizes risk, and the boundary layer leaks the monolith's mess into the new service.

Tier: Advanced · Workflow area: Legacy Modernization

An architecture-aware agent pass — grounded in the same evidence discipline as pb-21 — accelerates the three planning artifacts that matter: a seam map, an extraction order, and a drafted anti-corruption layer (ACL). Shopify's checkout extraction and Fowler's strangler-fig literature define the shape; the AI fills it in from your actual code.

When to use

  • A monolith capability (checkout, notifications, billing) slated for extraction into a service, with the decision made but the plan unwritten.
  • Evaluating whether extraction is feasible — the seam map is the cheapest honest answer, and "no viable seam" is a legitimate finding.
  • Repeated timeline slips on "we'll just extract it" — a sign nobody has mapped the actual coupling.

Not for extractions driven by fashion: if the monolith module is well-bounded and low-change, the map will show extraction buys little — listen to it.

Prerequisites

  • A completed or fresh module inventory in pb-21 style (entry points, side effects, implicit contracts, data ownership) — this playbook consumes that evidence.
  • Repo access for an agentic tool plus DB schema access (migrations directory or a schema dump) — data coupling decides extraction feasibility more than code coupling.
  • Production telemetry if available (call rates per entry point, table read/write volumes) to weight the plan with reality.

The workflow

  1. Map the seams with a coupling census. Ask for the boundary evidence, not an architecture essay:

    Target: extract the `notifications` capability from this monolith.
    Using the attached inventory and schema, produce a seam map:
    1. INBOUND: every call path from the rest of the monolith into
       notifications code — direct calls, signals/events, shared helpers.
       Cite file:line. Classify each: clean interface / reaches into internals.
    2. OUTBOUND: every notifications call into the rest of the monolith —
       models, services, utils it imports. Same classification.
    3. DATA: tables notifications reads/writes; for each, who else reads/
       writes it (sole-owner / shared-read / shared-write). Shared-write
       tables are extraction blockers — list them first.
    4. HIDDEN COUPLING: shared transactions spanning the boundary, shared
       caches/keys, monolith-global config or feature flags read inside.
    Rank all couplings by severance difficulty. Mark ASSUMPTION where the
    code doesn't prove it.
    

    Audit as in pb-21: spot-check citations, chase every ASSUMPTION, and cross-check the DATA section against the schema and a grep for each table name — shared-write tables missed here become distributed-transaction bugs later.

  2. Choose the strangle order. With the ranked coupling list, plan thin vertical slices, easiest-severed and lowest-risk first — the classic fig pattern grows coverage capability by capability, not layer by layer:

    Propose an extraction order as strangler slices. Rules: each slice is
    independently shippable and reversible; start with the lowest-coupling,
    lowest-write-volume flows; shared-write tables stay in the monolith until
    a dedicated data-migration slice; every slice states its rollback (flip
    the routing flag back). For each slice: scope, couplings severed, new
    interfaces created, risk, and a proof-of-completion check.
    

    You own the risk ordering — the model does not know that template rendering is politically radioactive or that Q4 freezes deploys. Adjust with that context, and weight by the telemetry (extract the 50-rps path last, not first).

  3. Draft the anti-corruption layer at the seam. The ACL is where monolith concepts get translated instead of leaked. Have the model draft it from real call sites:

    Here are the 9 INBOUND call sites [pasted]. Draft the ACL:
    - A narrow interface (NotificationGateway) expressing what CALLERS need,
      not what the notifications internals happen to expose today.
    - Translation at the boundary: monolith types (User model, Order) map to
      boundary DTOs (RecipientRef, OrderSummary) — no ORM models cross.
    - Every method: sync or async-with-outbox? What does the caller do on
      timeout/failure? No method may silently widen to "and also return the
      template object" — flag call sites whose current usage doesn't fit the
      narrow interface instead of widening it.
    

    The flagged misfit call sites are gold: each is either refactored to the narrow interface (usually) or forces an honest interface discussion (occasionally). An ACL generated by unioning current usage is just the old coupling with extra steps.

  4. Wire the seam inside the monolith first. Before any service exists, land the gateway interface in-process: callers use NotificationGateway, the implementation still calls the old code, a routing flag exists but routes nowhere. This is the cheapest place to discover the interface is wrong — and it is a pure refactor verifiable with pb-22 characterization tests. Fitness functions keep the seam from eroding while extraction proceeds:

    def test_no_new_direct_imports_of_notifications_internals():
        violations = grep_imports("src/", target="notifications.internal")
        assert violations <= BASELINE  # ratchet: may only decrease
    
  5. Package the charter and pressure-test it. Assemble: seam map (audited), slice order with rollbacks, ACL draft, data strategy for each shared table (stay / replicate-then-own / API-back), and fitness functions. Then red-team it in a fresh session: "Here is an extraction plan. Attack it: what breaks under partial failure between monolith and service? Which slice hides a shared transaction? Where will the ACL leak?" — then take the survivors to your senior engineers for the tribal-knowledge pass.

Key Takeaway:

The strangler-fig's safety comes from reversible slices behind a routing seam — and the seam is only real if the ACL is narrow and the fitness function stops regressions. An extraction plan without shared-write table answers is a wish, not a plan.

Tool variants

  • Claude Code (read-only + plan mode): best for the coupling census across a large monolith; extended thinking helps on the slice-ordering trade-offs; keep charter drafting in the same session as the audited map.
  • Copilot Chat / Cursor: run the census per-section on large codebases; agent mode can generate the fitness-function tests and the in-process gateway skeleton.
  • Deterministic companions: dependency-cruiser/ArchUnit/import-linter enforce the seam as CI rules (the fitness functions); pg_stat_user_tables or equivalent supplies real read/write volumes for the DATA section.

Verification

  • Seam-map citations spot-checked; every shared-write table independently confirmed via schema + grep, with an explicit strategy in the charter.
  • Slice 1 is genuinely reversible: rollback is a flag flip, demonstrated in staging.
  • The in-process gateway landed with characterization tests green — proving the interface fits before distribution costs arrive.
  • Fitness functions in CI, ratcheting: direct-internal-import count only goes down.
  • Red-team pass and senior-engineer review both produced charter edits (if neither did, they didn't engage).

Pitfalls

  • Letting the model draw generous seams. LLMs echo your framing — ask "map the seams" and you get seams even where extraction is inadvisable. The shared-write table list is the honesty check; respect blockers.
  • ACL by union. An interface that accepts everything callers currently do just relocates the coupling. Narrow interface + flagged misfits, always.
  • Big-bang data migration. Moving the tables the same day as the code multiplies risk; replicate-then-cutover per table, one slice each.
  • Planning without failure modes. In-process calls never half-fail; network calls do. Every ACL method needs a stated timeout/failure behavior before slice 1 ships, or you will design them during an incident.
  • Stale charter. The monolith keeps changing under the plan; re-run the coupling census before each slice — it is cheap now, that is the point of having it scripted.

Restricted-environment variant

  • Run the census over pb-21-style extracted artifacts (dependency graphs, schema, grep output, signatures) rather than raw source where client policy requires; coupling analysis survives sanitization well since it is structural.
  • Fitness functions, import-linters, and the in-process gateway refactor are entirely local work — the highest-value steps (4 and the ratchet) need no external model at all.
  • Local-model route: usable for organizing the census and drafting DTO translation code; keep slice-order judgment and ACL failure-mode decisions with humans — that judgment is thin even in frontier models and thinner locally.

Success criteria

  • A written, evidence-cited extraction charter exists where "we should extract notifications someday" used to live — including, possibly, the finding that extraction is not worth it.
  • Slice 1 ships behind a routing flag with a demonstrated rollback, and the fitness-function ratchet is enforcing the seam in CI.
  • Six months of slices later, the seam map still matches reality because re-running the census is a session, not a quarter.
This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in