How do you decide whether a site's redirects belong in a build-declared table or in per-request code?
answer
- ask what the rule needs to know
- path-only means declarable
- per-request buys state, costs compute
- encode the variable in the URL instead
- split brain across layers is the real risk
basics
~20 sStart from what the rule needs to know. Path-only rules belong in the declared table, where they are cheap, cacheable and reviewable. Rules depending on identity, experiment or database state need per-request code, paying compute and cacheability for that access.
solid answer
~50 sThe dividing line is **input**: a rule that only needs the incoming path can be declared once and executed at the deployment boundary; a rule that needs request state — session, experiment bucket, locale preference, whether a record exists — has to run where that state lives. Then weigh the rest: declared rules are near-free and cacheable but change at the speed of a deploy and are capped by the target; per-request rules cost compute on every hit, must declare what they vary on so caches do not share them, but can be instrumented and changed without a deploy if backed by data. Large, data-driven mappings stop being configuration and want a cached lookup. Whatever the split, keep one owner and one test suite asserting each source path's status and destination against a deployed preview.
go deeper
Remember that some redirects are written once in configuration and some are decided while handling a request, and that the second kind can look at things the first kind cannot.
Explain the tradeoff concretely: declared rules are cheap, cacheable and deploy-bound; per-request rules see session and database state but cost compute and complicate caching.
Demonstrate operating the table — rule counts against host limits, tests asserting status and destination on a preview, temporary rather than permanent for anything you may reverse.
Set the policy: a default layer, a written exception rule, one inventory and one owner, plus a plan for data-driven redirects at scale so the table never becomes an unreviewable generated list.
## The question behind the question "Where should this redirect live?" is really "**what does the rule need to know?**" A rule that needs nothing but the incoming path can be declared once, compiled into the build output and executed at the deployment boundary. A rule that needs the request — who this visitor is, what bucket they landed in, what locale they prefer, whether this record still exists — cannot, and has to run somewhere with access to that state. Everything else in the decision is cost, ownership and reversibility layered on top of that split. ## The dimensions worth weighing - **Input.** Path-only rules belong in the build-declared table. Anything conditional on identity, session, experiment assignment, feature flag or database state needs per-request evaluation. - **Change cadence and ownership.** A table in the repository changes at the speed of a deploy and is owned by whoever can merge. If marketing needs to add a redirect on a Friday afternoon, either that becomes a one-line pull request with a fast pipeline, or the rules move into data that is read at request time. - **Volume.** A handful of moved sections is a table. Tens of thousands of legacy article URLs is a dataset, and generating a rule per URL usually hits a host rule cap or makes the config unreviewable. Data-driven redirects want a lookup with a cache in front, not a giant static list. - **Cost per request.** A declared rule handled at the edge is effectively free. A per-request rule costs compute on every matching request — on a function platform, possibly an invocation and a cold start — for a response that contains nothing but a header. - **Cacheability.** A path-only redirect can be cached by shared caches and browsers. A redirect that varies per visitor must not be cached as if it were universal, which means either marking the response as varying on the deciding input or keeping it uncacheable — a cost that recurs on every request. - **Blast radius.** A bad pattern in a declared table sends whole path prefixes to the wrong place, and a pair of rules can loop. A bad per-request rule can do the same *and* take out the compute path. Both want tests; the per-request one also wants a kill switch. - **Reversibility.** A rule declared permanent is remembered by clients and shared caches, so "we will change it back next month" is not really available. Temporary is the right default for anything experimental, whichever side the rule lives on. - **Observability.** Declared rules executed at the edge often produce thin logs; per-request rules can be instrumented properly. If you need to know how often a rule fires and where it sends people, that pushes toward code or toward a target whose edge logs are good enough. ## A decision order that holds up 1. Does the rule depend on anything beyond the path? If not, **declare it**. 2. If it does, can the dependency be reduced to a path? Encoding a locale or a variant in the URL turns a per-request decision back into a declarable one, and makes the result cacheable again. 3. If it genuinely needs request state, put it in per-request code, keep it early and cheap, and make sure any caching in front is told what it varies on. 4. If the rule set is large and data-driven, stop thinking of it as configuration: back it with a lookup, a cache and an admin surface, and accept the per-request cost for the tail of rarely-hit URLs. 5. Whatever the split, keep **one owner and one test suite**: for each source path, assert the status and destination against a deployed preview, so a rule lost in translation or shadowed by another fails the pipeline rather than a user's navigation. ## The failure mode to plan for The realistic bad outcome is not a wrong destination; it is a **split brain**, where some redirects live in configuration, some in per-request code, some in a CDN console nobody in the team can see, and each was added by a different group. Then a URL matches two rules, ordering between the layers is undocumented, and debugging starts with "which layer answered?" The strategic answer is to choose a default layer, write down the narrow condition under which a rule may live somewhere else, and keep a single inventory that the test suite reads. A redirect table is a public contract with everyone who ever linked to you, and it deserves the same review discipline as the routes themselves.
- How can a per-request redirect be turned back into a declarable one?By encoding the deciding input in the URL. If the locale or variant is a path segment rather than something inferred from the request, the rule becomes path-only: declarable, cacheable as a single shared response, and testable without simulating a session. That is usually worth one extra redirect on the first visit.
- What goes wrong when a per-request redirect is cached like a static one?A shared cache stores the first visitor's destination and hands it to everyone behind it, so one person's experiment bucket or locale becomes the whole population's. Such a response must declare what it varies on, or stay uncacheable — which is part of the recurring cost of deciding per request.
- How would you keep a redirect inventory from fragmenting across layers?Choose a default layer, document the narrow conditions under which a rule may live elsewhere, and keep a single inventory that the test suite reads and asserts against the deployment. Rules added straight into a CDN console are the ones that escape review, so make that path require the same entry as any other.
saying these in an interview costs you the question
- Puts every redirect in per-request code by default
- Generates one static rule per legacy URL at any scale
- Declares experimental redirects permanent
- Caches a visitor-specific redirect without declaring what it varies on
- Lets each team add rules in a different layer
- Never tests the table against a real deployment