本ページはプロモーションが含まれています

IT全般

レガシーコード改善ガイドの書評|コード刷新の前に読んで、変更の手順を2つ変えた

CATEGORY
REV.
DRAWN BY
TOM
SHEETS
9枚
レガシーコード改善ガイド(テストで固めて、切り出して足す)

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

今の職場でコードを刷新することになり、その準備として読んだのが『レガシーコード改善ガイド』です。

結論から言うと、テストのない古いコードを、壊さずに変えていかないといけない人に合う1冊です。ただし449ページと厚く、例も古めなので、頭から通して読むより、困ったときに引く本として使うほうが向いています。

いちばん役に立ったのは「時間がないのに変更しなければなりません」の章でした。読んでから、コードを変えるときの手順を2つ変えています。

レガシーコード改善ガイドはどんな本か

書名レガシーコード改善ガイド ―保守開発のためのリファクタリング
著者マイケル・C・フェザーズ(監訳:ウルシステムズ)
出版社翔泳社
発売2009年7月
ページ数・定価449ページ(B5判)/4,620円(税込)
原書Working Effectively with Legacy Code(Pearson、2004年)

書誌情報と目次は、紀伊國屋書店のページによるものです。内容説明では、構造が複雑で理解できないようなコードを、理解し、テストできるようにし、リファクタリングを可能にし、機能を追加できるようにするテクニックを紹介する本とされています。

本の構成:仕組みを知る → 困りごと別に直す → 手法の辞典
1
PART 1 / 第1部変更のメカニズム検出と分離・接合モデル など
2
PART 2 / 第2部ソフトウェアの変更困りごとを章題にした19章
3
PART 3 / 第3部依存関係を排除する手法
NOTE部の見出しは紀伊國屋書店のページの目次による

特徴的なのは第2部です。章題が「時間がないのに変更しなければなりません」「このクラスをテストハーネスに入れることができません」のように、現場の困りごとそのものになっています。よくある質問の一覧のように、自分の困りごとから章を探せる作りです。

読んだきっかけ|コード刷新のため

LOG今の職場コードを刷新することになった

今の職場でコードを刷新することになり、古いコードを壊さずに変える方法を知りたくて読みました。

役に立ったところ|時間がないのに変更しなければならないとき

いちばん役に立ったのは、第2部の最初の章「時間がないのに変更しなければなりません」です。テストのない大きなメソッドに機能を足さないといけない、でも全体をテストで覆う時間はない。そんなときの進め方の章です。

この章は、原書では第6章「I Don't Have Much Time and I Have to Change It」です(出版社Pearsonの書籍ページの目次による)。原書の目次では、この章に Sprout Method・Sprout Class・Wrap Method・Wrap Class という4つの手法の節が並んでいます。

古いコードに直接書き足さず、新しい場所に書く4つの手法
手法
やり方
Sprout Method
新しい処理を、新しいメソッドとして書く既存のメソッドからは呼ぶだけ
Sprout Class
新しい処理を、新しいクラスとして書く既存のクラスがテストに載らないとき
Wrap Method
既存のメソッドを別名に移し、前後に処理を足す元の処理には手を入れない
Wrap Class
既存のクラスを包む新しいクラスを作る包んだ側で処理を足す
NOTE手法の名前は原書の目次による。説明は筆者の言い換え

4つに共通するのは、テストのない古いコードには最小限しか触らず、新しく書く部分をテストできる場所に置くことです。全体をきれいにする時間がなくても、足した部分だけは確かめられます。

読んでから変えたこと|テストで固めてから、切り出して足す

読んでから、コードを変えるときの手順を2つ変えました。職場のコードは載せられないので、説明用のJavaのコードで見せます。

古いコードを変えるときの手順
1
変更の依頼が来る
2
CHANGE 1 / 変えたこと1触る部分の今の動きを、テストで固める正しいかどうかではなく、今どう動くか
3
CHANGE 2 / 変えたこと2新しい処理は、新しいメソッドやクラスに書く既存のコードへの変更は、呼び出しの1行に
4
切り出した部分にテストを書く
5
固めたテストも通ることを確かめる
NOTE読んでから変えた2つ(テストで固める・切り出して足す)を1つの流れにしたもの

1. 変更の前に、今の動きをテストで固める

直す前に、今のコードがどう動いているかをテストに書き留めるようにしました。期待値は「仕様として正しい値」ではなく「今のコードが返す値」です。これがあると、変更のあとで今までの動きが変わっていないかを確かめられます。

// 直す前に、今の動きをそのままテストに書き留める
@Test
void 今の送料計算の結果を固定する() {
    ShippingCalculator calculator = new ShippingCalculator();

    // 期待値は「正しい値」ではなく「今のコードが返す値」
    // いったん実行して出た値を書き写す
    assertEquals(500, calculator.fee(2999, "TOKYO"));
    assertEquals(0,   calculator.fee(3000, "TOKYO"));
    assertEquals(800, calculator.fee(2999, "OKINAWA"));
}

