Microservices Migration Playbook

Extract a service incrementally with a tested contract, one data authority, safe shadow comparison, and explicit cutover and recovery gates.

trigger="A specific capability needs a different release, scaling, ownership, or failure boundary, and an internal module change cannot adequately address it." owner="The migration lead accountable for the capability's user outcome and continuity." participants={["Legacy application owner", "New service owner", "Data owner", "Consumer representatives", "Security reviewer", "On-call owner"]} prerequisites={[ "A measured problem, an alternative that retains the monolith, and an agreed success criterion.", "An inventory of callers, database writers, scheduled jobs, integrations, and business invariants.", "A representative test environment, protected test data, and an approved recovery window." ]} outputs={[ "An extraction contract and dependency map with clear ownership.", "A versioned routing and data-transition plan with reconciliation evidence.", "A tested cutover, rollback boundary, operator runbook, and retirement record." ]} doneWhen={[ "The extracted capability meets the agreed outcome without an unacceptable delivery or reliability regression.", "Every write has one authority and accepted operations can be reconciled across the transition.", "Shadow comparison cannot produce external production effects.", "Recovery has been exercised for new-path writes, not only for traffic routing." ]} />

Choose one extraction with a measurable purpose

Service extraction adds network, deployment, data, and operating boundaries. It earns that cost when a named capability benefits from independent change, capacity, or failure handling.

Use when to split a monolith to decide whether extraction is justified. This playbook begins with a candidate and takes it through migration. The strangler pattern explains the routing pattern in more detail.

Write the improvement you expect to observe. Examples include releasing a reporting capability without rebuilding the transactional application, or isolating a resource-heavy import from interactive requests. These are possible objectives, not claimed results. Compare them with a modular boundary, query improvement, or separate worker inside the existing deployment.

1. Map the capability and its hidden callers

The legacy owner traces a complete user operation through code, tables, stored procedures, jobs, exports, and external systems. Search for direct database access as well as documented APIs. A nightly finance export or support script can remain a dependency after interactive traffic has moved.

The data owner identifies invariants currently enforced by one transaction. If order creation and stock allocation must succeed together, moving them into separate services changes the consistency problem. Preserve that boundary, or agree a pending state and recovery process before splitting it.

Record the candidate's input, output, authorization, timeout behavior, error semantics, and data freshness. Assign a service owner with access to deployment and incident support. That owner may collaborate with a platform team; independence does not require duplicating every operational skill.

Gate: the team can list all known writes and critical consumers. Unknown ownership or an untested business invariant blocks live cutover.

2. Establish a compatible service contract

The application owners agree request and event schemas, version behavior, stable operation identity, and rules for retries. Include business semantics: an accepted export is not a completed export, and a timeout is not proof that no work occurred.

Build contract tests around actual consumers. Then test the combined workflow with representative dependencies and failure responses. A passing schema check cannot prove authorization, transaction behavior, or acceptable latency.

During coexistence, use an adapter to translate legacy semantics where necessary. Microsoft describes a facade and phased replacement in the Strangler Fig pattern. The facade itself needs capacity, monitoring, and a failure plan.

Avoid replacing every internal call with remote calls at once. A heavily connected boundary may need preparatory module changes before it becomes a useful service boundary.

3. Choose a data authority for every phase

Document the authoritative writer before, during, and after cutover. Do not independently write to old and new stores and assume a retry will repair every partial failure.

A local transaction can commit application state and an outbox record together. A publisher or CDC process can then update the migration target. AWS's transactional outbox guidance addresses the local database-plus-message gap. Duplicate delivery, ordering, consumer state, and external effects still need handling.

For a snapshot plus change stream, capture a consistent starting position using the selected database's supported procedure. Preserve changes that occur during the copy, including deletions. Track a checkpoint and validate the target at a known source position rather than comparing moving totals.

"type": "svg-architecture", "title": "Keep the legacy writer authoritative during data preparation", "nodes": [ ], "links": [ ], "caption": "This is a preparation phase. The new service does not become an independent production writer merely because its copy looks current." }} />

Compare representative records and business invariants in addition to counts. Equal row counts can conceal wrong amounts, missing updates, or duplicate logical operations. Protect the copied data with the same classification, access, and retention rules as its source.

Gate: copy, replay, and reconciliation can resume after interruption without losing their position or repeating business effects.

4. Make shadow comparison incapable of acting

The test lead decides whether read-only replay is sufficient or whether isolated writes are needed to compare behavior. Replayed production inputs may contain personal data, credentials, or business-sensitive values. Minimize or transform them under an approved data-handling plan.

