Upgrade to Pro — share decks privately, control downloads, hide ads and more …

リファクタリング入門 〜日常的にコードを育てるための第一歩〜

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Naoki Kitora Naoki Kitora
September 29, 2025

リファクタリング入門 〜日常的にコードを育てるための第一歩〜

Avatar for Naoki Kitora

Naoki Kitora

September 29, 2025

Other Decks in Programming

Transcript

  1. 自己紹介 木虎 直樹 株式会社ティアライン CTO アプリケーションエンジニア (Java, Python, Scala, JavaScript,

    etc.) インフラエンジニア (OS, ミドルウェア、ネットワーク、クラウド) プロジェクトマネージャ (アプリケーション開発、AI 開発、etc.)
  2. Agenda • リファクタリングと • なぜリファクタリングをする か • いつリファクタリングをする か •

    どこをリファクタリングする か (リファクタリング事例) • リファクタリング 進め方
  3. なぜリファクタリングをする か • コードが理解しやすく修正が簡単になるためバグ 入り込む余地が最小化 ◦ 生産性向上 ◦ コード 読まれる時間

    ほうが長い ▪ パフォーマンスよりもまず 可読性 • 最初からすべてを見通して詳細に設計する 不可能 ◦ 実装 過程で気づく ◦ 使ってから気づく ◦ 運用してから気づく • ソフトウェア 成長とともにエントロピーが増大 ◦ 追加開発により当初考えていなかったことが発生 ◦ 車輪 再発明 ◦ 具体性 高い (抽象度 低い、汎用性 低い ) 実装
  4. いつリファクタリングをする か • 変更要求に対応する準備 ◦ 変更を容易にするため • バグ修正や調査 タイミング ◦

    コード修正 前に何をしている か理解するため • 理解 できるが、良くない書き方を見つけたとき ◦ ゴミ拾い • 一通り 実装が完了し、コードレビューを依頼する前 ◦ 推敲
  5. どこをリファクタリングする か (リファクタリング事例) コード 不吉な匂い (Code Smells) する箇所 • 謎な名前

    (Mysterious Name) • 重複したコード (Duplicated Code) • 長い関数 (Long Function) • 巨大なクラス (Large Class) • ネストが深い (※) • etc. ※ Martin Fowler 著書リファクタリング「第 3章 コード 不吉な匂い」に 挙がっていない
  6. 謎な名前 (Mysterious Name) なぜダメ? 名付け 設計 • 適切な名前がつけられるということ 、そ クラスやメソッド、変数

    役割が明確か つ適切であるということ • 適切な名前がつけられていれ コードが読みやすい ◦ メソッド 実装を読まなくても処理が想像できる ◦ コードを詳細に追っていかなくても変数に何が入っているか想像できる
  7. 謎な名前 (Mysterious Name) public class InvoiceService { public double cal(double

    a, double b) { return a * b * 1.1; } } public class InvoiceService { private static final double TAX_RATE = 0.1; public double calculateTotalPrice(double unitPrice, double quantity) { double subtotal = unitPrice * quantity; return subtotal * (1 + TAX_RATE); } }
  8. 謎な名前 (Mysterious Name) 一種 ありがちなダメな名前 ***Flag (e.g. updateFlag) true (or

    false) 場合にどういう意味な か直感的にわからない 更新するべきかどうかを保持 : shouldUpdate 更新したというステータスを保持 : updated 更新しても良いかどうかを保持 : canUpdate ***Flag 副作用 if (updateFlag == true) { // 比較したくなる }
  9. 謎な名前 (Mysterious Name) 一種 これまでに見たも でも特に印象に残っているも latitude, mild 緯度 わかるけどマイルドって何

    ? 緯度 次に来る 普通に考えたら経度 (longitude) で ? 経度、ケイド、けいど、けいど ? けいど?! ……
  10. 重複したコード (Duplicated Code) public class DiscountCalculator { public double calculateForRegularCustomer(double

    price) { double discounted = price * 0.9; return discounted < 100 ? 100 : discounted; } public double calculateForPremiumCustomer(double price) { double discounted = price * 0.8; return discounted < 100 ? 100 : discounted; } }
  11. 重複したコード (Duplicated Code) public class DiscountCalculator { public double calculateForRegularCustomer(double

    price) { return applyMinimumPrice(price * 0.9); } public double calculateForPremiumCustomer(double price) { return applyMinimumPrice(price * 0.8); } private double applyMinimumPrice(double discounted) { return discounted < 100 ? 100 : discounted; } }
  12. 重複したコード (Duplicated Code) で最近見た例 • コピペで量産された 20個くらい 関数 (Python です)

    ◦ 関数 役割ごとに 10個から 20個存在する ▪ ここでいう関数 役割と データ読み込み、データ変換、データ書き出しなど ◦ 大体 コピペだが、長年 製品開発で一部だけ改良している亜種が存在 • 対応 ◦ 方針を検討 ▪ 関数をクラス メソッドとし、共通部分 抽象クラスに引き上げる ◦ 処理を分割 (メソッド/関数抽出) ◦ 抽象クラスを定義 ◦ 重複部分を抽象クラスに引き上げ ◦ 各関数を具象クラス・メソッドに変換
  13. 長い関数 (Long Function) なぜダメ? • 大抵 場合、複数 処理が 1つ 関数・メソッドに詰め込まれていて全体

    見通し が悪く可読性が低い ◦ 概要 掴みづらさを補足するためにコメントが付与されていることも多い • それぞれ 処理を個別にテストできない
  14. 長い関数 (Long Function) public class OrderService { public double calculateFinalPrice(Order

    order) { double subtotal = 0; for (OrderItem item : order.getItems()) { subtotal += item.getUnitPrice() * item.getQuantity(); } // 割引の適用 double discount = 0; if (order.getCustomer().isPremium()) { discount = subtotal * 0.1; } else if (subtotal > 10000) { discount = subtotal * 0.05; } // 税金の計算 double tax = (subtotal - discount) * 0.1; // 配送料の計算 double shipping = subtotal > 5000 ? 0 : 500; return subtotal - discount + tax + shipping; } } 変数 再代入も起こりがち
  15. 長い関数 (Long Function) public class OrderService { private static final

    double TAX_RATE = 0.1; public double calculateFinalPrice(Order order) { double subtotal = calculateSubtotal(order); double discount = calculateDiscount(order, subtotal); double tax = calculateTax(subtotal, discount); double shipping = calculateShipping(subtotal); return subtotal - discount + tax + shipping; } private double calculateSubtotal(Order order) { return order.getItems() .stream() .mapToDouble(item -> item.getUnitPrice() * item.getQuantity()) .sum(); } // 以下略 }
  16. ネストが深い public class UserService { public boolean canAccessResource(User user, Resource

    resource) { if (user != null) { if (user.isActive()) { if (resource != null) { if (resource.isPublic() || user.hasPermission(resource)) { return true; } else { return false; } } else { return false; } } else { return false; } } else { return false; } } }
  17. ネストが深い public class UserService { public boolean canAccessResource(User user, Resource

    resource) { if (user == null) return false; if (!user.isActive()) return false; if (resource == null) return false; if (resource.isPublic()) return true; return user.hasPermission(resource); } }
  18. ネストが深いわけで ないけど…… if (updateFlag) { // なんか処理する // まだ処理してる //

    まだまだ続くよ // その後も続く何十行もの処理 // …… // …… // …… // …… // …… // …… // 100行以上処理が続いてたんじゃないかなぁ } else { throw new RuntimeException("updateFlag が false なのは不正") } 例外処理 先が原則
  19. リファクタリング 進め方 • まず テスト ◦ Unit テストがなけれ テストを作る ▪

    あるべき仕様がわからない場合 To Be で なく As Is で良い で作る • 小さな変更と変更 たび テスト ◦ 壊れたときにすぐわかる ▪ デバッグ範囲が限定される ◦ 成功したらコミット ▪ 壊れてもすぐに戻せる
  20. まとめ • リファクタリングと • なぜリファクタリングをする か • いつリファクタリングをする か •

    どこをリファクタリングする か (リファクタリング事例) • リファクタリング 進め方