skip to content

As a lead, how would you decide what data a route must declare up front versus what deeper parts of the page may request themselves?

level: principalimportance: nice to knowfreq 38%

answer

  1. default plus named exceptions
  2. overlap and legibility on one side
  3. threading and coupling on the other
  4. budget per URL, not a blanket ban

basics

~20 s

Make route-declared the default for whatever the first view needs: it lets reads overlap and keeps a URL's cost legible. Allow documented exceptions for secondary, interaction-driven and highly personalised data, and enforce it with a per-route budget.

solid answer

~50 s

Start from what route-level declaration actually buys: matching reveals every read before rendering, so reads overlap, the route's cost is measurable per URL, and reviewers can see a URL's data contract in one place. That is worth defaulting to for everything the first meaningful view needs. Then be honest about the cost — a component deep in the tree that needs one extra field has to have it added to the route's data and threaded down, which couples the route to its subtree and tempts people to return whole records just in case. So write the default as a rule with named exceptions: secondary panels below the fold, data that only exists after an interaction, and anything so user-specific that hoisting it would make the route uncacheable. Enforce the default with a route-level budget and a review habit, not with an absolute ban, because absolute bans produce over-fetching.

go deeper

for a junior

Know the default and the reason for it: data the first view needs is declared by the route, so the reads overlap and the page arrives populated.

for a middle

Be able to argue both sides — overlapping reads and a legible contract against threading a field down a deep tree — and to say which data genuinely belongs at the route.

for a senior

Show that you would measure a route's data phase, spot hoisted data no component renders, and keep secondary panels off the critical path deliberately.

for a principal

Own the policy: a written default, a short exception list with reasons, a per-route budget that catches regressions, and a review habit that stops over-fetching becoming the coping strategy.

## What the default buys Declaring a route's data at the route is not an aesthetic preference; it produces four concrete properties. - **Overlap.** Because matching exposes every matched segment's read before rendering, independent reads can be issued together. Reads discovered during rendering cannot be: each one is found only when the component that needs it renders. - **Legibility.** A reviewer opening a route sees the whole data contract of that URL. Nobody has to trace the component tree to answer "what does this page cost?" - **Budgetability.** Cost attaches to a URL, so it can be measured, alerted on, and regressed against. "This route's data phase must stay under 300 ms" is a sentence you can enforce. - **Safety by position.** The read happens in the one place that holds server privileges and sees the request, so authorisation and field selection have an obvious home. ## What the default costs - **Threading.** A deep component needing one more field means editing the route's read, the type it returns, and every level in between. The friction is real, and it scales with tree depth. - **Coupling.** The route now knows what its descendants render. Move a component and the route's read is wrong; delete one and the read lingers, still paid for on every request. - **Over-fetching as a coping strategy.** When adding a field is annoying, people return the whole record. That inflates the payload, leaks fields the UI never shows, and hides which data is actually used. - **A worst-case first byte.** Everything hoisted into the route blocks the response. A rule that says *everything* must be route-declared eventually blocks the document on a read nobody is waiting for. ## Where to draw the line A policy that survives contact with a real team looks like a default plus named exceptions. 1. **Route-declared by default** for the data the first meaningful view needs — the record the URL names, the collection the page is about, anything a crawler or a link preview should see. 2. **Exception: secondary regions.** Panels that are not the point of the page, are below the fold, or are useful-but-optional. Hoisting them makes every visit pay for them. 3. **Exception: interaction-driven data.** Anything that does not exist until the user acts — a search result, an expanded row, a filtered view. There is nothing to declare at match time. 4. **Exception: highly personalised fragments.** Data so specific to one viewer that hoisting it would force the whole route to be produced per request, when the rest of it could be shared. 5. **Never an exception: the record the URL names.** If the primary content can be missing at first paint, the URL no longer means what it says. | Question to ask | Route-declared | Deeper read | |---|---|---| | Does the first view need it? | Yes | No | | Should a crawler see it? | Yes | Not necessarily | | Does it exist before the user acts? | Yes | No | | Does waiting for it delay content people asked for? | Accept and budget | Keep it out of the critical path | ## Enforcing without an absolute ban Absolute rules fail in both directions. "Everything at the route" produces bloated reads and a slow first byte; "fetch wherever you like" produces waterfalls nobody can see and a cost no one can attribute to a URL. Enforce the default instead with mechanisms: - A **per-route data budget** measured in continuous integration or in production timing, so a regression is visible without a reviewer noticing it. - A **written exception list** with the reason, so the next person does not re-litigate it in a pull request. - **Field selection in the read**, so returning a whole record is the thing that looks wrong in review, not the thing that is easiest. - A **periodic look at payload size**, which catches hoisted-and-forgotten data that no component renders any more. ## Answering it as a lead The interviewer is testing whether you can hold two costs at once. Say what the default is and why, say plainly what it costs the person adding one field to a deep component, name the exceptions you would write down, and finish with how the rule is enforced without becoming a ban. A candidate who only recites "fetch at the route, always" has not run into the second half of the tradeoff yet.

  • What early signal tells you the route-declared default has started to over-fetch?
    The payload grows without the page changing, and reads return whole records rather than selected fields. Compare what the route returns against what the UI renders; fields nobody reads are usually leftovers from a component that moved or was deleted, still paid for on every request.
  • How do you keep exceptions from quietly becoming the norm?
    Write them down with the reason and revisit them with the route's timing data. An exception that was granted for a below-the-fold panel stops being valid when that panel moves into the first view. Without a list, each pull request re-argues the question and the default erodes.
  • Why is a per-route time budget better enforcement than a code rule?
    Because the thing you care about is the user-visible cost of a URL, not where a line of code sits. A budget catches a slow read regardless of placement, survives refactors, and gives reviewers an objective number instead of a style argument that depends on who is reviewing.

saying these in an interview costs you the question

  • Says fetch everything at the route with no exceptions
  • Ignores the cost of threading one field through the tree
  • Treats caching as a substitute for removing round trips
  • Assumes hoisting a read cannot slow the first byte
  • Has no way to measure a route's data cost