A shadow service must not charge cards, send messages, reserve stock, or mutate the live authoritative store. Remove production credentials, restrict network access to external action endpoints, and replace effectful dependencies with isolated equivalents. A Boolean flag inside the code is not the only protection.

Test the safeguards deliberately. Send a request that would produce a payment or notification and demonstrate that the shadow path cannot reach the production provider. Tag shadow work so operators do not mistake it for actual user demand.

Normalize only differences that the contract permits, such as generated timestamps. Investigate missing results, authorization differences, and changed error behavior. Do not discard every mismatch to make the comparison pass.

5. Prepare service operations before routing users

The new service owner prepares deployment, configuration, secret rotation, health checks, dependency timeouts, capacity limits, and a support runbook. Decide which failures should reject work, which can remain pending, and which require escalation.

Use synchronous calls when the caller needs an immediate result. Use durable asynchronous work when pending execution, buffering, or independent consumption serves the business contract. Neither choice removes the need for operation identity and recovery.

Add telemetry for the complete migrated operation: arrival, acceptance, completion, errors, queue age where relevant, and reconciliation discrepancies. Preserve correlation across the facade and legacy adapters without exposing unnecessary customer identifiers.

Exercise a dependency outage and an overloaded target in a representative environment. The routing layer should not turn one unhealthy service into unbounded retries across the system.

6. Transfer authority with a recorded checkpoint

Routing traffic and transferring write authority are separate actions. Define a stable cohort or partition, and ensure every request and background job uses the same ownership rule. Related operations must not alternate between incompatible writers.

"type": "flow", "title": "Transfer a write boundary through explicit gates", "steps": [ ], "caption": "If writes cannot pause, use a separately designed and tested handoff protocol. A traffic percentage alone does not establish exclusive ownership of shared records." }} />

Before activation, verify target capacity, current data, compatible consumers, operator access, and recovery readiness. Record the cutover position and the first operation accepted by the new writer.

An illustrative reporting extraction may move reads before writes because reports can tolerate a defined lag. A billing extraction may require a controlled write pause and stronger reconciliation. Do not reuse one cutover policy across those different contracts.

Stop expansion if authorization differs, business outcomes diverge, latency breaches the agreed objective, or reconciliation falls behind. The migration lead records whether to continue, pause, roll back, or repair forward.

7. Define rollback after new-path writes

A flag can change routing quickly, but it cannot make old data current or restore a removed schema. State the last point where traffic-only rollback is safe.

| State at failure | Recovery path to prepare | | --- | --- | | Target has served only isolated comparisons | Stop shadow traffic and preserve discrepancy evidence | | Target serves reads, legacy remains writer | Route reads back if the old path still meets the contract | | Target has accepted authoritative writes | Fence writers, reconcile new changes, and verify reverse compatibility before returning authority | | New state cannot be represented by legacy code | Keep the new authority contained and repair forward, or use an explicitly approved conversion | | External effects have occurred | Reconcile each operation and use authorized compensation where appropriate |

Reverse synchronization needs conflict, ordering, and duplicate rules. Prevent replication loops. Test how both systems handle new fields, deleted records, and changed invariants before calling the migration reversible.

A compensation is a new business action. Refunding a charge or canceling fulfillment may fail or require approval; it is not a database rollback.

8. Prove recovery and retire exact dependencies

| Test | Acceptance evidence | Owner | | --- | --- | --- | | Copy stops midway | Resume reaches the expected checkpoint without omitted records | Data owner | | Message is delivered twice | Target remains correct without duplicate effects | New service owner | | Legacy job writes after cutover | Write is denied or follows the approved routing path | Legacy owner | | Shadow receives an effectful request | Production action is technically blocked | Security and test owners | | New writer fails after a commit | Accepted work remains traceable and recovery is executed | On-call owner | | Rollback crosses a schema change | Compatibility or the repair-forward boundary is demonstrated | Migration lead |

After stable operation over representative business cycles, inventory remaining legacy calls, scheduled work, reports, and retention duties. Remove only approved exact targets. Keep required records and restore capabilities for the agreed period. A quiet endpoint does not prove that a monthly job no longer depends on it.

9. Preserve authorization and tenant context across the new boundary

The extracted service must not trust a tenant, user or role merely because the legacy application forwarded it. Define how the service authenticates the caller or verifies a signed workload identity, how it resolves resource ownership, and where object and action authorization occurs. Test bypass routes that do not pass through the facade.

Map privileged support, batch and migration identities separately from customer traffic. A copy worker may need broad read access temporarily, but it should not become the permanent service identity. Scope credentials to the migration phase, record their use, rotate them where required and remove them at retirement.

