レガシーコード改善ガイド読書会
先日、会社でレガシーコード改善ガイド読書会を行った。1回しかやっていないので、今後どんな感じになるのか正直わからないが、1回目の振り返りを記してみる。
〆会、テスト勉強会(社内)などで有志を募ったところ、なんやかんやで10数名が名乗りを上げてくれた。どんなペースでやるかとか運営をどうするかとかを相談するために、参加希望者で集まった。週1回(木曜日、19時開始)、担当の章などを決めた。第1章〜第5章までは各自で読んで、読書会への参加の動機などを含めて簡単なポジションペーパーとしてまとめることにした。
わたしが、読書会の世話役として、会議室の確保(空き会議室を見つけて予約しただけ)、開催のアナウンスなどを行った。資料置き場の共有ディレクトリ、社内Wikiなどを立ち上げた。*1
テストがないコードはレガシーコードだ!
「テストがないコードはレガシーコードだ」という強烈なフレーズが表紙に書いてある。そうだそうだその通りだと思う。わたしたちが日々格闘しているのはレガシーコードだ。これをどうにかしたいという強い思いの人が当日集まった。
運用でトラブル対応に追われて殺伐とした日々を送っている人もいる。今、テストを書いていないけど、この状況をどうにかしたいと藁にもすがる気持ちできた人もいる。
10数人集まった。
各自、自己紹介、どこのグループで何をしているのか、読書会に対する期待などをのべあった。ベテランから新卒まで幅広い。エンジニアもいれば、マネージャーや理事もいる。多様性がある。
初回は、進行役として、わたしがそれぞれの章について大雑把に紹介したあと、フリーディスカッションをした。
ソフトウェアの変更
日々ソフトウェアは変更されている。本書では、その理由を
- 要件の追加
- バグの修正
- 設計の改善
- リソース利用の最適化
としている。
要件の追加は、新機能を追加したり、場合によっては既存の機能を(非互換に)変更することも含む。バグの修正は、ソフトウェアの振る舞いを変更することに他ならない。設計の改善は、振る舞いを変えないままソフトウェアを変更する。リソース利用の最適化は、振る舞いを変えないまま、性能を向上させたりする。
要件追加 | バグ修正 | リファクタリング | 最適化 | |
---|---|---|---|---|
構造 | 変化する | 変化する | 変化する | - |
新機能 | 変化する | - | - | - |
機能 | - | 変化する | - | - |
リソース利用 | - | - | - | 変化する |
ここでバグ修正というのは既存のテストでバグを発見できなかった場合だから、テストへの追加修正もあるし、要件追加の場合にもテストへの追加が発生する。
フィードバックを得ながらの作業
システム変更の方法は大きくわけて下記になる
- 編集して祈る(Edit and Pray)
- 保護して変更する(Cover and Modify)
編集して祈るが、業界標準になっている。注意深く作業をするのだけど、その編集によってシステムに不具合が発生しているかどうかよくわからない。そこでトラブルが発生しないように祈る。不安感に苛まれている。夜も眠れないメソッドである。
一方、保護して変更するの保護というのは「テストで保護する」という意味である。機能が正しく実装されているというのを確認するためのテストではなく、変更をみつけるために行うテストである。これは従来回帰テストと呼ばれていたものである。
システムのリグレッション(デグレード)をテストが見つけてくれる。心の平静が保てる。夜はぐっすり眠れる。安心である。心のセーフティーネットである。
われわれは、編集して祈る方式から保護して変更する方式に移行したいと強く誓い合った。(同士よ)
単体テスト
本書の定義では実行時間が0.1秒かかるテストは遅いテストだとしている。また、データベースのやりとりをする、ネットワークを介した通信をする、ファイルシステムにアクセスする、実行するために特別な環境設定を必要とする、というようなテストは単体テストではないとしている。
非常に厳しい定義を採用しているが、いろいろな意見が出た。現状ではテストがないのだから、仮に実行に10分かかってもないよりもはるかにましではないか(そうだそうだ)。
データベースにアクセスしないようにモックを作ることがある。うーん、それって結構設定とか面倒じゃないすかね?商用RDBMSの場合、個人PCに環境がないので、テストは開発マシンでしかできないっすね。やっぱ、MySQLとかオープンソースの場合、テスタビリティ(テストの容易性)が増えていいよね。そーすね。
データベースのやりとりってどーやってテストするんすかね。SQLとかパースするなんかを作るのかな。いやいや、そんな難しいことしないで、毎回空のデータベースを作って、初期データをぶっこんで、ふつーにSQLを投げる、そーすれば毎回同じ出力が出るからテストになる。物事を複雑化しない。
などという感じの議論があった。
接合モデル
既存コードのテストを書くとき、テストに向いていないかに気がつく。テストを書きやすいようにコードが書かれているということはほとんどない。レガシーコードのジレンマは、コードを書くためにテストを整備する必要があるが、多くの場合、テストを整備するためにコードを変更しなければならない、というところだ。
ここでは、接合部という概念を提示している。接合部とは、その場所を直接編集しなくても、プログラムの振る舞いを変えることのできる場所である。
テストをするために、接合部を利用して、プログラムの振る舞いを変える。48ページの例で、テストをするために、privateのメソッドをprotecteedメソッドにするというのが出ていた。ここで議論が白熱した。
ちょっとこれは、何のためにprivateにしているのかわからなくて、やりすぎだと思う。いやいや、テストをするために必要悪みたいなものじゃないか、あーだこーだ。
下記にそのような議論が載っているということを参加者が読書会後に教えてくれた。
http://www.itarchitect.jp/technology_and_programming/-/34161-2.html
わたしの現時点での結論は、privateのテストを書くためにソースコードを変更してprotected/publicなどにするのはやりすぎ、ブラックボックステストでいいというところだ。やはりテストのコストとメリットを天秤にかけたい。
ツール
xUnitなどのテストフレームワークは主に単体テスト向けに設計されたものである。レガシーコードをテストするには、テストコードを相当作り込まないといけない。
一方で、FITは統合テスト用のフレームワークでレガシーコードに手を加えることは必要ない。
レガシーコードには前から攻める
議論の中で、徐々に、レガシーコードの場合は、ホワイトボックステストをゼロから作っていくのではなく、ブラックボックステストを積み上げていくのが現実的な解ではないかというコンセンサスが出来た。
スクラッチからコードを書く場合、テストファーストで、テストを書いて、コードを書いて、またテストを書くというアプローチでいける。これは各メソッドに対するホワイトボックステストになる。これを繰り返しシステムを作る。
一方、レガシーコードの場合、統合テストを行う。ブラックボックステストを積み上げていく。トップダウンなアプローチである。これをわれわれは「レガシーコードには前から攻める」と呼ぶことにした。
テストを前から攻めるにはFITやseleniumのようなツールが使えるのではないか。
読書会の感想
予想以上にバラエティのある参加者に集っていただいた。そしてディスカッションが大変活発だった。積極的にいろいろな発言が飛び出した。自分のプロジェクトで困っていることや経験を話してくれた。
事例にはことかかない。様々なプロジェクトの事例を集めて分析したいと思った。成功するパターン、失敗するパターンなどを共有出来ればプログラマのコミュニティにとって素晴らしい貢献になる。
レガシーコード改善ガイドという本をきっかけに自分たちのプロジェクトを振り返り自分たちの仕事をより深く理解できればプロフェッショナルとしても一歩成長できるのではないかと思った。
読書会に参加してくれた人々に感謝したい。ありがとう。次回の読書会が楽しみだ。
当日のつぶやき
テストがないコードがレガシーコードだ。テストを書きながら新規開発するという状況ではなく、今ここにあるテストのないコードをどう保守するかという問題だ。 1:25 AM Apr 2nd webから http://twitter.com/hyoshiok/status/11432753940
動くコードがある。テストがない。従って、変更したら、どのような影響があるかわからない。祈るしかない。 1:26 AM Apr 2nd webから http://twitter.com/hyoshiok/status/11432826654
動いているコード。単体テストではなく、統合テストから作っていくしかない。受け入れテストとも言う。 1:27 AM Apr 2nd webから http://twitter.com/hyoshiok/status/11432862257
前から攻める。勉強になったなあ。今日のフレーズ。 QT @koduki: @hyoshiok レガシーコードには前から攻める! ですね 1:28 AM Apr 2nd P3:PeraPeraPrvから http://twitter.com/hyoshiok/status/11432914286
スクラッチからコードを書く場合は、Test Firstで、テストを書きつつ、コードを書き書きしてボトムアップに作っていく。レガシーコードの場合はトップダウンで書いていく。(受け入れテストツールなどを使いつつ) 1:30 AM Apr 2nd webから http://twitter.com/hyoshiok/status/11433006606
レガシーコード改善ガイド読書会が終わって感想戦に入ったのだが、今日はトラブル対応でお泊まりなんですよと言っていた @chocobokkle が不憫だ。テストを書いてレガシーコードを改善して、心の平静を取り戻そう。 1:37 AM Apr 2nd webから http://twitter.com/hyoshiok/status/11433338143
確かにそうだな。何かいいハッシュタグはないか。レガシーコード改善ガイド的な意味で。(yats全文検索に期待)QT @sato_ryu: @koduki @hyoshiok そういうタメになることをつぶやくときは何かしらハッシュタグつけていただけると助かります. 1:40 AM Apr 2nd P3:PeraPeraPrvから http://twitter.com/hyoshiok/status/11433484090
我々は実践的なレガシーコード改善パターンを開発できるんじゃないかと今日レガシーコード改善ガイド読書会で思った。だって、事例にはことかかないのだもの。3ヶ月もやったら、すげー。事例研究のネタの宝庫。 1:48 AM Apr 2nd webから http://twitter.com/hyoshiok/status/11433858589
*1:レガシーコード改善ガイドの読書会 未来のいつか/hyoshiokの日記 http://d.hatena.ne.jp/hyoshiok/20100324#p1