Goを使い複雑性を回避する

『銀の弾などない— ソフトウェアエンジニアリングの本質と偶有的事項』を書いたFred Brooksはその論文の中で、偶有的な複雑性と本質的な複雑性について重要な区別をしています。本質的な複雑性とは、問題特有の領域から生じる複雑性のことを指します。例えば、SMTPクライアントを作成しているディベロッパは、RFC 5321の核心の細かいところ全てに取り組む必要がありますが、これはSMTPクライアントの作業をする上で避けては通れないものです。これに対して偶有的な複雑性とは、私たちが自ら作り上げた問題から生じる複雑性のことを指します。

技術者としては、自らの選択で生じる偶有的な複雑性によって、余計な負担が増えないようにとても注意しなければなりませんよね。その意味では、言語の選択は偶有的な複雑性を軽減できる完璧な例と言えます。Webアプリケーションを書くのにアセンブリ言語を選びますか? C言語はどうですか? もちろん選ばないでしょう。ソフトウェアを作るには、高水準言語を使った方が時間もかからず表現の幅も広げることができますからね。Brooksはこの点に言及しつつ、ソフトウェア開発においては、偶有的な複雑性のほとんどを私たちは取り除いたと主張しました。同様に、ディベロッパにのしかかる偶有的な複雑性を不必要に増やすようなツール選びや技術を使ってしまうことで、自身への負担を増やしてしまうことがないようにするべきです。仮に偶有的な複雑性が増した場合、新米のディベロッパだと対処するのが難しくなりますし、より経験を積んだディベロッパも、システム全体の把握が徐々にできなくなるでしょう。そして最終的には、敏腕のディベロッパや最高の開発チームでさえ太刀打ちできなくなるかもしれません。

偶有的な複雑性の展開

この10年間で、ソフトウェアの構築やデプロイの方法には様々な変化がありましたが、残念ながらこの領域では、偶有的な複雑性が著しく増加したというのが私の感想です。私は2000年頃にWebアプリケーションの構築を始めました。まず使ったのはPHPです。当時、全てはとてもシンプルで、サードパーティのライブラリもパッケージ管理システムも必要ありませんでした。納品する準備ができたら、変更済みファイルをFTPサーバにアップロードするだけです。あとはApacheとmod_phpがやってくれます。もちろん、全てが完璧というわけではなく、ローカル環境の設定などは悪夢のようでしたが、テストといったものは存在もしていませんでした。

2005年頃から、Ruby on Railsを使い始めました。おかげで大きく現状が改善されたのです。この開発ツールは驚くべきもので、テストを書くのも簡単だし、開発環境がものすごくシンプルになりました。RubyにはRubyGemsがついてきます。おそらくこれが、私が初めて経験したパッケージ管理システム持つ言語だったと記憶しています。しかしこれらのパワーにはそれなりの代償があります。RailsはFastCGIではうまく動かせず、ApacheやMongrelで動かさなければなりませんでした。そして複数のアプリを同一のサーバで実行する場合は、”dependency hell”と呼ばれる問題が起こります。

Railsアプリケーションを構築し実行するのは簡単な仕事ではありません。アプリケーションの起動と実行に関わっている膨大な動作部分はぐらぐらしています。“典型的なRailsアプリをビルドしデプロイするために学ぶべきソフトウェアのリスト” を作ってみました。Ruby、Ruby Gems、Bundler、rbenv、unicorn、Rack、Rake、CapistranoそしてRails。もちろん、テストツールも必要です。バックグラウンドワーカーやメッセージキューが必要になることもあります。これら全部のツールに取り組みながら仕事を片付けることなんてできるでしょうか? 今はRailsだけの話ですが、同様の複雑性は現代のほとんどのテクノロジー・スタックに見られます。ブラウザでJavaScriptを使って作業するのに、[パッケージ管理システム(bower)](http://bower.io/)をインストールするためのパッケージ管理システム(npmjs)が必要だったりします。そして、多言語プログラマの増加によって、さらに複雑性が何倍にもなりました。最近終わったプロジェクトでは、Python、Ruby、JavaScriptを使いました。現行のプロジェクトではPython、Go、JavaScriptを使っています。どちらの場合も開発者たちは3つの言語とそれぞれのツールセットを知らなくてはいけません。これが現代のソフトウェア開発の世界です。

はっきり言っておきますが、これらのツールが悪いとも、それを書いた作者たちが悪いとも思っていません。これらのツールは計り知れない恩恵をもたらしてくれます。しかしながら大抵の場合、複雑性はさらなる複雑性を生む結果になります。私たちはみなアプリケーションの動作部分の数を減らす機会を探すようにしなければなりません。

Goはシンプルさを促す

Goは驚くほど単純な言語で、そのツールセットは完全でありながら最小限のものです。Rob NapierはGo Is a Shop-built Jigの中で、Goの単純性がどれほど有益かについて、すばらしいコメントを記しています。特に次の一文は秀逸だと思います。

Goは実際の問題を解決するだけなので、アンダーエンジニアードだと感じる。

私はこのコメントを一歩更に進めて次のように解釈しています。開発者がGoをアンダーエンジニアードだと感じるなら、それはGoが偶有的な複雑性を避けるという明確な判断に基づいてあらかじめ設計されたからです。この性質を表すGoの機能については深入りしません。その代わり、今回はGoのツール周りの2つの際立つ側面について取り上げます。これに比べると最近の典型的な言語のツール周りは悪夢のようです。

まず1つ目に、Goで構築したHTTPサービスはセルフホスティングだということがあります。もちろんこれはGoだけの機能ではありません。数年前Node.jsが脚光を浴びた時、webアプリケーションをセルフホストできるという点が最も喧伝された機能の一つでした。今やASP.NETですらアプリケーションをセルフホストする機能を特徴と打ち出しており、この機能は最新の標準ライブラリを使う利点と言えるでしょう。HTTPだけにとどまらず、Goはプロキシやウェブソケット、SPDY、そして豊富な他のネットワークライブラリなど、ネットワークアプリケーションを非常にシンプルに構築できます。その結果uWSGIやUnicornまたはTomcatのようなアプリケーションサーバを用いる必要がありません。RackやWSGIの詳細について、あなたは知らなくても大丈夫です。これでアプリケーションに関わる面倒を少し取り除くことができます。

2つ目は、Goではアプリケーションがバイナリにコンパイルされ、全ての実行時の依存関係がバイナリにコンパイルされるということです。セルフホスティングがRubyやPythonに比べて小さな飛躍だったとすれば、バイナリディストリビューションは別世界への飛躍です。言語ごとの実行環境や、パッケージ管理システム、環境を管理するツールなどが必要ありません。RubyやPythonの開発者がいかにベテランでも、異なる環境やバージョンを扱うのは非常に煩雑なものです。Goでは、違うバージョンで開発されたアプリケーションを同一サーバ上で使うのも簡単です。コンフリクトを気にせず同一依存環境で異なるバージョンを扱うことができます。このおかげで、システムだけでなくデプロイメントにおいても、大いに複雑さを回避できるのです。

複雑性を賢く選択する

Goがすべてのタスクを完璧にこなせるわけではありません。しかし、並行なネットワークサービスを構築する時には疑いようもなくGoを選択枝に入れるべきです。おまけに、RubyやPythonではツール周りの複雑性に出くわすことが多いでしょう。最終的にどの言語、どのツールを用いてアプリケーションを構築するのがベストなのかは、あなたや所属する組織の選択です。その選択をする際、ぜひ少し時間をとって、トレードオフについて熟考してみてほしいと思います。