ゴミ箱の中のメモ帳

まだ見ぬ息子たちへ綴る手記

フレームワークは使わない

世間のWebサービスでは多くがWebアプリケーションフレームワーク(以下、フレームワーク)をベースにして構築されている。RubyではRuby on RailsPythonではDjangoPHPではCakePHPPerlではCatalystが定評だろうか。

だが私が見てきた多くのサービスではそれらを使う意味がなく、むしろそれらフレームワークを使うことで余計な手間や問題を多く発生させていた。利用者の99%以上はフレームワークを使うことで失敗しており、残りの1%未満もフレームワークを使うことで何らかの問題を余計に発生させているだろう。

私の会社でも過去にいくつかのフレームワークをテストしたことがある。だがどれも現実的には使い物にならず、プロトタイプ開発であればまだしも本番サービスでは使うことはなかった。

知的生産力が劇的に高まる最強フレームワーク100

知的生産力が劇的に高まる最強フレームワーク100


簡単な理由としては、以下のようなものに成る。

フレームワークの流儀に従わなければならない

最も大きな問題がこれに成るだろう。

フレームワークにはORMからURLディスパッチャ(ルーティング)、テンプレートエンジンまで様々な機能が乗っている。それぞれを別のシステムに置き換えることは出来るようになっているものもあるが、基本的にはフレームワーク内で完結させることが前提となっているためそれらは密に結合しており不可分と成る。

ということは、そのフレームワークを使うにはそのフレームワークのルールに従うほかないのだ。例えばRuby on Railsの場合は、「Rubyで書く」と言うよりも「Ruby on Railsを書く」と解釈できるほどに独自の書き方が多い。Rubyというプログラミング言語を知っていることよりも、Ruby on Railsを知っていることが要求されるのだ。

これはDjangoCakePHPでも同じようなものに成る。そして、多くのフレームワークはバージョンアップごとに機能をジャンジャンと増やすために使わない機能の為にコードを書く必要が有り、さらにそのバージョンごとにサービスを修正する必要も出てくる。この使わない機能がジャンジャン増えることは、さらには使われない機能が削除されていくことにもつながり、自分のサービスで使っていた機能が削除されれば、それに追従してサービスを修正していかなければならないことにもつながる。

フレームワーク内では更に、YAMLHamlを使うことを前提としているものもあるため、そのフレームワークを使うだけで周辺の環境についても合わせる必要が有り、本来の言語以外にも多数の知識を覚える必要がある。またこれももちろんフレームワークの機能に従うのでバージョン毎に異なる。

このようにフレームワークを「使う」ということは、実際にはフレームワークを「使っている」のではなく、フレームワークに「従っている」と考えたほうが良いだろう。フレームワークに従わなければならないゆえに、フレームワークの流儀に合わせて書かなければならないのだ。

ということは、どれだけ優秀なプログラマが居たとしても、そのプログラマの能力とは関係なく、そのフレームワークの機能をどれだけ知っているかということで能力は決まる。

例えば私はサービスのプロトタイプを管理パネルの自動生成機能が便利なためにDjangoで数時間で制作するということが多い。それ故に社員もDjangoをある程度使えるようにはなっているのだが、管理パネルのシステムは少々ややこしい。ちょっとしたUIの変更でも、管理パネルの「HTML」を編集するのではなく、フレームワークとしてそれを指示する必要が出る。表の項目のソートや項目の順番、他モデルの項目の表示など色々な機能がそれぞれの機能としてフレームワークには搭載されている。

先日に社員にその管理パネルの修正を頼んだことがある。今までにDjangoを2年ほど使ってきた社員でも5時間ほど悩んで実装していた。それもその時間故に複雑にだ。だが実際には、フレームワークの機能を使えばたった数行のコードを加えるだけで実現できる修正だ。

このように、フレームワークを使うとは、プログラミングをするということではなく、フレームワークのルールに従いフレームワークを使うことに変わりはない。

今日にしても、Python Peeweeを使った検索機能の実装をSQLを使って実装している新入社員が居たが、それは「.contain()」を使えばそのまま実装できる。セキュリティの問題などもライブラリに従えば考える必要はない。Peeweeはフレームワークではなく単なるORMになる。これはフレームワークでなくてもライブラリ単位でも、果てはプログラミング言語にもいえることになるが、フレームワークはその巨大さ故に、プログラミング言語よりも覚えることが多いと言っても、プログラミング言語よりも便利メソッドを覚えておく必要があると言っても過言ではないだろう。

