技術要素編: web アプリが新陳代謝を続けるための依存関係の厳選(新規開発のメモ書きシリーズ1)

新規開発したプロダクトについて

「世の中に URL は出ているが、まだ正式公開していない」という微妙な位置付けなのでプロダクト名と詳細は避けつつ述べます。短めの開発期間で急いでつくって、慌てて年末にβ版をリリースしたところです。

次の動きに向けてまったりしたり、Inside Frontend の開催に向けて四苦八苦しているところでメモ書きです。

依存するパッケージの厳選

新しい技術、ライブラリを試すこと、それらを使って生産性の向上にチャレンジすることは必要です。とはいえ、程度が過ぎると逆に生産性を下げかねないからこそ、何のパッケージに依存するかは慎重に検討すべきです。あのライブラリがだめ、このパッケージはよい、という具体的な話ではありませんが、自分の普段の観点としては次の3つです。

  1. それを使うことで自分たちがワクワクできるか(モチベーション)
  2. 目的に対して適切な規模感のパッケージか(交換可能性)
  3. クライアントサイドスクリプトのバンドル時に肥大化しすぎないか(ファイルサイズ)

1. ワクワクできるほどのモチベーションがないとつらくない?

枯れたパッケージを使ったほうがメンテナンスコストは低く、エッジであればあるほど頻繁なアップデートや破壊的変更などでメンテナンスコストは高くなります。とはいえ慎重すぎてもチャレンジできないので、何らか判断基準を設けるわけですが端的に「それを使うことで自分たちがワクワクできるか」かしかありません。

モチベーションがあれば多少難解でもみんな使えるし、手がかかってもアップデートを待ちわびることができます。精神論ですが、それを使うことの意義や信念が共通になっていないのが最も苦しい、とかその程度の話です。

とはいえ実際に使ってみたけどイマイチだった、ということでモチベーションが実利を下回ることもありえるので、次に挙げる点にも気をつけています。

2. スコープが大きすぎて交換できないのはつらくない?

パッケージがターゲットにしている問題領域が広すぎると「ここは良いけどここは気にくわない」というときに部分的に他のパッケージと置き換えたり、そもそも使わなくなって破棄したりに大きな痛みを伴います。きっとみんな経験があることでしょう...。

npm のようなパッケージ管理ソフトウェアの上に成り立ったエコシステムと、そこから好きなパッケージを組み合わせて大きいプロダクトを作り上げる昨今のワークフローを鑑みるに、小さくて単機能なパーツのほうが都合良いことが多いです。

大きいパーツで身動きが鈍ると、逆に新しいチャレンジがしづらくなるなど、技術的な新陳代謝に支障をきたしてしまうリスクが出てきます。

3. クライアントサイドのバンドルサイズが大きいのもつらくない?

クライアントサイドで利用する JavaScript のバンドルサイズ(ファイルサイズ)が大きいと、ダウンロードだけでなく JavaScript の評価でも時間をもっていかれるのでページロードの速度に大きく差し障ります。ファイルサイズが 1MB を超えてくると gzip しても 300-400KB 程度で、わりと大きい JPEG 画像みたいなものがクリティカルレンダリングパス上に乗ってくることになります。

ファイルサイズは npm 本来のサーバーサイド的な文脈だとあまり気にされることはないかもしれませんが、クライアントサイドにとっては死活問題です。露骨に「大きい」パッケージはもちろん、immutable.js や Intl の polyfill など「意外と大きい」パッケージもあるため油断なりません。

最近よくありそうなのだと Please optimize the size of libs · Issue #332 · firebase/firebase-js-sdk のとおり、firebase の JavaScript SDK とかはナチュラルにクソでかいです。 import firebase from 'firebase/app'; のように必要なコンポーネントだけ読み込むのはもちろん、Dynamic Import などでロードを遅延させなければ酷いことになります。手元だと app と auth だけでも 150kb 超...。

今回の構成要素となった主要パッケージ

自分が手を動かす新規開発が2年ぶりくらいということもあり、自分の情報をアップデートしつつの開発だった一方で短期決戦も求められていたので、パッケージ選びはあまり冒険せず 過去に発表した構成要素 の多くが引き継がれています。

それぞれについてそれなりに意思決定の流れはあり、リポジトリの issue に記録されてはいますが、ブログなのでそこそこに割愛して書き進めます。目立ったモノの抜粋ですが、あとはもう compression とか cookie とか connect-timeout みたいなやつが主です。

アプリケーション

時間の都合もあり、過去の記事で紹介した fluxible ファミリーで SPA + SSR を素早く構築することを選択しました。fluxible の開発はすでに活発ではなくなっていますが、更新は細々と続いていたのでまだ現役で利用できました。薄っぺらいのでどうにでもなる反面、次回はさすがに使わないかも。

