embryo

エンジニアの備忘録

ViteでLegacyブラウザ対応する方法と注意点

今日はviteを利用したprojectでブラウザ対応を宣言しpolyfillなどを対応する方法についてまとめます。

トランスパイラを用いたブラウザ対応

babelなどのトランスパイラを利用している場合はbabel-preset-envを利用してbrowserslistとコードベースの内容に応じて自動でcore-jsのimport文を追加しpolyfillを解決する方法が用いられてきました。swcを利用している場合も同等の機能が用意されています。

viteのデフォルトブラウザ対応

こちらにもある通り、viteのブラウザ対応はESMのサポート状況を基準に行っており、検証する対象は構文にとどまっています。 viteがトランスパイラとして利用しているes-buildにbabelやswc相当の機能があることを期待するかもしれませんが、esbuildのtargetAPIのサポート状況に関する検証はスコープ外としています。 そのためviteで対応ブラウザのAPI利用の検証を行いたい場合以下の2つの方針が考えられます。実際はもっと細分化されますが大まかに分けると以下の2つになるかと思います。

  1. swc, babelを利用してトランスパイル時にpolyfillを読み込むように変換する
  2. tsconfigのlib設定やes-buildのsupportedオプションを利用し静的にサポートしていないAPI利用を制限する

2に関してはbrowserslistなどの設定から適切な設定を出力するライブラリなどが有志によって作られているかもしれません。

コニュニティプラグインとして vite-plugin-react-swcが提供されているのでswcのenvを利用することも考えられますが、現状swc関連の設定はハードコーディングされているため、ユーザーが自由に設定を指定するという仕様にはなっていません。

以下のpluginはswcrcの設定を読み込んでくれるため有効な選択肢になり得ますがこちらは検証していません。

github.com

@vitejs-plugin/legacy の利用

viteはコミュニティプラグインとして @vitejs/plugin-legacy を提供しています。

github.com

内容は前述したbabel-preset-envを利用した方法ですが、core-jsへのimport文を取り除いてpolyfill用のchunkを生成する仕組みになっているところや、古いブラウザとモダンブラウザでchunkを分割している点で工夫されています (以下modern / legacy chunk)。legacy / modernの分岐は前述のESM対応状況をベースにしており、デフォルトの挙動ではESMの対応が不完全なブラウザをlegacyとして判別するためpolyfillなどもlegacyブラウザでのみ読み込まれることになります。定常的にすべてのブラウザに対してpolyfillを提供するためには以下の3つの設定を適切に行う必要があります。

  • modernTargets
  • modernPolyfills
  • renderLegacyChunks

modernTargets はmodern chunkに対する対応ブラウザの宣言でbrowserslistのフォーマットで設定できます。modernPolyfills とセットで利用されることを期待され、加えて modernPolyfills を有効にすることでmodernと判別されたブラウザでもpolyfillのchunkが読み込まれることになります。

If modernTargets is not set, it is not recommended to use the true value (which uses auto-detection) because core-js@3 is very aggressive in polyfill inclusions due to all the bleeding edge features it supports. Even when targeting native ESM support, it injects 15kb of polyfills!

と記載があるように、polyfillの内容は実際のコードベースとブラウザ対応設定に対して最適化されたものが出力されるため最小限の内容になっているものの、ランタイムで最適化されるワケではないので最新のブラウザを利用しているユーザーも同様のpolyfillを読み込むことになります。そのため、公式では modernTargets である程度対応ブラウザを絞ったうえで利用するか、polyfill.ioを利用することを推奨しています。

renderLegacyChunks は ESMはサポートされているブラウザが対象である場合 legacy chunkが不要なので false を設定することで前述のデフォルトの挙動をOFFにすることができます。

まとめ

viteでbrowserslistを用いて宣言的にブラウザ対応を行う方法についていくつか解説しました。静的に検証するか、静的に検証した上でpolyfillを導入する2つの方法がありますが、多くのユーザーをサポートする必要があるプロダクトでは後者の方が有効なケースが多そうです。

