この記事はスタンバイ Advent Calendar 2021の8日目の記事です。
#はじめに
レガシーコードからの脱却を読んで、何回も読み返したいなと思った本だったので、各項目のメモと感想です。引用している箇所は引用で、自分なりの解釈が入った箇所はそのまま記載しています。
すでに読まれている方は思い返すために、まだ読んだことがない方は興味を持って頂き、本を読んでもらうきっかけになればと思っています。
#レガシーコードとは
(理由はなんであれ) 修正、拡張、作業が非常に難しいコードのこと。
出典:レガシーコードからの脱却
#本で述べられている重要なこと
本書で頻繁に登場している内容で、2点つ重要な点がある。
##プラクティスがなぜ重要なのか背景を理解すること
プラクティスとは原則を実現するための方法で、原則自体は簡潔で明確なものである。
つまりプラクティスの背景を理解するは、原則の背景を理解することである。
##プラクティスに沿ったテストを書くこと。テストがかけるコードを書くこと
9つのプラクティスを読んでいると、テストの話がいろんなところで登場する。
- テストがかけるようなコードをかくこと
- テストをかく方法
- テストケースのプラクティスなど
テストは普段何気なくかくが、テストの奥にある背景を理解してテストのプラクティスを学ぶことでより効果的なテストがかけるようになる感じた。
レガシーコードから脱却するためには、プラクティスの背景を理解することと、テストが重要である。
#9つのプラクティス
著者は人生のほとんどの時間を使い生産性が高いエンジニアを観察し、なぜうまく開発しているのかを分析してきた。
そして、その要因が再現可能であることを発見して、アジャイル開発手法のエクトリームプログラミング、スクラム、リーンからきた要素を取り入れて、9つのプラクティスを提案している。
この9つのプラクティスを実践することで、書いたコードがシンプルで、保守や拡張がしやすくなる。つまりレガシーコードになることを防ぐことができる。
ただし重要なことは このプラクティスを単に適応するのではなく、背景を理解した上で、適応すること である。
##1. やり方より先に目的、理由、誰のためかを伝える
ソフトウェアの機能を作る際に、企画者(プロダクトオーナーや開発マネージャー)は実装の詳細ではなく、目的や制約を説明するために、目的、理由、誰のためかを伝える。
具体的なやり方は、開発者に発見させて、開発者とプロダクトオーナーで協調して開発を進める。
出典:レガシーコードからの脱却
###期待する効果
- 開発者は一連の命令をただコーディングするよりも、開発者は発見に満ちた方法でコーディングするできる。
- 実装の詳細が隠れ、コードはよりシンプルで拡張するコストも低くなる。
###感想
スクラムでバックログを作成する際に、やり方ではなく、目的、理由、誰のためかを明記するが、この背景がちゃんと理解できた気がする。この仕組みによって開発者が能動的に発見しながら開発を行うことができ、かつ発見が開発を行うことで良い開発ができるのだと感じた。
##2. 小さいバッチで実行する
小さいバッチで実行することのメリット
- 理解しやすい
- 見積もりがしやすい
- 実装しやすい
- テストしやすい
- リスクも少なく、より多くのフィードバックの機会が得られる。
出典:レガシーコードからの脱却
ただし単に小さくするのではなく、自分達にとって正しいサイズにする。
小さく作る方法として タイムボックス と スコープボックス という手法がある。
-
タイムボックス
- タイムボックスは固定の時間の中でタスクに取り組むプラクティスでタイムボックスの中で、達成できるサイズにする。
- スクラムではスプリント、エクストリームプログラミング(XP)ではイテレーションと呼ばれる。
-
スコープボックス
- スコープボックスは時間を重視せずに、ストーリーや機能と言った形で終わらせるもの。
作業を適切なサイズに落とすときに、タイムボックスでもスコープボックスでもどちらでもは良いが、スコープの管理は時間の管理以上に訓練が必要になるので、機能を小さなタスクに分化するのに慣れるまではタイムボックスを使う方が良い。
出典:レガシーコードからの脱却
ストーリーとは「何が」「なんのために」「誰のために存在する」を定義したものである。
ストーリーも短いほど良い。見積もることも、理解することも実装するのも容易になる。
そのため方法として以下の戦略がある。
####ストーリーを分割する7つの手段
- 複数のことが混じったストーリーを要素に分解する
- 複雑なストーリーを既知のことと未知のことで分離する
- 未知のことをわかるまで繰り返す
- 受け入れ基準をもとに分割する
- (タスクの)依存関係を最小にする
- 意図を1つにする
- ストーリーをテスト可能に保つ
出典:レガシーコードからの脱却
###期待する効果
- 小さいタスクほど、見積もりもテストも簡単で扱いやすい.
- フィードバックサイクルを短くすることができる。
- 柔軟に進められる
- 間違った方向に進んでいた場合にすぐに修正ができる
###感想
UNIXの哲学にも Small is beautiful (小は美なり) という考え方が確立されていたり、ビジネスの場でも制約理論(TOC)の中で小さいバッチで回すことでスループットを効果的に発揮することができるなど、小さいことへのメリットがいろんな場面でも説明されている。
ソフトウエアを開発する際も、機能としても期間としても小さいことは良いことである背景がわかった。
そのためにも適切に小さく分割することが大事である。
##3. 継続的に統合する
継続的インテグレーションとは、リリース直前まで待つのではなく、ソフトウェアをビルドしながら統合すること。
バグを早期に直せるだけではなく、より簡単に統合できるよいコードを書く方法を学べる重要なプラクティスである。
出典:レガシーコードからの脱却
常に継続的インテグレーションを実行し、システムへ組み込んだ結果を確認する。そして、バグが混入していないか、コードがシステムのほかの部分とうまく連携できているかを確認する。
継続的な統合を行うために、自動化されたビルドサーバーは非常に重要であり、そのための戦略が以下になる。
####アジャイル開発に適したインフラストラクチャーを作るのための7つの戦略
- すべてをバージョン管理する
- ワンクリックでエンドツーエンドのビルドをする
- 継続的に統合する
- タスクの受け入れ基準を定義する
- テスト可能なコードを書く
- 必要な場所のテストカバレッジを維持する
- 壊れたビルドをすぐ直す
出典:レガシーコードからの脱却
###期待する効果
- コードを書くたびに統合することで、ソフトウェア開発に伴うリスクを軽減できる。
- リリース候補の検証を自動化することで、リリース直前の変更にかかることを無視できるよになる。
- フィードバックサイクルを短くすることで、開発者の行動の結果がすぐに把握できるようになる。
###感想
私が業務を行ってきた環境では、CI環境が当たり前のようにあり、新しいプロダクトを作る際もCI環境を最初に作っていた。 特にGit、Dockerを使ったCIをサポートするサービスが多くあるので、アジャイル開発に適したインフラストラクチャーは導入しやすいのではないかと思う。
しかしCIを作る環境が簡単になったとはいえ、CI環境で動かすテストは開発者が作っているものであり、継続的に統合をより意味のあるものにするために適切なテストを作ることが重要であると感じた。
##4. 協力しあう
一番価値あるリソースは働く仲間である。 一緒に働くとお互いから学ぶだけでなく、一緒に学べるようになる。
質の高いコミュニケーションを作り上げ、知識をグループに広げるために一緒に協働する。
特にコミュニケーションで重要なことは 共通の認識 である。
そのためにまず、自分たちのゴールの定義の共有、「品質」の意味の共有。「完成」の意味の共有を行わなければいけない。
複数の主体が、何らかの目標を共有し、ともに力を合わせて活動すること協働というが、協働は自然とできるようになるわけではなく協働も一つのスキルであり、学ぶ必要がある。
####協働のプラクティス
- ペアプログラミング
- 開発者が二人で1台のコンピューターを使って協働すること。
- チームで知識を広げる最も早い方法であり、名前を決めるなどの活動で特に有効。
- バディプログラミング
- 一人で開発を進め最後に時間をとってバディと一緒にそれぞれ完成させたコードをレビューする。
- スパイク
- 未知の課題回解決のために複数の開発者が一つのタスクに取り組むやり方で、使う時間をあらかじめ決めて進める。
- スウォーミング
- チーム全体もしくは複数人で、同一の問題に同時に取り組むこと。
- 「先に進めないような障害」を対応するときに適したアプローチ
- モブ
- チーム全体が普段から一緒になって単一のストーリーに取り組む。
- キーボードの前に一人が座り、残りのメンバーはナビゲーターとして課題に取り組む。
また協働を改善するために、チームのふりかえりと改善点を確認する方法として、レトロスペクティブというものがある。
#####レトロスペクティブの7つの戦略
チームの振り返りや改善かうどうの効果的に行うための戦略
- 小さな改善を探す
- プロセスを責めよ。人を責めるな
- なぜなぜ5回をやってみる
- 根本原因に取り組む
- 全員の意見を聞く
- 人に権限を
- 進捗を測ること
出典:レガシーコードからの脱却
期待する効果
- 協働のプラクティスを活用することで質の高いコミュニケーションができる。
- 質の高いコミュニケーションは、学習を増幅してチーム内で知識を伝搬できる
- 常にメンター、メンティーとなり、自分とチームのスキルを上げる機会が得られる。
- チームと業界全体を改善できる。
感想
同じ組織に優秀なエンジニアがいると自分も学べて成長できたり、
チーム開発を効率的に行うために、教え合ったり、わからないところは素直にわからないといったりといったことをなんとなく体験してきたが、この章で 一番価値あるリソースは働く仲間である。 という文面でとても納得感があった。
チーム開発を行う中で重要なことは個の得意なことを伸ばしたり、スキルも磨くことだけなく、一緒に学びあえる環境というのが個人とプロダクトと組織の成長へ繋がっていることを改めて感じた。
##5. 「CLEAN」コードを作る
良いコードは、特性が明確に定義され、はっきりした責務を担い、実装は隠蔽されている。
「CLEAN」コードとは以下の頭文字である。
-
Cohesive (凝集性)
- 凝集性とはクラスやメソッドが単一の責任を持つべきことを意味する
- 凝集性のあるコードは副作用を減らす
-
Loosely Coupled (疎結合)
- オブジェクト間の関係を明確な意図を持った状態に保つこと
- 疎結合なコードは利用しているコードに対して間接的にしか依存しない
- サービスを直接呼び出す代わりに中間層を通じて呼び出す
- 疎結合なコードはテストが容易である
-
Encapsulated (カプセル化)
- 実装の詳細を外部の世界からは見えない状態にする
- 単に状態やふるまいを非公開にするのではなく、インターフェースと実装を切り離すこと
- カプセル化されたコードは簡単に拡張できる
-
Assertive (断定的)
- コードが断定的とは、自分自分の責任は自分で管理することである
- 断定的ではないクラスやメソッドは仕事をするのに絶えず他のオブジェクトを参照したり呼び出しを行う状態
- 断定的なコードによってソフトウェアがモジュール化される
-
Nonredundant (非冗長)
- 日冗長とは、冗長さを含んでいない状態で、同じことを繰り返さないこと
- ほとんどの冗長性は明白なもので、コードの中で見つけやすいが、冗長が見つけにくことがある。
- 冗長性には、状態やふるまいだけでなく、冗長なテスト、冗長な関係、冗長なプロセスなどさまざまな形がある
- 意図しない冗長性は排除しないといけない。
- 非冗長なコードは保守の問題を減らす
#####コード品質を上げる7つの戦略
- 品質の定義を明確にする
- 品質のためのプラクティスを共有する
- 完璧主義を手放す
- トレードオフを理解する
- 「やり方」を隠す
- 良い名前をつける
- コードをテスト可能に保つ
出典:レガシーコードからの脱却
期待する効果
良いコードとは理解が容易で扱いやすいコードであると定義すれば、この原則を守っているコードの品質を識別できるようになる。これらのコード品質は小さなものに見えるかもしれないが、保守しやすいソフトウェアを作ることができる。
感想
「Clean Architecture」で説明されている内容と共通している部分が多かったが、プラクティスを実現するためにヒントとなる説明が豊富にあったので、よりプラクティスの解像度があげられるのではないかと思う。特にカプセル化をする際は、インサイドアウトではなく、アウトサイドインで設計するなど参考になった。
改めてなぜこれらのプラクティスが重要であるかを再認識できたし、コードを書くときに常に意識をしたいと思う。
##6. まずテストを書く
テストファースト開発は失敗するテストを作成し、合格するのに十分なコードを書くことで機能を作る。そのあと、必要に応じてリファクタリングし、失敗する別のテストを書くことを繰り返す。
出典:レガシーコードからの脱却
この順番にすることで利点がいくつかあるが、特に大事なことは頻繁なフィードバックが得られることである。
また先にテストを書くことでテスト可能なコードを最初から書くことができる。
コードを書いてからテストを書くと、システムが想定した通りに機能していることを確認ができるメリットもあるが、実際にはコードを書いた後にテストを書くのが難しく、コードをクリーンアップする作業が発生する。
特にテストができないコードを書いた場合は、他のものも再設計して書き直すといった状況に陥ってしまう。
期待する効果
- 作っているものが何に役に立つのか明確にできる。
- テストファースト開発を正しく行うことで素早いフィードバックを得られる。バグが発生した際にすぐに修正ができる。
- コードがテストによってサポートされている場合は、リファクタリングが安全にできる。
開発者が保守可能でテスト可能なコードを作成する助けになる。やり方がよくないと、テスト駆動開発は資産よりも負担になる。
###感想
普段私はテストファーストではなく、開発を先に行い、その後にテストコードを書く。
テストファーストについてはあまり、前向きではなかったが、今回の章を読んでイメージが大きく変わった。
もともと実装ができていない中テストを書いても失敗するだけだろうという認識でいたが、テストが失敗して、ちゃんとをテストが行われていることを担保するために、最初から失敗するテストを書くことが重要であるということに納得感があった。
##7. テストでふるまいを明示する
生きた仕様を作るには、テストにふるまいを記述する。
- テストスイートを使用して、ふるまいを記述し確認する方法
- テストを活用することで、目的を明確に説明できるようになるとともに、それらが生きた仕様になること
- テストは「セーフティネット」を提供してくれる。それによって、リファクタリングが可能になり、間違いがすぐにわかるようになること
- ふるまいのテストを書くことで、書くべきテストの種類と数を常時把握できること
テストを仕様として使うための7つの戦略
- テストを文書のように扱えるようにする
- 意図がはっきりわかる名前のついたヘルパーメソッドを使う
- 何が重要なのかを明らかにする
- 実装ではなくふるまいをテストする
- モックを使ってワークフローをテストする
- 書きすぎない
- 正確な例を使う
出典:レガシーコードからの脱却
期待する効果
テスト駆動開発を正しくできれば、開発者がより保守可能でテスト可能なコードを作成できる。だが、中途半端にやれば失敗する可能性もある。テストを実行可能な仕様と見なすこと。そうすれば、ふるまいに対し、どのようなテストを書くべきか完全にわかるようになる。
###感想
テストは仕様であるというフレーズをこれまでも聞いてきたが、そのためのプラクティスが詰まった章だった。
テストの書き方を見直したり、テストをどうかくか迷った時に指針にできそう。
##8. 設計は最後に行う
この設計はコードとテストが書かれた状態からの設計を指している。
保守可能なコードは読みやすく理解しやすいため、変更が簡単で柔軟にできる。
その状態で変更がしにくい要素を変更して持続可能な状態にする。
- 変更を難しくするプラクティス
- カプセル化の欠如
- 継承の過度な利用
- 具体的すぎる凝り固まった実装
- インラインコード
- 依存性
- 作ったオブジェクトを使うか、使うオブジェクトを作るか
- 持続可能な状態にするためのプラクティス
- 死んだコードは消す
- 名前を更新する
- 判断を集約する
- 抽象化
- クラスを整頓する
###期待する効果
- 意図を示すプログラミングは観点の凝集につながる。抽象のレベルがそろい、読みやすく、理解しやすくなる
- 開発の中で学んだことを設計に反映し、保守性に注意を払うことで、レガシーコードの死のスパイラルから脱出できる。
- 進化とともに継続的に改善するコードは、扱いやすく、保守コストも安く済む。
- オブジェクトの生成と利用を分離することで、テスト容易性を改善し、依存性を下げられる
###感想
この章で印象深かったのが「ソフトウェアは書かれる回数より読まれる回数のほうが多い」という文言である。
確かに、コードを書いて終わりではなく、その後に自分をはじめ多くに人に読まれる。そのためにも持続可能状態にすることの重要性を再認識した。
##9. レガシーコードをリファクタリングする
リファクタリングは外部へのふるまいを変更せずに、コードを再構築することである。
リファクタリングによってリスクを減らし、多くの価値を生み出せる。
ソフトウェアはリスクが高く頻繁に更新される可能性があり、リファクタリングをすることで、以下のコストを削減することができる。
- あとからコードを理解する
- ユニットテストの追加
- 新しい機能の追加
- さらにリファクタリングを行う。
リファクタリングによって意図がわからないメソッドをラッピングしたり、意図がわかる名前に変えたり、学習の要素をコードに埋め込むことができる。
出典:レガシーコードからの脱却
リファクタリングから価値を得るための7つの戦略
- 既存のシステムを学ぶ
- 小さく改良する
- レガシーコードをテストで改良する
- クリーンアップをしながら進める
- 詳細がわかったら実装を再設計する
- 進む前にクリーンアップする
- やってはいけないことを学ぶリファクタリング
出典:レガシーコードからの脱却
###期待する効果
- 機能追加の前準備と、実際の機能の追加を切り離すことで、作業が大幅に単純化され、バグ発生リスクが減ること
- リファクタリングがうまくなれば、クリーンなコードを自然に書き始めるようになる
###感想
リファクタリングは、新しいシステムを学ぶのにとても良い方法であると書かれてあり、確かにそうだなと思った。
新メンバーが自分のチームに入った時に、ちょっとしたリファクタリングを行ってもらうなどが、システムとしてもプロダクトとしても良いのではないかと思った。
本全体の感想
-
開発について
プラクティスのいろんなところでテスト可能であるという条件があり、テスト可能な状態であることがレガシーコード(修正、拡張、作業が非常に難しいコード)から脱却することにつながることを感じた。 -
開発の手法について
普段は業務ではスクラムのフレームワークに沿って開発をしており、特に目新しいプラクティスばかりではなかったが、何気なくやっていたことについて背景を理解すると、なぜこういう形式なのか気づくことができた。
このプラクティスの背景をわかりやすく解説しているのがこの本の良い点であり、もし開発の環境が何か良く進んでいない場合には、ぜひこの本を見返すと良いと思う。