An image build slowed from forty seconds to nine minutes with its dependency list unchanged - what step ordering explains that?
answer
- order by how often inputs change
- slow work first, volatile work last
- the manifest before the source
- a broad copy widens a step's inputs
- the install must not see source
basics
~20 sAlmost always the build copies the whole project into the image before it installs dependencies. That makes every source file an input of the copy step, so each commit invalidates it and forces a full dependency fetch on every single build.
solid answer
~40 sThe classic shape is: start from a base, copy the entire project directory in, install the declared dependencies, then compile. The copy step's inputs include every source file, so any edit invalidates it - and because the install step sits after it, the install's parent layer is new too and the dependency fetch runs from scratch on every commit. The fix is ordering by volatility: copy **only the dependency manifest**, install, and only then copy the rest of the source. Now the install step's inputs change only when the declared dependencies change, so an ordinary source edit misses from the source copy onward and skips the fetch entirely. Nothing about the dependency set changed as the build grew - the build simply stopped being able to reuse it.
code
pseudocode · 9 lines# build order that re-installs dependencies on every commit
step 1: start from the base image
step 2: copy the entire project directory into /app
step 3: install the dependencies declared in the manifest
step 4: compile and package the service
# inputs of step 2 = content of every project file
# an edit to any one of them -> step 2 misses
# -> step 3 has a new parent layer -> full dependency installgo deeper
Recall the rule in its short form: copy the dependency manifest, install, then copy the source - because a source edit must not invalidate the install.
Explain why the broad early copy makes every source file an input of the install step, and walk the before-and-after order out loud.
Frame it as the ratio between how often each input changes, and name the honest limit: a manifest edit still pays for a full install, by design.
Treat step ordering as a convention worth writing down for every service a team builds, because the cost is invisible until the dependency set is large and then it is everyone's cost.
## The shape of the slow build A build that starts fast and degrades without the dependency set changing is nearly always an **ordering** problem rather than a volume problem. The degraded shape looks like this: 1. start from a base image; 2. copy the entire project directory into the image; 3. install the dependencies declared in the manifest; 4. compile and package the service. At forty seconds this ordering costs nothing visible, because the dependency set is small. As the dependency set grows, step 3 grows with it - and step 3 is re-executed on **every** build, because step 2 is invalidated by every commit. ## Why the broad early copy is the whole cost A step that copies files in has the **content of those files** among its inputs. Copying the whole project makes every file in the project an input to that one step. An edit to any of them - a source file, a test, a documentation file, anything the step copied - changes that input, so the copy step misses, executes, and writes a new layer. That new layer is the parent of the install step. The install step therefore misses too, no matter that the dependency manifest is byte-for-byte what it was yesterday. The builder is not inspecting the manifest and concluding the dependencies are current; it is matching inputs, and one of the install step's inputs moved. ## The ordering rule Order the steps by **how often each one's inputs change**, slowest-changing first: 1. start from the base image; 2. copy **only the dependency manifest** into the image; 3. install the dependencies the manifest declares; 4. copy the rest of the project source; 5. compile and package. Now an ordinary source edit changes an input of step 4 and nothing earlier. Steps 1-3 are reused, the expensive fetch is skipped, and the build pays only for steps 4 and 5. | change made | broad-copy order | manifest-first order | |---|---|---| | edit one source file | copy, install, compile all re-run | only copy-source and compile re-run | | edit the dependency manifest | copy, install, compile all re-run | install, copy-source and compile re-run | | edit nothing | everything reused | everything reused | The second row is the honest limit of the fix: **editing the manifest still costs a full install**, because the manifest is an input to the install step. That is correct behaviour - the declared dependencies genuinely changed. The reordering does not make installs free; it changes the *ratio*, and the ratio is the point. Manifests change occasionally; source changes many times a day. ## What the fix does not do - It does not make a first build on a fresh builder any faster - there is no previous run to match against. - It does not shrink the dependency set or the image; it only stops re-doing work whose inputs did not move. - It does not help if the install step's definition itself embeds something volatile, such as an argument whose value changes per build. That value is an input, and it will invalidate the step just as a file edit would. ## Two ordering habits worth stating as rules - **Copy narrowly, in the order the steps need.** A step should copy what the *next* step actually consumes, and nothing else. A single broad copy is not cheaper than two narrow ones; it is one step whose inputs are everything. - **Put slow work above volatile work.** Anything placed *before* a volatile step is protected from it; anything placed *after* it pays for every edit. This is the whole of build-time tuning at this level - not a faster machine, and not a bigger cache. When an interviewer asks this, they want to hear the diagnosis and the ratio argument, not a recital of instructions. The sentence that lands is: the dependency install is being invalidated by an input it has no business depending on, and moving the source copy after it removes that dependency.
- What does the reordering buy on a build where the dependency manifest itself changed?Nothing on that build - the manifest is an input to the install step, so editing it invalidates the install and everything after it, which is the behaviour you want. The gain is in the ratio: manifests change occasionally, source changes constantly, and the common case becomes cheap.
- Where does the compile step belong in that order?After the source copy, because it reads the source. The ordering principle is one line: stable inputs first, volatile inputs last. Anything placed before a volatile step is protected from it, and anything placed after it pays on every edit.
- The install step takes an argument whose value differs per build. What happens?That value is part of the step's definition, so it is an input to the match. A per-build value invalidates the install on every run and undoes the reordering entirely. Keep volatile argument values out of steps you want reused, or move them into a later step.
saying these in an interview costs you the question
- Blames the network or the registry rather than the step order
- Thinks one broad copy is cheaper than two narrow ones
- Believes the builder notices the dependency list is unchanged
- Suggests a bigger builder instead of fixing the ordering
- Treats the slowdown as unavoidable growth in the dependency set
- Claims the reordering makes dependency installs free forever