constのついた引数からconstを取る

constになっちまうのは困るんだろ?

こまるぅ!

というわけでconst_castですね
http://d.hatena.ne.jp/tmatsuu/20090717/1247835994

int fun(const char* bar){
    char *foo = const_cast<char*>(bar);
}

アジャイルという教典を文化が壊す

Twitterでもブツブツ言っていたが、最近アジャイルのトレーニングを受けた。海外で。
参加してたメンバーは主にEU圏内とポーランド・ロシアあたりの人々で講師はアメリカ人。参加者はプロダクトマネージャからジュニアプログラマまで全員みたいな感じ。CTOもいましたね…
欧州と米では開発文化はかなり違っていて、一口に言ってしまえば欧州はプロセスがプロセスとして成り立たないレベルで自由なことが多いようだ。あんまりプロセス事態に興味がないかんじ。もちろん納期とかもゆるゆるでアメリカ以上にリリース日が遅れまくる。会社の人達は基本的なスキルは高いけど属人性を好むところがあり、いつまでたっても自分の手からタスクを離さない。情報の共有文化があまりない。コミュニケーションは基本口頭で、Skypeとかは使うけどあんまり好きじゃないようだ(これはいろんな国から来ているということもあるが、欧州人は基本的にあまり文字のコミュニケーションが好きじゃない。母国語が英語でない場合が多いせいだと思う)。

トレーニングではScrumとKanbanがメインだった。僕はウォータフォールからなかなか抜け出せない古い開発プロセスで開発を回している日本の会社ではScrumはわりと馴染みが良いかなと思っているが、ごりごりにピュアScrumをやろうとすると失敗するだろうなと思ってた。基本的にScrumってスプリントの部分ばかりフォーカスされて、その前のとこは?っていったら「完璧な仕様があります」「完璧な要求があります」みたいな感じで紹介されるじゃないですか。あれって非現実的だよね、みたいな。


ちなみにこのトレーニングではそこら辺が曖昧な状態での開発方法を扱っており、結局スプリント開始前のフェーズではみんなで膝を突き合わせて大きめのバックログを作るところにフォーカスをおいていたので、アメリカではすでに死屍累々を乗り越えて今に至るのだろうと思われる。
バックログの作り方は基本的に昔の要求分析と同じかなぁと思うが、ひとまずミッションステートメントを作って最終的なゴールと優先順位をおおまかに明確化して、そこから必要なバックログ(いわゆる要求ですね)を出していくので、新規開発ではここをしっかりやっていればあまりブレることはなさそう。要求を作っていく作業ってのは誰でもできるわけではなく、こういう時にSysMLとかは役に立つのだが、欧米ではそういうツールがあんまりはやってないのでそのへんの説明はなし。後で書くけどツールを使わない彼らのやり方は今後やっぱり問題になっていくだろうなと僕は思っている。
バックログができたらあとは日本でよく紹介されているScrumのやり方ですね。基本的に残業をしない欧州ではデイリーミーティングをやれば工数と期日がだいたい予測できるが、残業の多いアメリカと日本じゃどうなんだろうなと僕は思ったりする。


そんで面白かったのがもう一個のカンバン。変な日本語の読み方がいっぱい出てくるからいちいち笑うのだが、要するに優先順位をつけて上からタスクを流れ作業で処理していきましょうという工場の業務フローをソフトウェア開発に持ち込んだものだ。利点は期日どおりにリリースできること(もし終わっていないタスクがある場合も気にせずリリースする)。
講師がこれはすごいって賞賛していたのだが、みなさんどう思いますかね。僕は全然すごくないと思います。ってか当たり前すぎるじゃんみたいな。そもそもこれはフローであってプロセスじゃないから、Scrumの問題点を解決してくれるすごい開発プロセスには成り得ない。そもそもすでに優先順位が定まっているタスクが存在していることが前提であって、そうじゃなかったらグダグダになりまくりだろうという。もちろんその辺はScrumのスプリントに入る前の作業できちんと議論したうえでスプリント中にKanbanを使用するということらしいのだが、それでもねぇ…っていう。そもそもタスクを分解して流れ作業にするより、ある意味のある単位での要求を一人または数人で最初から最後まで仕上げるほうが生産性も品質も高まるってのが最近の日本のはやり(?)じゃないですか。なに逆行してんだっていう。



