「レガシーコードからの脱却 - ソフトウェアの寿命を延ばし価値を高める9つのプラクティス」を読んで

書籍

レガシーコードからの脱却 ―ソフトウェアの寿命を延ばし価値を高める9つのプラクティス

レガシーコードからの脱却 ―ソフトウェアの寿命を延ばし価値を高める9つのプラクティス

TL;DR

  • 新規開発や保守など関係なくソフトウェア開発で価値を高めるプラクティスが網羅されており、索引として使うのに最適。
  • 開発フローを一通り体験したことがある人であれば、この本を1冊読んだだけでも自組織で足りていなことに気づくことができる本。
  • (本の分量的に仕方がないが、)気になったプラクティスの深堀りは各自で行う必要がある。

この本を選んだ理由

  • 業務的にレガシーコードに触れる機会が多く今後仕事をする上での気づきを得られると思った為。
  • 名著になる!というコメントを見かけ是非読んでみたいと思った為。

印象に残った点

※ 下記の大括弧は該当する章番号。

過度なコメントは良いコードを書いていない言い訳になる[1.5]

- 過度なコメントはノイズになる
- WhatではなくWhyをコメントに残すべき
- 意図はコードで表現する
  - 追いかけやすい名前や一貫したメタファーを使う
  • 尊敬する先輩が「コードに魂が入っとらん。見れば分かる。」と言っていたのを思い出した。先輩の言葉は具体的ではない為詳しく聞いたところ、言いたいことは上記の内容だった。別の名著(リーダブルコード)を一緒に読んだとき、「オレこの本書いたっけ?」というだけはある。
  • ソフトウェアを保守する上でコードだけではなく、コメントも保守することになる。究極はコメントなくてもコードだけで意図が伝わることだろう。

ソフトウェア開発は未知の領域で行われる[1.7]

- 扱うプロジェクトが難しければ難しいほど、その時々の進捗を正確に定量化するのは難しい。
- マネージャーが開発者に作業終了の見込みを聞くと、開発者は分からないと答えることが殆ど。
  - 責任逃れでも権力闘争をしているわけではなく、単に分からないだけ。
- 開発者には3つの状態(終わった/始めていない/殆ど終わった)がある。
  • これはあるあるネタだなぁ。進捗がだいたい80%から進まない問題と同じ。対策としてはタスク粒度を細かくするくらいしか思いつかないなぁ。

ソフトウェア開発を成功させることは難しい[2]

- 成功の定義を「初期仕様の納期/予算/機能がすべて満たされていること」にすると、見積もり能力の計測にしかならない。
  - リリース後に顧客の要望を叶えているか、バクがでていないか、改修が簡単にいくかなどの計測をしていない。
- 失敗するプロジェクトも多く、この業界が課題を解決する道のりは遠い。
- アメリカ合衆国だけでも毎年100億ドル単位の損失を出している。
  • 現部署は見積もり能力の計測に力を入れすぎている気がする。その計測も重要であるが、顧客が求める要望を叶えているかどうかを正しく評価しなければ意味がない。
  • ソフトウェア開発を成功させることが難しいということを理解していない人が多い。まずはその現状を経営層が理解しないと話が進まないと感じる。

アジャイルキャズムを超えたとは言い難い[3.5]

- キャズムを超えるとは「アーリーアダプター」から「アーリーマジョリティー」の間を超え行き渡ること
  - アーリーアダプター:イノベーターと言われる新しい技術に飛びつく人の成功に感化され、その次に飛びつく人
  - アーリーマジョリティー:多くの不具合が解消され使いやすくなった頃に使い出す人
  • 著者はまだキャズムを超えていない認識なのか…。本内にも書かれているが確かに技術プラクティスの採用に一貫性がない。標準化されなければまだまだキャズムを超えたとは言えないのか。

ラクティスに従うのではなく原則に従うこと[4]

- 手術に使う器具を「消毒する」というプラクティスに従うのではなく、手術に使う器具を消毒する「理由」という原則に従うことには差がある。
  • どういった理由で実施しているのか?という原則を理解していないと何の意味もないことは日々感じている。
  • 良い例えなので、なぜ原則に従うのか説明が必要な時にパクりたい!

最高な開発者が1番キレイ好きな開発者[4.1]

