When a `replace` line points production builds at your team's fork, how long may that stand, and who can overrule it?
answer
- you now own code you did not write
- time-box it and name an owning team
- who gets paged when it needs a patch
- a published library cannot use this at all
- define the exit before the merge
basics
~20 sTreat a fork behind a replace line as time-boxed debt with a named owner and a written exit condition. Platform and security owners carry the divergence, from missed upstream patches to upgrade friction, so they get a veto on keeping it.
solid answer
~60 sThe engineering call is easy and the ownership call is not. A `replace` pointing at a fork ships a fix today; what it also does is transfer maintenance of that dependency to your team, silently. Upstream releases and security patches no longer reach the code you compile, and nobody outside the repository can see that from the outside. So I would merge it only with three things attached: a linked upstream pull request, a named owning team, and a review date in the comment beside the line. The exit is one of upstream merging, the code being inlined into our own module, or the fork being promoted to a properly published module we `require` — at which point it is a dependency with a version and an owner rather than a redirect. Two people can overrule me on keeping it: whoever runs dependency upgrades across the estate, and whoever owns vulnerability response, because they inherit the cost. And it is not available at all to a module we publish as a library — a replace does not reach consumers.
code
mod · 6 linesrequire example.com/upstream/parser v1.4.2
// FORK: carries upstream PR #812 (nil deref on empty input).
// Owner: billing-team. Review by 2026-11-01.
// Exit: drop this line once upstream v1.4.3 ships with the fix.
replace example.com/upstream/parser => example.com/ourorg/parser-fork v1.4.2go deeper
Understand that pointing a build at a fork is not free: someone must keep that fork current with upstream, and that someone is now your team.
Be able to describe the mechanics behind the policy — where the replace line lives, why it never reaches consumers, and what changes in the resolved build list once it fires.
Come with the review checklist: linked upstream change, owner, date, small diff, org-hosted fork, and a concrete exit. Show how you would notice the fork drifting behind upstream.
Own the standard across services: what qualifies for a fork at all, what the comment must record, who signs off, who can force the exit, and what the organisation does when a fork quietly becomes permanent.
## What is actually being decided The technical question — can we build against a fork — is settled: put a `replace` line in the main module's `go.mod` pointing at a fork you control, at a version you tagged. It works, CI is green, the fix ships. The decision that needs an owner is what that line costs over time and who pays it. ## The cost, stated plainly **You now maintain a dependency you did not write.** The upstream project keeps releasing. Its bug fixes, performance work and — critically — its security patches land on a codebase your build no longer uses. Someone on your team has to rebase the fork onto each upstream release, or knowingly stop taking them. **Vulnerability tracking gets subtler.** Anything that reasons about your dependencies from the resolved build list sees the fork's module path and version, not upstream's. Advisories are published against upstream's path. Whoever owns vulnerability response needs to know the mapping exists, or the fork becomes a blind spot precisely where you least want one. **Upgrades get harder, unevenly.** The next time someone raises the dependency across the estate, this service is the one that does not move, and the person doing the sweep discovers why only when it fails. **It does not compose.** A `replace` is honoured only in the main module. If the module carrying it is a library other teams import, the redirect never reaches them — they compile against unpatched upstream. That is not a policy preference, it is a hard constraint: the pattern is available to applications and services and simply unavailable to published libraries. ## The frame I would use Treat the replace as an explicit, dated loan rather than a config line. 1. **Entry conditions.** An upstream issue or pull request exists and is linked. The diff is small enough that re-applying it to the next upstream release is a routine chore, not a project. The fork lives somewhere the organisation controls, with the same access rules as first-party code — a fork on a personal account is a different and worse decision. 2. **A named owner.** A team, in the comment, not a person who may change roles. 3. **A review date.** In the comment and in whatever backlog the team actually reads. "Temporary" without a date is how these reach their second birthday. 4. **A defined exit.** Which of the three real exits are you taking: upstream merges and we require the release; we inline the handful of lines we need and drop the fork; or we promote the fork to a published, versioned internal module we `require` outright — visible in every build list, with an owner and a release process, which is the honest end state for a divergence that is not going away. 5. **CI that keeps the divergence honest.** At minimum something that notices when a new upstream release appears, so "we are waiting on upstream" stays a fact rather than a memory. ## Who can overrule The service's tech lead makes the call to merge it. Two other owners can overrule keeping it, because they carry the consequences: the platform owner responsible for moving the estate's dependencies forward, and the security owner responsible for responding to advisories against the dependency's upstream path. If either says the divergence has to end, the answer is a plan with a date, not a defence of the line. ## The escalation that matters When the fork is a year old and upstream has shipped three releases since, stop calling it temporary. The choice at that point is between upstreaming the change properly, replacing the dependency with one whose maintainers respond, or promoting the fork to a first-class internal module with its own versioning and owner. Continuing to describe a permanent fork as a pending upstream PR is the failure mode — not the fork itself, which is often the right engineering answer. ## The one-sentence version A `replace` to a fork is a legitimate tool with an expiry date, an owner, and a constraint that rules it out entirely for published libraries; the job of whoever owns it is to make sure it is written down as debt rather than discovered as a surprise.
- What changes if the module carrying the fork is a library other teams import rather than a service?The option disappears. A replace is honoured only in the main module, so consumers of your library compile against unpatched upstream and may not even get a compile error. The library must either carry the change in its own code, require a properly published fork module directly, or wait for upstream.
- What would you require in review before approving a fork-by-replace into the default branch?A linked upstream issue or pull request, a named owning team, a review date, a diff small enough to re-apply to the next upstream release, the fork hosted under organisational control, and sign-off from whoever owns vulnerability response for that dependency.
- The fork has been in production for a year and upstream has shipped three releases. What is the call now?Stop calling it temporary. Pick one: upstream the change properly, swap the dependency for one with responsive maintainers, or promote the fork to a published internal module with its own version, owner and release process. A permanent divergence deserves a first-class shape rather than a redirect line.
- How do you keep vulnerability tracking honest once a replace redirects a dependency?Track the resolved build list rather than the require lines, since that is where the fork's path shows up, and separately watch advisories published against the upstream path the fork stands in for. Write the mapping down where the security owner reads it, because nothing infers it automatically.
saying these in an interview costs you the question
- Calls the fork temporary with no owner and no date
- Assumes downstream consumers inherit the replace line
- Ignores that the fork stops receiving upstream security fixes
- Treats it as a purely technical call needing no sign-off
- Hosts the fork on a personal account outside org control