ただこのトレーニングを一番後ろから見てたんだけど、Kanbanに対する食付きがすごいのね。細々とグループワークがあったのでその様子を見る限りも、なんかKanbanは新しい何か、みたいな受け止められ方をされている。
でもよく考えると、欧米の人たちって、というか特に欧のひとたちっていうのは「プロセスを守る」という部分が非常におろそかなので、逆にKanbanみたいな思想を導入したほうが失敗が少ないのかもしれないなと思ったりした。これはどういうことかっていうと、彼らはプロセスやコスト・期日・仕様・設計ありとあらゆるものをおろそかにするが、その代わり要求されているものをできるだけ最善な形で出そうとする努力は惜しまない。品質に関しては元がいい加減なのでそれなりなのだが(テスト仕様とかひどいもんだ)、それでも彼らなりに最善をつくすべきだとは思ってるみたい。
つまり「要求を満たすこと>品質>その他>>>>>>期日」で、要求を満たすための議論は非常に活発に行われる。上下関係は一応あるけれども、上下関係があるから言えないというのもないし(中傷はしませんけどね)、関係性がフラットなのも日本とは違う。


いくつか問題があるとすれば、基本的にみんなスキルが高いという前提で話が進むので(なのでプロセスが不要)、抽象的な話のまま議論が終わったりすることと、エビデンスを残す習慣がない。議事録は残さないし、ドキュメントは嫌いだし、あとこれは欧州限定なのだがアメリカ人よりものいいが抽象的なので、欧州系の文化で育っていないと彼らがなにに満足して、なにで合意に至ったのかよくわからないことがある。そしてそれゆえにその後ちょっと具体的な話になった時に意見の相違が出てくることがあるのだ。これでXMLでもなんでもいいんだけどツールを使って限りなく具体化するということをやっていればよいのだが、議論しちゃえば早いでしょという感じでつかわない。そしていつの間にか仕様が変わっている。
基本的なスキルが高ければそれでいいのだが、要求をきちんと実装に落とせないタイプがいたり、ジュニアデベロッパーのほうが多かったりすると問題が続出する結果となる。テストも穴だらけなので品質は微妙なものが出てくる。
これが欧州のやり方だ。


アメリカはもうちょっとプロセスがしっかりしてるんじゃないかなと思うのだが(飽きもせず開発プロセスを発明してるし)、議論が当たり前のように行われるフラットなチームではこういうことが起こりやすいのかもしれない。
逆に日本だと絶対にいる「プロセス通りじゃない!」って怒るタイプの人がプロセス通りに物事をすすめることに貢献するし、テストがクソ緻密すぎてコストがかさみまくったりするが、一応あんまりにも予想できないものが出てくることはないような感じがする。もちろん開発中に明らかにダメなものを指摘できずにクソ化していく経過を見ることはできるし、出てくるものが妙にしょぼかったりもするのだが、一応は出した要求通りのものが出てくるのが日本だ。期日通りかどうかは微妙なところだが、そもそもスケジュールがタイトすぎる開発現場のほうが多いと思うのでその辺は気にしなくていい気がする。というか日本のスケジュール感で見積もったものを欧州でやらせようとすると多分三倍くらい時間がかかるな。出てくるものはちょっと豪華になるかもしれないが、どうしようもないクソになってしまっている可能性もあるので注意が必要だ。小さな開発チームでビジョンを共有していれば逆に素晴らしい物が出てくる可能性もあるが、ちょっとチームが大きくなるととたんに欧州は仕事が回らなくなる。



ぼくは思うのだが、結局ダメなプロジェクトはどんなプロセスを使っても駄目だし、文化に合わないプロセスはどんなに安全なプロジェクトでもダメにする。そしてその国や文化圏に応じて気をつけるべきことは違っている。
欧州に足りないのは決まったことを守るという視点、日本に足りないのはダメなものをダメだと指摘して、建設的な議論をするチームの文化。逆にいえばそれさえあれば、たとえウォータフォールでも、というかプロセスさえなくたってものづくりはうまくいくんじゃないだろうか。

いまさらか!なんだけどWindowsバッチのはなし

それでもやっぱり時々は書かなきゃいけないWindowsバッチ。複雑なことはやらせるなという話なのかもしれないが、他のスクリプト実行環境をどうしても入れられない場合などは書くほかないのである。
あとちょいちょいかいてあるとおりにならないんだけどこれはなんでなのかな?

