97
2014/9/27 レガシーコード改善勉強会 #wewlc_jp PHP版レガシーコード改善に 役立つ新パターン ヤフー株式会社 佐藤祐司

PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

  • Upload
    yahoo

  • View
    18.004

  • Download
    1

Embed Size (px)

DESCRIPTION

9/27に行われたレガシーコード改善勉強会で発表された資料です。 http://passmarket.yahoo.co.jp/event/show/detail/01pitgwzj67m.html

Citation preview

Page 1: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

2014/9/27 レガシーコード改善勉強会 #wewlc_jp

PHP版レガシーコード改善に 役立つ新パターン

ヤフー株式会社 佐藤祐司

Page 2: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

自己紹介

佐藤 祐司 •  2010年新卒入社 •  Webエンジニア •  PHP

@kuidaoring

Page 3: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

今日話すこと

• 私の業務とレガシーコード • パターンの紹介 • これで幸せになれるのか

Page 4: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

今日話すこと

• 私の業務とレガシーコード • パターンの紹介 → 本日のメイン • これで幸せになれるのか

Page 5: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

今日話すこと

•  レガシーコード改善の具体的な リファクタリングの具体的な方法

•  今あるコードを何とかテストで保護して、自分が手を加える部分のテストを 書けるようにしたい

Page 6: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

今日話すこと

•  レガシーコード改善の具体的な リファクタリングの具体的な方法

•  今あるコードを何とかテストで保護して、自分が手を加える部分のテストを 書けるようにしたい

Page 7: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

私の業務とレガシーコード

Page 8: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 9: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

サービス

サービス規模 •  58.3億PV / 月 •  1.4億UB / 月

出典:Yahoo! JAPAN 媒体資料 2014年6月改訂版 (PDF) http://i.yimg.jp/images/marketing/portal/paper/media_sheet_open.pdf

Page 10: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

サービス

トップページを作る部署の中の一つ PJメンバー約40名 •  開発約20名

PC …スマホ アプリ

ここ

Page 11: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

業務とレガシーコード

こんなことありませんか? •  少ない自動テスト •  リリース当時の開発メンバーはゼロ •  どんどん開発メンバーが増える

どうしてこんなふうになったのか

Page 12: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

新規リリース時

まずはローンチを目指す •  ローンチ、リリース優先 •  ヒットするかはわからない

ローンチだ!

Page 13: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

リリース後

サービスの成長を目指す •  機能を増やす •  メンバーも少しづつ増える

機能追加だ!

Page 14: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

ある程度成熟して振り返ると

•  メンバーの入れ替わり •  歴史的経緯によるコード •  場当たり的な修正

とりあえず直しました…!

今⽇日からよろしくお願いします!

Page 15: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

業務とレガシーコード

•  その時の判断が間違いだったかは わからない

•  優先順位の話 •  ただしずっとそのままでいい というわけではない

Page 16: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

業務とレガシーコード

過去には一部似た状況になったが、 •  有志による改善活動 •  社内でもノウハウがたまってきた

Page 17: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

ユニットテスト拡充 CI整備

Page 18: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

開発フロー整備

Page 19: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

業務とレガシーコード

どうしたか •  レガシーコードを地道に改善 •  レガシーコードを作りにくくする 仕組みや体制を整備

地道に少しづつ改善を進めた

Page 20: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

パターンの紹介

Page 21: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

パターンの紹介

•  レガシーコード改善の具体的な リファクタリングの具体的な方法

•  今あるコードを何とかテストで保護して、自分が手を加える部分のテストを 書けるようにしたい

Page 22: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

PHPって

•  Webに特化

•  置けば動く •  HTMLに埋め込める

•  テンプレートエンジン •  標準関数が豊富

Page 23: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

PHPにおけるレガシーコード

•  環境に依存 •  構造を持たない •  スーパーグローバル変数

•  $_GET, $_POST, $_SESSION など •  不用意なexit

Page 24: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

PHPにおけるレガシーコード

サンプルコードを用いて説明 •  adminユーザと一般ユーザで見せる メニューを変更する

Page 25: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 26: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする 2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 27: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする 2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 28: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

なにが問題になるか

•  DBに接続する •  exitでスクリプトが終了 •  getパラメータが必要 •  ビューとロジックの混在

Page 29: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

DBに接続

Page 30: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

exitで終了

Page 31: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

スーパーグローバル変数を参照して パラメータ取得

Page 32: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

ビューとロジックが混在

Page 33: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どうにかしてテストを 書けるようにするためのパターン

Page 34: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

•  関数オーバーライド •  ラップ関数

Page 35: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

関数オーバーライド

Page 36: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

関数オーバーライド

•  名前空間を使って組み込み関数などを 上書き

•  実際には上書きしてない •  元ネタ PHPでネイティブ関数を含むコードのテスタビリティを上げる2つの方法 - 絶品ゆどうふのタレ http://yudoufu.hatenablog.jp/entry/20110808/1312828535

•  名前空間が接合部になる

Page 37: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

index.php func()

関数オーバーライド

外部 リソース

プロダクトコード

Page 38: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

index.php func()

関数オーバーライド

外部 リソース

プロダクトコード

func()

テストコード

Page 39: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

接合部

接合部とは、その場所を直接変更しなくても、プログラムの振る舞いを変えることの出来る場所である

レガシーコード改善ガイドより

Page 40: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 41: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

コンストラクタで 渡すオブジェクトによって振る舞いを変更できる

Page 42: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

名前空間

PHP5.3から導入 PHP: 名前空間 ‒ Manual

http://php.net/manual/ja/language.namespaces.php

他の言語のパッケージやモジュールに相当

Page 43: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

名前空間