第2部には「変更する必要がありますが、どんなテストを書けばよいのかわかりません」「どうすれば何も壊していないことを確認できるでしょうか?」という章もあります。テストの書き方の考え方は単体テストの考え方/使い方の書評でも書いています。

2. 既存のコードに直接足さず、切り出して足す

もう1つは、既存の長いメソッドに処理を書き足さず、新しいメソッドに切り出すようにしたことです。たとえば「同じ注文番号が2回来たら、2回目は捨てる」という処理を足すとします。

// 既存の長いメソッドに、新しい処理を直接足す
public void registerOrders(List<Order> orders) {
    // ……テストのない処理が100行……

    // 追加:同じ注文番号が2回来たら、2回目は捨てる
    Set<String> seen = new HashSet<>();
    List<Order> unique = new ArrayList<>();
    for (Order order : orders) {
        if (seen.add(order.number())) {
            unique.add(order);
        }
    }

    // ……テストのない処理が続く……
}

これを、新しいメソッドに切り出して、既存のメソッドからは呼ぶだけにします(Sprout Methodの考え方)。

// 新しい処理は、新しいメソッドに切り出す
public void registerOrders(List<Order> orders) {
    // ……既存の処理……
    List<Order> unique = removeDuplicates(orders);   // 既存のメソッドへの変更は、この1行だけ
    // ……既存の処理……
}

// 切り出したメソッドだけなら、テストが書ける
List<Order> removeDuplicates(List<Order> orders) {
    Set<String> seen = new HashSet<>();
    List<Order> unique = new ArrayList<>();
    for (Order order : orders) {
        if (seen.add(order.number())) {
            unique.add(order);
        }
    }
    return unique;
}

既存のメソッドへの変更は1行だけになり、足した処理は removeDuplicates だけをテストすれば確かめられます。

合わなかったところ|例が古く、厚い

合わなかったのは2つです。1つは、例が古く、C++のコードが多くて読みにくかったこと。もう1つは、449ページと厚く、通して読むのが大変だったことです。

日本語版は2009年、原書は2004年の本です。言語やツールの事情は、今とはかなり違います。

ハマりポイント
頭から読まず、困りごとの章から引く

第2部の章題は、現場の困りごとそのものです。通して読もうとせず、今困っていることに近い章題を探して、そこから読むのが楽です。コードの例は、考え方だけを持ち帰り、今の言語に置き換えて読んでください。

困りごと別の読み方

第2部の章題から、よくある困りごとを選んでまとめました。

こんなとき読む章(章題)
時間はないが、テストのないコードに機能を足したい時間がないのに変更しなければなりません
クラスがテストで動かせないこのクラスをテストハーネスに入れることができません
何のテストを書けばいいか分からない変更する必要がありますが、どんなテストを書けばよいのかわかりません
クラスが大きすぎるこのクラスは大きすぎて、もうこれ以上大きくしたくありません
巨大なメソッドを変えたいモンスターメソッドを変更する必要がありますが、テストを書くことができません
壊していないか不安どうすれば何も壊していないことを確認できるでしょうか?

依存関係を切り離す具体的な手法は、第3部「依存関係を排除する手法」にまとまっています。Javaで依存を外から渡す基本はDI(依存性の注入)とはにまとめています。

向いている人・向いていない人

  • 向いている人:コードの刷新や保守で、テストのない古いコードを変えないといけない人
  • 向いていない人:最新の言語やツールの例で学びたい人。頭から通して読める薄い本がほしい人

あわせて読みたい

読んだ本はほかにも、トムの本棚に並べています。

よくある質問

Q. 2009年の本ですが、今でも役に立ちますか?

テストのないコードを壊さずに変える、という考え方は今も使えます。ただ、例が古く、C++のコードも多いので、考え方だけを持ち帰って今の言語に置き換えて読むことになります。

Q. 全部読む必要はありますか?

ありません。第2部は困りごとが章題になっているので、今困っていることに近い章から読めます。私も、いちばん役に立ったのは「時間がないのに変更しなければなりません」の1章でした。

Q. 原書はありますか?

あります。Michael C. Feathers の「Working Effectively with Legacy Code」(Pearson)です。

REVISIONS / 改訂履歴
REV.DATE内容
22026-10-16図解を描き直し(図面のデザインにそろえた)
12026-10-13初版
NEXT STEP
次にテストのないメソッドへ処理を足すときは、足す処理を新しいメソッドに切り出してみてください。

既存のメソッドへの変更は呼び出しの1行だけ。切り出したメソッドには、テストを1つ書いておく。それだけで、足した部分は確かめられます。

NEXT READ本棚から1本

AIで開発を組み直すなら、まずこの5冊から

入門・実践・設計まで、Claude Codeの本をレベル別に5冊選んで比べました。

→ おすすめ5冊を見る

  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

IT全般 ・