meta

Escaping the Fork: How Meta Modernized WebRTC Across 50+ Use Cases (opens in new tab)

Meta escaped the “forking trap” by replacing its divergent WebRTC fork with a modular architecture based on the latest upstream release. The system builds legacy and current WebRTC versions side by side, enabling runtime A/B testing across more than 50 use cases before rollout. This improved performance, binary size, and security while establishing a repeatable process for continuous upstream upgrades.

Why the WebRTC Fork Became a Problem

  • Meta’s RTC stack supports Messenger, Instagram video calls, Cloud Gaming, and Meta Quest casting.
  • Internal optimizations and bug fixes gradually caused its WebRTC fork to diverge from upstream.
  • As the fork accumulated custom changes, merging community improvements became increasingly expensive and risky.
  • A one-time upgrade was impractical because WebRTC serves billions of users across diverse devices and environments.

Requirements for a Sustainable Upgrade Strategy

  • Meta needed to:
    • Run legacy and upstream-based WebRTC implementations simultaneously.
    • Dynamically assign users to either version for safe A/B testing.
    • Statically link both versions into the same application.
    • Maintain custom patches in a monorepo without repeatedly rebuilding the migration process.
  • Standard patch-file workflows were considered difficult to scale for Meta’s large codebase.

Shim Layer and Dual-Stack Architecture

  • A shim library was placed between application code and WebRTC.
  • Applications call a unified, version-neutral API rather than calling either WebRTC implementation directly.
  • A runtime “flavor” configuration routes each call to either the legacy or latest implementation.
  • Shimming at the lowest practical layer avoided duplicating the higher-level call orchestration library:
    • Full duplication would have added about 38 MB uncompressed.
    • The shim-based design added roughly 5 MB, an 87% reduction.

Resolving C++ Symbol Collisions

  • Linking two WebRTC copies normally violates the C++ One Definition Rule and creates thousands of duplicate symbols.
  • Meta automated namespace rewriting:
    • webrtc:: in the current version became webrtc_latest::.
    • The legacy version became webrtc_legacy::.
  • Global functions, variables, and classes outside namespaces were moved into namespaces where possible or assigned flavor-specific names.
  • Macro conflicts, including RTC_CHECK and RTC_LOG, were addressed by:
    • Removing unnecessary includes.
    • Renaming infrequently used macros.
    • Sharing modules such as rtc_base between versions to reduce duplication and shimming work.

Preserving Backward Compatibility

  • Renaming symbols could have broken existing call sites, especially code built for only one WebRTC flavor.
  • An initial solution forward-declared every required symbol, but this created a large and fragile maintenance burden.
  • The improved approach used C++ using declarations to bulk-import a flavor namespace into the familiar webrtc:: namespace.
  • This preserved existing source-level APIs without adding binary overhead, while allowing Meta to migrate selected call sites incrementally.

Runtime Flavor Dispatch

  • Shim adapters and converters must instantiate objects from either the legacy or current namespace.
  • A template-based helper library keeps shared adapter logic in one place.
  • Template specializations handle version-specific behavior.
  • A global flavor enum, initialized during application startup, determines which WebRTC implementation is used.
  • The design also supports single-flavor builds during the transition.

Meta’s approach demonstrates that large internal modifications do not have to require a permanent fork. A low-level shim, automated renamespacing, compatibility imports, and template-based dispatch provide a practical foundation for continuously rebasing custom functionality onto upstream WebRTC while safely validating each release through A/B testing.