Your team proposes caching every derived value in the app as a standing rule — how would you evaluate that policy?
answer
- price it, do not argue taste
- comparison, memory, staleness, review
- needs heavy compute and stable inputs
- three of four combinations lose
- compute by default, cache where measured
basics
~20 sAnswer with a cost model: each cache adds an input comparison, retained memory, a dependency set that can go stale, and review cost. It pays only where compute exceeds the comparison and inputs are stable — so cache at measured hot spots.
solid answer
~50 sI would take the proposal seriously and then price it. A cache adds a comparison on every request, holds its last result in memory, introduces a dependency set that can be too narrow and serve a stale value, and costs every future reviewer attention. It pays only when the computation clearly exceeds the comparison *and* the inputs are stable across the renders you care about — both halves. Of the four combinations of cheap/expensive compute and stable/unstable inputs, three are losses, and a blanket rule lands in all four while hiding which caches were ever justified. So I would counter-propose: compute by default, shrink the input before caching, cache at measured hot spots with a note on what was observed, write the threshold down, and audit existing caches. Tooling that inserts the caching itself would change my answer.
go deeper
Take away the rule of thumb: caching is not automatically an improvement, because a cache costs a comparison and holds its result in memory.
State the condition for a cache to pay: the computation must clearly exceed the comparison and the inputs must be stable across the renders you care about.
Bring evidence. Name what you measured, show that you tried shrinking the input first, and be ready to delete a cache that never hits.
Answer a blanket proposal with a bounded policy, priced in three currencies — time saved, memory retained, correctness risk taken — and name the condition that would change your mind.
The proposal sounds disciplined: cache every derived value and you can never be caught recomputing. It is worth taking seriously and then answering with a cost model, because the rule optimises a cost that is usually small and pays for it with costs that are usually invisible. ## What a cache actually costs Wrapping a derivation in a cache is not free. Each one adds: - **A comparison on every request.** The runtime must check the current inputs against the remembered ones. For a derivation that is a sum or a short filter, the comparison can approach the cost of the computation it is avoiding. - **Retained memory.** The last result is held for as long as the cache lives, including results nothing will ask for again. Across a large tree and long-lived stores that adds up, and results that hold references keep other objects alive with them. - **A way to be wrong.** A dependency set can be too narrow and serve a stale value. Recomputation has no such failure mode. A blanket rule multiplies the number of places that can rot, particularly where the set is declared by hand and can drift from the body. - **Reading cost.** A reviewer now has to check a dependency set as well as a computation, on every derivation in the codebase, and the signal that "this one was cached because it was expensive" is gone once everything is cached. ## When it pays Caching wins when the saved computation clearly exceeds the comparison **and** the inputs are stable across the requests you care about. Both halves are required: | Situation | Verdict | |---|---| | Heavy derivation, inputs stable across most renders | caching pays — this is the case the mechanism exists for | | Heavy derivation, inputs change on nearly every render | no saving; you pay compute plus comparison | | Cheap derivation, inputs stable | saving is noise; you pay memory and review cost | | Cheap derivation, inputs unstable | strictly worse than not caching | Three of the four rows are losses. A rule that applies caching everywhere lands in all four, and the two failure rows are the ones nobody notices, because nothing breaks — the app is simply carrying overhead and a little more risk than it needs. ## What I would propose instead 1. **Default to computing during render.** It is correct by construction and cheap for the overwhelming majority of derivations. 2. **Cache at identified hot spots**, with a note saying what was observed. That keeps the cache honest and makes it reviewable later, when the data size or the code has changed. 3. **Write the threshold down** so it is not re-argued per review: roughly, derivations over large collections, sorting and grouping, index building, parsing — and a standing invitation to shrink the input first, because a derivation over the visible rows often removes the cost with no cache at all. 4. **Require evidence for a new blanket rule**, including the memory side. "We measured this page" is a reason; "recomputing feels wasteful" is not. 5. **Check the caches you have.** A cache that never hits is worse than none, and it is common enough that auditing existing ones usually finds more value than adding more. ## The part that changes the answer Two considerations can legitimately move the decision: - **Who inserts the cache.** Where the framework or its compiler decides caching automatically, the human costs — a dependency set to maintain, a reviewer's attention, drift between body and declaration — largely disappear, and the remaining question is memory. A rule that says "let the tooling do it" is very different from a rule that says "every engineer writes one by hand". - **Consistency as a value in itself.** A team can rationally prefer a uniform pattern over a locally optimal one, because uniformity is cheaper to review and teach. That argument is real, but it has to be made about *review cost*, not about performance, and it still has to answer the memory question and the stale-value question. ## How to close Reframe the proposal from a performance rule into a trade with three currencies: microseconds saved, bytes retained, and correctness risk taken on. Then offer the smaller policy that captures the upside — compute by default, cache where measured, prefer shrinking the input, write the threshold down, audit the caches that exist — and name the one condition that would change your mind, which is tooling that inserts the caching for you. Answering a blanket proposal with a bounded counter-proposal, rather than a flat refusal, is what the question is really testing.
- Which single condition would most change your answer to this proposal?Whether a human or the tooling inserts the cache. When the framework or its compiler decides caching automatically, the costs that made me resist — a dependency set to maintain, drift between declaration and body, a reviewer's attention on every derivation — largely disappear, and the remaining question is retained memory. A rule saying "let the tooling do it" is a different proposal from one saying "everyone hand-writes caches".
- How would you make the resulting policy stick without re-arguing it in every review?Write the threshold down with examples — large-collection derivations, sorting and grouping, index building, parsing — plus the standing instruction to try shrinking the input first. Require a one-line note beside each cache saying what was observed, which makes it reviewable later when the data or the code has changed, and schedule an occasional audit for caches that no longer hit.
- What is the argument for uniformity that a blanket rule can legitimately make?That a consistent pattern is cheaper to review and teach than a judgment call at each site, and consistency has real value on a large team. It is a fair argument, but it has to be made about review cost rather than performance, and it still owes an answer on retained memory and on the stale-value risk that hand-maintained dependency sets carry.
saying these in an interview costs you the question
- Treats caching as free and therefore always safe to add
- Argues from taste instead of pricing comparison, memory and risk
- Ignores that a cache with unstable inputs is strictly slower
- Forgets that retained results are a memory decision at scale
- Rejects the proposal outright instead of offering a bounded alternative
- Assumes a cache is effective without ever auditing whether it hits