2019年振り返り

前職の振り返り含むので何だかんだ1年半の振り返り。

2019年

やったこと

  • 婚約
  • 退職&開業
  • iOS Frontend開発
  • Web Frontend開発
  • Web Frontendのトレーナー
  • 技術書典

向き合った細かいトピックなど

  • Web Frontend

  • iOS

    • ふつうの自動化
    • ふつうのナビゲーション整理
    • ふつうのMVVM
    • ふつうの最適化
    • Background Transferの検証
    • デザインシステム
    • 負荷試験
    • APM、分散トレーシング
    • 型システム
    • PicoCTF
    • 税制

他にも細かいものは沢山あるがある程度取り組んだもののみ。 思い出したら追加します。

OSS

総括

フリーランス開業とか技術書典でバタバタして色々半端な状態。 今まで気になった周辺技術を調べて深堀りしてたらそこそこ積み上がってたみたいな感じの学び方だったけどそろそろ逆算して計画的に進めないと厳しい。

フリーになってやることはそこまで変わってないけど求められるコードの品質は下がったとは思う。

2020年

やりたいこと

  • 結婚
    • します
  • 法人化
    • 多分した方がお得
  • アルゴリズム、プロコン
    • 業務でも使ったりする
    • 取り敢えず水色が目標
  • TaPLの理解
    • 70%理解してます。くらいは言えるように。
  • 言語化の習慣、アウトプット
  • バックエンド業務
  • デザインシステムの成果物(パターンライブラリなど)の本
    • 技術書典で半端なものを出してしまった反省
  • 2Dゲーム制作(趣味)
    • unity楽しい
  • ML
    • ツールを扱えるレベル

優先度順。英語と言いたいところだけどキャパオーバーなので除外。 特にインプット/アウトプットやプログラミングのトレーニングは習慣化しないと厳しい。

DIの目的とメリットについて

今日はDependency Injection(以下DI)の目的を整理しました。 できるだけ目的やメリットをベースにまとめていきます。

tl:dr

  1. DIはオブジェクトを外から渡すこと → モジュールは依存するオブジェクトの依存解決(生成)方法を意識しない
  2. 抽象に依存することにより疎結合に → モジュールは依存するオブジェクトの実装の方法を意識しない
  3. 2に静的解析が組み合わさると大変便利

依存するオブジェクトの参照を解決する方法

DIの目的は特定のモジュールの振る舞いと依存解決の方法を分けることです。 今回は例としてapiサーバーにアクセスするためのクライアントクラス(以下APIClient)を実装します。 APIClientは内部でhttp通信を行うためのクラスを参照する必要があるものとします。

1. 内部で生成する

export class APIClient {
  httpClient: HTTPClient;
  constructor() {
    this.httpClient = new HTTPClient(baseURL: "https://example.com");
  }
  request(config) {
    this.httpClient.request(config)
  }
}

内部で必要なプロパティを生成する方法です。APIClientの振る舞いを変更する方法は内部の実装を変える必要がある他、 HTTPClientのConstructorに変更があった場合に、APIClientの実装を変更する必要があります。

2. (Static)Factoryパターンを使って取得する

export class APIClient {
  httpClient: HTTPClient;
  constructor() {
    this.httpClient = HTTPClientFactory.create();
  }
}

export class HTTPClientFactory {
  static httpClient: HTTPClient
  
  static setInstance(httpClient: HTTPClient) {
    this.httpClient = httpClient;
  }
  
  static getInstance() {
    if (!!this.httpClient) {
      return this.httpClient;
    }
    return this.httpClient;
  }
}

Factoryパターンを用いることで、APIClientの実装とHTTPClientの生成を切り離すことに成功しました。 HTTPClientの生成に変更があった場合もAPIClientに変更を加える必要はありません。 実装の切り替えはFactoryクラスのgetter, setter経由で行います。