- 速いプログラマー≠雑なプログラマー
- コードの品質を高く保っている「にも関わらず」速いのでなく、コードの品質を高く保っていた「からこそ」速い
  - インスタンス変数をアルファベット順や意味のある別順にする
  - 使われなくなったコードを即座に削除する
  • 品質を高く保っているからこそ速い。うーん、素晴らしい。これは方針を選択する時に考えていきたい。

ソフトウェア開発も守破離[4.1]

- 守:型である形式知(プラクティス)の取得
  -  ソフトウェア開発のルールや禁忌事項を学ぶ
- 破:理論の理解&実践
  - プラクティスの背後にある原則としての理論を学ぶ
- 離:最高の熟練
  - プラクティスと理論の境界が曖昧な状態
  - 継続的な学習によってのみこの領域に到達できる
    - 本当に熟練するには10000時間必要
  • 10000時間必要なのか。1日8時間で1250日。月に20日働いて62.5ヶ月。一人前なるのに5年か…実際はそこで終わりではないのがソフトウェア開発。
  • 日々少しでも学び続けることが大事だなぁ。「型を知ってるからこそ型破り」という言葉があるので、常に学ぶ姿勢は持ち続けたい。

ラクティス1:やり方より先に目的/理由/誰のためかを伝える[5]

- ソフトウェア開発をする上でプロダクトオーナーや顧客から知りたいことは下記になる
  - 「何」が欲しいのか?
  - 「なぜ」欲しいのか?
  - 「誰」のためのものなのか?
    - 「どうやって」やるのかは不要。それを考えるのがソフトウェア開発者。
- 実装の「標準化」は不要。振る舞いの定義や何のテストを書くべきかは「標準化」したい。
  - 解決方法/実装方法は千差万別。そのやり方を選択したトレードオフを理解していれば問題ない。
  - 振る舞いの定義やどんなテストを書くかが決まれば多くのことが共通のものとなる。
- プロダクトオーナーが大事なことの1つは重要ではないことを葬り去ること。
- ストーリーで目的/理由/誰のためかを語ると良い
  - 所謂ユーザストーリー。
- 受け入れテストに明確な基準を設定する
  - ハッピーパス(正常系)だけでなく、代替処理やアンハッピーパスエラー条件もテストに含める。
- 受け入れ基準を自動化する
  - テストの自動化をすることで抽象が具体化される
- 機能の目的、理由、誰のためかを定義することで開発にあてる時間の?を取り戻せる
  - 実装を詳細に説明した要求をドキュメント化することをやめれば可能
  • 「どうやって」作るのかまで決められたら不満になるだろうなぁ。トレードオフを踏まえた開発者としての考えを判断するところはPOと話がしたい。
  • 受け入れ基準の自動化は保守コストの面だけではなく、抽象的な受け入れ基準を具体化するというメリットもあるのか!より具体化し一般化できてないと自動化できないもんなぁー。
  • POの7つの戦略の中に「質問にすばやく答える」というものがあり、現在のプロジェクトでもボトルネックになってると感じている。複数の仕事を抱えるPOなのでこのままで良いのか不安を感じる。
  • POと開発チームとのあいだで協調できるようになればより楽しく開発者として働けるのだろう。今組織が揺れているが、良いように再構築され、協調して働ける環境になればいいなぁ。

ラクティス2:小さなバッチで作る[6]

- 1年の開発サイクルで開発者が大きなウソをつくのではなく、2週間のイテレーションの中で小さなウソをつく。
- ソフトウェア開発と製造は違う。人の数を倍にしたからといって生産性は倍にならない。
  - 製造とは異なり人同士のやりとりが多いことに起因する。
- リリースサイクルを短くすればプロセス効率が上がる
  - プロセス効率:1つのタスクに費やされる割合
- メトリクスは顧客にとって価値のあるもので計測すべき
  - コード行数は顧客に何の価値ももたらさない
- タスクを99%完成まで持っていくのでは不十分
  - このままではリスクが未知のまま。開発したらシステムに完全に統合して初めてリスクが減る。
