Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
【PHP】破壊的バージョンアップと戦った話〜決断と説得
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Satoshi Kaneyasu
January 19, 2025
Programming
590
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
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
380
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
380
【業務アプリのモダナイズ】このまま保守・改修し続けて大丈夫ですか?
satoshi256kbyte
1
76
今から学ぶベクトルデータベース基礎知識からAmazon Bedrock Knowledge Basesまで
satoshi256kbyte
2
24
Amazon Managed Grafana で Amazon DynamoDB のデータを可視化する Infinity Plugin + プライベート API 構成
satoshi256kbyte
1
42
AI駆動開発にグラフDBを重ねてみた
satoshi256kbyte
2
850
AWS Transform Customによる Spring Boot 2.xから4.xへのVerUp
satoshi256kbyte
2
170
運用ダッシュボードの設計を誰も教えてくれないのだけどみなさんどうしてるんですか? - チームに監視するという文化を根付かせるための第一歩を踏みたい -
satoshi256kbyte
1
240
AWS CDK ExpressモードとCI/CDの組み合わせ
satoshi256kbyte
1
79
Other Decks in Programming
See All in Programming
全ての永続トークンを、生まれる前に消し去りたい!
sylph01
2
880
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Rails World 2026, Austin, Texas
marcoroth
0
220
残高管理から台帳サービスへの進化
artoy
1
200
Verilogで学ぶCPU自作入門
uyuki234
8
4.1k
カツオ、ご期待ください
suneo3476
0
190
複雑なUIコンポーネントとの ⻑い付き合い⽅:波及するバグからテスト戦略を経て、安全な再設計へ
mixplace
0
410
當我們開始不讀 Code - AI Agent 時代,Code Review 與 Clean Code 還剩下什麼?
jame2408
1
200
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
500
Web Framework Benchmark Battle 2026, Any%
a_matsuda
2
2k
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
Everything will be SERVERLESS — 信じて運用した10年の経験値 / Everything Will be Serverless — Lessons Learned from 10 Years of Operational Experience
seike460
PRO
1
1.1k
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
610
Featured
See All Featured
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
330
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
410
Code Review Best Practice
trishagee
74
20k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2.1k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
310
Joys of Absence: A Defence of Solitary Play
codingconduct
1
550
Building Applications with DynamoDB
mza
96
7.2k
How to Talk to Developers About Accessibility
jct
2
580
HDC tutorial
michielstock
2
960
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
How to Ace a Technical Interview
jacobian
280
24k
Prompt Engineering for Job Search
mfonobong
0
520
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]破壊的変更のあるバージョンアップ作業の流れ