ただしこのコードにはいくつかの問題があります。

  • グローバルにインスタンスを保持しているため、どこからでも書き換えられる
  • モジュール内部で依存解決を行なっているため、実装を見ないと依存関係がわからない
  • Factoryクラスへの依存が発生する

3. DI(Consturcutor Injection)を使う

export class APIClient {
  constructor(private client: HTTPClient) {}
}

上記はConstructor経由でオブジェクトを渡すConstructor Injectionと呼ばれます。 APIClientは渡されるオブジェクトを使用するだけなので、依存解決の方法や依存オブジェクトの生成方法を意識する必要がありません。 また、依存関係はConstructorに全て宣言されているため、依存解決を検証しやすいというメリットがあります。

4. DI(Lazy Injection)を使う

export class APIClient {
  setClient(client: HTTPClient) {
    this.httpClient = client
  }
}

依存オブジェクトが得られるタイミングがモジュールの生成タイミングと異なるケースや、後から実装を差し替えたいケースがあります。 このような場合にはsetter経由で依存オブジェクトを渡します。

この場合生成時にパラメータが初期化されているか分からないので、あくまでConstructor Injectionを実装した上で必要な場合に実装するのが良いです。

DIと抽象化プログラミング

APIClientの依存する対象はHTTPClientとは限りません、典型的な例で言えばテストの場合、実際にHTTP通信を行って データを返却する必要はありません。そのため、HTTPClientの振る舞いはそのままに実装を変えたいケースがあります。 以下のコードではHTTPClientの振る舞いと実装を分割しています。

interface APIBackend {
  request(config)
}

class HTTPClient: APIBackend {
  request(config) {
    ...
  }
}

class MockClient: APIBackEnd {
  request(config) {
    ...
  }
}

export class APIClient {
  client: APIBackend;
  constructor(private client: APIBackend) {
  }
}

上記のようにAPIClientが依存する機能をAPIBackendとして定義することで、APIClientが依存するオブジェクトを容易に変更できます。 APIClientは自身が必要な機能だけを意識して実装するため、実際に渡される依存オブジェクトとは疎結合になります。 つまり、APIClientのテストを書く場合、APIClientで書いているロジックのみをテストすれば良いことになります。

DIと静的解析

静的解析は必ずしも必要なわけではありませんが、実行前に依存解決の検証ができるので、安全にプログラミングができます。

依存対象に変更があった場合、影響するモジュールは容易に判別可能ですし、いわゆるnull許容のような型の解析をサポートしている言語では 依存するオブジェクトが渡されているタイミングも示すことが可能であるため、開発時にコードから様々な情報を得ることができます。

以下の例ではclientインスタンスはモジュール生成タイミングで必ず渡されているので安全に使用できますが、 authTokenは生成タイミングでは存在しないため、内部でも気をつけて取り扱う必要があることがわかります。

export class APIClient {
  authToken: string?

  constructor(private client: APIBackend) {
  }

  setToken(authToken: string) {
    this.authToken = authToken;
  }
}
よく混同されがちなDI Containerの話

DIを用いることでそれぞれのモジュールは疎結合になりますが、実際にはそれぞれの依存を解決するレイヤーが必要になります。 DIはデザインパターンであって、依存グラフを解決する仕組みではありません。そこで、それらを解決する仕組みの一つがDI コンテナです。 詳しくは検索すれば良い記事が沢山転がっていますのでそちらをどうぞ。

まとめ

DIの目的について順番に解説しました。実際の現場では抽象化や静的解析と組み合わせて使用されることがほとんどですが、 あくまで各モジュールを疎結合に保つためのデザインパターンなので、言語によらず意識的に実践することで結果的に全体の設計が綺麗になっていくかと思います。 対象となるスコープも小さいので導入も簡単ですね。

技術書典5にサークル参加しました その2

前回はサークル参加の反省をメインにまとめましたが、今回はイベント自体の感想を一般参加者目線のまとめになります。