文字列を切り刻む

なん文字目から切ればいいということがわかっている場合はかんたんなのだが、そうでない場合はfor文まわしてdelimsでスプリットしていかねばならないらしい。しかもfor文の中のステートメントは先に展開されてしまうので、取得した文字列をこの配列に代入して…というのは案外難しいらしい。

set n=0
for /f "tokens=1-3 delims=_ " %%i in ('command') do @call :subset %%i %%j %%k

:subset
    set /a n+=1
    set data[%n%]=%1

けどこの場合echo data[n]とやりたい場合の書き方がよくわからなかったりするのである…素直にグローバルスコープの変数に入れて使いまわしたほうが良いのかもしれない(モダンなプログラムをかいている人には気持ち悪いと思うが)


ちなみにdelimsはデリミターのことだが、マニュアルには複数指定可、デフォルトはタブとスペースがきくとかいてある。じゃぁスペースとカンマで分けたい場合はどうするの?という場合は結構探したけど書いてなかった。やらないわけ無いと思うんだが…

正解は

for /f "tokens=* delims=, " ほにゃらら

スペース以外でも分割したい場合、スペースはパラメータの区切りとして認識されるので、そうされないために最後にdelimsを指定すること。ほいでもってdelims= ,とするとカンマがわけわかりませんょといわれてしまうので、delims=, である。エスケープ使えればいいんだけどね

ループに引数をわたす

:subset %%i %%j

このブロック名の後ろにいる人が引数。使うときは番号で呼ぶ。

:subset
    set result=%1
    set value=%2

ブロックというかメソッドと思えば良いらしい

ブロックを出てからの挙動

ふつうの文が最後の場合はいいのだが、for文の場合は一旦forの終端に戻り、そのままシーケンシャルに実行されるので、ブロックの最後に次に行きたいブロックの場所を指定しておかねばならない。つまり

for /f "token=1,2 delims=,; " %%a in (test.txt) do @call :nextLoop %%a %%b


:nextLoop
set a=%1
for /f "token=1,2 delims= " %%i in ("%a%") do @call :nextnextLoop %%i %%j

とした場合、最初にメインループ→nextLoop→もとのループにもどる→もう一回nextLoopとなるようだ。呼び出し元に戻ってもらいたいものだがそういえばプログラムとはそういうものだった。

単体テストのないソフトウェアまたは単体テストを書く習慣のないチームに単体テストを導入する方法

単体テストの習慣がない場所で必ず聞く言葉がある。

単体テストを書けば不具合はなくなるの?」

答えはNOである。

単体テストを書けば工数が減るの?」

厳密に言えば、答えはNOである。


単体テストは変更・追加開発した場合のデグレーションを減らすことはできるが、変更・追加箇所に不具合がないことを保証する方法論ではない。もちろんTDDで開発して、単体テストを予めかいておけば、いくらか不具合を減少させることはできるし、検証テストでは作るのが難しいテスト条件でのテストを行うこともできるので、まったく完全なNOではない。検証テストで発見される不具合が少なければ、不具合修正と再テストの時間を減らすことができるので、工数が減る場合もある。


でも、単体テスト銀の弾丸ではない。単体テストができるのは、テストケースで定義した状態・状況において不具合が発生すると教えることだけである。


ということを頭においた上で、テストを導入する方法を考えていかなければならない。



テストを導入するにあたって考えるべきは下記の5点。

  1. 単体テスト分の工数増加をどのようにプロジェクトマネージャやプロダクトマネージャに認めさせるか
  2. 自動化テストの環境をどうやって構築するか
  3. 自動化テストの範囲をどこまでと定めるべきか
  4. テストを書きたがらないエンジニアにどうやって書かせるか
  5. 膨大な既に書かれているテストをどうやってマネージメントするか

1. 単体テスト分の工数増加をどのようにプロジェクトマネージャやプロダクトマネージャに認めさせるか

単体テストに関係する記事や本などを読んでいると必ず出てくる問題だが、僕はこれが一番簡単な障害だと思っている。


まずひっそりと一人ではじめ、その結果が出たらマネージャに見せる。
これだけだ。


