コンパイラかく語りき

import { Fun } from 'programming'

【要約・抜粋】レガシーコードからの脱却を読んだ

「レガシーコードからの脱却」を読んだので、その抜粋と要約。

はじめに

本書はソフトウェアの開発とコストを下げるのを助ける。

レガシーコードとは、修正拡張がむずかしいコード。

第一部 レガシーコード危機

1章 何かが間違っている

レガシーコードの弊害と克服について描かれる。

ウォーターフォール型の開発がソフトウェア開発に向かないことが述べられる。何かが動作するためには、全部が動作しなければいけない。まとめて作るのは変更不可能でもよい物理空間向き。仮想空間では当てはまらない。

ソフトウェア開発はパンを作るための公式ではなく、マリナーラソースを作るためのレシピのようなもの。

新しいタスクは未知の領域へと足を踏み入れること。

成果に影響を与える力がないと感じると、人はモチベーションをなくす。

ソフトウェア開発は創造的なもの、しかしプロセスは創造性に影響しない(ダメなマネジメントによってプロセスが複雑化する無意味さ

複雑なプロジェクトの進捗は測定しづらい、開発者自身が分からないこともある。

コードを書く時間がほとんどない状況は、品質向上を装っている時に出現する。

ソフトウェア工学ハンドブック」のようなものは存在しない。最善の選択を下す道標のようなものはない。

私たちは、ただ難しい質問をするだけでなく、自分たちの根強い考え、身近な信念、排他的なコミュニティを乗り越える勇気を持たなければいけない。難しい質問に答えるために一緒に働き、効率的で、信頼性が高く、品質重視の職業となるように働いていかなければいけないのだ。

p.19 に1章のふりかえりがあり。

2章 CHAOSレポート再考

CHAOSレポートとは、スタンディッシュグループによる、ソフトウェア開発の成功率を追ったもの。10年の歴史がある。レポートはプロジェクトを「成功」「問題あり」「失敗」の3つに分類するが、成功の割合は1/3にとどまる。しかし、成功と問題ありの定義は的確とは言えない。なぜなら成功とは「当初の要件と予算と納期を満たしていること」なので、当初の見積もりが間違っている可能性をはらむ。問題ありでも、リリース後に大きく価値を生むことが考慮されていない。唯一、失敗だけが「プロジェクトが途中で中止されたこと」というクリアな基準を伴っている。

プログラミングのプラクティスが貧弱、といった技術的な部分は「問題あり」に繋がることが多い。これは「コードの変更」「バグの修正」「複雑さの扱い」の3つに分解される。コードは変更されるもの。しかしそれはコストとリスクを発生させる。開発者は変更できないコードを書き過ぎている。バグは見つけるのが難しい。見つけやすくするべきだし、そもそもバグを生まないことが望ましい。バグはソフトウェア開発のプロセスの欠陥。国立標準技術研究所(NIST)のコメントによると、80%の開発コストが障害の特定と修正にかかっている。言い換えれば20%しか価値を作るために使えない。また、スタンディッシュグループによると、プロジェクトでユーザーがよく使う機能は20%にすぎず、45%の機能は全く使われないことが分かった。

私たちの業界が効果的で効率的になるには、まだ遠い道のりが残っている。 p.30

ソフトウェア開発の失敗コストについて。毎年莫大な額のコストがかかっている。また、値段のつけられない、失った機会もある。リリース後の保守コストの高さにも注目。私たちはこの問題に取り組まなければいけない。

p.34 に2章のふりかえりがあり。

3章 賢人による新しいアイデア

戦後日本に派遣されたエドワード・デミングはあるアイデアを持ち込んだ。それは品質改善に集中させること。顧客に届ける価値を最大化することを、組織の構成員に集中させること。それはトヨタの文化に組み込まれ、リーンと呼ばれた。リーンの特徴の1つは、無駄を排除すること。ソフトウェア開発における無駄とは、手をつけたものの完了していない仕事。換言すると、顧客に直接的な価値をもたらさないものは無駄の可能性がある。アジャイルは誤解されることが多い。単純に導入するだけではなく、他のプラクティスと組み合わさることで初めて価値が出る。アジャイルの利点は、開発をディスカバリープロセスにできる点。ストーリー(要件)を仲立ちにして、開発者とプロダクトオーナーが対話する。目的や理由といった話をする。そして開発者はなにを作るべきかを十分に理解する。スプリント、つまりタイムボックスを設けることの目的は急いで仕事をすることではない。見積もりも実装も検証も容易になるレベルまで、タスクを分解し、成果を作り出す単位にすること。

産業革命で学んだ多くのことは、ソフトウェア開発にとって無意味。

ソフトウェア開発にはさまざまなスキルや能力、つまり技能が必要とされる。しかし、あらゆる問題に対応できるようなスキルは習得できない。ソフトウェア開発は左脳と右脳の両側を使う数少ない領域の1つだ。

コードを書く私たちは良いコードを書くには独創力や創造力が必要とされることを知っている。

だが、ソフトウェア開発は職業としてまだ若く、ソフトウェア産業はほかの産業に使われる側にある。 p.42

アジャイルを採用する上で最大の成功要因となるのは技術的卓越性を求めること。

p.44 に3章のふりかえりがあり。

第二部 ソフトウェアの寿命を伸ばし、価値を高める9つのプラクティス

4章 9つのプラクティス

南カリフォルニア大学のバリー・ベームによると、リリース後にバグを発見・修正することは、要求や設計の段階で修正するのに比べて100倍のコストがかかる。

専門家は自身の知識を独自の方法で整理している。重要な差別化要素を説明するための自身の語彙を持っていることも多い。メタファーやアナロジーを使い、自分の経験を踏まえた信念を作り上げる。理解のためのコンテキストがほかの人とは異なるのだ。

とても大きな結果を出しているソフトウェア開発者は、ほかの人とは異なる考え方をしている。彼らは技術プラクティスとコードの品質に注意を払う。彼らは何が重要で何が重要でないかを理解しているのである。 

いちばん重要なのは、ソフトウェア開発の専門家は他の人よりも高い品質基準を維持していることだ。 p.49

コードの品質を高く保っていた「にも関わらず」速いのではない。コードの品質を高く保っていた「からこそ」速い。

合気道と同じく、守破離が肝要。ルールを理解し、実践することで、完全に手に馴染むようになる。

単一責務の原則、オープン・クローズドの原則、など様々な原則がある。これらは正しいこと行えるようにする包括的な知恵。原則は特定の事象を一般化するのに役立つ。原則を実現するのがプラクティス。プラクティスの条件は、 ほとんどの場合に価値があるものである。学ぶのが容易である。教えるのが容易である。 実施がシンプルである。考えなくてもやれるくらいシンプルである。

質問(これをやるべきか?など)は協力だけど骨が折れる。プラクティスの背後の原則がわかれば、思い悩まずに実行できる。プラクティスが質問を不要にし、対応の不確実性を消し去る。

原則はドライブの目的地で、プラクティスはその道筋。プラクティスは私達が原則を理解するのを助けてくれるし、原則は私達がプラクティスを正しく使うのを助けてくれる。ただ、単なる理論家や実務家にならないように、両方のバランスが大事。

予測か対応か。未来予測はあてにならない。それよりも対応可能なコードを書く。ソフトウェアは資産であり、資産価値は現在生み出せる価値ではなく、将来に生み出される価値にも依存する。正しいことをする、バグがない、速い、といった特徴はソフトウェアの外部品質。本書が説明するのはソフトウェアの内部品質。良い内部品質によって、外部品質の実現が近づく。内部品質は結果ではなく原因。

9つのプラクティスは以下の通り。

  1. やり方より先に目的、理由、誰のためかを伝える
  2. 小さなバッチで作る
  3. 継続的に統合する
  4. 協力し合う
  5. 「CLEAN」コードを作る
  6. まずテストを書く
  7. テストでふるまいを明示する
  8. 設計は最後に行う
  9. レガシーコードをリファクタリングする

9つの理由として、人が一度に記憶できるのは7±2個と言われてるから、らしい。

p.60 に4章のふりかえりがあり。

5章 プラクティス1 やり方より先に目的、理由、誰のためかを伝える

やり方ではなく、目的、理由だれのためかをシェアする。これにとって実装の詳細は隠れ、コードはよりシンプルで、拡張するためのコストも低いものになる。ストーリーが要求に代わり、開発が発見になれば、事前に洗い出そうとしていたときよりも良いプロダクトが作れる。

p78 に5章のまとめがあり。

6章 プラクティス2 小さなバッチで作る

作業単位は、観察可能な行動として見えるものであるべき。作業は小さくするのではなく、正しいサイズにする。小さなバッチで仕事をすすめるということは、想定ではなく検証を続けるということである。これによって、マインドセットが変化する。開発していて、自分が分からないことが何なのかを考えるようになる。

リリースサイクルを短くすることで、効率が上がる。ここでの効率とは、ソフトウェア開発プロセスのどのくらいの割合が1つのタスクに費やされているかということ。同時に着手するものが多いと、全体の効率は下がる。タスクの切り替えが必要になる。リリースサイクルが長くなるほど、要求を文書に書く必要が出てくるし、正式なテストフェーズや統合フェーズが必要になる。つまりウォーターフォールを強制される。

コードの行数には本質的な価値はない。顧客にとっての価値を生むのがゴール。小さいものは良いこと。なぜなら、「理解しやすい」「見積もりしやすい」「実装しやすい」「テストしやすい」

「小さな蛇は隠れて成長する必要がある」(ハイチのことわざ)

複雑なストーリーを分割するには、既知のことと未知のことを分離する。未知への対応は「未知を既知にする」か「あとで対処する」かのどちらか。

ストーリーは大きいので、作業項目であるタスクに分解する。理想は4時間ほどで終わるもの。人間は機械ではないので、稼働率50%前後が理想的。

価値を届けるためのプロセスに関して、全体のプロセスの最適化に寄与しないような部分最適化は無意味。本番環境のコードでいつも欠陥が見つかるようなら、開発プロセスが壊れていることを意味する。生産性は計測できない。生産性の計測は、品質を犠牲にする。実現したプロダクトと、それを作るプロセスの両方において、価値の創出と計測に焦点を当てる。

  1. 103 に6章のふりかえりがあり

7章 プラクティス3 継続的に統合する

 継続的にデプロイ可能とは、実際にリリースするかどうかではなく、リリースできることを意味する。実査の判断はビジネス上の決定にもとづく。

「最初の一歩は旅の半分」韓国のことわざより

ストーリー完了の定義を、開発の完了ではなく統合された時点におくこと。

アジャイルインフラストラクチャの7つの戦略

  • すべてをバージョン管理する
  • ワンクリックでエンドツーエンドのビルドをする
  • 継続的に統合する
  • タスクの受け入れ基準を定義する
  • テスト可能なコードを書く
  • 必要な場所のテストカバレッジを維持する
  • 壊れたビルドをすぐ直す

機能がシステム全体のコンテキストで動くようになるまでは、本当の意味で完了してないのだ。 p.114

リスクを減らす7つの戦略 - 継続的に統合する - ブランチを避ける - 自動テストに投資する - 未知の中で働く 1つまたは一連の質問に焦点を絞る。短いタイムボックスを設定してチェックインし、進捗を測りながら進める。 - 価値がわかる最小のものを作る - 頻繁に検証する

ソフトウェアのリスクを減らすというのは、適切なものを適切なやり方で作っているのを確認することだ。早期から頻繁にユーザーからのフィードバックをもらうことで、適切なものが作れていることがわかる。継続的にビルドに統合できるような変更可能なコードを作る上で、よい技術プラクティスにしたがうことで、適切に進められていることがわかる。  p.116

p.116 に7章のふりかえりがあり

8章 プラクティス4 協力しあう

すべてのスキルには方法論、テクニック、プラクティス、原則がある。協働も1つのスキルだ。p.119 チームで働くとき、単なるチームメインバーでいる、もしくは何らかの形でチームの隣にいるだけでは十分ではない。チームの文化に浸かり、チームの本当の一員にならなければいけない。 p.120

IBM 時代、筆者は契約社員であり、正社員と異なり個室がもらえなかった。大部屋に突っ込まれて仕事をしていた。でもそれを楽しんでいた、というエピソードが好き。

ソフトウェア開発は社会的な活動だ。 p.122 コミュニケーションで重要なのは教養の作業場所よりも共通認識だ。だからこそ私達はまず、自分たちのゴールの定義の共有、「品質」の意味の共有、「完成」の意味の共有から始めなければいけない。 p.122 私達は高域帯でのコミュニケーションを好むので、世間話は得意でないこともある。 p.122

ペアプロの組み合わせは、真逆、新旧、ランダムの3通り。バディプログラミングは、1日の終りにお互いのコードをレビューし合う。ペアプロへの導入。

  • スパイク: 未知の課題解決。ある期間の中で、私はこれを調べる、というもの。
  • スウォーミング: チーム全体もしくは複数人で障害に取り組む

p.139 に8章のふりかえりがあり

9章 プラクティス5 「CLEAN」コードを作る

私達が議論するコードの品質は正確な計測が可能。

良いコードは

  • Cohesive(凝縮性)
  • Loosely Coupled(疎結合
  • Encapsulated(カプセル化
  • Assertive(断定的)
  • Nonredundant(非冗長)

ソフトウェアがどう作られているかではなく、ソフトウェアを使うことによって得られる体験から始める。

依存が少なければ少ないほど、コードの変更は簡単になる。

カプセル化のポリシーは、「必要に応じて公開する」。すべてのデータをプライベートで始めて、あとからパブリックにする。

冗長性は必ずしも形状の繰り返しではない。冗長性とは、意図の繰り返し。

コードの品質を上げる7つの戦略

  • 品質の定義を明確にする
  • 品質のためのプラクティスを共有する
  • 完璧主義を手放す
  • トレードオフを理解する
  • 「やり方」を隠す
  • 良い名前をつける
  • コードをテスト可能に保つ

保守しやすいコードを書く7つの戦略

  • コードの共同所有を取り入れる
  • リファクタリングを熱心に行う
  • 常時ペアで進める
  • 頻繁にコードレビューをする
  • ほかの開発者のやり方を学ぶ
  • ソフトウェア開発を学ぶ
  • コードを読み書きして、コーディングの練習をする

p.160 に9章のふりかえりがあり

10章 プラクティス6 まずテストを書く

テストは仕様であり、ふるまいを定義するもの。と理解しておく。テストの種類は「受け入れテスト(顧客テスト)」「ユニットテスト(開発者によるテスト)」「それ以外のテスト(QAテスト)」。

テストファーストな開発について。最初に書かれるテストは、あくまでふるまいの仮設でしかない。実際にコードを書くことで動作し、回帰テストの役割も果たすようになる。そしてソフトウェアの寿命までずっと価値を保証する。

ユニットテストの「ユニット」とは、ふるまいの単位のこと。メソッドやエンティティの単位ではない。ふりまいが変わらないなら、テストを変える必要はない。

常に必要最小限のテストを書くこと。やり方が良くないと、テスト駆動開発は資産よりも負債になる。

テスト駆動開発は、良い設計をするための、自然に考える方法をサポートするフレームワークを提供してくれる。

優れた受け入れテストのための7つの戦略

  • 作っているものが何に役立つのか明確にする
  • 誰が何のために何をしたいのかを知る
  • 受け入れ基準を自動化する
  • エッジケース、例外、代替パスを表す
  • 例示を使って詳細を具体化し、矛盾を一掃する
  • 受け入れ基準とふるまいの分離
  • 各テストを一意にする

優れたユニットテストのための7つの戦略

  • 呼び出し側の視点に立つ
  • テストを使ってふるまいを表す
  • 新しい違いを生み出すテストだけを書く
  • 失敗したテストにパスするためのコードのみを書く
  • テストを使って、ふるまいを作る- コードをリファクタリングする- テストをリファクタリングする

p.179 に10章のふりかえりがあり

11章 プラクティス7 テストでふるまいを明示する

必ず成功するテストはダメなので、まず失敗することを確認する。それから、成功するように実装をする。レッド・グリーン・リファクタの3つのフェーズ。

機能毎に、最適な種類のテスト、最適なテストの数というものがある。

バグが起こったら、バグを再現するような失敗するテストを書く。次にテストがグリーンに変わるようにバグを修正する。

テストを仕様として使うための7つの戦略

  • テストを文書のように扱えるようにする
  • 意図がはっきり分かる名前のついたヘルパーメソッドを使う
  • 何が重要なのかを明らかにする
  • 実装ではなくふるまいをテストする- モックを使ってワークフローをテストする
  • 書きすぎない
  • 正確な例を使う

バグを修正する7つの戦略

  • そもそもバグを作らない
  • できるだけ早くバグを見つける
  • バグを見つけられるように設計する
  • 正しい質問をする
  • バグをテストの不足と見なす
  • 欠陥を元にプロセスを修正する
  • 失敗から学ぶ

p.204 に11章のふりかえりがあり

12章 プラクティス8 設計は最後に行う

  ソフトウェアの設計について、開発サイクルの最後に実施したほうが効果的・効率的なものもある。 

コードの変更を難しくする良くないプラクティスは以下の通り。   - カプセル化の欠如 - 継承の過度な利用 - 具体的すぎる凝り固まった実装 - インラインコード - 依存性 - 作ったオブジェクトを使うか、使うオブジェクトを作るか

持続可能なコードを書くための5つのプラクティス

  • 死んだコードを消す
  • 名前を更新する
  • 判断を集約する
  • 抽象化
  • クラスを整頓する

コードは書かれる回数の10倍読まれている。p.211

意図によるプログラミング、大切なことが書かれている気がするけど、きちんと理解できなかった。読み返す。

プログラミングの最小単位は、論理積論理和、否定など。全てはこのレベルまで詳細化できる、が、その途中で意味のある単位にまとめて処理をする。その単位が抽象。

循環複雑度は減らすべき。条件分岐が多いほど、循環複雑度は増す。

創発設計をマスターする7つの戦略

コードをクリーンにする7つの戦略

  • コードに語らせる
  • テストを足すために、接合部を作る
  • メソッドの凝集性を高める(メソッドやクラスを抽出する。理想的なメソッドは4行以内 by ロバート・マーチン
  • クラスの凝集性を高める
  • 判断を集約する(ビジネスルールを集約する
  • ポリモーフィズムを導入する
  • 生成をカプセル化する

p.219 に12章のふりかえりがあり

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

リファクタリングのビジネスレベルでの意義とは、以下の4つのコストの削減。

レガシーコードは時限爆弾ではなく地雷。コードが機能していて、変更やアップグレードが必要ないならそのままにしておく。

コードのリファクタリングは、コードを書くときにしてはいけないこと、代わりにすべきことを学ぶ上で最速の方法の1つ。これによって、最初からクリーンなコードを書くようになる。ソフトウェアは実を結ばずに死ぬか、思った以上に長く生き残るかのどちらか。

オープン・クローズド原則とは、ソフトウェアは拡張に対して開かれているが変更に対して閉じられているべき。というもの。リファクタリングもそのようにする。まず、拡張したいコードをリファクタリングし、安全にしてから拡張していく。

リファクタリングから価値を得るための7つの戦略

  • 既存のシステムを学ぶ
  • 小さく改良する
  • レガシーコードをテストで改良する
  • クリーンアップしながら進める
  • 詳細がわかったら実装を再設計する
  • 進む前にクリーンアップする
  • やってはいけないことを学ぶリファクタリング

いつリファクタリングを行うかについての7つの戦略

  • 重要なコードがうまく保守されていないとき
  • コードを理解している人がいなくなったとき
  • 新しい情報によって、より良い設計が見つかったとき
  • バグを修正するとき
  • 新機能を追加するとき
  • レガシーコードのドキュメントを書くとき
  • 作り直すより安いとき

p.238 に13章のふりかえりがあり

Amazon

楽天