戦利品

一人サークル参加してしまったため、半ば諦めていたのですが隣のサークルの方のご好意で回ることができました。ありがとうございました!

時間は15:00頃で本は売り切れているところも多かったですが、DLカードをメインで狙っていたので全く問題なかったです。以下戦利品です(あまり買えなかった)。

f:id:sue71:20181014215435j:plain

一周だけ回って気になるものを買うという感じだったのであまり量は買えませんでした...。 あとで買っておけばと後悔したものも沢山あるのですがBooth版などを期待したいと思います。

f:id:sue71:20181014215441j:plain

ちなみにこちらは販売している際に、とあるお客様が「UIに携わるものとしてこういった活動をしてくれる方に感謝している」とくださったものです。こちらこそとても励みになりました。ありがとうございます。

知らない技術に出会える場

今回特に参加してよかったなと思ったのは知らない技術に出会えるところですね。本当に色々な技術領域の方が参加しているので、まったく触れることのなかった技術に触れるきっかけになります。

「水晶雫世界」さんが頒布されていたCrystalDiskInfoの開発秘話本ですが、僕が不勉強なのもあり名前も知りませんでした。WindowsSSD/HDDモニタリングといえばコレというほどデファクトOSSだそうです。知らなかったものの普段Webばかりの自分にとってはSSD/HDDのモニタリングツールというワードには惹かれるものがあり、購入しました。受け身で知らない技術に出会えるのは嬉しいですね。

「技術季報」は去年売る側だったのですが、今回は買う側でした。正直パンフレットかなくらいの認識だったのですが「サークルの自動配置」などの技術記事が書いてあったりと技術書として楽しめました。

暗号通貨の分野にも興味があったので「NEO」に関する書籍を購入したのですがこちらはDLカードを無くしてしまいました...。しかし、購入した際、月一で勉強会を開いているという情報をサークルの方から頂いたので参加してみようかなと考えています。

まとめ

今回はサークルとしても一般としても参加できたのですごく楽しかったです。こういったイベントを支えてくれるスタッフ、サークルの皆さんには感謝しかないです。次回開催時も参加するつもりなので皆さんよろしくおねがいします。

技術書典5にサークル参加しました その1

頒布したもの

techbookfest.org

「本 + PDFダウンロードカード セット」で500円で頒布しました。

用意したのは以下。

  • 本は100部
  • PDFダウンロードカード100枚 (+予備94枚)

※ ダウンロードカード6枚は手違いでダメにしてしまいました

結果

詳しく時間を見ていないのですが12:30頃に本は完売してホッとしていました。 その後もダウンロードカードのみで購入していただき最終的には残り8枚でした。

  • 本+PDFダウンロードカード: 100セット完売
  • PDFダウンロードカード88枚

でした。予備のカードまで割と買っていただけてかなり嬉しかったです。 とにかく安心しました。

なぜデザインシステムをテーマにしたか

Web、iOSのフロントエンドエンジニアとして働いていくなかで、かねてからUIの共通化コンポーネント化は開発効率だけを目的にするものではないし、UIコンポーネント=正義というどちらかというとエンジニア目線の押し付けはデザイナーにとってもユーザーにとっても嬉しくない、と思っていたのでデザインシステムはそのあたりの話をするのにちょうどよい、ということでこのテーマでの執筆を決めました。端的にいうと次のようなことを話したかったのです。

  • 感覚に訴えかけるための要素がデザインには存在する
  • 無機質なUIのセットですべてが構成されるわけではない
  • きれいなコンポーネント化はユーザビリティに貢献できる

執筆の反省

計画もなく取り敢えず書く。といった感じだったのでギリギリになって知らないことが増えてきて焦りだすわけです。印刷所について調べることもないので当然デッドラインなども知らないわけです。なんとかなるだろうと思って書いていました。当たり前ですが締切を事前に調べておき、マストで必要なものを洗い出し、それからじっくり書き始めるのが良かったかと思います。