立ち上げたばかりの会社で比較すべきプロジェクトが全くない状態であればこの手法は効かないが、多分そういう会社は少ないだろう。僕の会社はプロジェクト規模大小様々あるので、必ずしも比較できるわけではないが、それでも一人でひっそりと始めることはいつからでもできる。一人で出来る範囲であれば、できることも限られている。大きいプロジェクトであれば、テストコードのない部分とある部分が必ずでき、そしてその部分での検証テスト結果が出るはずだ。
テストコードのある部分にだけきわめて検証テストの不具合発生率が低ければ成功である。
では失敗した場合は?

失敗の原因は幾つもあると思う。テストの経験がなかった、自分のスキルが低かった、テスト導入に時間をとられてコードの質が下がった(本当はそれはよくないので、そんなになるくらいなら寧ろテストは書かないほうが良い)などなどあるが、それはいくらでもあとから挽回できる。一回目ダメだったら二回目を狙おう。せっかく書いたコードはレポジトリにサブミットしておけばよい。本番では使われないコードなので多少の質は悪くても問題ない。大事なのは続けることである。


成功した場合で注意すべきは、現状の工数で問題なかったから、という理由で次回以降でも単体テスト分の工数を上乗せすることを許してもらえない場合だ。これはかなり根深い問題で、もし検証テストが思ったよりも早く終わったなら、その分を実装の工数にいれてくれというしかない場合もある。最悪の場合、検証が早く終わるならその分スケジュールも詰められるだろうと猶予分を削られる場合だ。これはもう本当に根強くこんこんと説得をし続けるほかない。上司が物分かりがよいか、新しいもの好き、もしくは周りに流されるタイプであることを願うほかない。そうでないのなら、仕方ないのでひっそりと頑張るしかないだろう。いずれにせよ、冒頭で書いたとおり単体テストは工数を短縮するための銀の弾丸ではないので、必要なときに必要なところだけを書くという運用方法に自分で切り替えていくことも必要かもしれない。

2. 自動化テストの環境をどうやって構築するか

これは自分の環境に合わせてやってください、なんだが、一応。
最低限必要なのは、

たまに仕様書・設計書を見ずに間違ったテスト対象コードからテストを書いて、不具合がつかまえられないのでテストは意味が無いという人がいるが、テストを書くときに仕様書・設計書を確認して意識的にテストNGを出して欲しいと思う。

3. 自動化テストの範囲をどこまでと定めるべきか

プロジェクトによるので一概には言えないが、結合テストシステムテストの範囲を定めるように単体テストの範囲も定めるべきである。自動化テストは(あれば)UIテストも含むので、それにはシステムテストの範囲も含まれることがある。どこからは手動のテストでマネジメントをするのかきっちり定めないと、自動化テストの範囲も決まらない。

本来は単体テスト結合テストの前までの範囲を受け持つので、結合先はモックなどをつくって擬似データを発生させる必要がある。状態の変化が必要な場合はモックがかなり高度・大規模になる場合もあり、テストが非常に煩雑になるので、そこはシステムテストの責任を委譲するのも手である。個人的にはモックを作成しなければならないテストは、結合テストの範囲とするのが一番シンプルかなと思う(が、だからと言ってしないわけには行かないこともたくさんある)

範囲として考えられるのは小さい方から順に下記の通り

  1. モックを使用しない範囲で正常系だけを通す(プロジェクトが小さい・不具合発生時のリスクが小さい場合)
  2. モックを使用しない範囲でデシジョンカバレッジを100%にする
  3. モックを使用しない範囲でコンディションカバレッジを100%にする
  4. モックを使用して正常系を通す(結合時の不具合発生が予想され、かつ不具合の特定が難しいと判断出来る場合もしくは結合時の不具合発生をできるだけ抑えたい場合)
  5. モックを使用してデシジョンカバレッジを100%にする(状態の変化をデータで持っておく必要があるため、テストデータが爆発的に増える)
  6. モックを使用してコンディションカバレッジを100%にする
  7. モックを使用したテストが出来るように全体的にコードの設計を見直し、ステートメントカバレッジを100%にする(非現実的)

