はじめに:フォーク・トラップ(Forking Trap)とは
Meta(旧Facebook)は、Messenger、Instagram、Cloud Gaming、Meta Quest VRキャスティングなど、数十億のユーザーのリアルタイムコミュニケーションにWebRTCを活用してきました。しかし、オープンソースライブラリをモノレポ(monorepo)にフォークした瞬間から「フォーク・トラップ」が始まります。当初は些細な最適化やバグ修正から始まったとしても、アップストリームが進化し、内部機能が蓄積されるにつれて、アップストリームのコミットをマージするコストは指数関数的に増大します。
Metaは数年にわたる大規模マイグレーションを経て、50以上のユースケースを最新のアップストリームベースのモジュラーアーキテクチャに移行しました。本稿では、その過程で得られた重要な知見を共有します。
参考資料: 本記事はMeta Engineering Blogの原文を基に再構成しています。原文の全文はこちらでご確認ください。

核心アーキテクチャ:シムレイヤー(Shim Layer)とデュアルスタック(Dual-Stack)
Metaが直面した最大の技術的課題は、C++静的リンカのOne Definition Rule(ODR)違反でした。同じライブラリの2つのバージョンを同一バイナリに静的にリンクすると、数千のシンボル衝突が発生します。Metaはこの問題を解決するために**シムレイヤー(Shim Layer)**を導入しました。
シムレイヤーの動作方式
// 例: シムレイヤーによるバージョンディスパッチ
// shim/api.h (統一API)
namespace webrtc_shim {
enum class Flavor {
LEGACY,
LATEST
};
class VideoEncoderFactory {
public:
virtual std::unique_ptr<VideoEncoder> CreateEncoder(
const SdpVideoFormat& format) = 0;
virtual ~VideoEncoderFactory() = default;
};
} // namespace webrtc_shim
// shim/encoder_factory_adapter.cc (テンプレートベースのアダプター)
#include "shim/api.h"
#include "webrtc_latest/api/video_codecs/video_encoder_factory.h"
#include "webrtc_legacy/api/video_codecs/video_encoder_factory.h"
template <typename T>
class VideoEncoderFactoryAdapter : public webrtc_shim::VideoEncoderFactory {
public:
explicit VideoEncoderFactoryAdapter(std::unique_ptr<T> impl)
: impl_(std::move(impl)) {}
std::unique_ptr<webrtc_shim::VideoEncoder> CreateEncoder(
const webrtc_shim::SdpVideoFormat& format) override {
// 実際の実装に委譲
return impl_->CreateEncoder(ConvertFormat(format));
}
private:
std::unique_ptr<T> impl_;
};
// ファクトリ関数: グローバルフレーバー設定に基づいて適切な実装を生成
std::unique_ptr<webrtc_shim::VideoEncoderFactory>
CreateVideoEncoderFactory() {
if (GetGlobalFlavor() == webrtc_shim::Flavor::LATEST) {
return std::make_unique<
VideoEncoderFactoryAdapter<webrtc_latest::VideoEncoderFactory>>(
std::make_unique<webrtc_latest::VideoEncoderFactory>());
} else {
return std::make_unique<
VideoEncoderFactoryAdapter<webrtc_legacy::VideoEncoderFactory>>(
std::make_unique<webrtc_legacy::VideoEncoderFactory>());
}
}
重要なポイント
- 自動名前空間変更(Renamespacing): スクリプトを使用して、すべてのC++名前空間を
webrtc::→webrtc_latest::、webrtc_legacy::に自動変更しました。 - マクロ衝突の解決:
RTC_CHECK、RTC_LOGなどのマクロは、不要なインクルードの削除、使用頻度の低いマクロの名称変更、共通モジュール(rtc_baseなど)の共有によって解決しました。 - 後方互換性の維持:
using宣言を使用して、既存のwebrtc::名前空間に新しいフレーバーのシンボルをインポートし、既存コードを修正せずに済ませました。 - コード生成の自動化: AST解析に基づいてシムコードを自動生成し、1日1シムから3〜4シムへと生産性を向上させました。
このアプローチにより、バイナリサイズの増加を約38MBから5MBへ、87%削減しました。
![]()
第二の課題:モノレポでの継続的アップグレード(Feature Branches)
Metaは機能ブランチをサポートしないモノレポを使用しているため、アップストリームパッチを追跡しリベースする別の方法が必要でした。最終的に選択したのは、別のGitリポジトリで機能ブランチを管理する方法です。
ワークフロー概要
# 1. アップストリームリリースタグをベースにブランチを作成
git checkout -b base/7499 tags/7499
# 2. 各パッチごとに機能ブランチを作成(例: debug-tools)
git checkout -b debug-tools/7499 base/7499
# ... パッチを適用してコミット ...
# 3. アップストリームアップグレード時(例: 7499 → 7559)
git checkout debug-tools/7499
git merge base/7559 # コンフリクト解決
git branch -m debug-tools/7559
# 4. すべての機能ブランチを順次マージしてリリース候補を作成
git checkout -b rc/7559 base/7559
git merge debug-tools/7559
git merge hw-av1-fixes/7559
# ...
この方式の利点は:
- 並列化が容易で、多数のブランチを同時に処理可能
- Gitの履歴とコンテキストが自動的に保存される
- 将来のLLMベースの自動コンフリクト解決に適している
- 各機能ブランチをそのままアップストリームにコントリビュート(Pull Request)できる

結果と示唆
Metaはこのプロジェクトを通じて、WebRTCのバージョンをM120からM145にアップグレードし、主要アプリでCPU使用率を最大10%削減、クラッシュ率を最大3%改善、バイナリサイズを100〜200KB(圧縮時)削減するという目に見える成果を上げました。
日本の開発エコシステムにおける適用コンテキスト
日本のSIerや大規模サービスにおいても、オープンソースライブラリをフォークするケースは少なくありません。特に金融系のように安定性を最優先する環境では、フォーク・トラップに陥りやすいと言えます。本記事のアプローチは、以下のような状況に応用できるでしょう:
- レガシーWebRTCベースのビデオ会議ソリューションを運用している企業
- 大規模モノレポを使用している組織(例:メルカリ、LINE、楽天)
- A/Bテストが必須なリアルタイム通信機能を開発しているチーム
注意点: このアーキテクチャはC++テンプレートと名前空間操作に対する深い理解が必要であり、初期構築コストが大きいです。小規模プロジェクトや短期的なフォークにはオーバーエンジニアリングになる可能性があります。
合わせて読みたい記事
次のステップとしての学習方向性
- WebRTCの深堀り: SDPネゴシエーション、ICE、DTLSなどのプロトコルレベルの理解
- C++モジュール化: C++20モジュールを活用した、よりクリーンなシムレイヤー設計
- AIによるメンテナンス: Metaが言及したAIエージェントを活用したビルドエラーの自動修正やコンフリクト解決手法の探求