以下大きなしくじりです。

  • 印刷は一部カラーにする予定が締切に間に合わず、モノクロに
  • PDFダウンロードカードもギリギリ。前日にQRの不備に気づき貼り直す
  • 事前提出について確認しておらず当日頒布停止

準備の反省

ほとんどを前日に用意しました。100均素晴らしいです。

用意したもの

  • テーブルクロス(布)
  • 見本誌を立てかけるためのやつ (当日忘れる)
  • 値段+ポップを印刷したもの
  • スケッチブック
  • 小物入れ(ペン+DLカード他入れるためのもの)
  • 小銭入れ
  • 小銭(500円✕5枚、100円✕50枚)
  • カッターとか諸々

必要だったもの

  • A4くらいのポスターを立てるやつ
  • 机の前にはる本の説明書き
  • 太いペン
  • サークルの名刺

後述するのですがお隣のサークルさんに大変親切にしていただいたのでなんとかなりましたが、 結構色々足りなかったです。特に本が完売してからは何売ってるか全然わからない状態になってしまったので 大きめのポップは必須でした。

サークルの名刺は欲しがる方が結構いらっしゃって、DLカードとよく間違われていました(これは本当に申し訳なかったです)。次回は必ず用意しようと思いました。

当日の反省

取り敢えず一人で参加するのは本を買いに行けないし、トイレも飲み物もままならないし何より寂しいです。なんとかなるかなと思ったましたが当然なんとかなりませんでした。

お隣のサークルの「水晶雫世界」さんには大変親切にしていただきました。お店番を代わっていただいたり、ペンを貸していただいたり、話し相手になっていただいたりと最初から最後まで本当にお世話になりました。こういった方達に出会えただけでも参加して良かったと思います。

techbookfest.org

まとめ

色々と段取り悪くバタバタしていましたが、本当に楽しかったです。多分次回もまた何か書きます。 ちょっと長くなりそうだったので、戦利品やイベント自体の感想は別の記事にてまとめようと思います。

技術書典スタッフの皆さん、サークル参加の皆さん、当日足を運んでくださった皆さん、お疲れ様でした。 ありがとうございました。

言語ファイルの命名規則と管理方法

今日は言語ファイルの管理方法及び命名規則に関してです。技術書典5でデザインシステムの本を書く予定なんですが、その中でも言語は重要な要素なので一度整理しておこうと思います。

なぜ言語ファイルを管理するのか

実際のところ本当に多言語対応するつもりなのであれば言語ファイルの管理だけでは厳しいところが多いです。言語以外の差異も数多くある上に、UI上で日本語と同じように再現することも簡単ではないからです。それでも言語ファイルを管理したい理由は2点あって

  • 用語の更新を簡単にしたいから
  • 表記揺れを無くしたいから

です。つまるところUIを再利用可能なコンポーネントの組み合わせで作りたいという欲求と変わりません。

どのように管理すべきか

色々なアプローチがあるのですが、現状私が確認してるもので大きい区分で分けると

  • コード中にある文字列を抽出して言語ファイルとして出力する
  • 別途言語用のファイルを定義してコード中から呼び出す

の2点があります。前者は何も考えずに渡されたデザインに記述された文字列を記述できるが、表記ゆれは起こりやすい。後者は文字列を別途定義する分コストが高いが設計次第ではサービス内で使用している用語を整理するソースになり得る。といったメリット・デメリットがありそうです。

ちなみに再利用性を考えない設計にした場合には、前者のようなアプローチが良いかなと思います。 どちらの方針を取るかは言語管理の目的を用語の整理とするか、多言語化のための単なる定数とするかで異なります。

どのように設計するか

キーに関してルールがない場合、再現なくネストしていくため、現状私の開発時は <コンテキスト>.<書式>.<適当なキー> としています。

コンテキスト