フレームワークのバージョンアップに追従する必要がある

これも「流儀に従う」と同じようなものになるが、フレームワークの「人気の高さ」は「機能の充実」と比例する。多くの人間を取り込むには、その多くの人間が必要としている機能を多く実装するのが最もな近道になり、それ故にバージョンアップごとに機能を増やさなければ「ならない」。また、無駄に増やされた機能は削除されていく。

そして、その機能を増やす、又は変更する、又は削除すると言う変化があると、それはフレームワークの変化に直結し、そのフレームワークの変化はフレームワークの「使い方」の変化に直結する。「使い方」とは「書き方」であり、フレームワークのある機能を進化させるには、既存の書き方を変化させる必要があるということに成る。

ということは、フレームワークをバージョンアップさせるにはフレームワーク上のコードを修正する必要があるのだ。サービスを安定して運用していたとしても、フレームワークのバージョンアップのために修正が必要になる。修正するということは何らかのバグが潜む可能性が発生し、さらには機能の変化のためにコードを変更する必要があれば、そのサービスの安定性にも関わる。機能削除により実装方法の変更の必要が出れば、それはフレームワークを使ったゆえの無駄なリスクにしかならない。

であれば、フレームワークのバージョンを上げなければいいのではないかと思われるのだがそうはいかない。OSであればLTSなどの長期間サポート版があるし、プログラミング言語にしても基本的に安定版のサポートは長い。Python2.7は2020年までのサポートがあるので、実質10年間のサポートだ(これは正直勘弁願いたいところもあるが)。だがフレームワークというのは基本的にサポート(メンテナンス)期間が短い。

例えばDjangoでは基本的にメンテナンスは最新バージョンの1つ前までとなっており、最近では半年に1度バージョンアップをしているので、基本的には1年間のメンテナンスとなる。Django1.4はユーザが多かったために、または1.4での変化と、それ以後の変化が大きかったゆえにLTSとなったが、これもあと半年ほどでメンテナンスが終了する。私もつい先日にDjango1.4からDjango1.7への移行作業を経験したが、かなりの変更を要した。プロトタイプであったためにDjango1.4からDjango1.7対応の変更ではなく、Django1.7のプロジェクトを新たに作りviews以外のコードを書き直したほどに成る。正直、今後Django1.4からの移行作業にはいくつかの問題が出るかと思っている。

また、このようにフレームワークのLTSから最新安定版へのアップデートは大変に成る。基本的には常に最新に追従したほうが安心だ。Djangoも1.4、1.5、1.6、1.7と追従していけばここまで大掛かりな修正は必要なかったのかと思うが、半年に一度もフレームワークのためだけにメンテナンスをするのはコストとしても馬鹿にならない。

これは特にRuby on Railsで顕著で、バージョンをいくつか飛ばすと非常に変更がややこしくなる。Ruby自体がバージョン間での互換性にあまり気を使われていないし、その上で動くRuby on Railsは更に互換性の維持がされていない。Ruby on Rails2.3から3.2に移行するときには、一度3.0対応に修正してから3.2対応に載せ替えたほどに成る。更にその際にはjQueryCoffeeScript等の変更も入っていたため、半分以上を書き直したと言っても良いかも知れない。

このように、フレームワークのバージョンアップには多様なリスクと、メンテナンスコストがかかり、さらにフレームワークのバージョンは短命でメンテナンス期間が短いゆえに、それを常に追従する必要が出来てしまう。

フレームワークから分離が出来ない

これもここまでに書いてきたこととほぼ重複する。フレームワークはそれが一つの構造をなしているがゆえに、フレームワークで一度作ると、そのフレームワークから離れることが出来ない。

先にも書いたように、フレームワークを使うということはプログラミングをするよりもそのフレームワークに従った書き方をしているので、フレームワークを変更することや、フレームワークから離れることは、それは一から作り直すことに直結する。