名前空間の影響を受けるもの •  class •  interface •  trait(5.4以降) •  関数 •  定数(const)

Page 44: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

定義

Page 45: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

参照

Page 46: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

名前空間

•  名前空間の中であれば、 組み込み関数やグローバルな関数と 同じ名前の関数を定義できる

•  同じ名前空間内であれば、 名前空間の指定を省略できる

Page 47: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 48: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

組み込み関数と同じ名前で定義できる

Page 49: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

名前空間内の関数が呼ばれる

Page 50: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

関数オーバーライド

どういう時に有効か •  上書きしたい対象が関数でしか 用意されていない 外部の影響を受ける組み込み関数を そのまま使っている場合など

Page 51: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

ラップ関数

Page 52: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

ラップ関数

変数の参照などを直接行うのではなく 関数を経由して行うようにする

Page 53: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

Page 54: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

なにが問題になるか

•  DBに接続する •  exitでスクリプトが終了 •  getパラメータが必要 •  ビューとロジックの混在

Page 55: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする •  環境の分離 •  スーパーグローバル変数の間接参照 •  exitの検討

2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 56: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする •  環境の分離 •  スーパーグローバル変数の間接参照 •  exitの検討

2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 57: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

環境の分離

テスト実行時にもDBに接続しにいってしまう •  DBに接続する関数をなんとかしたい •  関数オーバーライドを使う

Page 58: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

環境の分離

1. テスト対象のスクリプトに 名前空間の定義を追加

2. テストコードでテスト対象と 同じ名前で名前空間の定義を追加

3. 上書きしたい関数と同じ名前の 関数をテストコードで定義

Page 59: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

環境の分離

1. テスト対象のスクリプトに 名前空間の定義を追加

2. テストコードでテスト対象と 同じ名前で名前空間の定義を追加

3. 上書きしたい関数と同じ名前の 関数をテストコードで定義

Page 60: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 61: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

環境の分離

1. テスト対象のスクリプトに 名前空間の定義を追加

2. テストコードでテスト対象と 同じ名前で名前空間の定義を追加

3. 上書きしたい関数と同じ名前の 関数をテストコードで定義

Page 62: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 63: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

すでに他の名前空間があればその下に追加

Page 64: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

環境の分離

1. テスト対象のスクリプトに 名前空間の定義を追加

2. テストコードでテスト対象と 同じ名前で名前空間の定義を追加

3. 上書きしたい関数と同じ名前の 関数をテストコードで定義

Page 65: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 66: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 67: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

index.php db_get_user()

関数オーバーライド

プロダクトコード

DB

Page 68: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

index.php

関数オーバーライド

プロダクトコード

テストコード

db_get_user() DB

db_get_user()

Page 69: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

注意

名前空間を定義することで 関数が呼び出せなくなる場合がある

Page 70: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

名前空間の定義を 追加

Page 71: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

参照できなくなる

Page 72: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

名前空間の影響

どうするか •  関数を名前空間の外に再定義

Page 73: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 74: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする •  環境の分離 •  スーパーグローバル変数の間接参照 •  exit, dieの検討

2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 75: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

スーパーグローバル変数

変数なので代入して書き換えが可能 •  他のテストケースに影響してしまう •  変数を直接参照しなければいい •  ラップ関数を使う

Page 76: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 77: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする •  環境の分離 •  スーパーグローバル変数の参照 •  exitの検討

2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 78: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

exitの検討

exitのなにが問題になるか •  スクリプトを終了 PHPUnit自体も終了する

•  exitは関数ではない

Page 79: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 80: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 81: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

実行されない

Page 82: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

exit、dieの必要性の検討

成功・失敗も わからず終了

Page 83: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

exitの検討

exitは言語構造、予約語 •  名前空間内でも「exit」という 名前の関数を定義できない

•  「関数オーバーライド」が使えない

Page 84: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

exitの検討

どうするか •  returnの代わりに使ってませんか

•  returnに変える •  exitする部分をラップ関数にする

•  その後に関数オーバーライドを使う

Page 85: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 86: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp
Page 87: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする 2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 88: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

テスト

•  ビューとロジックが混ざっているので ロジックのみの検証はおそらく不可能

•  条件によって変わるビューを検証する

•  assertRegex, assertContains

•  HTMLであればassertTagなど

Page 89: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

どう対応するのか

1. テストを書けるようにする 2. テストで保護する 3. リファクタリング 4. テスト、リファクタリングを繰り返す

Page 90: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

リファクタリング

まずはベタなものを適切な単位に分離 •  ファイルを分離 •  関数に分離 •  クラスに分離

Page 91: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

課題

globalが邪魔をする •  グローバル変数ではなくなる ことによってうまく動作しなくなる

•  「コンパイラまかせ」が使えないので 動かすまでわからない

Page 92: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

課題

•  関数内でグローバル変数を参照している 部分を引数に置き換えるように リファクタリングする

•  地道に。。。 •  参考 テスト不能な PHP コードをリファクタリングするための戦略http:/www.ibm.com/developerworks/jp/opensource/library/os-refactoringphp/

Page 93: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

これで幸せになれるのか

Page 94: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

これで幸せになれるのか

これだけで根本解決はできない •  とりあえずテストは書けるようになった •  適用できるケースは それほど多くない(かも)

•  今回は「今」できること

Page 95: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

レガシー

小改善 テストで 保護

リファクタリング

イケてる

Page 96: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

これで幸せになれるのか

全体を考えて設計の変更などを 行う必要がある •  フラットなPHPからフレームワークへ •  地道にテストコードを増やしていく 継続的な改善が必要

Page 97: PHP版レガシーコード改善に役立つ新パターン #wewlc_jp

ありがとうございました