CONCEPT Cited by 3 sources
Internal fork divergence¶
What it is¶
Internal fork divergence — colloquially "the forking trap" — is the monotonic process by which a company's internal fork of a large open-source project drifts apart from upstream until merging new upstream commits becomes prohibitively expensive. The company's fix-in-fork is cheap the first time, cheaper than waiting for upstream review, and "it starts with good intentions: You need a specific internal optimization or a quick bug fix" — but local patches accumulate, upstream also evolves, and after a few years rebasing all internal patches onto current upstream is so costly that the fork is de facto frozen at its original upstream baseline.
The consequences compound:
- Cut off from community upgrades — new features, performance wins, and platform support never arrive.
- Cut off from community security patches — CVEs disclosed upstream aren't automatically carried in.
- Carry a growing private maintenance tax — every internal engineer changing the fork has to reason about it without the community context; onboarding is expensive.
- Locked in — escaping requires writing off the accumulated fork work or undertaking a large migration project to bring the internal tree close enough to upstream to rejoin.
Canonical statement¶
"Permanently forking a big open-source project can result in a common industry trap. It starts with good intentions: You need a specific internal optimization or a quick bug fix. But over time, as the upstream project evolves and your internal features accumulate, the resources needed to merge in external commits can become prohibitive." — Meta, 2026-04-09 WebRTC post (sources/2026-04-09-meta-escaping-the-fork-webrtc-modernization)
Canonical instances on the wiki¶
Meta × libwebrtc (2026-04-09)¶
Meta forked Chromium's libwebrtc for performance / platform customizations. Over time the fork drifted years behind upstream across 50+ RTC use cases. Escape plan: a dual-stack shim lets legacy + upstream coexist in the same binary so per-app A/B testing can migrate one use case at a time, then delete the legacy code. Now at M145, "living at head." See patterns/fork-retirement-via-ab-test.
Meta × FFmpeg (2026-03-09)¶
Meta's internal FFmpeg fork carried multi-lane-encoding and in-loop-decoding patches. Escape plan: Meta co-developed both features upstream with FFlabs and VideoLAN across multiple FFmpeg releases (6.0 → 8.0, 7.0+), then retired the fork. See sources/2026-03-09-meta-ffmpeg-at-meta-media-processing-at-scale + patterns/upstream-the-fix.
Meta × jemalloc (2026-03-02)¶
A variant case: Meta is the upstream steward but the project's own core engineering principles had drifted. Not a consumer-side fork-divergence, but a stewardship drift with analogous symptoms (technical debt, slowed progress, community friction). See patterns/stewardship-reset-for-foundational-oss.
The three escape routes¶
Given an internal fork that has diverged:
- Upstream the patches (FFmpeg shape) — pay the years-long cost of landing each internal feature upstream with full community scrutiny, then delete the fork.
- Dual-stack A/B migration (WebRTC shape) — build a shim so upstream and fork coexist in the same binary; A/B-test the upstream path per use case; delete the legacy path after each migration.
- Abandon, starting from current upstream — nuke the fork, rebase remaining must-have patches onto current upstream with full knowledge the intermediate internal work is lost. Not named in Meta's 2026 posts but implicit as the baseline the first two mechanisms improve on.
The residual-patches question¶
Even after fork retirement, most organizations carry some internal patches (hardware-specific integration, proprietary features, performance tweaks upstream rejected). Two disciplines apply:
- patterns/keep-infrastructure-specific-patches-internal — patches tied to internal infrastructure that upstream cannot test or validate stay internal deliberately, with reverse-rebase cost accepted.
- patterns/external-feature-branch-repo-for-monorepo-patches — track the residual patches as Git feature branches rebased onto each upstream release tag; preserves history, parallelizable, submit-ready.
See also¶
- patterns/upstream-the-fix — the default escape route when the patch generalizes.
- patterns/fork-retirement-via-ab-test — Meta's WebRTC-style parallel-harness migration.
- patterns/stewardship-reset-for-foundational-oss — the upstream-side analogue.
- patterns/keep-infrastructure-specific-patches-internal — the decision-not-to-upstream discipline.