Guided Problem
MiniDock Build 07: Rebuild Without Reusing Stale Work
- Time
- 25m
- Level
- intermediate
- Artifacts
- not specified
The Build Cache
Design claim: A build cache is sound only when its key captures every input that can change an instruction's result.
Starting model
- You can trace how an ordered build recipe becomes immutable layers plus runtime configuration.
- The useful vocabulary at this point is deliberately small: cache key, parent layer, instruction input, cache hit.
Speed is useful only if reuse is sound
Repeated builds should reuse unchanged work without silently returning layers produced from stale files, arguments, or parent state. Treat the release as data with identity and history, not as the accidental contents of one machine. Reuse is safe only after that data model is precise.
The tempting shortcut is straightforward: cache a step using only its Dockerfile instruction text. The shortcut looks efficient until immutable history, shared content, or reproducibility becomes observable. Identical text can produce different output when the parent layer, copied context, build arguments, or relevant metadata changes. The constraint turns a convenient file operation into an identity or ordering bug.
Key the computation, not its label
A cached layer is reusable when the world that produced it is provably identical. The builder's cache key is: parent layer ID + instruction text, and for COPY/ADD also the checksums of the copied files (the text COPY src/ /app doesn't change when your source does — the content check catches that). Because each key includes the parent, a single miss changes every descendant's parent, so everything after the first miss rebuilds. This cascade is why instruction order is a real design skill: stable things first, volatile things last.
The cache key combines the operation with the identity of its parent state and every declared input that influences the result. The mechanism makes inputs and resulting content explicit, allowing later stages to reuse facts instead of trusting names or mutable directories.
Read the structure from references toward immutable content, noting where ordering or private state changes the meaning of an otherwise shared object.
flowchart TB
I["next instruction"] --> Q1{"parent layer
cache hit so far?"}
Q1 -- no --> MISS["rebuild this layer
(and all after it)"]
Q1 -- yes --> Q2{"instruction text
unchanged?"}
Q2 -- no --> MISS
Q2 -- yes --> Q3{"COPY/ADD file
checksums unchanged?"}
Q3 -- no --> MISS
Q3 -- "yes / not a COPY" --> HIT["reuse cached layer
(skip execution)"]
style HIT fill:#f0faf2,stroke:#1f7a34
style MISS fill:#fff8ec,stroke:#f2ddb0
It shows cache keys depending on parent state, instruction, and external inputs. Its central claim is that a cache hit is valid only when all result-determining inputs are identical; the labels therefore describe authority rather than decorative grouping.
One miss changes the suffix, not the universe
A build file ordered COPY deps-list → RUN install → COPY src/ → RUN compile touches only source code between builds. The expensive dependency install stays cached; only the last two steps rerun:
The builder computes keys in order; a miss produces a new layer and invalidates dependent keys, while an independent unchanged stage can still hit. Follow identity through the operation and ask which completed fact authorizes each reuse or transition.
The second figure tests the same model in motion: it traces hits until a changed input creates a miss and a new dependent chain.
sequenceDiagram
participant B as Builder
participant C as Cache
B->>C: COPY deps-list — key: parent+text+checksum
C-->>B: HIT — reuse layer 1
B->>C: RUN install — parent cached, text same
C-->>B: HIT — reuse layer 2 (the slow one, skipped!)
B->>C: COPY src/ — checksum differs
C-->>B: MISS — rebuild layer 3
B->>C: RUN compile — parent changed
C-->>B: MISS by cascade — rebuild layer 4
It traces hits until a changed input creates a miss and a new dependent chain. The ordering is valid only when it continues to preserve the stated invariant under retries and interruption.
A stale hit is worse than a slow build
A source file changes under an unchanged COPY instruction, so the builder must miss that step and every dependent step. A partial or stale result must remain distinguishable from a complete image state; otherwise the cache or store begins to lie.
A cache hit is valid only when all result-determining inputs are identical. The invariant is phrased in bytes, ownership, or completed prefixes so it remains true across storage implementations.
Make MiniDock justify reuse
MiniDock may accelerate builds only after it can explain why reused content is equivalent. MiniDock can adopt the content rule without preselecting the learner's attachment point.
What carries forward
- A cache hit is valid only when all result-determining inputs are identical.
- Reuse unaffected build work without permitting stale output after an input change.
- The rejected shortcut remains a diagnostic: if the design starts depending on it again, the original constraint has probably been lost.
See what actually stuck.
Take the practice scenarios now.