コンテキストはスコープを表します。今開発しているプロダクトでは 共通部分のcommon, ドメイン知識を持つdomain, 表示領域を表すmodule で分けています。

  • common : ドメインに依存しないような用語、例えば「メールアドレス」などもそれに当たります。
  • domain : ドメインに依存するような用語、例えば「ユーザー」、「ユーザーの氏名」などです。
  • module : 表示領域、Viewに依存します。例えば「ユーザー一覧」などがこれにあたります。

コンテキストを用意している理由は適用の範囲を表すことはもちろん、文脈を意識するようにするためです。同じ単語でも文脈が異なれば別の意味を持っている可能性があります。

書式(タイプ)

以下のようなタイプを定義しています。

  • label
  • button
  • message
  • placeholder

書式をなぜ用意しているかというと、言語の検索をする際に必要な情報になるからです。また書式のバリデーションの用途も兼ねています。 例えば button の場合、あとで見返したときに一律して「〜する」という書式になっているのかなどを確認するためです。

コンテキスト間の依存関係

f:id:sue71:20180924204601p:plain

言語間の依存関係は図のようになるはずで、例えばユーザーデータが次のように表される場合

interface User {
    id: string;
    name: string;
    email: string;
}

このメールアドレスのキー管理は

const user = {
    "domain.user.label.email": "ユーザーのメールアドレス"
};

としたいのですが、「メールアドレス」の表記がコンテキストによって「Eメール」、「メール」と表記が異なるのはサービスとしてまずいので

const common = {
    "common.label.email": "メールアドレス"
};

const user = {
    "domain.user.label.email": "ユーザーの{common.label.email}"
};

の関係であるべきです(参照の形式は適当です)。同様にユーザー一覧用の言語ファイルは

const userList = {
    "module.userList.label.user_email": "{domain.user.label.email}",
    "module.userList.button.ok": "{common.label.ok}", 
    "module.userList.message.hint": "ユーザー一覧でユーザーを追加できます"
};

となり、commonmoduleから参照可能なものは参照し、固有のものは個別に定義します。

これはモジュールなのかドメインなのか

最早言語ファイルに限った話ではないのですが抽象的なレイヤーが挟まるとどれに入れるべきか迷うことがあるはずです。 例えば「ユーザーを追加する」リンクやボタンの文言はモジュール、ドメインどちらに入れるのか、などです。

正直なところドメインオブジェクトがきちんと分析されているか左右されるのですが、ドメインオブジェクトの振る舞いとしてドメイン配下に定義するのがいいと思っています。 しかしそれをチーム全体に浸透させるのは難易度が高いのも事実なので迷うくらいなら機能の用語としてしまってモジュールに定義するのが良いかと思います。

まとめ

現状実践している言語ファイル管理の方法について簡単にまとめました。言語ファイルを管理する目的によって管理方法は様々なので、まずは目的を整理する必要がありそうです。

AtomicDesignが解決してくれないこと

今日はAtomicDesignについてです。

概要

弊社のとあるチームでAtomicDesignの導入を試みていたのですがあまり上手くいっていないようでした。そこでなぜ上手く行っていなかったのか考えて整理したので、その内容をまとめました。

AtomicDesignとは

Design Language System (DLS) を作る上でのパターンの一つです。詳しくは下記を参照してください。

atomic design

大まかに説明するとデザインパーツの粒度を定義し、それらの組み合わせでデザインパーツを構成していこうというものです。 粒度は下記のように定義されています。

Organisms > Molecules > Atoms

フロントエンド界隈には大分普及してきたようで最近では活用事例を耳にする機会も多くなりました。

AtomicDesignのメリット

まずはAtomicDesignのメリットについてですが以下の2点がよく挙げられています。

共通言語が作られる

粒度を共通言語として定義することにより、開発者間でのコミュニケーションが円滑に進みます。

分解粒度が属人化しづらい

