AirbnbのエンジニアであるMaja Wichrowska、Tae Kim両氏は、同社が直面したビジネスおよび技術的な課題に対応するために、同社のデザインシステム(design system)のアーキテクチャと実装が発展した状況について解説した。
Airbnbのデザインシステムは、UXとプロダクトニーズを中心に展開されている。2016年にAirbnb Experiencesが一般公開されたことにより、Airbnbのアプリケーションとサイトの中心は、単純な宿泊手配を離れて、エンドツーエンドの旅行全体へと移行した。当時のAirbnbのコードベースは、Wichrowska氏によれば、フラグメンテーション、複雑化、パフォーマンスという3つの問題に苛まれていた。
フラグメンテーションの問題は、エンジニアたちがそれぞれ、自分の慣れ親しんだフレームワークやライブラリを使用したことに端を発していた。結果として、JavaScript、CoffeeScript、jQuery、React、CSS、Sassなどが混在していたのだ。複雑化は、コードベースやスタイリングのニーズの拡大によってもたらされたものだった。エンジニアたちが次々とCSSファイルに追記して、既存のファイルを上書きし続けていたのだ。Wichrowska氏は、スタイルボタンに使用するCSSファイルの例として、次のようなものを紹介している。
core.scss
.button {
background: #ff5a5f;
color: #ffffff
}
custom_page.scss
.button {
background: #008489;
}
yet_another_custom_page.scss
.button {
background: #a61d55;
padding: 1px 1px;
display: block
}
このような追加作業が行われるようになった根本的な原因は、CSSセレクタがコードベースのいずれかの部分で使用されているかどうかを追跡するのが難しいことにあった。ポジショニングやレイアウトでは、この問題は特に深刻なものだった。既存のセレクタやファイルを削除することでページ表示に意図しない変化が発生したり、その原因となったセレクタを後から見つけ出すことが大変な作業になる場合があったのだ。使用されないCSSを維持することでバンドルサイズが拡大し、ページローディングが徐々に遅くなることによって、パフォーマンスが大きな問題になった。
フラグメンテーションの問題には、UIやコンポーネントに関してはReactのみを使うように、Design Language System(DLS)を改良することで対処した。コンポーネントのカスタマイズは、コンポーネントの公開インターフェース(Reactの場合はprop)を経由することで実現した。一貫性と予測可能性を達成するために、スタイルのカスタマイズをpropによって行うようにしたのだ。例えばアルパカ色(alpaca color)のボタンには、自由にカスタマイズ可能なstyle
やclassName
などのpropではなく、isAlpaca
というpropを用意するようにした。
Kim氏はさらに、コンポーネントのCSSとコンポーネントのマークアップを強く結合するCSS-in-JSによって、それまで存在したメンテナンス性の問題を解決した。CSS-in-JSによってスタイルのオーバーライドを抑止し、テーマ設定を可能にし、遅延ローディングとツリーシェイキング(Tree Shaking)との組み合わせによる重要なCSSのみによるシッピングを可能にしたのだ。これにより、複雑性やパフォーマンスの問題は大きく改善された。当時のAirbnbのエンジニアが述べていたように、DLSの優れた部分は、
応答性や再利用性といった問題が、DLSの一貫性と再利用性によってすべて対処できる点にあります。DLSが進むべき道であることは疑いありません。
話は2018年まで進む。DLSの採用が増加するにつれ、そしてAirbnbのビジネス範囲が拡大するにつれ、問題点はより明確になった。2018年になると、マーケット固有のブランディングによる宿泊施設の新たなティア、ビジネス旅行への進出、コンシェルジュサービス、ピアスペース(peer space)などによって、プロダクトポートフォリが大幅に拡大した。これら新しいプロダクトには独自のレイアウト、デザイン、イメージの使用などが必要だった。
フラグメントが再び発生したのだ。新たなスタイルカスタマイズ用propのためにDLSをアップデートする必要が生じるということは、新たなpropがそもそもDLSに本当に相応しいものなのか、DLSガイドラインに従っているか、といったことを決定する長いプロセスが、アップデートの度に必要になるということを意味していた。近づく締め切りにプレッシャを感じると、開発者は設計システムのコンポーネントに頼るよりも、自分でコンポーネントを手早く書いてしまおうとするかも知れない。そうなれば、デザインシステムのそもそもの目的が台無しになる。結果的にKim氏は、同じコンポーネントが何度も何度も書き直されるのを目の当たりにすることになった。その方が生産性で勝るからだ。この方法は、スタイルだけでなく、アクセシビリティにおいても、コンポーネントの一貫性と予測可能性を低下させることになった。
さらに、開発者がコンポーネントにカスタマイズ用のpropを追加したことにより、コンポーネントのコードベースが肥大化し、複雑で扱い難いロジックを抱え込むことにもなった。これにより、外部ライブラリやツリーシェイキングを含まないボタンコンポーネントが、縮小形式で1kbから33kbにまで肥大した、とKim氏は言う。
ボタンとしては大きな数値です。
さらに氏は<button>
コンポーネントのコードベース全体を紹介して、公開インターフェースに30以上のpropがあり、それぞれが複雑なコンフィギュレーションを備えていることを示してみせた。コンポーネントのカスタマイズ機能はこのように、実装、資料、使用方法、メンテンナンスの面で問題を発生させたのだった。2016年に一度解決された複雑性とパフォーマンスの問題が、再び頭を擡げてきた。
その後のイテレーションで、Airbnbのデザインシステムをモノリシックな設計に移行することで、さまざまなAirbnbビジネス用のインスタンスが独自のパレットを追加したり、配色によって差別化することが簡単にできるようになった。このデザインシステムでは、(Reactによる)モジュラーアーキテクチャとベース+バリアントパターンも採用されている。ベース+バリアントパターンは、変更対象ではないコア部分とカスタマイズ可能な部分との分離によってコンポーネントを構成するもので、コンポーネントのコア動作、構造、および(アクセシビリティやデフォルトのスタイルなどの)外観を実装するコンポーネントファイルで実装する。ベースコンポーネントは、外観の変更などのカスタマイズ目的で後から拡張することが可能だが、実装済みのスタイルをカスタマイズされたコンポーネントでオーバーライドすることは許されていない。
Airbnbの<button>
コアコンポーネントには、この方針に従って、動作をカスタマイズするコールバック(onClick
、onHover
、prop)と、ボタンのラベルの前後に配置するコンポーネント用のプレースホルダ構造(renderLeading
、renderTrailing
)が用意された。プライマリボタンはprimaryButton.jsx
ファイルに、セカンダリボタンはsecondaryButton.jsx
ファイルに、それぞれ実装される。いずれのボタンも、baseButton.jsx
に実装されたベースボタンを拡張したものだ。
ここで選択されたアプローチでは、変更の難しいpropインターフェースによる制限の代わりに、JavaScript言語による完全なフレキシビリティを持ったコンポーネント動作のカスタマイズを可能にしている。それと同時に、ベースコンポーネントは自由に拡張可能であるのに対して、バリアントコンポーネントには制約(オーバーライド不可)を設定することで、一貫性と予測可能性が確保されている。このアプローチでは、使用するコンポーネントだけをインポートすればよいため、使用しない機能のためのバンドルサイズが不要になることで、バンドルサイズの削減が可能になる。
講演の全内容がReactConfのサイトで公開されており、上記以外のコードスニペットや詳細な説明を参照することができる。ReactConfはFacebook Reactの公式イベントとして、2019年はネバダ州ヘンダーソンで10月24、25日に開催されている。