SkillByAIOpen interactive version →

Lesson 20 / 25

Extracting a Service Safely

Extract a module into a service, including its data, step by step.

Code first, data last, always reversible

A safe extraction follows small steps. 1. Modularise in place: make the capability a clean module with an internal API and its own tables (see the modular monolith section). 2. Branch by abstraction: put an interface in front of the module so callers do not care whether the implementation is in-process or remote. 3. Build the service behind that interface, initially reading the monolith's data or a replicated copy. 4. Parallel run: call both implementations, serve the old result, compare, and log differences. 5. Switch reads, then writes behind a feature flag, gradually by percentage. 6. Move the data: the new service becomes the owner; migrate with dual writes or change data capture, verify, then remove the old tables. 7. Delete the old code. Every step can be rolled back, and the monolith keeps working throughout.

Branch by abstraction with a feature flag

Callers use one interface; a flag decides which implementation serves the request.

class PricingPort:
    def quote(self, cart): ...

class InProcessPricing(PricingPort):
    def quote(self, cart):
        return legacy_pricing.calculate(cart)        # existing monolith code

class RemotePricing(PricingPort):
    def quote(self, cart):
        return pricing_client.post("/quotes", json=cart.to_dict()).json()

def pricing_for(user) -> PricingPort:
    if flags.enabled("pricing-service", user):     # 1% -> 10% -> 100%
        return RemotePricing()
    return InProcessPricing()

# parallel run: compare without affecting users
def quote_with_shadow(cart, user):
    primary = InProcessPricing().quote(cart)
    try:
        shadow = RemotePricing().quote(cart)
        if shadow != primary:
            log.warning("pricing mismatch", extra={"cart": cart.id})
    except Exception:
        log.exception("shadow pricing failed")
    return primary

Moving house one room at a time

You do not move everything on one day and hope it fits. You furnish one room in the new house, live in it for a while, then move the next, and only cancel the old lease when the last room is done.

Quick check: In a service extraction, when is the data usually moved?

  • First, before any code changes
  • Never; services always share the monolith database
  • Only during a scheduled outage
  • Late, after the new service has been proven with reads and writes behind a flag
Answer

Late, after the new service has been proven with reads and writes behind a flag — Moving data is the riskiest step, so it comes after the service has been validated.