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

[PHPカンファレンス沖縄2024]「無理なくできるだけ安全に」テストもないレガシーコードをリ...

Z.O.E.
September 27, 2024

 [PHPカンファレンス沖縄2024]「無理なくできるだけ安全に」テストもないレガシーコードをリファクタリングするテクニック

PHPCON沖縄2024 で登壇した際の資料です。
セッション詳細: https://fortee.jp/phpcon-okinawa-2024/proposal/4584f78e-6281-450d-8e8d-41495a89a6b7

#phpcon_okinawa #track_b

Z.O.E.

September 27, 2024
Tweet

More Decks by Z.O.E.

Other Decks in Programming

Transcript

  1. X(旧twitter): @for__3 #phpcon_okinawa #track_b リファクタしたいけど、つらい • リファクタしたいところ=複雑で分かりづらい ◦ 機能追加、改修した際にどこに影響があるかわからない ◦

    結果として→ バグりやすい=⼿を出しづらい 7 リファクタしたい ただリファクタすると‧‧‧ 複雑でバグりやすい 障害‧不具合発⽣!
  2. X(旧twitter): @for__3 #phpcon_okinawa #track_b リファクタしたいけど、つらい • リファクタしたいところ=複雑で分かりづらい ◦ 機能追加、改修した際にどこに影響があるかわからない ◦

    結果として→ バグりやすい=⼿を出しづらい 8 リファクタしたい 複雑でバグりやすい 障害‧不具合発⽣! ただリファクタすると‧‧‧ リファクタするための テクニックを使おう!
  3. X(旧twitter): @for__3 #phpcon_okinawa #track_b 補⾜: よりきれいにコードを分割していく • コードをよりリーダブルで保守性の⾼いものにしていくた めには、凝集度を上げ結合度を下げていく必要がある 23

    凝集度 1. 機能的凝集 2. 逐次的凝集 3. 通信的凝集 4. ⼿順的凝集 5. ⼀時的凝集 6. 論理的凝集 7. 偶発的凝集 結合度 1. 内容結合 2. 共通結合 3. ハイブリッド結合 4. 制御結合 5. スタンプ結合 6. データ結合 本セッションで紹介するテクニックは基本的に結合度を下げるためのテクニック 分かりやすく 機能追加し易い 影響受けにくく 壊れにくい
  4. X(旧twitter): @for__3 #phpcon_okinawa #track_b リファクタ対象をよく理解する • レガシーコードはソースコードから理解できない事が多い • ログを仕込みながら、わかったことはコードコメントに書 いていく

    ◦ 特に利⽤箇所と利⽤⽅法を明確にしておき、テストが書けるも のは書いておく • 過去のPRやコミットメッセージなども活⽤するとよい 28
  5. X(旧twitter): @for__3 #phpcon_okinawa #track_b 31 function traceLog(){ $traces = debug_backtrace(1);

    foreach($traces as $i => $trace){ if (!empty($trace['object']) && is_object($trace['object'])) { $trace['object'] = get_class($trace['object']).':class'; } if (is_array($trace['args'])) { foreach ($trace['args'] AS &$arg) { if (is_object($arg)) { $arg = get_class($arg).'::class'; } } } $text[$i] = ."#".$i." ".$trace['file'].'('.$trace['line'].') '; $text[$i].= (!empty($trace['object'])?$trace['object'].$trace['type']:''); $text[$i].= $trace['function'].'('.implode(', ',$trace['args']).')'; } echo implode("\n", $text) . "\n"; } debug_backtrace() サンプルコード
  6. X(旧twitter): @for__3 #phpcon_okinawa #track_b 32 public function case2($collection) { +

    traceLog(); $collection = $collection ->filter(function ($i) { return $i % 2 === 0; }) ->values() ->map(function ($i) { return $i * 3; }) debug_backtrace() サンプルコード
  7. X(旧twitter): @for__3 #phpcon_okinawa #track_b #0 /var/www/app/Console/Commands/MeasurePerformanceTrait.php(59) App\Console\Commands\ExampleLazyCollection:class->traceLog() #1 /var/www/app/Console/Commands/ExampleLazyCollection.php(36) App\Console\Commands\ExampleLazyCollection:class->case2(Illuminate\Support\LazyColl

    ection::class) #2 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36) App\Console\Commands\ExampleLazyCollection:class->handle() #3 /var/www/vendor/laravel/framework/src/Illuminate/Container/Util.php(41) Illuminate\Container\{closure}() #4 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93) unwrapIfClosure(Closure::class) #5 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(35) callBoundMethod(Illuminate\Foundation\Application::class, Array, Closure::class) 33 debug_backtrace() サンプルコード
  8. X(旧twitter): @for__3 #phpcon_okinawa #track_b • リファクタ対象をよく理解する ◦ debug_backtrace()を使う • 利⽤箇所が多い関数をリファクタする

    • でかいコードは細かく分割していく ⼿強いレガシーコードをリファクタするテクニック 34
  9. X(旧twitter): @for__3 #phpcon_okinawa #track_b phpstan/phpstan-deprecation-rules を利⽤する • リファクタ対象に @deprecated をつけ

    phpstan-deprecation-rules を使うことで利⽤している箇所 を⼀覧化することもできる 36
  10. X(旧twitter): @for__3 #phpcon_okinawa #track_b phpstan/phpstan-deprecation-rules を利⽤する • リファクタ対象に @deprecated をつけ

    phpstan-deprecation-rules を使うことで利⽤している箇所 を⼀覧化することもできる • ただし、こちらの場合はあくまで静的解析によるもので、 どういう引数が使われてるかまでは出せない 37
  11. X(旧twitter): @for__3 #phpcon_okinawa #track_b phpstan/phpstan-deprecation-rules を利⽤する • リファクタ対象に @deprecated をつけ

    phpstan-deprecation-rules を使うことで利⽤している箇所 を⼀覧化することもできる • ただし、こちらの場合はあくまで静的解析によるもので、 どういう引数が使われてるかまでは出せない • CIと組み合わせてdeprecatedが増えてないことをチェックするのも良い 38
  12. X(旧twitter): @for__3 #phpcon_okinawa #track_b • リファクタ対象をよく理解する ◦ debug_backtrace()を使う ◦ phpstan/phpstan-deprecation-rules

    を利⽤する • 利⽤箇所が多い関数をリファクタする • でかいコードは細かく分割していく ⼿強いレガシーコードをリファクタするテクニック 40
  13. X(旧twitter): @for__3 #phpcon_okinawa #track_b 43 $ find . -name "*.php"

    | xargs grep 'mbTrim' ./src/app/Http/Controllers/Front/ArticlesController.php: $name = \Text::mbTrim($request->input('name')); ./src/app/Http/Controllers/Front/ArticlesController.php: $title = \Text::mbTrim($request->input('title')); ./src/app/Http/Controllers/Front/ArticlesController.php: $body = \Text::mbTrim($request->input('body')); …etc 例) mbTrimをリファクタしたい
  14. X(旧twitter): @for__3 #phpcon_okinawa #track_b 44 class Text { public static

    function mbTrim($text) { // 元の処理 } + public static function mbTrim2($text) + { + // 新しい処理 + } 例) 新しい処理をmbTrim2として追加
  15. X(旧twitter): @for__3 #phpcon_okinawa #track_b 46 //利用箇所 $name = \Text::mbTrim($request->input('name')); +

    $name2 = \Text::mbTrim2($request->input('name')); + // $name と $name2の結果が違うときだけログを出力 + if ($name !== $name2) { + \Log::info('name: ' . $name . ' name2: ' . $name2); + } 例) 追加したmbTrim2を処理にいれる
  16. X(旧twitter): @for__3 #phpcon_okinawa #track_b 利⽤箇所が多い関数をリファクタするのは⼤変 • 利⽤箇所が多い関数をリファクタする際は、既存関数と別 で関数を分けてリファクタしよう • 既存関数と新規関数をどちらも実⾏して、実⾏結果が違う

    ときだけログなどに出⼒するようにし、実際のロジックに は既存関数の処理を使うと良い 47 → 要は既存ロジックの実⾏結果を元にスナップショットテストを やっているようなイメージ
  17. X(旧twitter): @for__3 #phpcon_okinawa #track_b 49 //利用箇所 $name = \Text::mbTrim($request->input('name')); +

    // featureフラグのときはリファクタしたmbTrim2を使う + if ($onRefactor) { + $name = \Text::mbTrim2($request->input('name')); + } 例) 特定条件のときだけリファクタした関数を使う
  18. X(旧twitter): @for__3 #phpcon_okinawa #track_b 50 //利用箇所 $name = \Text::mbTrim($request->input('name')); +

    // featureフラグのときはリファクタしたmbTrim2を使う + if ($onRefactor) { + $name = \Text::mbTrim2($request->input('name')); + } or + // 1%の確率でリファクタしたmbTrim2を使う + if (mt_rand(1, 100) <= 1) { + $name = \Text::mbTrim2($request->input('name')); + } 例) 特定条件のときだけリファクタした関数を使う
  19. X(旧twitter): @for__3 #phpcon_okinawa #track_b • リファクタ対象をよく理解する ◦ debug_backtrace()を使う ◦ phpstan/phpstan-deprecation-rules

    を利⽤する • 利⽤箇所が多い関数のリファクタテクニック • でかいコードは細かく分割していく ⼿強いレガシーコードをリファクタするテクニック 52
  20. X(旧twitter): @for__3 #phpcon_okinawa #track_b 補⾜: よりきれいにコードを分割していく • コードをよりリーダブルで保守性の⾼いものにしていくた めには、凝集度を上げ結合度を下げていく必要がある 57

    本セッションで紹介するテクニックは基本的に結合度を下げるためのテクニック 分かりやすく 機能追加し易い 影響受けにくく 壊れにくい 凝集度 1. 機能的凝集 2. 逐次的凝集 3. 通信的凝集 4. ⼿順的凝集 5. ⼀時的凝集 6. 論理的凝集 7. 偶発的凝集 結合度 1. 内容結合 2. 共通結合 3. ハイブリッド結合 4. 制御結合 5. スタンプ結合 6. データ結合
  21. X(旧twitter): @for__3 #phpcon_okinawa #track_b 59 株式会社ウィルゲート 10年⽬ シニアマネージャー∕VPoE やってること -

    教育∕1on1∕採⽤ - PM∕SRE∕インフラ 興味あること - オブザーバビリティ∕⾃動化∕開発⽣産性∕PHP - 沖縄の美味しいご飯 池添 誠(いけぞえ まこと)