例えば先に書いたORMのPython PeeweeからSQLAlchemyに移行することはそれほど難しくはない。SQLAlchemyのメソッドに合わせたラッパを作ることでも解決できるし、そもそもにメソッドを多少変更するだけ程度の修正で済む。だがフレームワークはその全てのコードがフレームワーク上での動作を前提にしているので、フレームワーク全体のラッパを書くことなどフレームワークを作る作業と変わりないし、メソッドの変更とは基本的にほぼすべてのコードの書き直しに直結する。

この分離コストが馬鹿にならないゆえに、一度フレームワークで作られたサービスは、そのフレームワークの機能の99%を使わなくても、そのフレームワークのバージョンアップが何の恩恵がなくても、フレームワークのバージョンアップをすることによって修正が発生したとしても、そのフレームワークにしがみついて行く必要がある。このようなサービスは私も多々見てきた。

無駄なリソースが使われる

先に書いたように、フレームワークはあまりにも巨大故に大規模なサービスでもそのフレームワークの90%以上の機能を利用していないだろう。だが、フレームワークは一枚岩故に、プラグイン形式以外の基礎部分は全て読み込まれて動作する。例えば私はフレームワークのORMやURLディスパッチャの動作のみを期待したサービスを見たこともあるが、それらはその単純な機能を使うためだけに巨大なフレームワークをメモリ中に動作させており、更にはその巨大なシステム故に、一つのリクエストから静的ファイルを返すためだけにも巨大なシステムが連鎖反応して動作している。

静的ファイルであればPentiumIIIのようなハードでも毎秒何十リクエストでも余裕で処理できるが、フレームワークが乗った途端にそれが厳しくなる。私がWebプログラミングを始めた時代はPentiumIIで動いているWebサーバもあったが、それでもサービスを支えていた。だが最近はCore i7の様なサーバでもちょっとしたWebサービスを支えることが出来ないような現実となっている。

例えば静的ページのように静的ファイルを生成して出力すれば良いようなものでも、フレームワークを使っているがゆえにそれが出来ずにフレームワークで処理されるテンプレートエンジンを使い毎回生成し出力している。またはキャッシュプロセスを立ち上げて出力している。このような無駄なリソースが使われるがゆえに無駄に高機能なサーバが必要になり、更にはそのサーバでも動作させるためにパフォーマンスチューニングが必要に成る。月に100万アクセス程度のサービスであればさくらインターネットの安価なVPSでも十分にさばけるのだが、フレームワークを使うがゆえに無駄な人的、物理的コストをかけているのだ。この静的生成をうまく利用しているのがMovableTypeだろう。あれは見習うべきだ。

私はPythonでPeeweeとBottleを使ってサービスを作ることが多くなったが、そのような単純な二つのライブラリを使うだけでも大抵のWebサービスは構築が出来る。データベースも静的生成をうまく使えばSQLiteでも十分だ。MySQL専用のサーバなど必要ない。



このような理由から私はプロトタイプ以外ではフレームワークを使わない(もちろん仕事として請け負ったメンテナンスでは既存フレームワークを操作するが)。PeeweeとBottleを利用した便利関数をまとめたファイルを用意しただけで誰でも手軽に使える低リソースでメンテナンス性の高いサービスが実現できる。またPeeweeとBottleは互換性を壊す修正にぶち当たっていない(次回のBottleのバージョンではテンプレートが少し変更になるが)。

大規模なWebサービスでは独自に制作したフレームワークを使うこともあるが、それも結局はライブラリを結合させてまとめたものになる。バージョンアップはライブラリ単位で問題ない上に、ライブラリ単位では社内にメンテナもいるのでバージョンアップにも干渉できたりもしている。

ライブラリ単位ではバージョンアップが頻繁に成ることもあるが、それはフレームワーク全体のアップデートのように大規模なものではなくファイルの差し替えで済む場合が多く、修正が必要になったとしてもそれはそのライブラリの範囲に限定される。URLディスパッチャの呼び出しがクラスになったからと言ってそれに合わせて全体を修正する必要も、DBマイグレーションシステムに合わせてDBを変更する必要もない。


フレームワークを使わないと言うのは私の判断であり、フレームワークが便利だというのも理解はしている。だが、猫も杓子も「Webサービスにはフレームワークを使う」という発想をしていることは非常に危険であると感じている。これは我社の新入社員にも思ったことだ。

フレームワークは一つの選択肢とするだけで、是非とも一度は「フレームワークを使う必要があるのか」と言うことを考えて欲しい。