今回は Node.js を学び始めた初心者おっさんの、ちょっとモヤっと違和感を感じている部分を書いてみました。正直、技術的な内容は薄い気がしますので、おっさんウォッチャー以外には価値があまりない投稿かも、です。
概要
ある日のことです。私は自分の小さなサンプルプログラムで、ちょっと設定値を保存したくなりました。軽い気持ちで Cloudant DBを選び、モジュールを導入して、唖然とします。
私のシンプルで小さなサンプルプログラムは、信じられないぐらい巨大なアプリと化していました。可愛い仔猫を育てているつもりが、いきなり象になったぐらいの驚きです。
npmやモジュールの仕組みは確かに便利です。でも安易に頼りすぎるのもどうか?そのモジュールは本当に必要なのか?そんな話を少しさせてください。
cloudant モジュールを導入してみる
まずは以前の投稿 IBM Bluemixで最もシンプルなNode.js環境を作成してみる の ダウンロード のリンクから、3ファイル、578バイトしかないサンプル環境をダウンロードします。
ここに cloudant モジュールを導入してみます。
コンソール上でサンプルのフォルダ内に移動し npm install cloudant --save とタイプしてモジュールを導入します。--save オプションを使用したので、package.json には以下の1行が追加されました。
{
"name": "simple-nodejs",
"main": "server.js",
"version": "1.0.0",
"engines": {
"node": "6.*"
},
"dependencies": {
"cloudant": "^1.8.0" // これが追加される
}
}
これだけなら良いのですが、作成された node_module フォルダのサイズが驚きです。
2,225のファイル数!7.98Mバイトのサイズ!が一気に増えました。元が小さかっただけに1.4万倍のサイズ増加です。何かの間違いか?と目を疑います。
なぜそんなにサイズが大きいのか?
cloudant モジュールの中にも package.json があるので、その中の dependencies 項目を見てみましょう。
"dependencies": {
"async": "2.1.2",
"cloudant-nano": "6.5.0",
"debug": "2.2.0",
"request": "2.76.0"
},
cloudant モジュールは async, cloudant-nano, debug, request モジュールに依存していることがわかります。そしてこれらの4つのモジュールも他に依存しており… がずっと続いて膨大なサイズになっているようです。
実はこれは npm install cloudant を実行したとき、画面に表示される依存ツリーでわかります。ちょっと長いですが全部引用します。
更に見ていくと
まず request モジュールはバージョン違いで複数使われているように見えます。
また各モジュールのフォルダのなかを見ていくと、LICENSE はまだわかるとして、README.md やら test フォルダやら、example フォルダやら。実行にはどうみても不要と思われるファイルがけっこうあります。
みなさん、富豪プログラマーばかりなんですかね。
ここからは素人の意見ですが
以上、Node.js を使い始めてからどうもモヤモヤするモジュールまわりの巨大化の話をざっと書きました。
モジュールは便利ですし、npm ツールの仕組みもよくできています。依存関係を気にせず利用できるのは便利。
でも便利だからって大雑把になってもいい、わけじゃないとおもいます。
モジュール気楽に使いすぎ問題
何か処理を実装したいとき、適したモジュールを見つければ、楽に確実に実装できます。これは素晴らしいことです。しかしそのモジュールの必要性、もっと言えばサイズや処理量に対して、得られる対価はそれ以上でしょうか?
いちばん気になるケースは、ある処理を記述したい場合に、ちょっとググってモジュールを利用したサンプルを見つけ、それを真似することです。元のコードを書いた人はモジュールの中身を良く知って書いているのでしょうが、それを見て真似するほうは理解が薄い場合が多いとおもわれます。
その状態でまたググって他のサンプルを見つけて利用すれば、それはまた別の方の異なったコンセプトのコードでしょうから、同じような処理なのに使うモジュールが違う、ということさえ発生しそうです。
Easy to use、それは良いことですが、コピペの集合にはあまり価値はありません。
コア機能が提供されていない問題?
Node.js で Express など大きなパッケージは目にしますが、クライアント側で使用されている jQuery のように手頃で主要な機能群を提供するミドルウェア的なモジュールが欠けている気がします。
程よい粒度のミドルウェアがない、というか。
その結果、あるモジュールの一部の機能だけ使ってる、という状況が重なり、それがさきほどの「モジュール気軽に使いすぎ問題」の根っこにある気がしています。
皆が良く使う機能を抽象化し、少しコードを足せば他にモジュールがなくとも実装できる、どうしても細かい機能が必要なときだけモジュールを導入する、なんて感じにはならないものでしょうか。
え?Express とかいろいろあるパッケージがそんな感じ?うーん、であればデファクトスタンダードが確立されていない問題になるんですかね?ただ Express ってそれだけで巨大じゃないですか?Core部分だけとか、使わないサブモジュールを削除するような管理機能があるのでしょうか。
そもそもサイズに無頓着な問題
npm の詳しい仕組みはわからないのですが、GitHub のプロジェクトからファイルを丸ごとコピーしてくる仕組みなのでしょうか?そんな間抜けな仕組みではないと信じたいですが。
モジュール開発には、公開用のファイルだけ分離する仕組みが必要です。もし無いのであれば、npm の仕組み自体がサイズに無頓着に設計されている、富豪的なコンセプトの仕組みなのかもしれませんね。有るのに皆がそれを活用していないだけ、だといいんですが。
各種ドキュメント、説明画像、example や test 関連はアプリに組み込まれる配布パッケージから外すべきだとおもいます。昔のように1バイト単位で削れとまでは言いませんが、あきらかに実行に不要なファイルが無駄に自分のアプリに組み込まれてしまうのは、私は嫌ですねぇ。自分の頑張りが誤差以下になってしまいますよ。
【追記】コメントで教えていただき、npmにも .npmignore ファイルが使用できることがわかりました。こちらなどを参考にライブラリ製作者の方々には資源節約にご協力いただくことをお願いしたいとおもいます> npmにライブラリをアップロードするときに.npmignoreで生成物を公開/制限するパターン
そして循環する
という感じで、いろんなモジュールに依存したコードを書いて、それがモジュールとして公開される。それらを利用したモジュールがまた公開される。バージョン依存があると何度も組み込まれる。
それらの積み重ねで、今回のように「1つモジュールを使用しただけで、node_moduleフォルダの中身が大変なことに」という状況が生まれたとオモイマス。
まあ話半分で
私は富豪プログラミングに馴染めない、昔からの古き悪きプログラマーですから、ちっと時代錯誤なこと言っているような気もします。Node.js もまだ初心者で、その奥深さも知りませんし。
コンピューティングパワーはどんどん上がっていますし、人が時間をかけて資源を削減するぐらいなら、その人件費よりインフラ増強費用のほうが安いよ、なんて現実もあります。まあ価値観はそれぞれです。
ただ Node.js 自体にもいろいろ機能があり、またモジュールにもいろいろ可能性があるのに、全体的にもっと美しいナニカになれそうなのに、いろいろ勿体ないなぁと思いつつ…
まあもう少し Node.js まわりを学んでいきます。
1年後にはすっかり慣れて「モジュール最高!メガ単位のサイズなんて誤差の範囲だぜヒャッハー!ちまちまコード行数なんか削ってるなよ老害!」とか言っているかもしれません。ま、それはそれで楽しそうです。