⏱ 読了目安: 約4分
- FigmaはOTではなく、CRDTの概念を独自に簡略化した軽量プロトコルを採用し、高い同期性能を実現している。
- サーバー到着順を信頼する設計によりタイムスタンプを排除し、プロパティ単位の競合解決でオーバーヘッドを最小化している。
- 楽観的更新時のチラつき防止や循環参照の割り切りなど、理論よりもユーザー体験を優先した設計判断が鍵となっている。
理論の「いいとこ取り」という設計判断
我々エンジニアがリアルタイム協調編集機能を実装しようとしたとき、真っ先にぶつかる壁が「競合解決」です。Google Docsで有名なOT(Operational Transform)は、テキスト編集には強力ですが、図形や親子関係が複雑に絡み合うデザインツールでは、操作の組み合わせが爆発的に増え、実装コストが肥大化します。Figmaが選んだ道は、CRDT(Conflict-free Replicated Data Type)の考え方をベースにしつつ、中央サーバーという「絶対的な権威」を前提に、理論を大胆に削ぎ落とすという極めて現実的なアプローチでした。
Figmaの同期設計において最も秀逸なのは、タイムスタンプを完全に排除した点です。一般的な分散システムでは、どの更新が最新かを判定するために高精度なタイムスタンプやベクトルクロックが必須となりますが、Figmaは「サーバーに届いた順序こそが正義」と定義しました。これにより、サーバー側で到着順を確定させるだけで、クライアント間での複雑な調整を不要にしています。これは、Last-writer-wins registerの概念を、サーバーという中央集権的なハブを活用することで極限まで軽量化したものです。我々が自社プロダクトで同様の機能を実装する際、つい「分散環境での完全な整合性」を追い求めて複雑なアルゴリズムを導入しがちですが、Figmaの事例は「サーバーを信頼できるなら、複雑さは捨ててよい」という強力な教訓を与えてくれます。
また、UIのレスポンスを維持するための「楽観的更新」と、それに伴う「チラつき(flickering)」の制御も非常に泥臭く、かつ美しい実装です。クライアントは自分の操作を即座に画面に反映しますが、サーバーからの確定通知が届くまでの間、自分の変更が上書きされるリスクを抱えます。Figmaはここで「自分がまだサーバーの承認を得ていない変更と衝突するサーバーからの更新は、意図的に無視する」という判断を下しました。これは、ユーザーの操作体験を最優先し、一時的な不整合を許容する代わりに、視覚的な安定性を担保するエンジニアリングの勝利と言えるでしょう。この「何を捨てて何を守るか」というトレードオフの判断こそが、Figmaのあの「ぬるぬる」とした操作感の正体なのです。
木構造と循環参照への割り切り
Figmaのドキュメント構造は、本質的には「ObjectID」と「Property」の2階層マップに過ぎません。この単純な構造が、複雑な木構造の同期を可能にしています。特に興味深いのは、親子関係(reparenting)の管理です。多くの開発者が陥る罠は、親子関係を別のデータ構造として管理しようとすることですが、Figmaは「親へのリンクを子オブジェクトのプロパティとして持たせる」という極めてシンプルな設計を採用しました。これにより、オブジェクトのアイデンティティを維持したまま、プロパティ単位の競合解決ルールをそのまま適用できるようになったのです。
しかし、この設計には「循環参照」という厄介なエッジケースが潜んでいます。例えば、AをBの子にし、同時にBをAの子にするような操作が並行して行われた場合、理論上は無限ループや整合性の崩壊を招きます。ここでFigmaがとった対策は、循環検出ロジックをクライアントに実装するという重い道ではなく、「循環が発生したら、該当オブジェクトを一時的にツリーから切り離す」という、ある種の「割り切り」でした。サーバーが最終的な権威として変更を拒否するまでの間、クライアント上ではオブジェクトが一時的に消えるという犠牲を払うことで、システム全体の複雑性を劇的に下げているのです。
さらに、兄弟順序の管理には「Fractional Indexing(分数インデックス)」が採用されています。これは、オブジェクト間の順序を0から1の間の分数で表現し、挿入時にはその平均値をとるという手法です。これにより、リストの途中に要素を挿入する際、他の要素のインデックスをすべて更新する必要がなくなります。この手法は、チャットアプリのメッセージ順序や、タスク管理ツールのカード並び替えなど、あらゆる「順序付きリスト」の同期に応用可能です。Figmaの設計を読み解くと、高度な理論をそのまま使うのではなく、自分たちのユースケースに合わせて「いかにシンプルに実装を落とし込むか」という、シニアエンジニアとしての矜持が随所に感じられます。我々が明日から取り組むべきは、最新のライブラリを探すことではなく、自分たちのドメインにおいて「何が原子的な操作単位なのか」を定義し直すことではないでしょうか。


コメント