# Extracting a Service Safely — Monolith vs Microservices

Source: https://www.skillbyai.com/en/monolith-microservices/g-extract

> 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.

```python
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.

**Quiz:** 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
- [x] 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.
