Your source control stores every version of a 200MB binary and the repository is now 40GB. What is being stored, and why can you not delete it?
Roughly 200 near-full copies, because delta compression works on text and a relinked binary differs throughout, so each version costs almost its own size. Deleting the file changes nothing: history is content-addressed and every past commit still reaches the blob. The design fix is that build outputs belong in a registry that can expire them, because version control is built never to forget.
What the interviewer is scoring
- Does the candidate derive the version count from the ratio and recognise it as an ordinary release cadence
- Whether delta compression is explained as failing on relinked binaries rather than being absent
- That deleting the file is understood to leave the blob reachable from history
- Whether the three remedies are separated by what each one fixes, with history rewriting priced as a coordinated event
- Can they articulate why an artefact store solves this and version control structurally cannot
Answer
Short answer
Large binaries in Git stay expensive because every historical commit can still reach the blob, so deleting the file from the latest revision does not shrink the repository. Use shallow or partial clones for immediate relief, move future binaries to Git LFS or an artifact registry, and rewrite history only as a coordinated migration.
Divide before you diagnose
40 GB over 200 MB is 200. So the repository is holding on the order of 200 versions of that file, and at one commit of it per working week that is four years of an entirely unremarkable release cadence.
Nothing malfunctioned. No runaway process, no bug, no accident. Version control was asked to keep every version of a large file for ever and it did precisely that. Saying this first matters, because the instinct in the room is to look for a mistake, and the design decision - that a build output lives in source control - is the mistake, made four years ago and working as specified since.
Then price it in the unit that gets the work funded. A clone transfers the whole history. 40 GB over a 100 Mbit/s link is 320,000 megabits divided by 100, so 3,200 seconds, or about 53 minutes for one fresh clone. A CI fleet doing 200 fresh clones a day moves 8 terabytes a day and spends 176 machine-hours waiting. That is the number to put on a slide, not the 40 GB.
Why 200 versions costs 200 copies
The mechanism is worth stating because it explains why this is not a compression problem you can solve with better settings.
Git stores each version of a file as an object, compressed individually, and then packs objects together and stores similar ones as deltas against each other. Delta compression is why a repository with a decade of source history is small: successive versions of a source file differ in a handful of lines, so the delta is a few hundred bytes.
A compiled binary does not behave that way. Change one line of source, relink, and symbol addresses shift, layout moves, embedded build metadata changes, and the resulting bytes differ throughout the file rather than in one region. There is no small delta to find. So each version costs close to its own compressed size, and 200 versions of a 200 MB binary costs close to 200 times 200 MB rather than one copy plus small diffs.
This also predicts what you will find when you look: the repository size grows in even steps that track your release cadence, with no compaction ever recovering any of it. git gc will run happily and reclaim almost nothing, because there is nothing unreachable to collect and no better packing available.
Why deleting it does nothing
Committing a deletion removes the file from the working tree going forward. It does not remove the object.
Git is content-addressed. A commit names a tree, the tree names the blob by the hash of its contents, and every one of the 200 historical commits still names its version of that blob. The objects are reachable, so garbage collection will not touch them, and a clone that fetches the history fetches all of them. The repository's size is a property of its history, not of its current checkout - which is the sentence that answers the question as asked.
That is not a defect. It is the guarantee you bought: any commit can be checked out and will produce exactly the bytes it produced then. You cannot have that guarantee and also have the storage forget.
Three remedies that fix three different things
They are frequently offered as alternatives. They are not - they address different problems, and a real programme of work uses all three in this order.
Transfer less, immediately. Clone shallowly with a depth limit, or use a blob-filtered partial clone so a checkout fetches the file contents it needs rather than every version ever committed. This is a change to your CI configuration, it can ship this afternoon, and it removes most of that 8 terabytes a day. It shrinks nothing on the server. It is still the right first move, because it converts an urgent cost problem into a merely untidy one and buys you the time to do the rest properly.
Stop adding, next. Git LFS replaces the file's contents in the repository with a small pointer file and stores the bytes in a separate store, fetched on checkout. New versions of the binary stop landing in the packfile, so the growth stops. Existing history is untouched, so the 40 GB stays exactly where it is. That surprises people, and it is worth being explicit about: LFS is a change to the future, not a repair of the past.
Shrink, last and deliberately. Rewriting history to strip the blobs out is the only thing that reduces the repository, and it changes the identity of every commit from the earliest rewritten one onward. Everyone re-clones. Every open branch and pull request has to be rebuilt on the new history. Every build record, deployment log, ticket comment and release note that references a commit sha now points at nothing. That is a coordinated event with a comms plan and a window, not a maintenance task, and it should be scheduled after the first two changes have removed the urgency.
The design answer: this was never version control's job
Everything above is remediation. What an interviewer is probing is whether you can say where the file should have lived, and the reasoning is a clean one.
A compiled binary is a build output. It is derived, reproducible in principle from a commit, and its value is as a retrievable copy of what was shipped - which is exactly what an artefact registry or an OCI registry exists to provide. Both address content by digest, both let a build record point at an immutable artefact, and both give you the thing source control cannot: a retention policy.
That contrast is the whole point. Version control is designed never to forget, and an artefact store is designed to forget on a schedule. A registry can hold every release for ever, keep the last twenty development builds, and expire the rest nightly, so 200 versions becomes 20 plus the tagged releases without anyone rewriting anything. Git's history has no such affordance by construction: forgetting requires changing every commit that came after, because forgetting and immutability are in direct opposition there.
So the target state is that the repository holds source and the registry holds outputs, with a build record linking a commit to the artefact it produced. Reproducing what shipped in March means reading the build record, pulling the artefact by digest, and having the commit available if you want to rebuild it. You keep every capability the 40 GB was buying and you gain an expiry policy.
Where candidates stop too early
Two answers arrive quickly and both are incomplete in a diagnosable way.
The first is git rm and git gc, which treats this as accumulated rubbish. It is not rubbish, it is reachable history, and the fact that gc reclaims nothing is the evidence. A candidate who proposes it and then explains why it fails is doing better than one who never proposes it, because the reachability argument is the actual mechanism.
The second is to go straight to rewriting history, which is technically effective and operationally the most disruptive of the three options - and it is the one people reach for first because it is the one that makes the number smaller. Ordering the remedies by disruption, and shipping the cheap CI change before the coordinated rewrite, is what a reviewer reads as judgement.
The last thing worth volunteering is prevention, because otherwise you will do this again. A pre-receive hook or a CI check that rejects a commit adding a file over some size threshold puts the constraint where it cannot be forgotten. Server-side rather than local, because a local hook is advisory and the person who most needs it is the person who has not installed it.
The 40 GB is 200 near-full copies of a file that delta compression cannot help, and it is reachable from every past commit, so deleting the file changes nothing. The design fix is that build outputs belong in a registry that can expire them, because version control is built never to forget.
© 2026 Preptima. Originally published at preptima.com.
Likely follow-ups
- CI does 200 fresh clones a day. Which change do you make this week, and what does it not fix?
- A build record from last year references a commit sha you are about to rewrite. What happens to it?
- Someone commits a 200MB binary again next month. What stops them, and where does that check live?
- Which binaries do legitimately belong in the repository, and what makes them different?
Related questions
- A transform has been writing wrong revenue figures for three days and six downstream tables have consumed it. How do you backfill the corrected data without double-counting anything?hardSame kind of round: scenario4 min
- Your consumer-driven contract test passes in CI, but production rejects a request because a supposedly optional field is missing. What did the contract testing actually miss?hardSame kind of round: concept4 min
- Your error budget burn alert pages every few hours, but half the time nobody outside the team has noticed anything. How do you tune it without simply making it quieter?hardSame kind of round: concept5 min
- Two clients open the same record, both edit it, and the second save silently overwrites the first. How would you use ETags to turn that lost update into something the client can see and handle?mediumSame kind of round: concept4 min