- 1日の稼働時間を100%としたら理想時間(本当の価値を生み出す時間)は50%前後が理想的
- ソフトウェア開発を計測する戦略
  - 価値実現までの時間を計測する。全体最適化に寄与しないような部分最適化は無意味。
  - 機能毎の顧客価値を計測する。バックログを作ることで価値の高い項目により多くの時間を割けるようにする。
  • 小さなウソをつけばリスクを小さくでき、後になってビックリすることはないということか。
  • プロセス効率を上げると待ち時間が減る、タスク切り替えが減る、フィードバックが増えるというメリットがある。今は2ヶ月×3の6ヶ月で一度のリリースとしているが、これを更に半分にできないか上長と話をしてみる。
  • 今のプロジェクトでは生産性をコード行数で測定している。顧客にとっては意味がないことは分かっているが組織目標になっているから頑張っている。オカシイよね…。組織の仕組みから変えないと変わらない問題でこれを解決するのは骨が折れるだろう。
  • リスクの観点からも完全な統合を急ぐ必要があるのか。今のサイクル半分にしたいなぁ。
  • 理想時間か。近いことを自分のチームは計測している。このアプローチは間違っていなかった。ただ、価値実現までの時間や機能毎の価値を測れてはいない。まずは自分達でできそうな価値実現までの時間を測っていきたい。

ラクティス3:継続的に統合する[7]

- 痛みに対処する方法は回避するか順応するかの2つ
- ブランチを避ける
  - コードの統合によってリスクは0になる。リリースする際にブランチを統合するよりも、フィーチャーフラグを使用したほうが良い。
  • 回避、回避で後手後手に回ってることは確かにあるなぁ。少しづつ順応できるように小出しに対応していきたいものだ。
  • リスク低減のためにフィーチャフラグを使うのか。なるほど。引き出しが増えた。
  • 以前読んだ継続的デリバリーに同じことが書いてある。少し昔の本ではあるが継続的デリバリーの本質は変わっていないということを認識できた。

ラクティス4:協力しあう[8]

- ストロングスタイルペアリング by ルウェリン・ファルコ
  - 「あなたのアイデアをコンピューターに伝えるまでのあいだに、必ず他人の手を経由しなければならない。」
  - 説明することで脳の活動領域が変わり、想像以上に詳細を明確に理解できる
- バディプログラミング
  - ペアリングに恐怖や抵抗がある時に有効
  - ほとんどの仕事は一人でやるが、例えば1日の最後の1時間だけバディとコードレビューするやり方。
- スパイク
  - 未知の課題解決のために複数の開発者が1つのタスクに取り組むやり方
  - 使う時間を限定して行う
  - 短期的な問題解決のための委員会を作るイメージ
  • ストロングスタイル良い。頭の使い方が変わるというのはその通りだと思う。名前もステキ。黒パンが似合いそう。
  • バディプログラミングか。これなら導入しやすいだろうなぁ。ちょっとやってみよう。
  • 2人チームで動いているが解決し辛い問題は時間を決めて二人で話しながら解決するスタイルを取っていた。コレ、スパイクっていうんだ。

ラクティス5:「CLEAN」コードを作る[9]

- 良いコードは「CLEAN」なコード。CLEANは下記5つのワードの頭文字を取っている。
  - Cohesive(凝集性)
    - クラスやメソッドが単一の責任を持つべき
  - Loosely Coupled(疎結合)
    - 利用しているコードに対し間接的にしか依存しない
  - Encapsulated(カプセル化)
    - インターフェイス(やろうとしていること)を実装(どうやってやるか)と切り離す
  - Assertive(断定的)
    - オブジェクトは独立していて、自身の責任を持ち、自分で管理する
  - Nonredundant(非冗長)
    - 意図の繰り返しを避けるべき
- 保守しやすいコードを書くには「コードの読み書き」を繰り返し練習すると良い
  • 「Clean Code」本を読むと更に深く知れるのか。読みたい本がドンドン増えていく~。
  • 本の中で「性能や別の制約によってコード品質をトレードオフにしてしまうケースが多い。多くの場会、正しい設計を選択すればコード品質が上がる。」とあった。確かにそういうケースに当たったことがある。元々の設計の大事さを再認識。
  • コードの読み書きをしないとCLEANコードのプラクティスが習慣化しないのか。圧倒的に足りていないので読み書きする時間を取りたい。

ラクティス6:まずテストを書く[10]

- テストは色々な種類がある
  - 受け入れテスト=顧客テスト
  - ユニットテスト=開発者によるテスト
  - それ以外のテスト=QAテスト
- 自動化すれば人間の介入を排除でき、外部からの依存性が下がる
  - 人間の介入が必要とすることもある(例:GPS)
- 「ユニットテスト」の「ユニット」が表現するのは振る舞い。
  - ここで言う「ユニット」はメソッド、クラス、モジュール、関数などではないため、新しいメソッドやクラスを追加してもテストを追加する必要はない。
  • 振る舞いを表すためのテストを作ることが大事ということか。確かにクラスやメソッドを増やすたびにテストコードメンテしてたら辛いのは分かる。