再利用性の高いUIコンポーネントを作っていく際、どうしても開発者のセンスによるところが大きかったように思えますが、とにかく細かく分解して足し合わせるというメソッドなので属人化しにくいのもメリットの一つと言えるでしょう。

※ しづらい、としたのは限られた階層しか無い以上必ず属人化はするからです。

AtomicDesignで起こりがちな問題

AtomicDesignを採用する時によく起きている問題をまとめました。

分解されている == 良いUIコンポーネントではない

Atomic Designの粒度に分解して配置することを目的にしてしまい、1度しか使わないUIコンポーネントが量産されるケースを現場でよく見かけます。目的は再利用することなので、分解していく過程でどこが再利用出来るかを考えるべきです。

グループ化やパターン化は解決してくれない

良いUIコンポーネントを作るためには適切なグループ化とパターン化が必要です。例えばボタンの場合は下記のようなグループ化、パターン化を行うことで再利用性が高まりますが、AtomicDesignをただ実践するだけではそうはなりません。

Group Pattern
TextButton type, size
IconButton type, size, icon

ユーザビリティが良くなるわけではない

UIコンポーネントの分解粒度が最適化されることがユーザーにとって嬉しいわけではありません。あるUIコンポーネントのルールがアプリケーション内で一貫していることで初めてユーザーにとって嬉しいものになります。

そもそも良いUIコンポーネントとは

AtomicDesignをきっかけにDLSを作り始める事自体は良いことです。ただAtomicDesignを実践する前に良いUIコンポーネントとはどういうものなのかを整理し、チェックすると良いかと思います。

再利用性が高いこと

主に開発効率化のためになりますが、UIコンポーネントに関して言えば最も重要な要素です。新たにページや機能を制作する際に、今まで分解したパーツを使用できないか、またそれらをどのようにグループ化されていれば矛盾なくパーツにはめ込めるのかを考えましょう。

最終的には下記のように文字で指示書が書けるのが理想です。

f:id:sue71:20180502232454p:plain

ルールと拡張性のバランス

そのUIパーツが本質的にどういう性質なのかを考えましょう。画面だけ見ていると見えていない性質が見えてくるはずです。 例えば下記のようなナビゲーションバーが提供されている場合

f:id:sue71:20180502231513p:plain

- 左部はページの遷移をコントロールするものである
- 中央はページを説明するものである
- 右部はページに対するアクションを提供するものである

というルールが考えられます。

ある程度ルールが定まっているとその拡張性の方向も限られてくるため実装も最適化されます。 ルールが定まっていない場合そのUIパーツ内で拡張性の方向が定まらないため、パターン化がしづらくなります。これが1度しか使われないコンポーネントが量産されること、もしくはUIコンポーネントの分岐が多くなる原因になります。

また、必ずしも汎用的なルールに落とし込めないことに注意してください。時には似ている形でも全く別の欲求によってデザインされている場合があります。 こういった場合は無理にパターンに当てはめるのではなく、特殊なケースとして考えるのが良いかと思います。

ちなみに自分はどうしているか

基本的には汎用的なUIコンポーネントと、より詳細度の高いUIコンポーネントの2つを定義しています。(名前は適当です

またユーザービリティを意識した場合、必ずしも汎用性の高いパーツのみで完結するとは限らないため、各ページ内でしか使用しないものも定義することを許容していますが、依存を分かりやすくするためにページのディレクトリ配下に置くようにしています。

詳しくは別の記事でまとめたいと思いますがOOUI(UX)などが近い考え方になると思います。

まとめ

AtomicDesignの実践時によく見かける課題について説明しました。

「設計とはフレームワークではなく良いコードを書こうとする意思である」という言葉も記憶に新しいですが、DLSやUIコンポーネント設計にも同じことが言えます。

手法に名前をつけることは大変意義があることであるのには間違いは無いのですが、ベストプラクティスを考えることが最も重要です。つまり、あるパターンを実践するのを目的とするのではなく、良いUIコンポーネントを作ることを目的としましょうということです。