Include authorization state in migration tests. A copied record with stale permissions can expose data even when its business fields match. Test revoked users, transferred ownership, deleted tenants, support impersonation and background jobs. Recheck current authorization before executing a delayed action whose permission can change.

Protect correlation and debugging data. Use stable operation identifiers without copying full payloads or credentials into every log. Define access and retention for shadow inputs, discrepancy stores, dead-letter queues and migration exports. The extracted boundary adds evidence stores as well as application stores.

If an authorization mismatch appears, stop the affected cohort and contain the new path. Determine whether data, policy, identity propagation or code is wrong. Do not normalize security differences away as expected migration noise.

10. Rehearse a cross-service transaction and uncertain outcome

Choose a workflow that currently uses one database transaction and trace how the proposed service boundary changes it. Identify which local state commits first, which message or call follows, what the user sees, and how an operator resolves partial completion.

Where a saga is appropriate, distinguish orchestration or choreography from the business compensation itself. AWS's saga pattern guidance describes distributed-transaction concerns including compensating actions. A compensation is not guaranteed to succeed, and some effects cannot be undone.

Inject failures at each handoff:

  • before the first local commit;
  • after commit but before publication;
  • after publication but before acknowledgment;
  • after an external provider accepts the action but before the response;
  • during compensation; and
  • while one participant runs an older contract version.

For every case, accepted work must appear as completed, rejected, pending or unknown under an accountable owner. A retry occurs only when the operation identity and downstream behavior make it safe. If the external outcome is unknown, query its authority or route to reconciliation rather than repeating blindly.

Keep a discrepancy queue with age, impact, owner and permitted actions. Do not treat a dead-letter record as resolved work. Review whether the user and support team can understand the current state without database forensics.

11. Govern compatibility and service lifecycle

Record the contract source, provider and consumer versions, supported pairs, deprecation signal, notice route, migration guide and retirement authority. Use additive transitions where feasible, but verify behavior beyond schema. Defaults, authorization, ordering, rate limits and error meaning can break a consumer without removing a field.

Microsoft's API design guidance discusses resource-oriented design and versioning considerations. Apply the relevant ideas to the actual protocol and consumers; it is not evidence that one version strategy works for every interface.

Run contract tests against the built artifacts that will coexist. Keep a consumer inventory including jobs, reports, support tools and external integrations. A quiet integration may be seasonal or reserved for recovery. Retirement needs owner evidence, not a short period with no traffic.

Assign the new service a lifecycle owner for runtime, dependencies, deployment, data store, on-call, capacity and recovery. Extraction creates these obligations. If no team can fund them, revisit the boundary before the old implementation is retired.

12. Compare the intended benefit with the migration burden

Measure the original constraint using the same definition after representative operation. Did releases become more independent? Did heavy work stop affecting interactive users? Did failure containment improve? Report new network latency, support effort, incident paths, data synchronization and platform cost alongside the intended gain.

Separate transition cost from steady-state cost. Coexistence may require duplicate infrastructure, adapters, comparison jobs, extra telemetry and specialist support. Track every temporary component with an owner and exit condition so the migration does not become permanent dual operation.

The final decision can expand, narrow, pause, return the capability to a module or retain the current boundary. One successful extraction is not approval to decompose the entire system. Preserve failed hypotheses and the conditions under which another candidate should be reconsidered.

Set the next review trigger against observed coupling, support load, data discrepancy, contract churn, recovery failure or a changed product boundary. Re-run the relevant exercise when that trigger occurs instead of assuming the original migration evidence remains current.

Reusable extraction record

Record the business capability, measured problem, alternative considered, consumers, invariants, old and new owners, contract versions, authoritative writer by phase, data checkpoints, shadow restrictions, cutover scope, stop conditions, rollback boundary, recovery evidence, and retirement approvals.

"The extraction has a measured purpose and a viable operating owner.", "Callers, jobs, database writers, and business invariants are mapped.", "Data copy and synchronization have checkpoints, duplicate handling, and reconciliation.", "Shadow work cannot create external production effects.", "Cutover gives each write scope one authority, including background jobs.", "Rollback after new-path writes has been tested or a repair-forward boundary is explicit.", "Legacy retirement preserves required data, reports, and recovery obligations." ]} />

Limitations

This sequence assumes the team can change or intercept the relevant workload. Unmodifiable systems, shared vendor databases, strict transaction requirements, or short coexistence windows may require a different migration. Record those constraints and obtain data, security, and service-owner approval before the first live transition.