ラクティス7:テストでふるまいを明示する[11]

- 小さな単位でレッド/グリーン/リファクタを何度も繰り返す。
  - レッドから始めることでテストがちゃんと失敗することを担保できる。
- テストは一意であるべき。
  - 1つのテストで失敗する理由はシステム内で1つだけ。
  - これを意識するだけで1つのことを扱う焦点を絞ったコードになる。
- バグをテストの不足とみなす
  - バグの修正をする前に失敗するテストを書き、修正によりテストをパスすることを確認する。
- 欠陥をもとにプロセスを修正する
  - そもそもなぜバグが発生したのか?というプロセスを確認する。
  • テストで失敗する理由が一意になることを意識するだけでもコードが良くなるなぁ。
  • 二度と同じバグを出さない為にバグを修正する前にテストを書くのか。テストファーストだとこういうアプローチも出来るのか。
  • バグが発生したのでテストケースを足して完了ではなく、バグが起きたプロセスのチェックまで行う事に言及していて好感が持てる。失敗からの学びは大事だもんなぁ。

ラクティス8:設計は最後に行う[12]

- コーディングとクリーニングは別タスクとして実行すると良い。
  - コーディングは解決方法を探す作業。クリーニングは動くコードを保守可能にする作業。別々に考え集中すべき。
- 大規模なリファクタリングは通常数カ月ごとに返済作業を行う。
- コードは書かれる回数の10倍読まれる。
- 必要なら設計を変えることをためらわないこと。
- 品質は保証できない。品質は作り出すもの。
  - 品質を検証するのではなく、作り込むことに集中する。
  • テスト駆動開発では片付けられないような大規模な負債のリファクタリングは数カ月ごとに行うのが一般的なのか!!今のプロジェクトは負債を返えすことが一切できてない。
  • 10倍も読まれるのか。読むことを意識したコードでないといけない理由がよく分かった。
  • 設計に思い入れを持つ気持ちは良く分かる。創発設計をする上で一番重要なスキルと書いてあり、とても難しい部分だとも感じる。ココが一流との分かれ目な気がする。
  • 「品質は保証できない。品質は作り出すもの。」この考えはなかった。作り込みによって品質が出来上がるのか。

ラクティス9:レガシーコードをリファクタリングする[13]

- リファクタリングの対象は顧客が使っていて変更の可能性がある部分
- リファクタリングはコードを書くときにしてはいけないこと/代わりにすべきことを学ぶ上での最速の方法の1つ
  • 当たり前のことだが、リファクタリングにもコストがかかる。事業のメリットがなければ無理にリファクタリングをする必要もない。開発者だけの目線でモノを語らないようにしたい。
  • リファクタリングは辛い作業だと思っていたが、学習効果が大きいことを知った。これを知るだけでも取り組む意欲が異なる。

テストコードを書くことがスピードを落とす原因とはならない[14.1]

- ソフトウェア開発を制限する要因はタイピングではない。
  - 仕様書読み込み、ドキュメント作成、会議出席、デバッグなどコードを書く以外にも沢山時間を使う。
  • テストコードを書くとスピードが落ちると言う主張を会社でも聞いたがよく考えれば無理があるなぁ。切り返しとして覚えておきたい。

その他所感

  • 元職場の先輩がこの本を読んで「 前はイケてるチームのやってたことが、みんなが知っておくべきことになってるんだなーと、組織のレガシー化について考えた」とコメントされていた。「組織のレガシー化」これはレガシーコードよりたちが悪く、今の会社にも起きているように感じる。まずは自分の知識を高め、対応が打てるようになりたい。
  • 新規開発や保守など関係なくソフトウェア開発で価値を高めるプラクティスが網羅されており、この本を1冊読んだだけでも今の組織で足りていなことに気づくことができる。索引として利用するには素晴らしく良い本。買ってよかった。ただ、もっと深く知りたいとなった場合、それぞれのプラクティスを深堀りする必要がある。
  • 初めてウォーターフォールによるソフトウェア開発を言及した時点で「このやり方は機能しないんだろう」と次のページに書いてあったとのこと。これに対し、「どうやら、これまでに誰もそれを読んでないようだ。」とジョークを飛ばしている。文化の違いを感じた。

今後