Monday, March 17, 2014

「技術的負債」をコントロールする定量評価手法への期待

「「技術的負債」を問いなおす」というタイトルでJAWS DAYS 2014で話してきた #jawsdays - delirious thoughtsにて、追記でコメントいただけたので、外野として好き放題言わせてください。すばらしいスライドありがとうございます&いつもすみません。

僕が興味がもつとすると、それは「技術的負債」の定量評価手法についてです。

なぜ、そういう前提を置くかと言うと、それは、たとえばKrutchenによる「技術的負債」の定性評価は、とてもわかりやすいものの、技術を取捨選択するツールとしては使えないからです。

スライドでは、技術評価における将来の不確定性を象徴する問題としてSSDの普及前夜にシャーディングをがんばって実装してしまう例をご紹介いただきましたが、実際、そのような不確実性を織り込んだ正しい決定を我々が日々のエンジニアリングで下すことができているのか疑問に感じることが多いです。思いつくがままに挙げると、例えば以下のようなことを疑問に感じたことがあります。

  • ライブラリやフレームワークを採用するにあたり、その将来性を何年程度のスパンで評価すればいいのか
  • ウェブサービスを開発するにあたり、最初からシャーディングの機能を作り込むことは正しいのか
    • それとも、サービスが当たってからシャーディング機能を実装するほうが利益は大きいのか
  • プロトタイピングにおいて、きれいな実装を行うべきなのか、それとも汚くても早期に実装できたほうがいいのか
    • プロトタイプのコードをサービスインする確率によって答えは変わるはずだが、その確率の閾値はいくらなのか

これらの例からもわかるように、ライブラリ等の部品を選定する際の評価や、規模拡大や拡張を前提とした作り込みの程度を決定するには、「技術的負債」についての定性評価ではなく定量評価が必要です。そして、kentaroさんによる「割引率」という概念の導入は、それを可能にするという点で、僕には興味深く感じられます。

感覚的に言うと、

  • 割引率は、ある期間の後にそのソフトウェアをメンテすることになる可能性
  • 期間毎の割引率に「技術的負債」の解消に必要なコストを掛けて積分したものが、予測されるTCO
  • 予測されるTCOを最小に抑えるのが、正しい技術的選択

のような形として、定量的に評価し、現時点において正しい選択を取るためのツールとして「技術的負債」を使うことが可能になるのではないか。

「これからはnode.jsでしょ」とか「サービスがヒットしたら必要になるから、シャーディング機能は作り込むべき」といった感覚論ではなく、技術的判断をサポートする定量評価手法として「技術的負債」を使えればいいなと思う次第です。


23:37追記: ↓とのことです。ありがとうございます。

Monday, March 10, 2014

拡張可能なWeb APIの設計原則と、バージョン番号を使う理由について

APIのバージョニングは限局分岐でやるのが良い - Hidden in Plain Sightにはブコメしたのですが、Rebuild: 35: You Don't Need API Version 2 (Kenn Ejima)でも本件に言及があったようなので、少し一般論を書いておきたいと思います。


■Web APIの設計原則について

そもそも、良いAPIとはどのような特性をもつものでしょうか? 一般的に、以下の2点が挙げられると思います。
  • 拡張が容易である
  • 拡張時に後方互換性を破壊しない

ウェブの場合は、これに加え、
  • スケーラブルである
  • HTTPに起因する問題に上手に対処できる
ことが求められます。

前2者はウェブに限らない要件です。これを満たす設計手法としては、
  • リクエストおよびレスポンスのパラメータを拡張可能に
  • 互換性を壊す拡張が必要な場合は、関数名を変える
    • 古い関数は従来と同じ機能を提供する
  • ステートフルAPIは、抽象データ型の使用
といった手法が一般的です。

たとえばMicrosoft WindowsのAPIを見ると、多くのリクエストとレスポンス型が拡張可能であり(cbSizeというフィールドで拡張領域を指定する)、非互換な拡張がされたAPIは「〜Ex」という名前をもっており、ステートの表現にはHANDLEという抽象データ型が使われていることが分かります。

これに対し、後2者はウェブ固有の色合いが強い要件です。そして、そのための設計手法として、
  • (出来る限り)ステートレスなAPI
  • 冪等性に対する配慮
が求められる、ということが知られています(したがって、「ステートフルなAPIにおける抽象型の利用」という一般的な設計手法は検討対象外となる)。

以上の2者をまとめると、Web APIを設計する基本原則として、以下の4点を採用するのが良い、ということになります。
  • 拡張可能なパラメータ
    • JSONのような拡張可能なシリアライズフォーマットを用い、拡張可能な形で入出力パラメータを設計する
  • 互換性を壊す必要がある場合は関数名を変更する
  • できるだけステートレス
  • 冪等性に対する配慮
    • よく分からなければ、とりあえずRESTを使う

このような設計をとっていれば、APIを拡張しても後方互換性を確保することができますし、バージョン番号による分岐も必要になりません。


■バージョン番号をURLに含めるべき理由

述べたように、拡張可能なWeb APIの設計において、バージョン番号による分岐という概念は必ずしも必要なものではありません。ですが実際は、それでもバージョン番号をURLに含めるべきだと考えられることがあります。

APIが拡張(ないし変更)されていくということは、すなわち、サーバサイドの提供するAPIの「バージョン」とクライアントアプリケーションが期待するバージョンの齟齬による互換性問題が発生しうるということを意味します。そして、そのような互換性問題は、実行中の良くわからないエラーではなく(データ保存しようとしたらエラーとか最悪ですね!)、明確にトラブルシュートできる、つまり、起動時のバージョンチェックで検出され、ユーザーや管理者に明確に通知されることが望ましいということになります。

実際には、以下のような要件のもとで、確実に機能するバージョンチェック機構が望ましいということになります。
  • クライアントが「廃止された」APIに依存しているかどうかは、サーバサイドでないとチェックできない
  • クライアントアプリケーションの開発者は、しばしば「バージョンチェックを怠る」

もうお分かりでしょうか。この要件を最も単純に実現する手法が、APIのURLにバージョン番号を含めるという手法なのです。APIに対する全てのリクエストにバージョン番号が含まれるため、「クライアント側でバージョンチェックを怠った」がゆえのエラーは発生しようがありません。また、サーバサイドのログにもバージョン番号が残るため、管理者の側で問題の切り分けを要求された場合にも、対処することが容易です。

これが、バージョン番号をAPIのURLに含めるべきと考えられる理由です。必ずしも全ての条件において必要ではないかもしれませんが、一考に値するのではないでしょうか。

なお蛇足ですが、APIのバージョニングについては、サービスとしてしか提供しないソフトウェアの場合は、後方互換性を崩す度にバージョン番号の変更が必要です。また、サーバをソフトウェア製品として提供する場合には、機能拡張を行う度にバージョン番号の変更が必要になります注1(2014/3/11加筆)。ですので、特に後者のバージョン番号発番については、semantic versioningを参考に、MAJOR.MINORのような記法を採用するのが望ましいと言えるでしょう。


参考文献: Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

注1: クライアントの機能が特定のマイナーバージョン以降に依存することもあり、かつ、サーバサイドのバージョンがクライアントサイドのそれより古いことがあり得るため