Table of Contents
今携わっているプロジェクトではScrumで開発していて、私自身2年ほどスクラムマスタを経験した。 うちの会社はかなり保守的で、ごく最近までウォータフォールで開発するのがあたりまえだったので、そこから文化を変え、マインドシフトし、アジャイルなプロセスに順応していくにはそれなりに苦労があった。 今でも、アジャイルに慣れていないエンジニアがアジャイルなチームに入ってくると、やはりいろいろな違いに戸惑っているように見えるし、こちらとしても期待するアウトプットがなかなか出てこなくて困ることが多い。
私はスクラムマスタというロールを任されてはいるが、どちらかと言えばテックリードやアーキテクト的な役割に期待されている気がしていて、そっちに力が入ってしまうのが実情。 そんな状況なので、コードレビューには結構時間を割いているんだけど、アジャイルなエンジニアとそうでないエンジニアが書くコードにはなんだかとても重大な差異があるような気がずっとしていた。 で、最近それを説明できるまでに考えがまとまってきたので、ここに書き残しておく。
ウォータフォールの問題
ウォータフォール開発は以下のような特徴がある。
- 綿密な計画を事前に立てて、計画通りに開発を進めることを重視する。
- 機能設計 ⇒ 詳細設計 ⇒ コーディング ⇒ テストといった感じに、全体の計画を工程でフェーズ分けして、手戻りなく、一方向に進むことを重視する。
- 各フェーズでは全開発項目を並行して進め、ソフトウェア全体の整合性を取ることを重視する。
- 各フェーズで、包括的で完成されたアウトプットを作ることを重視する。アウトプットは各フェーズで詳細にレビューする。
- 綿密な設計ドキュメントを整備し、適時アップデートしていくことを重視する。理想的には、コーディングは設計ドキュメントをプログラミング言語に射影するだけの単純作業になる。
これはこれで、バグの少ないソフトウェアを、大人数で、非属人的に、低リスクで開発するのに向いた開発手法ではある。 しかし、設計ドキュメントの整備にかなりの時間がかかり、開発工程全体が長くなりがちなのと、動くソフトウェアが出来てくるのが開発終盤になってしまうのが欠点。 ソフトウェアがビジネスにおいて果たす役割がかなり大きくなった昨今では、ウォータフォール開発はビジネスのスピードや変化についていけない時代遅れの手法という評価になった。 代わりに台頭したのがアジャイル開発だ。
現代では世界の企業のの95%がアジャイルなプロセスを導入している。 いまだにウォータフォールを採用するのは、例えばNASAのロケット制御ソフトウェアみたいな、バグがあると数十億ドルと世界トップレベルの人材が吹っ飛ぶようなソフトウェアの開発プロジェクトだ。 (因みにNASAのコーディング規約は、再起関数禁止、静的に回数が決まらないループ禁止など、常軌を逸した厳しさ。)
アジャイル開発のDRY
ウォータフォールとアジャイルの違いは大量にあるが、パッと見て分かりやすい違いは設計ドキュメントの有無であろう。 アジャイルな開発では設計ドキュメントを排除するのが基本。
ウォータフォールなプロセス:
ウォータフォール開発では、こんな感じにいろんなドキュメントを作ることになる。 加えて、ソースコードには大量のコメントを書くのが良しとされていた。(うちの会社だけ?) また、テストフェーズでは、何をどう操作して何をどう確認するという詳細なチェックリストを作り、経験の有無にかかわらずだれでも機械的に同じテストができるようになっているのが理想とされていた。(うちの会社だけ?)
沢山設計ドキュメントを書いても、結局顧客が手にするのはソフトウェアだけ。(ユーザマニュアルは別として。) 顧客が目にする唯一の「真実 (Truth)」は、インストールされて手元で動いているソフトウェアだ。
この「真実」の挙動や構造を説明するもの、つまり「真実の情報源 (Source of Truth)」はどこにあるか。 機能設計書や詳細設計書は言うまでもなく「真実の情報源」。 ソースコードのコメントも、どのような処理をしているのかを説明するものが多いだろうから、「真実の情報源」と言える。 ソースコード自体も、「真実」の挙動を決定づけるものなので「真実の情報源」だ。 テスト設計書やチェックリストも、「何をしたとき、どうなる」ということを記述しているので、「真実の情報源」になる。 このようにウォータフォール開発では、唯一の「真実」を様々な形で何度も表現することになるので、開発工数が膨らむことになる。
アジャイル開発では、これをプロセスにおけるDRY (Don’t Repeat Yourself)と断罪する。 つまり、「真実の情報源」が複数になることを悪として、「真実の単一情報源 (Single Source of Truth)」にしなさいと言う。 これには、ソフトウェアの挙動を変更したいときに修正すべき「情報源」を少なくして、修正工数を下げつつ、「情報源」間のずれによるバグを防ぐ狙いがある。
アジャイル開発プロセスにおける真実の単一情報源
アジャイル開発プロセスにおける「真実の単一情報源」とは何か。 それは、ウォータフォール開発における「真実の情報源」から何を削減するかという話になるわけだけど、結論は明らかだ。 絶対に捨てられないものが一つだけあるので、それ以外を排除することになる。 残すのは当然ソースコード(自動テストのコードも含む)。 ソースコードが無ければソフトウェアは動き得ないので。
アジャイルなプロセス:
ソースコードが「真実の単一情報源」なので、それ以外の「真実の情報源」はDRY違反。 これがアジャイル開発で設計ドキュメントやテストドキュメントの作成を避ける理由だ。 ただし、注意すべきなのは、設計そのものを否定しているわけでも、設計の情報をチームで共有をすることを不要としているわけではないこと。 アジャイル開発は、それらを重要視しつつも、それらのためにドキュメントを作るという作業を排除した。 ドキュメントが無ければ、設計しても人に伝えられず、すぐに立ち消えてしまうように思われるが、アジャイル開発ではそれを別の媒体に記録する。 この媒体が何かというのも明らかで、ソースコードしか作らないんだからソースコードそのものということになる。
要するに、ソースコードを動く仕様書兼設計書にするということ。 日本人にとっては英語が母国語ではないので実感がわき辛い気がするが、ソースコードは割と自然言語を駆使して書いている。 プログラミング言語の機能やライブラリやフレームワークが強力になって、プログラミングパラダイムやコーディング手法の進化もあって、ソースコードは設計を伝達する媒体として十分な表現力を持つようになった。
アジャイル開発の現場でよく、ソースが仕様書みたいなことを言うのを聞くが、それは伊達ではなく、言葉通りの意味ということだ。 「アジャイル開発という名目を掲げて仕様書作成をさぼっているから、ソースコードを見て仕様を読み解け」などという開発者の怠慢・傲慢ではなく、「仕様書のように美しく読みやすく体系立ったソースコードを書きます」という決意表明なのである。
(因みに現実的には、ソースコード以外の「真実の情報源」を完全に排除できるわけではない。ユーザマニュアルが最たるものだし、設計ドキュメントに関しても、大域的な設計は別途簡単にでもまとめておいたほうがいい場合が多い。結局は理想のDRYと現実との間でバランスをとることが大事。)
ソースコードを動く仕様書兼設計書にするには
ソースコードを動く仕様書兼設計書にするために実践すべきことはたくさんある。
フォーマッタをかける
体裁が整っていないコードは読み辛い。 フォーマッタは、アジャイル開発の普及につれて、IDEが提供する補助的な機能の一つから独立したツールへ、さらにプログラミング言語の一部へと昇格を遂げた。
変数名、関数名を説明的にする
変数名や関数名などの識別子は、仕様書における用語であったり、章節のタイトルであったり、文の主語・目的語にあたるものなので、明確で、正確で、曖昧さが無く、一貫性があるべき。 例えば、
tmp
とかlist
とかの何が入るかわからない変数名はよくないし、HTTPでデータを取得する関数にはgetXxx
よりもfetchXxx
の方がいい。 また、なでしこで書かない限りは英語で名付けることになるので、英語をしっかりリスペクトした名前にすること。 よく、複数の要素を保持する変数(i.e. コレクション)に単数形の名前をつけちゃうことがあるんだけど、これはダメということ。型を付ける
型をうまく付けてやることで、値(の集合)が表すものを説明できるだけでなく、継承などによって値間の関係を表現したり、インスタンスメソッドなどによって値と処理の関係も表現することができる。 例えば、
{ "status": "xxx", "body": "yyy"}
みたいにプレーンなオブジェクトに値を詰めるよりも、HTTPResponse
みたいなクラスのインスタンスでデータを扱うほうがはるかに意図を伝えられる。コード生成ツールを活用する
コード生成ツールは大抵、なんらかのDSLをもとにソースコードを生成するようになっている。 このDSLは、特定のドメインに対するソフトウェアの挙動を効率よくわかりやすく記述できるように設計されているので、ソースコードを直接書くよりも設計の意図を伝えやすい。 また、実装上のDRYを進める一助にもなる。 例えばswagger-codegenを使えば、YAMLで記述したREST API仕様から、サーバコード(の一部)とクライアントコードに加えて、APIリファレンスまでも生成できる。 プロセスのDRY違反無しに設計ドキュメントを得られるようなものなので、非常に強力だ。
ライブラリやフレームワークを活用する
ライブラリを使うことで、処理の詳細が隠蔽されて、より抽象的にコードを書けるようになる。 これはドキュメントにより豊かな語彙を使えるようなもの。 また、フレームワークを使うことでボイラープレートが削減され、記述が統一され、体系的にコードを書けるようになる。 これは優れたドキュメントテンプレートやガイドラインを得たようなものだ。
効果的なコメントを書く
個別の処理内容を説明する、「~をする」という形のコメントはDRYに反するので排除する。 効果的なコメントは以下のようなもの
- 「~のためにする」とか「~なのでする」といった、「何」ではなく「なぜ」を説くもの。
- 「~であることを想定」といった事前条件、事後条件。
- 「~してはいけない」とか、「~に注意」といった、見落としやすい落とし穴の注意喚起。
- 「将来的に~すべき」とか、「~は未実装」といった、既知の欠陥を示すもの。
- 大きな処理を要約するコメント。大抵はモジュールやクラスやメソッドに付ける、JavaDocやJSDocやdocstringの形式であるべきであろう。
-
SOLIDやKISSのような設計原則はウォータフォール時代から大事にされていたもので、ソースコードに設計の意図を反映させるためのものではないが、従っていれば意図の伝わりやすいコードになりやすい。 例えば単一責任の原則に従うことで、設計ドキュメントの章節を適切な粒度に分割するような効果が得られる。
-
自動テストのコードは、ソフトウェアの挙動を説明する重要な情報源になる。 ソースコードだけでは伝えにくい事前条件・事後条件・境界条件なんかは特に、テストコードで伝えるのが効果的。 JestというJavaScriptのテストフレームワークでは、テストコードを書くファイル名の拡張子を
.spec.js
にするが、これはテストコードが仕様(i.e. specification)を表現すべきものということを示している。 ドメイン駆動設計 (DDD)を実践する
ドメイン駆動設計の手法を使えば、ソフトウェアが扱う問題領域の構成要素や事象やそれらの間の相互作用を、ソースコードに紐づく形でモデリングすることができる。 設計と実装が連動するので、ソースコードがより直接的に深く設計を伝えられるようになる。
これらのプラクティスはウォータフォール開発のころから実践されてきたものも多いが、設計の意図をソースコードに込める目的を意識しているかによって実践レベルに差が出てくる。 この差がアジャイルなエンジニアとそうでないエンジニアとの差異の一つであろう。
ソースは、上手く構造化されていて、変更に強く、効率よい処理方式で書かれているというだけでは、アジャイル時代には不十分。 ソースは仕様書なので、設計の背景や意図を明確に伝えるものでなければいけない。 アジャイルなチームに入ってくる人たちには、まずはこの点をしっかり認識してほしい。
参考文献
アジャイル時代のコーダーになるには以下のような本が役に立つ。 古典ばっかりだけど。