個人的には2までにするか、4までにするか5までにするか、それ以上を頑張るかの判断を最初ですべきだと思う。
僕の扱うプロジェクトはソフトウェアだけで完結するものではなく、自分では手を加えることのできない他社製品を制御するのがほぼ必須である。この場合は不具合発生時のリスクを見積もった後、2か4かの選択をする。大抵の場合モックが必要な箇所は4, 必要ない場所は3で作る事が多いかな。枯れたシステムを制御する場合は2でもほぼ問題がない。制御するシステムが増えてくると4または5でないと、問題切り分けができなくなる。
また、くわえて上位の自分では手を加えられない大規模なシステムと結合しなければならない場合もある。この場合は結合テスト時の不具合発生の問題切り分けが非常に煩雑でリスクが高いので、4または5を目標にすることが多い。余力がある場合は6。一部システムテストで責務を負うべきところも単体テスト・自動化テストでやってしまうこともある。ここらへんは品質みつつという感じかなぁ…

4. テストを書きたがらないエンジニアにどうやって書かせるか

テストを書くのは意味が無いという人は必ずいるし、その信念はひとつの真理ではある。社会人として尊重した上で、それでも書いて欲しいとお願いするときにどうするかということを考えていかなければならないこともある。社会人なので。

テストを導入する手順は次のようにするとわりとスムーズだ。

  1. まず一人である程度枠組みを作る(レポジトリへのサブミットと同時に単体テスト走らせ、その結果を通知または閲覧できるようにする)
  2. コードレビュー時にかならず単体テストコードもレビュー対象に含める
  3. 新しいもの好きな人を巻き込み、複数人でプロジェクトにテストを含める作業をする
  4. いろいろ問題が出てくると思うので、テストコードを改善する
  5. テストをかきたがらないエンジニアのコードレビュー時にテストは?と必ず聞く
  6. 検証テストの不具合発生時、不具合発生理由を聞いた際にまずテストを書いてそれが本当かどうかエビデンスを出してくれと頼み、テストの書き方を教える。書けるまで徹底的に付き合う。その場合の文句とかはちゃんときき、一緒に解消方法を考える
  7. 必要ならテストをフレームワーク化する
  8. 不要な部分までテストが必要!という人がいたら、こういう目的で今回は単体テストを行っているので、あればよいですが無理しなくてよいですと伝える
  9. プロジェクトの変更・追加の前に単体テストの責務の範囲についてチームで話し合う。また検証テストと単体テスト・自動化テストの被る部分についてコンセンサスをとっておく
  10. 2, 4, 5, 6, 8, 9を繰り返す

スキルのないエンジニア(俺のことだ)の場合要求分析の練習にもなるので、6とかはすごく大事だとおもう。9ができるようになればほぼ定着していると思うので、そのまま続ける。新しいもの好きな人はあきるのも早い場合が多いので、ある程度テストコードが増えてきたら、テストを書きたがらないエンジニアを巻き込むことを考え始めたほうが良い。

5. 膨大な既に書かれているテストをどうやってマネジメントするか

TDDなどでは殆ど触れられないが、テストコードのマネジメントは非常に難しい。プロジェクトの規模が増えればもちろんテストコードは増えるし、結合先の状態が変わるテストをしようと思ったら、テストデータも増える。

テストコードにはコメントを書く

そもそもテストコードは読みにくい。僕はコメントはできるだけ書かない派(どうしても必要な箇所にだけ書くべきだが、それ以外は書くべきではない派)だが、テストコードにはコメントを入れておいたほうが良いかもしれない。テストケース名も、どれだけ長くなっても見るだけで何のテストかわかるほうが良い。テスト失敗したっていうメールが飛んできたけど何のテストでコケたのかパッと見分からないというのは結構不幸だ。

また、テストコードはできるだけテンプレート化しておいたほうが良い。これは複数人で同じテストコードを書く場合・スキルの低いエンジニアがテストを書く場合もあるためだ。むしろテストコードはスキルの低いエンジニアこそ書くべきなので(その人が書く場所は不具合が発生しやすくなるため)、テンプレートとガイドラインは必須だと思っておいたほうが良いのかもしれない。


テストコードをオブジェクト指向にする

状態を持つようにしたり、特定のテスト条件のデータを返す必要がある場合、データはデータ用コンポーネントに全て持っておいたほうが良い(DBのことではない)。またテスト条件を作成するコンポーネント、テストを実行するコンポーネント、モック作成のコンポーネントなど、オブジェクト指向にすればするほど、複数人での並行開発が楽になる。もちろん単体テストの範囲が狭い場合はそこまで頑張る必要はない。

