Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
【PHP】破壊的バージョンアップと戦った話〜決断と説得
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Satoshi Kaneyasu
January 19, 2025
Programming
540
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
【PHP】 破壊的バージョンアップと戦った話〜決断と説得
Satoshi Kaneyasu
January 19, 2025
More Decks by Satoshi Kaneyasu
See All by Satoshi Kaneyasu
運用ダッシュボードの設計を誰も教えてくれないのだけどみなさんどうしてるんですか? - チームに監視するという文化を根付かせるための第一歩を踏みたい -
satoshi256kbyte
0
27
AWS CDK ExpressモードとCI/CDの組み合わせ
satoshi256kbyte
1
38
AWS CDK ExpressモードとCI/CDの組み合わせ
satoshi256kbyte
0
16
AWS re:Invent 2025の少し振り返り + DevOps AgentとBacklogを連携させてみた
satoshi256kbyte
3
220
Amazon_Cognito_で構築する_スケーラブルな_Web_アプリケーション__シングルページ_Web_アプリケーションに認証を組み込む
satoshi256kbyte
0
43
人間とAI、どちらが書いたコードもCI/CDでチェックしてみよう
satoshi256kbyte
0
45
今こそ押さえておきたい アマゾンウェブサービス(AWS)の データベースの基礎 おもクラ #6版
satoshi256kbyte
1
290
今こそ押さえておきたい アマゾンウェブサービス(AWS)の データベースの基礎
satoshi256kbyte
1
63
人間とAI、どちらが書いたコードもCICDでチェックしてみよう
satoshi256kbyte
1
93
Other Decks in Programming
See All in Programming
SLOをサービス品質の共通言語にするために 取り組んできたこと
wakana0222
0
560
改善しないと、タスクが回らない。 “てんこ盛りポジション” を引き継いだ情シスの、入社3ヶ月の業務改善録
krm963
0
190
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
450
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
110
5分で問診!Composer セキュリティ健康診断
codmoninc
0
610
はてなアカウント基盤 State of the Union
cockscomb
1
1.3k
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
270
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
180
今さら聞けない .NET CLI
htkym
0
130
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
370
吝嗇家のためのAI活用 / AI development for miser - ChatGPT + Issue Driven Development
tooppoo
0
200
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
140
Featured
See All Featured
Fireside Chat
paigeccino
42
4k
Claude Code のすすめ
schroneko
67
230k
Automating Front-end Workflow
addyosmani
1370
210k
Music & Morning Musume
bryan
47
7.3k
Optimising Largest Contentful Paint
csswizardry
37
3.8k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
160
The untapped power of vector embeddings
frankvandijk
2
1.8k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
How to Think Like a Performance Engineer
csswizardry
28
2.7k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
390
Being A Developer After 40
akosma
91
590k
It's Worth the Effort
3n
188
29k
Transcript
【PHP】 破壊的バージョンアップと 戦った話〜決断と説得 PHP勉強会in広島 vol.3 2025.01.24 SATOSHI KANEYASU 1
自己紹介 氏名:兼安 聡 所属:株式会社サーバーワークス アプリケーションサービス部 在住:広島(フルリモート) 担当:DevOps、技術支援、PM、SM 2024 Japan AWS
Top Engineers (Database) 2024 Japan AWS All Certifications Engineers Certified ScrumMaster PMP X:@satoshi256kbyte 2
このお話の設定 ➢PHP5→PHP7.3、同時にCakePHP2→CakePHP4への移行を行った話をします。 ➢この話に出てくるプロジェクトは最終的に無事終わってるので安心してください。 ➢インフラ周りの話は今回は対象外とします。 ➢システムはBtoBのWEBシステムです。 ➢サポート切れに伴うバージョンバップです。 ➢故に、期限はマストです。 ➢既存コードに自動テストはありません。 ➢チームにオリジナルの開発者はいません。 ➢CakePHPは2から3の間に破壊的バージョンアップがなされています。
3
CakePHPの破壊的バージョンアップとは 主にモデルの扱いが大幅に変わり、 同名メソッドでも入出力フォーマットが変わっていることを指します。 // CakePHP 2.x $this->loadModel('Post'); $post =
$this->Post->find('first', [ 'conditions' => ['Post.id' => 1] ]); echo $post['Post']['title']; [ 'Post' => [ 'id' => 1, 'title' => 'CakePHP 2 example' ] ] use Cake\ORM\TableRegistry; $postsTable = TableRegistry::getTableLocator()- >get('Posts'); $post = $postsTable->find('all') ->where(['id' => 1]) ->first(); echo $post->title; App\Model\Entity\Post Object ( [id] => 1 [title] => 'CakePHP 3 example' ) CakePHP2 CakePHP3以降 出力形式 出力形式 4
CakePHPの破壊的バージョンアップとは 主にモデルの扱いが大幅に変わり、 同名メソッドでも入出力フォーマットが変わっていることを指します。 // CakePHP 2.x $this->loadModel('Post'); $post =
$this->Post->find('first', [ 'conditions' => ['Post.id' => 1] ]); echo $post['Post']['title']; [ 'Post' => [ 'id' => 1, 'title' => 'CakePHP 2 example' ] ] use Cake\ORM\TableRegistry; $postsTable = TableRegistry::getTableLocator()- >get('Posts'); $post = $postsTable->find('all') ->where(['id' => 1]) ->first(); echo $post->title; App\Model\Entity\Post Object ( [id] => 1 [title] => 'CakePHP 3 example' ) CakePHP2 CakePHP3以降 出力形式 出力形式 5 昔ながらの配列を中心とした実装は、 中に何が入っているか不明瞭で、バグの温床となりやすい。 (いわゆる配列地獄) これの是正がフォーマットが変えられた理由(だと思う)
いきなり頓挫したところから話を始めます ➢PHP5→PHP7.3、同時にCakePHP2→CakePHP4への移行プロジェクト ➢厳密には、プロジェクト内でCakePHP2→3→4というステップを踏ませている ➢これを、各開発者が既存ソースを解析して新Verで作り直すという方法で開始。 ➢途中まで進めてこのままでは頓挫する!となったところから話は始まります。 ➢なお、新Verで作り直すという方針においては、一定のルール作成とトレーニング期間を設けており、 各開発者に特段の落ち度はありませんでした。 ➢純粋に難易度が高すぎ・作業量が多すぎで無理だった次第です。 6
4つの決断と説得 ➢プロジェクトを立て直し、最後までやり切るまでに4つの決断と説得を行っています。 変換関数を作り、既存コードを活かす 安易にフレームワークを乗り換えない 静的解析とフォーマッターをフル活用する ソースコードのメトリクスを算出する 7
決断と説得その1 8
変換関数を作り、既存コードを活かす 決断 • 入出力フォーマットが変わっている全ての 箇所について、変換関数をかまして極力 既存コードを流用できる方向にする • モダンなコーディングスタイルの優先度を 下げる 上層部への説得
• 方針転換により作業が一旦停滞するが、 コードが流用可能になるのでリカバリでき る • 既存コードが流用可能となれば、バグが あるとしたら変換関数がおかしいか、変換 関数がうまく適用できてないかになる。 • となれば、動かして通れば大体OKといえ る • テストに人海戦術が効かせやすく、プロ ジェクト後半のリスクが下がる 9
変換関数適用の流れ $this->loadModel('Post'); $post = $this->Post->find('first', ['conditions' => ['Post.id' => 1]])
$this->loadModel('Post'); $post = $this->Post->findOld('first', ['conditions' => ['Post.id' => 1]]) 入出力フォオーマットが変わっているメソッドを、一旦一括でリネームする リネームしたfindOldは、中で新旧メソッドの入出力フォーマットの変換を行う findOldは、@depcreatedをつけて将来的には廃止する意思を示しておく。 実際にはモデルだけでなく、バリデーションなど各所に変換関数を仕込む。 CakePHP2 10
変換関数適用の流れ /** * @deprecated 本メソッドは新規コードには使用しないでください */ public function findOld($type, $params
= []) { $where = $params['where'] ?? []; $query = null; select ($type) { case 'first': // ここで新しいfind()を呼ぶ $query = $this->find('all’)->where($where)->first(); break; // 以下、他の$type } if ($query === null) { return []; } // 新しいfind()は遅延実行のクエリを返すので、ここで実行しつつ配列に変換 $results = $query->toArray(); // CakePHP 2の配列構造に変換 return 変換メソッド($results); } 11
変換関数適用の流れ /** * @deprecated 本メソッドは新規コードには使用しないでください */ public function findOld($type, $params
= []) { $where = $params['where'] ?? []; $query = null; select ($type) { case 'first': // ここで新しいfind()を呼ぶ $query = $this->find('all’)->where($where)->first(); break; // 以下、他の$type } if ($query === null) { return []; } // 新しいfind()は遅延実行のクエリを返すので、ここで実行しつつ配列に変換 $results = $query->toArray(); // CakePHP 2の配列構造に変換 return 変換メソッド($results); } 12 パラメータ指定ではなく、メソッドチェーンになってるので 変換を入れる
変換関数適用の流れ /** * @deprecated 本メソッドは新規コードには使用しないでください */ public function findOld($type, $params
= []) { $where = $params['where'] ?? []; $query = null; select ($type) { case 'first': // ここで新しいfind()を呼ぶ $query = $this->find('all’)->where($where)->first(); break; // 以下、他の$type } if ($query === null) { return []; } // 新しいfind()は遅延実行のクエリを返すので、ここで実行しつつ配列に変換 $results = $query->toArray(); // CakePHP 2の配列構造に変換 return 変換メソッド($results); } 13 最近のFW・ライブラリは、 クエリの遅延実行が多いのでフォローを入れないと 昔の感覚では使えないことに留意
変換関数適用の流れ /** * @deprecated 本メソッドは新規コードには使用しないでください */ public function findOld($type, $params
= []) { $where = $params['where'] ?? []; $query = null; select ($type) { case 'first': // ここで新しいfind()を呼ぶ $query = $this->find('all’)->where($where)->first(); break; // 以下、他の$type } if ($query === null) { return []; } // 新しいfind()は遅延実行のクエリを返すので、ここで実行しつつ配列に変換 $results = $query->toArray(); // CakePHP 2の配列構造に変換 return 変換メソッド($results); } 14 単にtoArrayするだけではCakePHP2と同じデータ 構造にならない(ネストの深さが合わない)ので、 調整する関数を挟む
決断と説得その2 15
破壊的バージョンアップに振り回されるなら、 いっそLaravelに移行した方が早いのでは? 16
安易にフレームワークを乗り換えない 決断 • ビジネスが維持できることを最優先に考え る • Laravelには行かない、CakePHPのまま とする チームへの説得 •
サポート切れによるバージョンアップは基 本マイナスをゼロに戻す作業 • Laravelにしても開発者の満足感以外 のメリットはない • これ以上のリスクを背負うべきではない • CakePHPは悪いフレームワークではない • Googleトレンドなどを見てもCakePHP の需要は一定以上はキープされている • 後にCakePHP5が出るのでこの判断は正しかった 17
決断と説得その3 18
静的解析とフォーマッターをフル活用する 決断 • 静的解析はPHPStanを使用し、Level Maxとする • フォーマッターは、{}を[]に変換するなど に使用 • CakePHP2からの移行では、使わなくな
るインスタンス変数が大量に発生する • これらの移行の成否を目視で確認しきる のは無理なので、静的解析を活用する チームへの説得 • 機械でできるチェックはやらせる • 機械によるフォーマットはバグを生まないと すると言い切る • PHPStanはMax以外は選んだ理由に 妥当性が見つからないだろう • Levelを下げるよりも例外を指定した方 が良い • 下準備が必要なのは受け入れる、後で 苦労するよりずっと良い 19
CakePHP2からの移行では、インスタンス変数が大量に不要になる class Post extends AppModel { public $validate = [
'title' => [ 'notEmpty' => [ 'rule' => 'notEmpty', 'message' => 'タイトルは必須です。' ], 'maxLength' => [ 'rule' => ['maxLength', 255], 'message' => 'タイトルは255文字以内で入力してください。' ] ], 'content' => [ 'notEmpty' => [ 'rule' => 'notEmpty', 'message' => 'コンテンツは必須です。' ] ] ]; } CakePHP2 例えばバリデーション定義 20
CakePHP2からの移行では、インスタンス変数が大量に不要になる namespace App\Model\Table; use Cake\ORM\Table; use Cake\Validation\Validator; class PostsTable extends
Table { public $validate = [略 public function validationDefault(Validator $validator): Validator { $validator ->notEmptyString('title', 'タイトルは必須です。') ->maxLength('title', 255, 'タイトルは255文字以内で入力してください。') ->notEmptyString('content', 'コンテンツは必須です。'); return $validator; } CakePHP3以降 ➢CakePHP3以降ではバリデーション定義はメソッドで定義する ➢静的解析で未使用のインスタンス変数をチェックすることで、バリデーション定義の移行漏れ を拾う 21 これが残っていても何も意味はない。 故に静的解析で未使用変数を拾うことで移行漏れ を見つけられる。
下準備が必要なのは受け入れる ➢静的解析はメソッドコメントなどが揃ってないと十分に力が発揮されない ➢これについては受け入れて数日間粛々とコメントを追記していく 22
PHPStanはCakePHPに沿った静的解析ができる ➢PHPStanはプラグインを入れることでCakePHPに沿った静的解析ができる ➢https://github.com/CakeDC/cakephp-phpstan ➢MVCに違うものが混じっているとこのプラグインが活かしきれない ➢本PJの時は帳票出力を丸ごと移動させ、↑のプラグインを参考に帳票出力用の静的解析 ロジックを作成して解析させた ➢同じことをする必要はないと思う ➢Modelにいろんなものが混じってるのだけは脱却し、 ディレクトリで単位で対象にする・しないとかの選択ができるように持っていくのが大事 23
CakePHPのMVCに本来ないものを移動させる ➢MVCの概念が出始めた頃に作られたシステムは、モデルに他のものが混じってることがある ➢混じってると静的解析がうまく動かないので移動させる . ├── Controller ├── Model │ ├──
モデル │ └── モデル(実質帳票出力処理) └── View . ├── Controller ├── Document │ └── 実質帳票出力処理 ├── Model │ └── モデル └── View 24
決断と説得その4 25
ソースコードのメトリクスを算出する 決断 • 移行前後のステップ数を計測 • 増減した理由を整理しておく • PHP Mess Detector
(PHPMD)を用 いて、移行前後の複雑度などを計測 • 同じく変化の理由を整理しておく 関係者への説得 • バージョンアップはQCDに対する目が厳し いので、品質に対する説得材料として使 用する • ステップ数・複雑度の低下は、ディレクトリ 構造や重複コードの整理によるものと説 明 • ステップカウントはテストケース・バグ数と 照らし合わせ、IPAのゾーンモデルを元に 品質の証明材料として使用 26
ゾーンモデル 27 ➢引用:https://www.ipa.go.jp/archive/files/000072870.pdf
まとめ 28
まとめ 29 ➢破壊的バージョンアップと戦うには、技術だけでなく、施策の決断・順番、そして説得が必要 ➢もしみなさんが破壊的バージョンアップと戦うことがあれば、これらを参考にして欲しい。 変換関数を作り、既存コードを活かす 安易にフレームワークを乗り換えない 静的解析とフォーマッターをフル活用する ソースコードのメトリクスを算出する
ご清聴ありがとうございました。 30
参考リンク 31 ➢[Zenn]CakePHP2からCakePHP4への移行のポイント ➢[Qiita]破壊的変更のあるバージョンアップ作業の流れ