Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

リファクタリングのための第一歩

 リファクタリングのための第一歩

リファクタリングに関する勉強会資料です!

More Decks by NearMeの技術発表資料です

Other Decks in Programming

Transcript

  1. 1 ⽬次 1. リファクタリングとは? 2. リファクタリングの例2つ a. メソッドチェーンの利⽤ b. ポリモーフィズム

    3. リファクタリングは本当に必要か? 4. リファクタリングのユースケース 5. まとめ ※細かいリファクタリング⼿法については触れていません!
  2. 3 リファクタリングとは? • リファクタリング(Refactoring) ◦ 外部的な振る舞いはそのままで、内部のコードの可読性を⾼めるなどの 内部構造の改善のこと Ex) - 不要な定数の削除

    - 異なるがカプセル化できる様な関数群をまとめる - あるキャンペーン⽤の料⾦計算ロジックなど - ifロジックなどでのネストの深いコードの改善 - early return などでネストを減らす etc…
  3. 5 リファクタリングの例 • 汎⽤的な for ループをメソッドチェーンに置き換える Ex) 配列 nums から、偶数であるものを取り出して表⽰する

    const nums = [1, 2, 3, 5, 8, 13] const result: number[] = [] for (const num of nums) { if (num % 2 === 0) { result.push(num) } } console.log(result)
  4. 6 リファクタリングとは? • 汎⽤的な for ループをメソッドチェーンに置き換える Ex) 配列 nums から、偶数であるものを取り出して表⽰する

    const nums = [1, 2, 3, 5, 8, 13] const result: number[] = [] for (const num of nums) { if (num % 2 === 0) { result.push(num) } } console.log(result) forの中全体を確認することで、 フィルタする処理だということがわかる
  5. 7 リファクタリングとは? • 汎⽤的な for ループをメソッドチェーンに置き換える Ex) 配列 nums から、偶数であるものを取り出して表⽰する

    const nums = [1, 2, 3, 5, 8, 13] const result: number[] = [] for (const num of nums) { if (num % 2 === 0) { result.push(num) } } console.log(result) forの中全体を確認することで、 フィルタする処理だということがわかる フィルタすることは分かりやすく示したい!
  6. 8 リファクタリングとは? • 汎⽤的な for ループをメソッドチェーンに置き換える Ex) 配列 nums から、偶数であるものを取り出して表⽰する(filterメソッドの利⽤)

    const nums = [1, 2, 3, 5, 8, 13] const result: number[] = [] for (const num of nums) { if (num % 2 === 0) { result.push(num) } } console.log(result) const nums = [1, 2, 3, 5, 8, 13] const result = nums.filter( (num) => num % 2 === 0 ) console.log(result)
  7. 9 リファクタリングの例 • ポリモーフィズムの利⽤ Ex) Dog, Catクラスにて、鳴き声を出⼒する class Dog {

    makeSound(): string { return "Woof"; } } class Cat { makeSound(): string { return "Meow"; } } function playWithDog(dog: Dog) { console.log(dog.makeSound()); } function playWithCat(cat: Cat) { console.log(cat.makeSound()); } const myDog = new Dog(); const myCat = new Cat(); playWithDog(myDog); playWithCat(myCat);
  8. 10 リファクタリングの例 • ポリモーフィズムの利⽤ Ex) Dog, Catクラスにて、鳴き声を出⼒する class Dog {

    makeSound(): string { return "Woof"; } } class Cat { makeSound(): string { return "Meow"; } } function playWithDog(dog: Dog) { console.log(dog.makeSound()); } function playWithCat(cat: Cat) { console.log(cat.makeSound()); } const myDog = new Dog(); const myCat = new Cat(); playWithDog(myDog); playWithCat(myCat); やっていることは同じ (コードの重複)
  9. 11 リファクタリングの例 • ポリモーフィズムの利⽤ Ex) Dog, Catクラスにて、鳴き声を出⼒する abstract class Animal

    { abstract makeSound(): string; move(): void { console.log("Moving..."); } } class Dog extends Animal { makeSound(): string { return "Woof"; } } class Cat extends Animal { makeSound(): string { return "Meow"; } } function playWithAnimal(animal: Animal) { console.log(animal.makeSound()); } const myDog = new Dog(); const myCat = new Cat(); playWithAnimal(myDog); playWithAnimal(myCat); コードの重複の解消 抽象クラスの作成
  10. 18 リファクタリングのための現状分析 • 該当コードになった経緯の追跡⽅法 (1) Git Blame - コードベースで blame

    から対象PRを探し出して、そこからタスクチケットなどを頼り に当時のコンテキストを追う
  11. 19 リファクタリングのための現状分析 • 該当コードになった経緯の追跡⽅法 (1) Git Blame - コードベースで blame

    から対象PRを探し出して、そこからタスクチケットなどを頼り に当時のコンテキストを追う → PRのDescriptionや、元のタスクチケットなどが整理されていない場合、どうしてその 様なコードになったのかなどは追いきれない可能性がある → Design Docや、タスクチケットは、どちらかというと意思決定の最終段階に偏っている ことが多い
  12. 21 リファクタリングのための現状分析 • 該当コードになった経緯の追跡⽅法 ⭐ (2) ADR - Architectural Decision

    Record - 意思決定のプロセスをドキュメントとして保存しておくもの → 過去のコンテキスト、意思決定のための⽐較検討、ステータスを確認できる! → 過去の意思決定を踏まえて、そもそもリファクタリングの必要性、作業にかかりそうな コスト予測がよりしやすくなる!
  13. 22 リファクタリングのための現状分析 • 該当コードになった経緯の追跡⽅法 ⭐ (2) ADR - Architectural Decision

    Record - 意思決定のプロセスをドキュメントとして保存しておくもの → 過去のコンテキスト、意思決定のための⽐較検討、ステータスを確認できる! → 過去の意思決定を踏まえて、そもそもリファクタリングの必要性、作業にかかりそうな コスト予測がよりしやすくなる! ※ADRについては過去の勉強会で扱っています → https://speakerdeck.com/nearme_tech/architecture-decision-record-adr
  14. 26 ビジネスサイドの理解とリファクタリング • リファクタリングとビジネス要件の衝突例 Ex) キャンペーン計算ロジック • 現状 ◦ 複雑なキャンペーンロジックがコードベースに埋め込まれている

    ◦ 新⼈エンジニアが「コードが汚い」という理由だけでリファクタリングを実施 • 結果 ◦ ビジネス要件(特定条件での割引適⽤など)が失われる ◦ 顧客への請求ミスや収益減少などの重⼤な問題につながる
  15. 29 ビジネスサイドの理解とリファクタリング (1) ビジネス背景の把握 • 前提 ◦ 意思決定は、ADRやその他ドキュメントなどでまとめておく • やること

    ◦ 過去のドキュメント(ADRや設計書)やGit履歴から背景を追跡 ◦ 関係者(プロダクトマネージャー、営業チームなど)との対話を通じて、コードに埋め込ま れたビジネスルールを確認
  16. 34 リファクタリングの際に考慮する「複雑性」 • 良い複雑性 ◦ ⽬的が明確 ▪ 複雑性がビジネス価値や競争優位性を⽣み出すために存在している • Ex)

    特定顧客向けのカスタマイズ機能や業界特化のルール ◦ ⾃律性がある ▪ 各コンポーネントが独⽴しており、他に影響を与えず変更‧運⽤可能 ◦ 明瞭さ ▪ コードや設計が⾃⼰説明的であり、意図がすぐに理解できる ◦ ユニークさ ▪ 各部分が特定の役割を持ち、他と重複していない
  17. 35 リファクタリングの際に考慮する「複雑性」 • 悪い複雑性 ◦ ⽬的不明確 ▪ なぜそのコードや設計が存在するのか理解できない ◦ 相互依存性が⾼い

    ▪ ⼀部を変更すると他の部分に影響が波及する ◦ 不透明さ ▪ コードや設計が分かりづらく、意図や動作が把握困難 ◦ 冗⻑性 ▪ 同じような機能や処理が複数箇所に存在
  18. 37 「複雑性」をフレームワークで分類する 〜クネビン‧フレームワーク〜 1. 単純(Simple) - 問題例 - コード内のハードコーディングされた定数を削除し、設定ファイルに移動する -

    特徴 - 解決策が明確で、標準的な⼿順で対応可能 - 対応策 - 定数を設定ファイルに移動し、コードから参照するように変更
  19. 38 「複雑性」をフレームワークで分類する 〜クネビン‧フレームワーク〜 2. 困難(Complicated) - 問題例 - ⼤規模なクラスが単⼀責任の原則を犯しているため、リファクタリングして責務を 分割する必要がある

    - 特徴 - 専⾨知識が必要で、複数の設計オプションが存在 - 最適な解決策を⾒つけるためには分析が必要 - 対応策 - クラスの役割を分析し、関連する機能ごとに新しいクラスやモジュールに分割
  20. 39 「複雑性」をフレームワークで分類する 〜クネビン‧フレームワーク〜 3. 複雑(Complex) - 問題例 - ユーザーインターフェースの改善によるユーザーエクスペリエンス向上を⽬指すリ ファクタリング

    - 特徴 - 因果関係が不明瞭で、ユーザーの反応が予測しづらい - 試⾏錯誤による学習が必要 - 対応策 - プロトタイプを作成し、ユーザーテストを繰り返してフィードバックを得る。
  21. 40 「複雑性」をフレームワークで分類する 〜クネビン‧フレームワーク〜 4. 混沌(Chaos) - 問題例 - リリース直前に発⾒された重⼤なバグの修正 -

    特徴 - 即座の対応が求められ、原因究明よりもまずはバグ修正が優先される - 対応策 - 迅速に修正を⾏い、テストを実施して影響範囲を確認 - 根本原因を分析し再発防⽌策を講じる
  22. 41 「複雑性」をフレームワークで分類する 〜クネビン‧フレームワーク〜 5. 無秩序(Disorder) - 問題例 - 複数の異なる技術スタックやアーキテクチャが混在しているプロジェクトで、どこ から⼿をつけるべきか不明

    - 特徴 - 問題がどの領域に属するか不明確で、どこから⼿をつけるべきか判断しづらい - 対応策 - 状況を整理し、それぞれの領域に分類してから適切な対応策を決定する
  23. 43 リファクタリングユースケース • メルペイの「あと払い」サービスリファクタリング - コンテキスト - ⻑年のコーディングによるコードの負債の積み重ね - アプローチ

    - SOLID原則に基づき、コードの責務を明確化し、依存関係を整理 - マイクロサービス化を進め、独⽴性と可読性を向上 - 成果 - コード変更時の影響範囲が明確になり、⽣産性が向上 - テストスタブを⽤いたユニットテストの効率化に成功 https://engineering.mercari.com/blog/entry/20221221-refactoring-along-solid/ S : 単⼀責任の原則(SRP) O :オープン‧クローズドの原則(OCP) L : リスコフの置換原則(LIP) I : インタフェース分離の原則(ISP) D: 依存関係逆転の原則(DIP)
  24. 44 リファクタリングユースケース • PIP-Makerの⼤規模リファクタリング - コンテキスト - Adobe FlashからHTML5やJavaScriptへの移⾏が必要となり、⼤規模な技術移⾏が 求めらていた

    - アプローチ - フロントエンドとバックエンドでTypeScriptを採⽤し、技術的親和性を⾼める - IaaSからPaaSへの移⾏を通じて、システムの拡張性と保守性を向上 - 成果 - 成果処理速度と操作性が⼤幅に改善され、ユーザー満⾜度が向上 https://sg.wantedly.com/companies/company_641519/post_articles/515613
  25. 45 リファクタリングユースケース • ラクスによる技術的負債解消プロジェクト - コンテキスト - ⻑年放置されていた技術的負債が開発効率を低下させていた - アプローチ

    - リファクタリング対象をパターン分けし、優先順位を設定 - 開発チームとビジネスサイドで共有しながら進⾏ - 成果 - 可読性の向上により、レビュー効率が改善 - 開発チーム全体で納得感を持ちつつリファクタリングを実施 https://logmi.jp/brandtopics/323993
  26. 46 参考⽂献(1/2) • 糞コードは直すな。 ◦ https://qiita.com/kotauchisunsun/items/d03c1e6936ffb250e4a1 • 新卒エンジニアが3ヶ⽉間のリファクタで学んだこと ◦ https://techblog.openwork.co.jp/entry/2022/05/20/121238

    • リファクタリングをする際にソースコードの設計からはじめてはいけない ◦ https://tech-blog.monotaro.com/entry/2023/11/28/090000 • ラクスによる技術的負債解消プロジェクト’ ◦ https://logmi.jp/brandtopics/323993 • PIP-Makerの⼤規模リファクタリング ◦ https://sg.wantedly.com/companies/company_641519/post_articles/515613
  27. 47 参考⽂献(2/2) • メルペイの「あと払い」サービスリファクタリング ◦ https://engineering.mercari.com/blog/entry/20221221-refactoring-along-solid/ • クネビン(カネヴィン)フレームワークとは?分類やスクラム開発導⼊時の活⽤例を解説 ◦ https://hirolaboratory.com/%E3%82%AF%E3%83%8D%E3%83%93%E3%83%

    B3%E3%82%AB%E3%83%8D%E3%83%B4%E3%82%A3%E3%83%B3%E3%83 %95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%8 2%AF%E3%81%A8%E3%81%AF%EF%BC%9F%E5%88%86%E9%A1%9E%E3% 82%84/ • リファクタリング(第2版)- 既存のコードを安全に改善する - ◦ https://www.ohmsha.co.jp/book/9784274224546/