寧ろテストは頑張らないくらいの勢いで書いていないと続かない

テストコードをレガシー化させない

テストが膨大になるに連れ、必ずレガシー化したテストがでてくる。だが単体テストの場合、そのテストを飛ばして実行するということができてしまう。これではテストの意味がない。
もしレガシー化している・なんのためにあるのかよくわからないテストが出てきたら、そのテストを実行し、なにをテストしているのかを確かめたあと、書き換えよう。時には捨ててしまうこともありかもしれない。
テストコードもソースコードの一部なのだ。テストコードだからといってレガシー化させてよいわけではない。死んだコードは不要なコードだ。おなじステートメントを通るテストケースが2つあり、そのふたつが別である意味がないのであれば、片方は削除すべきだ。別である意味があるのなら、それはコメントに書くべきである(大抵の場合は意味ないが)。

テストコードはできるかぎり低コストで高パフォーマンスを実現したほうがよい。必ずしも上司やチーム内に認められるわけではないテストコードだからこそ、それは常に頭の片隅において置かなければならない。しかし開発時のどこからともなく来る不安や、不具合発生時のやけくそなテストコードは別段非難されるべきものではない。あとから適切に処理してやればよいのである。そんな時間はないだろうか? だが、処理をするのは、テストコードを書いた人でなくてもいい。時間がないなら、作る。作れないのなら、他の人に委譲する。一人で開発しているのでなければ、たぶんきっとできるはずだ。


http://www.hyuki.com/yukiwiki/wiki.cgi?FlawedTheoryBehindUnitTesting

テストを書く

http://t-wada.hatenablog.jp/entry/debugging-tests
和田さーん!

テスト駆動開発(TDD : Test Driven Development)は、プログラマが自分の不安を克服し、自分が書くコードに自信を持ちながら一歩一歩進んでいくための手法です。不具合の発生は、端的に言えばこれまでの「自信」を揺らがせる事態です。テスト駆動開発者は不具合にどう立ち向かうのでしょうか?

やはりテストを書いて立ち向かってゆくのです。


チーム内にテストを書く習慣を持ち込んで三年、最初のうちは工数が増えるだけだ(あるある)、テストを書いても不具合がでるじゃないか(あるある)、システムテストでカバーすればいい(あるある)などという抵抗があり、それでも僕は淡々と雨の日も、晴れの日も、雪の日も、朝も夜も深夜も、終電後のオフィスでも、GW中の人気のないオフィスでも、自動テストをかき、そのプラクティスの勉強会をし、Jenkinsを導入し、定着を図ってきた。


三年で、ずいぶんと書いてもらえるようになったと思う。


エンジニアは新しいものが好きだ。新しいものがあれば古いものはすぐに捨てて、そちらに飛びつきたがる。自動テストで毎日定期的にビルドを走らせているのに、静的解析ツールが出たと聞けばそれを試したがり、一度使えることがわかればそのまま忘れ去ってしまう。その横で、僕は単体テストを書き、やがてそれだけでは足りないので社内用単体テストフレームワークを作り、その使い方のドキュメントを書き、GUI自動テストを導入し、ひとまず上司を納得させるためにそれで長期試験と負荷試験を実施し、そしてそれをCIツールに組み込んでいった。今月の半ばにようやくその長い道のりは終わった。


それでも、不具合は発生する。
そのたびに誰かが口にする。ユニットテストだけではダメなんじゃないか。自動テストではだめなんじゃないか。
そうではないのだ。不具合が発生したのは、テストが書かれていない部分なのだ。テストは常に完全ではなく、また完全になることはない。しかし書き続けていけばやがて不具合が検出される。不具合が検出された場所は、もう二度と不具合の出ない場所だ。なぜなら、不具合が発見されれば、テストコードが書かれるからだ。
もちろん、そのコストとリターンが見合わないということは大いにあるだろう。どこまでを自動化テストの責務とするか、それはその時状況によって決めていくしかない。ステートメントカバレッジ100%にすることが常に正義なわけではない。プロダクトアウトの製品だけを作っているわけではないのだから、ここでは「最低限正常系のテストだけでも」という方針でテストを書くので構わない。1%でも、あるいはたった0.1%の進歩でしかないとしても、ないよりはマシである。それがテストを書くということなのだと思う。

