Which platform changes can make a working policy rule stop enforcing with no error at all?
answer
- written against a shape, not a schema
- absence and compliance look identical
- the field moved; the rule did not
- a default makes absence impossible
- nobody edited it, so nobody suspects it
basics
~20 sAnything that moves the shape the rule reads: a field renamed or relocated between API versions, a kind served under a new group-version, a field that gains a default so it is never absent, or an engine change to how the input document is built.
solid answer
~50 sA rule is written against a shape, not against a checked schema, so when the shape moves the rule simply selects nothing and produces no denial — a fail-open, and a quiet one. Four common causes: a field is relocated or renamed in a new API version, so the path the rule reads is absent; a kind is promoted to a new group-version and the rule's own version or kind test no longer matches; a field gains a server-side default, so a rule that fires on its absence never fires again; or an engine major changes how the input document is assembled and the rule now reads the wrong subtree. The tell is a rule that was correct against yesterday's objects and is still correct against yesterday's objects — nobody edited it, so nobody suspects it. Detection has to come from outside the rule: a must-deny canary, a corpus replay, or a denial rate that drops to zero.
code
yaml · 24 linesapiVersion: extensions/v1beta1
kind: Ingress
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80go deeper
Know that a rule reads a specific path in a specific shape, and that if the shape changes the rule quietly does nothing rather than complaining. Be able to name one example, such as a field moving between API versions.
Be ready to enumerate the causes — relocated field, new group-version, newly defaulted field, changed input document — and to explain why none of them produce an error. Expect a follow-up asking how you would detect it.
Demonstrate the operational answer: canaries in every accepted shape, denial-rate baselines, and a corpus replay before the upgrade window. Be able to argue when a rule should deny an unrecognised shape rather than ignore it.
Own the estate-level tradeoff: fail-closed on unknown shapes buys certainty and spends developer trust. Decide which rules get that treatment, and how the platform team absorbs the migration noise rather than pushing it onto every product team.
## Why the break is silent rather than loud A policy rule expresses a condition over a document. It contributes a denial when the condition matches and contributes nothing when it does not. There is no third state that means *I looked for something and it was not there.* Absence and compliance are the same observable outcome. That is why an upgrade that changes the shape of the documents flowing past a rule produces no error, no warning and no failing test — it produces silence, and silence is indistinguishable from an estate that has become compliant. Contrast that with the loud failures you do get: a rule that references a construct the engine major removed will not compile or will error at evaluation, and those you find immediately. **The dangerous class is the rule that still parses, still runs, and matches nothing.** ## The four shapes of the break **1. A field moves or is renamed across API versions.** The classic case: the same routing information expressed under two versions of a kind, with the backend reference at a different path in each. A rule that reads the old path finds nothing in a new-version object and lets it through. The object is not malformed and the rule is not wrong — they no longer talk about the same place. **2. A kind is served under a new group-version.** If a rule's own selection test names a version or a group, and the platform starts serving or storing the kind under a different one, the rule stops selecting the objects it was written for. This is the same failure as (1) one level up: not the field moved, but the door the rule was watching. **3. A field acquires a default.** A rule that denies when a field is *absent* — no owner label, no resource limit, no explicit setting — depends on absence being possible. The moment the platform or an admission-time defaulter fills that field in on every object, the absence never occurs and the rule never fires again. Note the direction: this one does not break because the rule looked in the wrong place; it breaks because the condition became unsatisfiable. **4. The engine changes the input document.** An engine major can change how it assembles what the rule sees: a different wrapper around the object, a renamed top-level key, a payload that used to be a single object and is now a list. Rules that navigate from the root are all suddenly reading one level off. A fifth, subtler variant is worth naming because it looks like the opposite: a rule keeps working, but its *data* goes stale. A deny-list of deprecated API versions and kinds, held as data beside the rule so it can be updated without touching logic, is only as good as its last refresh. The rule matches perfectly and denies exactly the versions on the list, while the platform has deprecated three more that the list has never heard of. Nothing is silent here — the rule is loudly enforcing an out-of-date policy — but the effect on your estate is the same gap. ## Why you will not notice from inside the rule The rule was not edited. The suite is green (its fixtures are in the old shape too). The enforcement point is up and answering. Every signal a team normally watches says the system is healthy. The only signals that move are ones you have to have built in advance: - **A must-deny canary per rule**, re-expressed in every shape you accept — one in the old version, one in the new. If both keep failing, the rule reaches both. - **Denial-rate monitoring.** A rule whose denials go from a handful a week to zero on the day of an upgrade is making a statement. This works only for rules that fire occasionally; a rule that legitimately never fires gives you no baseline, which is one more reason a canary matters. - **A corpus replay** of the estate's real stored objects against the new engine-and-rules pair before the upgrade window, diffing decisions against the old pair. Objects that flip from deny to allow are exactly this bug. ## Making the class of break loud Where it matters, invert the check. For an object the rule *does* select, treat a missing expected field path as a violation in its own right rather than as nothing to say — the rule then denies a shape it does not understand instead of waving it through, and the first new-version object turns into a visible, attributable denial rather than a silent hole. That is a deliberate trade: you accept some false denials during a migration in exchange for never failing open. It is the right trade for a small number of high-value rules and the wrong one for a broad advisory library, so choose per rule rather than globally.
- A field your rule keys on just gained a server-side default. Why is that a problem, and how does it differ from a renamed field?A rule that denies on absence needs absence to be reachable. Once every object arrives with the field populated, the condition can never be satisfied and the rule is dead while looking perfectly healthy. A renamed field breaks the rule's navigation — it is reading the wrong place; a defaulted field breaks the rule's premise — it is reading the right place and the answer is now always the same. The fix differs too: repoint the path in one case, re-express the intent as a check on the value in the other.
- Does an engine major upgrade break rules in the same silent way?Mostly no, and that is why it is less feared than it should be. Removed syntax and renamed built-ins fail at load or evaluation, and you find them in minutes. The silent cases are semantic: a change to how the input document is assembled, or to how the engine reads a rule's result, so the rule evaluates fine and answers about the wrong thing. Those are the ones a corpus replay catches and a compile check does not.
- How do you make this class of break loud instead of silent?For selected high-value rules, treat an unrecognised shape as a violation: if the object is one this rule governs and the expected field path is not present, deny and say so. You trade a burst of false denials during a migration for never failing open. Pair it with a must-deny canary written in each accepted shape and a pre-upgrade corpus replay, so the break has three chances to surface before an attacker finds it.
saying these in an interview costs you the question
- Expects a stale field path to raise a type or schema error
- Assumes rules are validated against the platform schema at load
- Thinks a green test suite covers an API version change
- Confuses a rule that errors with a rule that matches nothing
- Believes only rule edits can change what a rule enforces