A declarative description is correct, yet the engine keeps choosing a slow or wrongly ordered plan — how do you intervene without giving up the paradigm?
answer
- get the plan before changing anything
- missing fact or missing judgement
- truth first, strategy last
- a hint describes the engine, not the system
- the hatch loses the free re-run
basics
~20 sPrefer stating a fact the engine was missing, such as a dependency or a narrower property, over dictating strategy. A hint is the next resort and pins you to today's engine; a literal step is last, forfeiting derived order and idempotence.
solid answer
~40 sFirst find out what the engine actually planned, because the fix depends on whether it lacked information or lacked judgement. If a relationship is real but undeclared, declare it — that keeps the description a set of true facts and costs nothing. If the description is ambiguous or too broad, narrow it so fewer plans satisfy it. Only then reach for a hint that names a strategy: it works, but it encodes an assumption about one evaluator that will quietly rot when the evaluator improves. The escape hatch — a literal step in the middle of a declarative description — is the last resort, because everything the paradigm was giving you (derived order, a safe re-run, a readable diff) stops at that step and becomes your responsibility again.
code
yaml · 10 linesitems:
- id: api
kind: service
needs: [store, schema-change] # edge the engine could not infer
- id: warm-caches
kind: run-step # escape hatch: a literal instruction
command: "warm the caches for api"
needs: [api]
skip_when: "cache hit rate above 0.9" # the precondition you must write yourselfgo deeper
The takeaway is that the engine, not you, chooses how the work is done, and when its choice is poor there are limited ways to influence it. Know that such ways exist rather than assuming the description is always at fault.
Be able to name the levers and their order: declare a missing fact, narrow an over-broad description, hint at strategy, and only then use a literal step. Say why the first costs nothing and the last costs the most.
Demonstrate having done it. Start by recovering the plan, distinguish a missing fact from bad judgement, and describe what you rebuilt around an escape hatch — its precondition, its edges, its postcondition check — so the rest of the description kept its properties.
Own the policy. Decide when hints are allowed and how they are reviewed after an engine upgrade, what an escape hatch must carry to be merged, and the threshold at which a description riddled with hatches should be moved to an openly procedural tool instead.
## Where the abstraction leaks "Say what, not how" holds only while the evaluator's *how* is good enough. The moment its plan is too slow, badly ordered, or disruptive in a way you care about, you need influence over a decision you deliberately gave away — and every way of getting it back costs something. This is the characteristic leak of the declarative family, and it is the thing senior interviews probe, because juniors talk about the paradigm's benefits and seniors have paid for them. The first move is not a fix at all: **recover the plan**. A declarative surface that cannot show you what it intends to do before it does it is unusable at scale, precisely because the plan is the only artifact you can reason about. Once you have it, the diagnostic question is sharp: **did the engine lack a fact, or lack judgement?** ## Three levers, in increasing cost 1. **State a fact the engine did not have.** A relationship you knew about and never declared; a property left open that you actually require; a scope broader than your real intent. This is not a hint — it is the description becoming more truthful, and it survives engine upgrades because it says nothing about strategy. 2. **Hint at strategy.** Most surfaces accept some form of "do it this way": prefer this access path, use this degree of parallelism, handle this group together. It works today, it is a statement about one evaluator's internals, and it has no expiry date attached to it. 3. **Drop to a procedural step.** The escape hatch takes a literal instruction and runs it. Everything else you were enjoying stops here: the engine cannot order it by dependency unless you declare its edges, cannot skip it on a matching system unless you write its own precondition, and cannot show it in a gap-preview as anything but "this will run". | Lever | What you keep | What you pay | |---|---|---| | Declare a missing fact | everything the paradigm gives you | nothing, provided the fact is actually true | | Narrow an over-broad description | derived order and re-runnability | some flexibility you may have wanted | | Strategy hint | mostly intact, in practice | tied to one evaluator's behaviour; rots silently as it improves | | Procedural escape hatch | only what you rebuild yourself | ordering, idempotence, preview and repair become your job | ## Why a hint rots and a fact does not A declared dependency stays true no matter who evaluates it — it is a claim about your system. A hint is a claim about the engine: that its planner, on this data, at this size, makes a choice you want to override. Data grows, the planner improves, and the hint keeps being obeyed long after it stopped being right — often turning from an optimisation into the thing making the run slow. That asymmetry is the whole reason to order the levers as above, and it is why any hint you do write deserves a note recording what it was working around and how to tell when it is obsolete. ## Living with the escape hatch When the hatch is genuinely the right answer — the vocabulary simply cannot express what you need — treat the step as code that has lost its safety net: - **Give it a precondition** so a run over a matching system skips it. Without one it fires on every pass, and you have just lost idempotence for the whole description. - **Declare its edges** to the items around it, since nothing in a literal step tells the engine what it reads or writes. - **Make it small and named**, so the diff shows a reviewer exactly where the paradigm stops. - **Assert the postcondition** it is supposed to establish, because no gap comparison will do it for you. - **Put an expiry on it.** Escape hatches survive by being invisible; a periodic review that asks whether the vocabulary has caught up is what keeps one exception from becoming the house style. ## The judgement being tested The answer an interviewer is listening for is an ordering, not a technique: *get the plan, add truth first, narrow second, hint third, escape last, and record why*. The failure modes on either side are both real. Reaching for the escape hatch immediately gives up a repair path and a reviewable diff for a problem that a declared dependency would have fixed. Refusing the hatch on principle, when the vocabulary genuinely cannot say what you need, produces descriptions contorted into shapes nobody can read — which costs more than the honest step would have. The paradigm is a trade, and a senior engineer is expected to know the price of each way out of it.
- Why prefer declaring a dependency over adding an ordering hint?A declared dependency is a claim about your system and stays true under any evaluator; it also documents the relationship for the next reader. An ordering hint is a claim about one engine's planner, so it keeps being obeyed after that planner improves — and by then it is likely the reason the run is slow. Facts age well, strategy does not.
- What does a literal procedural step cost inside a declarative description?Everything the surrounding items get for free. It cannot be ordered by inference unless you declare its edges, it runs on every pass unless you write its own precondition, it cannot be shown meaningfully in a gap preview, and it establishes no property the engine will later check. You rebuild each of those by hand or you do without them.
- How do you stop one escape hatch from becoming the house style?Make each one visible and dated: a named step, a comment recording what the vocabulary could not express, and a review that revisits it when the surface gains features. The danger is not the first hatch, which was probably justified, but the fifth one copied from it by someone who never knew the description was supposed to be declarative.
- When is the honest answer that this surface is the wrong tool?When the escape hatches outnumber the declared items, or when almost every item carries a strategy hint. At that point you are writing a script in an awkward syntax and paying the declarative surface's overhead without its benefits — better to move that part of the work to something openly procedural and keep the desired-state description for what it models well.
saying these in an interview costs you the question
- Strategy hints are permanent improvements once measured
- A procedural step behaves like any other declared item
- The engine's plan cannot be inspected before it runs
- Declaring a dependency and hinting an order are the same fix
- If the plan is bad the description must be wrong
- Escape hatches need no precondition of their own