http://anond.hatelabo.jp/20130325165709

去年一年振り回されたプロジェクト(まだ振り回されている)がこんなかんじだな。

  • 工期が決まっていない→なんとなく決まってはいるが、しかし長くなることはなく短くなる一方である
  • 企画書の完成日が決まっていない→うちが企画元ではないのであれだが、なんとリリースされても企画書が出て来なかった
  • 競合調査より先に自分たちだけで企画を考える→一応やってはいたぽい
  • ターゲット層の調査より先に自分たちだけで企画を考える→ターゲット層も使う機器についても調査せずに企画が先行
  • 議事録を取らない→まじでなかった
  • 議事録をとり始めても訂正や意見がない→誰も議事録を見ていない
  • 企画書のひな形を作っても曖昧な表現で駄目出しだけする→ひな形すら出て来なかったので、うちで勝手に書いた
  • 思いつくままに会議を進める→これだ
  • 業務進行の定石を無視して効率の悪い方法を採用する→これだ
  • リーダーがウェブに疎い→まさにこれである
  • メンバーに技術者がいない→技術者は一応いたが展開中だった
  • キラーコンテンツの準備より先に課金方法を取り上げる→これだwww
  • ブレスト中にダメ出しする→企画が上がってこないので勝手に書いたストーリーで(それもどうなんだ)要求分析したが確かに駄目出しが多かった(かと言って代案もない)
  • 事あるごとにその分野に疎いことをほのめかす→一部の人々が。いまだに言ってる
  • 素人だからできると豪語する→それはなかったな
  • 素人をターゲットにする→まぁこの分野玄人が少ないので…
  • 素人であるの指摘に対して口論で勝利しようとする→そういう人がいました
  • ユーザとしての疑似体験を却下→却下したわけではないが取り入れる時間がなかった
  • 問題の切り分けをしない→しないひとがいました。それより優先順位が付けられないことが一番の問題だと思う
  • 先人が利用してきたツールや手法を否定する→ウォーターフォール全否定、とかね 否定するなら新たにきっちり決めるかとおもいきやなし崩しで始まったりとか。それを指摘すると逆ギレするとか。別にウォーターフォール反対派ではないしかと言ってアジャイル慎重派でもないんだが、こう行こうという指針がないまま始めるのはどうなのかと思う。だが、だいたいアンチ・ウォーターフォール派はそれをきかないんだよなぁ。なにか決めたらアジャイルじゃなくなると恐れてるみたいで。
  • 一度選定したテーマを途中で覆す→企画が途中でひっくり返ったりとかね
  • 高すぎる目標を設定する→無理だと言っているのに即販ツール作ってとかね
  • 高すぎる目標に対して全部企画に盛り込もうとする→無理だと言ってるのにね。でも企画が一番強いので逆らえない→なし崩し→現場が阿鼻叫喚というのが何度も繰り返された
  • 小さくリリースするの発想を理解しない→最初に理解してもらえなかったが、最近は理解してくれている
  • 一ヶ月以内に市場調査及びユーザの調査をしない→それは一応してたかな
  • 一ヶ月経過してもサービスの制作に入る見込みが立たない→要求は決まってなかったけど最低限絶対に必要なところは開始するなどした
  • 一ヶ月経過してもチームは素人のまま→一部はちゃんと玄人化したがいつまでも素人、もいる
  • 素人の作るサービルは成功の確率が低いことを理解しない→それはなかったな

ギリギリ許容される範囲内での失敗+すこしずつ啓蒙して良い方向へ向かおうとしている+どんな時でも最低限テストは書き、できるテストは自動化し、ツールを導入して繰り返しの作業は消していくという地道な作業で現在も何とか動いているし、これからは改善するだろうと思っている。ぼくはただ淡々と自動化して悪いところはやめて煩雑にならないようにきをつけて(おれがめんどい)、だれにでも使えるようにしてる(呼び出されるのめんどい)けれども、やはりアンチ・ウォーターフォール派の抵抗が根強くてそれをひっくり返される。アンチ・ウォータフォールだからといってアジャイルなのかというとそういうこともないのが困る。ただとにかく書類を書きたくない、責任を取りたくないという信念のもとに行動する人とどうやってチームメンバとして付き合っていけばよいのか、これからも考えなければならない。

