A facade in front of a 12-year-old monolith routes 3 of about 20 capabilities to new services, matched by URL prefix. A new mobile build starts calling an endpoint nobody listed in the facade's configuration. What happens, and what should the facade's behaviour for an unmatched route be?
Show the full answer Hide the answer
What happens, step by step
It depends entirely on one line of configuration, and the two possible answers are a non-event and an outage.
If the facade is an allowlist — match the three new prefixes, send everything else to the monolith — the unmatched endpoint goes to the monolith, which has handled it in production since before the facade existed. Nobody notices. The mobile build works.
If the facade is a blocklist — match the legacy prefixes, send everything else to the new services — the unmatched endpoint goes to a service that has no handler for it. The client gets a 404, or a 502 if the gateway cannot resolve a backend. The mobile release is broken for every user who installed it, the error appears only on paths nobody enumerated, and the first hypothesis in the incident channel is a mobile bug rather than a routing default.
Why the default must be the legacy system
The monolith is the implementation of record for everything that has not been explicitly moved, so the safe default is the one that is true by construction. An allowlist can only be wrong by omission, and omission routes to a system that works. A blocklist is wrong by omission too, and omission routes to a system that does not exist yet.
The same reasoning decides the direction of every migration default: make the unmigrated path the fallback, so the failure mode of an incomplete inventory is old behaviour rather than no behaviour. The cost is that a capability you did move can keep receiving traffic on a path you forgot to list, which appears as the new service looking suspiciously quiet. That is a reconciliation problem, and it is far cheaper than a 404.
What stops it
- A route table test that enumerates every path the monolith serves, asserts each one resolves to exactly one backend, and fails the build when a path appears in neither list. The monolith's own routing table or access logs generate the list, so this costs an afternoon.
- An alert on unmatched-route rate above zero, which with an allowlist default is informational and tells you where undocumented clients live. Expect a handful of hits a week in a twelve-year-old system.
- One place where the routing decision lives. When clients also carry their own base URLs, the facade stops being the inventory of what has moved and nobody can answer "what is still on the monolith".
When this is the wrong answer
A blocklist default is right in exactly one phase: the end. When 19 of 20 capabilities have moved and the remaining one is enumerated and shrinking, flipping the default to the new platform means the facade's configuration lists what is left on the monolith, which is the inventory you now want and the shorter list to maintain. Flip it once, with the full route table test as the gate, and treat the flip as a cutover with its own rollback rather than as a cleanup.
A facade is also the wrong tool when the legacy system has no network boundary to intercept — a batch scheduler or a shared database with no request path. There, the equivalent default lives in the dispatch table, and the same rule applies: unknown work goes to the implementation that has always done it.