FigmaのTypeScript移行への道: 独自プログラミング言語をコンパイルして除去する
(figma.com)TypeScriptへのFigmaの道: 独自プログラミング言語をコンパイルして除去する
- Figmaは、モバイルレンダリングアーキテクチャの中核を Skew という独自のプログラミング言語で実装してきた
- レンダリングエンジンで追加性能を得るために考案された言語
- 開発を1日も停止せずに、Skew を自動的に TypeScript へ移行する方法を紹介
Skew言語の始まりと限界
- Skewは Figma の初期段階で副次的なプロジェクトとして開始された
- 当時は、Web とモバイルの両方でサポートされるプロトタイプビューアを迅速に構築する必要があった
- より高度な最適化とより速いコンパイル時間を可能にする、全体をコンパイル対象とする JavaScript プログラミング言語へと発展
- 時間が経つにつれ Skew コードが蓄積し、限界が明らかになった
- 新人エンジニアがなじみにくい
- 残りのコードベースと簡単に統合できない
- Figma の外部に開発者エコシステムが乏しかった
- 拡張しづらさが元々の利点を上回るようになった
TypeScript移行を可能にした要因
- モバイルブラウザでの WebAssembly サポート拡大
- Skewエンジンのコアコンポーネントを C++ エンジンの対応コンポーネントへ置き換え
- TypeScript への移行でも性能劣化が少なくなった
- チームの成長により、開発者体験に注力できるリソース配分が可能になった
コードベースの変換プロセス
- 目標:Skew コード全体を TypeScript に変換すること
- 手動で再実装する代わりに自動化された移行を選択
- 開発速度の低下を防ぎ、ユーザー向けのランタイムエラーと性能低下を防止
- 3段階のロールアウトプロセス
- Skewで記述、Skewでビルド
- 元のビルドプロセスを維持し、トランスパイラを開発、TSコードを GitHub にチェックイン
- Skewで記述、TypeScriptでビルド
- TypeScriptコードベースから直接本番トラフィックのロールアウトを開始
- 開発者は引き続き Skew で記述し、トランスパイラが Skew を TS に変換
- TypeScriptで記述、TypeScriptでビルド
- 開発におけるソース・オブ・トゥルースとして TS コードを採用する必要があった
- 自動生成プロセスを止め、Skew コードをコードベースから削除
- Skewで記述、Skewでビルド
トランスパイラ作業の補足
- コンパイラはフロントエンドとバックエンドで構成される
- フロントエンド:入力コードの構文解析と理解、型チェック、構文チェックを実施
- IR(中間表現)へ変換し、元の入力コードの意味論とロジックを完全に捕捉
- バックエンド:IR をさまざまな言語に変換
- トランスパイラとは、人間が読めるコードを生成する特別な種類のコンパイラ
移行過程で直面した課題
- 配列
destructuringの性能問題- JavaScript の配列
destructuringを使わないことで最大25%の性能向上
- JavaScript の配列
- Skew の「devirtualization」最適化
- ロールアウト工程で追加ステップを挟み、devirtualization がコードベースの動作を壊さないよう対処
- TypeScript では初期化順序が重要
- トランスパイラはこの順序を尊重するコードを生成する必要があった
開発者体験のための Source Map 活用事例
- 開発者生産性を高めるため、移行を容易にしデバッグ体験をスムーズにすることに注力
- ソースマップを使って、コンパイル済みコードとソースコードを紐づける
- ブラウザは JavaScript のみを理解できる
- ソースマップにより、ブラウザはコンパイル済み JavaScript バンドル上で、ソースコードのブレークポイントをどこで止めるべきかを判断可能
- 3段階プロセスでソースマップを作成
- TypeScript → JavaScript のソースマップを作成
- 各 Skew ソースファイルごとに Skew → TypeScript のソースマップを作成
- ソースマップを構成し、Skew から JavaScript へのマッピングを作成
条件付きコンパイル処理の事例
- Skew ではトップレベルの
if文で条件付きコードコンパイルを許可- コンパイル時定数を使って条件を指定
- 同一コードベース内で別のビルドターゲットを定義可能
- TypeScript には条件付きコンパイル機能がない
- バンドル段階で実行するよう変更
- esbuild の
definesとデッドコード除去機能を使用 - バンドルサイズがわずかに増加する副作用が発生
TypeScript時代のプロトタイピング開発
- Skewコードを TypeScript に移行したことで、Figma のコアコードベースが近代化
- 内部・外部のコードとより容易に統合できる道を開いた
- 開発者がより効率的に作業できるようになった
- 当時は TypeScript が適切ではなかったが、今は確実に適切な選択だった
- TypeScript移行のすべての利点を享受するために後続作業を進行中
- 残りのコードベースとの統合
- ずっと簡単になったパッケージ管理
- TypeScriptエコシステムの新機能を直接利用することなど
GN⁺の見解
-
Figma は独自のプログラミング言語 Skew から TypeScript への移行を非常に体系的かつ段階的に進めた。開発を中断せずに自動化された移行を行った点が印象的だ。企業規模が拡大するにつれて生じる技術的負債を解消し、エコシステムの変化に合わせた良い事例だと思う。
-
パフォーマンス重視の独自言語から汎用言語へ移行する際、WebAssembly の登場など技術環境の変化が重要な役割を果たした。技術選択では当面の要件に基づく判断も重要だが、技術の進化速度と方向性も考慮すべきであることを示している。
-
開発者体験を考慮したソースマップ活用、条件付きコンパイル処理など、実務で参考になる具体的な手法が紹介されていて良い。既存レガシーとの互換性を保ちながら段階的に移行するプロセスが印象的だ。
-
大規模なコードベースでこのような作業を行うには、自動化されたコード変換ツールの開発が必須だと思われる。Skewコンパイラを活用したトランスパイラ開発が中核だったように見える。コンパイラ理論と内部実装の専門知識が求められる作業だと思う。
-
プログラミング言語の移行は単なるコード変換以上の影響を及ぼす。開発文化とエコシステム全体にプラスの変化をもたらすことができる。慎重なアプローチは必要だが、開発組織の実力があるなら挑戦する価値はある。
1件のコメント
Hacker News のコメント
Andrew Chan(プロジェクト関係者)によると、Figmaはほぼ10年間、他の部分でTypeScriptを使っており、ほとんどの期間でSkewよりTypeScriptの比率が高かった。Skewはモバイルエンジン、プロトタイピングプレイヤー、ミラーリング機能など一部の製品領域で使われていた。
FigmaにJavaScript向けの独自言語があったこと自体が驚きで、しかもそれがTypeScriptより速かったのがさらに驚異的だった。その後、より遅いTypeScriptへ移行した。
元Figma CTOのEvan Wallaceによれば、Skewはより厳密な型システムにより実現可能になった高度な最適化のおかげで、TypeScriptより1.5~2倍高速だった。
配列の分割代入時に、JavaScriptが配列を直接インデックス参照するのではなく配列を反復するイテレータを構築するのは興味深い。JSがなぜ配列を直接インデックス参照しないのか気になる。
Skewはコールバックだけだったようだ。async/awaitのような最新のJavaScript機能や、より柔軟な型システムが言及されている。
Figmaは、権限制限のようなセキュリティ上の問題を解決するために、独自のTypeScript DSLとコンパイラを作成した。
大企業ごとに独自の内部ツール、言語、Kubernetesを持っていて共有しないのは残念だ。Skewがオープンソースだったなら、もっと良いTypeScriptになっていたかもしれない。
FigmaがWebAssemblyを採用した理由が気になる。
学びは明快だ:独自言語は作らないこと。
TypeScriptに反対する人たちの意見を聞いていて興味深い。TypeScriptはほぼすべてのコード行を改善する、欠点がほとんどないツールだ。彼らは新しい技術を学ぶのが怖いのか、時間をかけたくないのか、実用性を誤解しているようだ。TypeScript反対派に賛同するなら、その理由をもっと深く考えるべきだ。そうでなければ大きなデメリットを被ることになる。