今春からようやく会社でレガシーコード改善ガイドを使って勉強会をするようになった(やっと…!)のでちょっと古いエントリを再掲しておく。

「テストを書けばよいのでは?」の一言がためらわれる時もある

整然とし、秩序だった美しい世界を構築する人がいた。設計もコードも、リファクタリングする指針でさえも一貫したポリシーを持って行われていることが、経験のない僕にもよくわかった。たしかに彼が手を入れた部分は美しかった。でもその周りには広大な廃墟とスラムが存在していたのだ。古い、改修しながら使っているライブラリはすでに原型をとどめていない。彼はすこしずつ手直しをしてすこしずつ改善していたけれども、ライブラリには手をつける暇もなく去っていった。広大なスラムだけが残った*1

しばらくそこを離れていた僕がもどってきたとき、最初の仕事はコードレビューだった。かの整然とした世界を構築できる人の代わりにしては僕はあまりにも物事を知らなさすぎるが、それでも何百分の一かの貢献は期待されている。
似たようなコードをそのままコピーして新しいメソッドにしている部分を目にして僕は、同じ処理を抽出してメソッド化したらよいのではないかというレビューをしたが、担当者はスラム化したライブラリがうまく動かなくなるのを怖がってその指摘を受け入れなかった。ライブラリにはもちろんテストがない。クラス関係も複雑で、設計書も仕様書もない。そのコードは関連会社から譲り受けたものだから構築した人もいない。そして、設計ができずスキルの低い僕は自信がなかった。それでもやるべきだとは言えなかった。

レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)

「レガシーコード改善ガイド」を読み始めた。

序文からしてすでに涙目である。
自分のコードがひどいのは前から分かっているし、なにもひどさに涙しているわけではない。

僕はコードが書けない。知識はあっても、簡単なプログラミングができても、僕はコードを書けないと思っている。自信がないのだ。自分が作るものが本当に意図したとおりに動くのか、わからない。作れば作るほど発散し、自分でも構造がつかめなくなっていくことを知っているから怖いのだ。誰かが作ったコードを正確に読み取り、その動作がなんの機能に当たるのか、わかっているつもりになっている気がする。そして僕は変更しない方向を選んでしまう。「できるだけ変更せずにとっておいて新たに書き加える」。その繰り返しが膨大な二度と使わないメソッドとクラスのゴミの山を積み上げることになると知っているのに。

きれいなコードは有益ですが、それだけでは不十分です。テストなしに大規模な変更をしようとすると、チームは危険な賭けに出ることになります。転落防止の網を張らずに空中ブランコをするようなものです。とてつもないスキルが必要であり、ステップごとに何が起きる可能性があるかをきちんと把握しておかなければなりません。
(中略)
ここまで、テストについてかなりたくさん書いてきましたが、本書はテストについて解説している本ではありません。どんなコードでも自信を持って変更できるようにするための本です。本分の各章では、コードを理解し、テストで保護し、リファクタリングし、機能を追加するための手法について説明しています。

はじめ、単体テストを書くのは嫌いだった。面倒でしょうがなかった。でもモックオブジェクトに出会って以来、僕は単体テスト職人になりたいとすら思うようになっている。モックオブジェクトで依存関係を切り、仕様書をにらみ、設計書と照らし合わせながら一つずつテストケースを組み立て、そのテストはどうやってすべきか頭をひねる作業は単純に楽しいというのもある。でもそれよりも効率よくそのメソッドが、クラスが、何を意図しているのか、そしてどういう振る舞いをするのか頭の中に自信を持って組み立てていくことができるということの方が大きい。自分の行った変更が問題なかった、ほかの部分を壊していないと自信を持って言えることの方が大きい。
向かう対象が大きく複雑で汚いほどその作業は楽しいことを僕は知っている。そうやって少しずつ設計とは何かを理解できるようになる。仕様として出てくる機能の背景が気になるようになる。

きっとこれから10年、幸運にも開発者でいられたとしてもきっと僕は整然として矛盾も無駄もない世界を構築することはできないだろう。でも、その世界を理解するための道具はある。始めから終りまで迷うことなく出力することはできなかったとしても、地道にゆっくりと這い寄ることはできる。大きな手術を施すことになったとしても、正してゆくことはできる。たとえスキルが高くなくても、美しい世界を保守することはできるのだ。

*1:廃墟は破棄された。