後に続く記事で改めて述べますが、SPA はクラサバのテンプレート(コンポーネント)共通化のオマケみたいなもんで、必要な要件ではありません。

React 系

recompose とか怪しげなユーティリティを使いたい欲を我慢して、必要なものだけ。カルーセルとか自力で書くのだるい系 UI もβ版のうちは存在しなかったので大分少量です。

intl についてはそもそも本当に国際化必要なのか?という疑念もありますが、後から付け加えることになると本当に最悪&テキストの管理リストとして使えばいいか、という事情で利用しています。Number Formatting とかはオマケ。

super-image は似たようなの作ることになりそうだし、知ってるプロジェクトのプロダクトだから入れてみるか〜...と入れてみたたものの、結局オーダーメイドの別モノを作ることになりそうな気配。

テストユーティリティ

テスト周りの設定や速度に悩まされることが多かったので jest を採用し、test-utils が辛かったので enzyme にしました。ふつうです。jest ちょっと大きいかな?とは思ったのですが、ava はなんかこう...ちがった。

アグレッシブに色々とアップデートしたら Build on node 9 on Circle by SimenB · Pull Request #4851 · facebook/jest でしばらく Circle CI 上のテスト死んでたけど....。うん、しょうがない。

モジュールバンドラ

Service Worker のように単一にバンドルできそうなところで rollup を使い、Code Splitting が必要なアプリケーション本体には webpack を使いました。先日リリースされた rollup の Code Splitting は同僚に試してもらいましたが、まだ設定が大変そうなので保留中です。

トランスパイラー + α

TypeScript と postcss ( with 必要なプラグイン ) という構成で、テスト周りと同様にこちらも至極ふつうです。

同僚が CSS Modules を使いたいということで用意しようとしたところ、webpack の設定に思い悩むのアホらしくなったので postcss-modules + 補助スクリプトで webpack に依存せず CSS modules に対応しました。

コードスタイル

少しだけ eslint/typescript-eslint-parser を試しましたが、Lint ごときに人柱立ててる場合ではなかったので tslint を入れました。補助スクリプトなど一部は JavaScript で記述しているため eslint も併用しています。

prettier は同僚に最近導入してもらいましたが便利ですね。WebStorm の Auto Import で挿入される import 文を手直ししていたのが自動修正になって幸せ。

タスクランナー

「男は黙って make 」に入信しました。そんなに使いこなしているわけではないんですが、gulp ほどヤバくなくて、npm scripts ほど不自由でない、という加減に落ち着いています。

タスクランナーという概念は画期的でしたが、設定を記述するのではなく、処理の流れを命令できることを喜び始めたあたりで我々は引き返すべきだったのかもしれません。

諸事情で導入されなかったパッケージ

前述したような厳選過程で色々な検討は行われつつ、入れたかったけど入れられなかった系のパッケージも若干数あります。

ジェネリック React ( preact, inferno )

元々は軽量互換ライブラリの developit/preactinfernojs/inferno の使用を検討していました。が、ちょうど React v16 のリリース時期と立ち上げ時期が重なり、エコシステムが v16 に向けて対応を進める中、本流ではない preact や inferno は最新版の enzyme などとの互換性に難が出てしまい一旦見送りました。

JavaScript の実行時イニシャルコストを考えると、様子をみて早めに置き換えておきたいところ。これを踏まえると、React v16 でいう Error Boundaries や Portals のような新機能を積極的に利用するのに気が引けてしまう側面も。

手に余りうるライブラリ

たとえば rx だとかそういうのですが。一部のメンバは気持ち良く書けるかもしれないが、運用や機能量産のフェーズで個人に固有文脈の知識を求めすぎるとか、それで良く書ける問題は抽象化してそもそも隠蔽したい、みたいなやつは導入を避けました。

前述のジェネリック React の導入に失敗した手前、清貧を尊びたい気持ちもあり...。

地雷を踏みまくって fastify からも撤退

web サーバーとして fastify/fastify の導入を検討して途中まで使っていました。しかし、数回面倒な地雷を踏んでしまってモチベーションが消えたので途中で epxress に切り替えました。機会があればまた今度。

今回は以上です

こうして並べてみると面白みがないどころか、ひょっとして世間的にはちょっと古くさいのでは....という感すらありますね。ここ数年、色々な宗派が市民権を得つつあるものの、個々のパッケージが解決できるレベルの話だとそうそう大きい変化はないんじゃないかなーという感じ。

コード設計編とアーキテクチャ編が続く、はず。