レガシーな営業支援システムのPHPとLaravelをアップグレードした時の話
TL;DR
- アップグレードの事前準備、実作業、発生した問題、追加で対応したことについて細かく書いてあります
- アップグレード以外にも筆者が現場参入時に気をつけていることについて簡単にふれています
- 盛りだくさんなので時間があるときにでも読んでください
概要
- 2023年07月19日に、業務委託(フルリモート)としてテクニカルイネイブリングチームに入る
- ジョイン後のキャッチアップや、営業支援システム利用方法のチュートリアルを実施
- Laravelの5.5系と10系(システム使用中のバージョンと最新)で、素組してテスト導入の検証
- 営業支援システムにテスト導入とNode.jsのアップグレードを行う
- 2023年10月4日からアップグレード作業を開始して、2024年1月17日にリリース完了
- ElasticSearchからOpenSearchへ移行作業も行った
- 細かい不具合は6つぐらい出たが、大きなものはなく、その後1週間くらいで安定
ジョインしてから半年でリリース(内アップグレード作業は3ヶ月ぐらい)
今回は、上記の作業時に注意したことや、知見などを共有したいと思います。
説明は下記の順番で行なっていきます。
- 新しい現場に入る時に気をつけている点
- 実際に現場に参加したときの話
- アップグレード前にやったこと
- アップグレード作業の話
- 総括
- 最後に
新しい現場に入る時に気をつけている点
- 初回は遅刻しない(初対面で与える印象は絶大なので、変にマイナスになることはしないように!!)
- 遅刻しないためには睡眠をしっかりと取っておきたい。
- オリンピアンの平均睡眠時間は、8時間半なので、睡眠時間は8時間を基本としたい
- 当日風邪などで休まないように風邪ひく行動は取らない。
- 仮に風邪の兆候があったら、厚着して、葛根湯を飲んで、栄養のある食事をして、早めに寝る
- 遅刻しないためには睡眠をしっかりと取っておきたい。
- 人間関係が拗れると大変なので、いつも以上に注意を払う(空気を読む)
- 現場のシステムとメンバーの状況把握と、自分ができること、考えていることを周りのメンバーに伝える(言ったら不利になることも隠さず伝える)
- 余談ですが、フルリモートでの作業がメインとなるため、見た目大事ということで、オンラインで話す時はミラーレスカメラを用意して使っています(aps-c or micro 4/3で十分綺麗)
また、気をつけないといけない点ではもう一つ、人によってはですが、判断基準が人情の人がいます。
判断基準が人情の人は、いくら客観的にうまくやっても、失敗とみなされることもあります。
そのため、判断する上層部や周りのメンバーがどのように考えているのか?を把握することが、とても大切になります。
理想的には、経済の流れとか、会社の流れとか、より上位の流れも把握できていると、なお良いです。
人間関係改善と経済の流れを知りたい人は下記の書籍を見ると良いと思います
・ハーバード流交渉術(原著)(論理的に交渉を進めたい場合)
・人を動かす 改訂版(2023/09/04に改訂版が出た)(人情で交渉を進めたい場合)
・野生の経済学で読み解く 投資の最適解(日本経済の流れを知りたい場合)
上記の技術ですが、自分だけ得するように使う場合は逆効果となるので、
みんなの利益を考えて妥当なところに着地するように使ってください。
また、さらに踏み込む場合は、下記の本を読んでみると考え方やコミュニケーションの役に立つと思います。
・確率思考 不確かな未来から利益を生みだす(運とスキルと感情の考え方)
・1秒で答えをつくる力 お笑い芸人が学ぶ「切り返し」のプロになる48の技術(笑いの力大事です)
次は、現場に参加したときの話です。
実際に現場に参加したときの話
当初、TypeScriptのプロジェクトをやるという話でテクニカルイネイブリングチームに入りました。
なんやかんやで、PHP製の営業支援システム(レガシー)にテストを導入することに(!?)
理由は下記となります。
- 僕が開発言語にこだわりがない
- Rubyが好みじゃないので、それ以外ならいいかなと
- Rustを仕事でやってみたいとは思っている
- レガシーシステムへのテコ入れのほうが、新規のプロジェクトをやるよりも難易度が高い
- 難易度が高い方が、価値が高く、評価も経験値も上がるので、チャレンジする価値あり
- 僕がPHPとLaravelは3ヶ月くらいしか経験が無いという意味でも難易度高そう
アップグレード前にやったこと
営業支援システムの状態調査しました(2023年7月時点)
- PHP 7.0.33(初回リリース 2015/12/03, EOL 2018/12/03)(8年、5年経過)
- Laravel Framework 5.5.3(初回リリース 2017/08/30, EOL 2020/08/30)(6年、3年経過)
- Node.js v12.22.12
- 注)サーバとしては使っておらず、静的ファイルのビルドのみで使っている
- MySQL5.7(今回アップグレードの対象外)
- テストコードは存在したが、動かしてみるとエラーが多く、そもそも何年も動かしていない状態
- M2のMacに、local環境構築後、適当に動かしてみると、1ページを開くのに10秒ぐらい掛かっている!?
- STG-DBのdumpをlocalにレストアする時間: 1.5時間かかる
- migration運用もしていなかったので、DBに変更が入るたびにレストアしないと動かない状況だった
- 2024年5月に、MySQLのアップグレードが控えており、それまでにDBのドライバは上げておきたいところ
アップグレード前の7月から10月の期間にやったこと
- システムの担当チームと週次定例を開催(チームとの交流を深める And 進捗報告や仕様などを確認するため)
- テクニカルイネイブリングチームとの週次定例も開催(自分の作業が伝わっていなかったので、週次で伝えることに)
- 営業支援システムにテストを導入
- PHPUnitで導入(Pestで入れたかったが、アップグレードしないと入らないので一旦諦めた)
- 僕が書いたテストではカバレッジが4%にしかならなかったが、それでもアップグレード時にとても頼りになった
- GithubActionsでテストが動くようにした(GithubActionsは、初見だったので手間取りました)
- キャッシュ機能を使ってビルド結果を保存。ビルドが不要な場合は高速にテストが終わるようにセットアップ(15分ぐらいが2分ぐらいとなる)
- docker composeを立ち上げてのテストで実施
- depends_onを入れたが、DBの完全な立ち上がりまでは待ってもらえず、簡易スクリプトで対応
- M1以降のMacだと環境構築手順書通りにやっても動作しなかったので、手順を整えた
- イメージが古すぎてdebianのリポジトリのURLをアーカイブURLに変える必要があった
- docker-composeの記述も怪しくRedisが落ちていたので修正
- 数年ぶりのDockerだったのでリハビリも兼ねる
- 踏み台サーバ経由のDB接続が難しすぎたがなんとかセットアップ
- SSHのポートフォワーディングについて詳しくなる
- OPCacheのセットアップをしてみて動くことを確認した
- Node.jsをv20で動くように改修した
初見となる技術もありましたが、基礎の理解や色々な技術の経験があるため、そこまで手間取らずに導入を完了できました。
上記をやったことによって、3ヶ月の期間である程度、周りのメンバーに、僕の実力とか信用できるか?を把握してもらえたと思う。
僕自身もある程度、営業支援システムを理解できた。
次からアップグレードの話に入るのですが、その前にアップグレードの目的について話します。
言語やフレームワークをアップグレードする目的
- 第一は、脆弱性などのセキュリティ対策
- 第二は、新しいライブラリが古すぎて導入出来ないので出来るようにしたい
- 第三は、
僕が作業する人たちの経験値アップ出来るような環境にしたい(オプション)
第一と第二が目的なので、そこから大きくはブレないようにしたい。
達人プログラマと言い張る系エンジニアとしては、第三はオプションなので、第一と第二に影響ない範囲で行おうと思ってました。
アップグレード開始前の準備
- おそらく僕がデバッグで手一杯になるかつ、そこまでシステムを詳しく知らない
- そのため、別途テストのメンバーを追加してほしいと要請する
- 大規模アップグレードで、テストもほぼ無い状態だと、バグ無しで出すことはおそらく不可能なため、先にそのことを伝える(期待値コントロール)
- 失敗することが分かっていれば対策も取れるし、大事にはならなかったりする
- うまくいった時の効果も大きいので、そのことも伝える(リスクあたりのリターンは大きいと伝える)
- インフラ側はAWSだったが、会社ごとに管理方法や設定も違うため、インフラメンバーを追加してほしいと要請する
- 僕自身もAWSを自由に触る機会はなかったため、応援要請した(GCPは触ったことがある)
- 先に出しても問題がない修正は、切り出して本番に出していく
- 全体的に影響があるので、全機能をテストしてもらうように申請した
- これにより、普段なら入れづらいような修正も入れやすくなった
アップグレードの進捗次第では削るが入れてみたいものを列挙
今回は、大規模にテストを行える機会なので、全体影響がありそうなものを優先度高めに設定。
また、進捗が遅れた場合、優先度低から削っていくこととした。
- 【優先度:高】PHPにOPcacheを入れる
- localで10秒掛かるのを短縮して開発速度を上げるため(また、基本的に入れておきたいものでもある)
- 【優先度:中】AWSのGraviton3のインスタンスを使ってみる(x86からArmに変わる)
- localでは、M1系Macを使っており、本番とlocalを合わせる文脈ではArmCPUのインスタンスを使いたい
- AWSがクラウド専用に開発したArmCPUということもあり、コストが下がってパフォーマンスが上がるため、検証してみたい
- 【優先度:高】apacheからnginxに変更する
- apacheよりもnginxのほうが、広く使われているため、エンジニアのモチベーションが上がるので移行したい
- 設定次第の部分もあるが、nginxのほうが高速に動くため、localでの開発速度向上を期待できる
- 【優先度:中】AmazonLinux2から、AmazonLinux2023に変更したい
- SREチームから要望だったが、EOL後のアップグレードを考えると古いOSを使うよりは最新を導入しておきたい
- 【優先度:高】Local環境用のdocker環境を新環境と合わせる
- 本番とlocalを合わせることで、本番でのエラーをlocalで再現しやすくなる
- デバッグ時のlocal再現が重要なため、優先度:高とした
- 【優先度:低】migrationがゼロから動かすとエラーとなるのでLaravelのMigration Squashingを実行する(削った)
- GithubActionsでのテストでmigrationが使えなかったため、ゼロから使えるようにしたい
- リリース後すぐに対応してマージ済み
- 【優先度:低】PHPUnitをPest化する(削った)
- BDD的な書き方もできるし、テストをパラレルで実行できるので乗り換えたい
- リリース後すぐに対応してマージ済み
アップグレード作業に必要なメンバー紹介
- アップグレードをメインで作業するエンジニア(僕)
- 営業支援システムの開発者 兼 理解者(営業支援システムリーダー)
- インフラ担当(元営業支援システムの開発をしていたSREメンバー)
- 直近で営業支援システムのOSをAmazonLinux2にアップグレードしていたので適任だった
- QAテスト(営業支援システムが初見の新QAメンバーと、チームの手が空いている人にテスト依頼)
- システム利用部署の方にも受け入れテストを実施してもらいました
上記を最低限必要なメンバーと見積もって、先に応援申請をしておきました。
(初日から全員揃ったわけではなく、最初は上述の2名でスタートした形です。)
見積もり
めちゃくちゃどんぶり勘定で、作業で2週間から3ヶ月と話していた。
(この時点では、何が起こるか?わからなさすぎて、見積もれ無い気もする)
結果としては収まったが、もうちょっと多めに見積もった方が安全だったなぁと。
また、見積もりのポイントは、何月何日みたいに、点では言わないことです。
「最低でも2週間は必要。大体3ヶ月ぐらいかなと思います。また、2ヶ月で終わる確率は30%です。」のような感じで、範囲と確率で話すと良いと思います。
大抵見積もりを聞いてくる人は、見積もり期間が大きいことよりは、期間をオーバーすることを恐れています。
なので、オーバーするぐらいなら、多めに言った方が良いことが多いです。
(過剰すぎる見積もりもどうかとは思いますが。。。)
見積もりに関しては、「ソフトウェア見積り(著スティーブ マコネル)」を見てみると良いと思います。
アップグレード作業の話
実際の作業時にやったこと
- 2023年10月4日からスタート
- いずれ独立したテスト用の環境が必要になるので、SREメンバーに依頼しておいた
- 最初に、いきなり最新化して動かしてみた
- まぁ、全く動かなかったし、取っ掛かりも掴めなかった
- 動かないとは思っていたが、予想以上に分からないことが多かった
- ボトムアップ以外に選択肢が無いことが判明
- 手間がそこまでじゃないからやったのですが、時間が掛かるならやらなかったです
- IntelのTickTackみたいに、「PHPを1つ1つ最大まで上げたらLaravelを1つ1つ最大まで上げる」を交互に繰り返すことに
- 確認方法
- 自動テストを実行する
- 起動して簡単に開けるページを開けることを確認
- 更新系も数箇所ためす
- OKなら次に進めていった
- ザルな確認ですが細かく全体を見ることができなかったので、この対応でした
- Gitのブランチは1つ上げるごとに切っていった
- エラーが起きたときに、どこまでのバージョンなら大丈夫か?を戻って確認できるようにしていた
- 10月20日には、上記のザルな確認はパスして、最新で動作していた
- 確認方法
- 大方動いてからは、独立したテスト用の環境で、QAさんに確認してもらった
- 独立環境で確認が終わったらSTG環境へ出して、その後、本番環境へ
大きく問題となった事柄
PHPは、実行時にエラーとなるため、通常ならビルドエラーとなるコードがあっても立ち上がってしまうのが辛かった
ここはコンパイル型の言語のほうが良いなと思う。
テンプレートエンジンはtwigで、twigの特殊機能を使っていたが、その機能が外されて、プラグイン実装して入れ直す必要があった
スタックオーバーフローでも、開発者に負担を強いる変更を安易にやりすぎと文句を言っていた。
元々twigをLaravelで使えるようにするライブラリを使用しており、そちらの実装をパクれたので、プラグイン実装はそこまで大変じゃなかった。
ElasticSearchのTypeが廃止されて、ライブラリを上げたら動かなくなったため、OpenSearchに移行した
- 必須機能ではなかったため、QAテストが後回しになっていたことで発覚が遅れた
- 利用部門テストが近い時期で発覚したため、年末休むことを前提に、残業多めで一気に対応した
- ElasticSearchは初見だったが、めちゃくちゃ使い込んでいる使い方ではなかったので、2週間くらいでなんとかなった
- 使っていないコードも多く、消しながら対応した
- 不要コードは、後から見た人が辛いので気づいたら消してください(切実!!)
- ある程度、リファクタ込みでやったが、完全なリファクタをすると間に合わないので、ライトな形とした
- テスト導入した担当者としては当然かもですが、テスト駆動開発で対応しました
Laravel Eloquent(ORM)のバグ数が、ぶっちぎりだった件
- Cloujure言語の作成者のRich Hickeyも言ってましたが、ORMはOMG(オーマイゴッド)だ!!
- DBのjson型カラムの返却値が文字列から、配列に変更になっており、castしている部分でエラーとなった
- segvも出た(循環参照が原因で、不要コードを削除して回避した)
- コアダンプを調べてみたが何やらループしてそうな内容だった(解析は楽しかった)
- ORM使うとしても入れ替えられるように、機能を出来る限り抑えて使うべき
バリデーションが一部コールされない処理があり、独自対応していたが、アップグレードでコールされるようになってそこが問題となった
Laravelのクラスを勝手に継承して処理している部分で、Laravel側のコード変更により動かなくなるコードが出た
- PHP8.3から、継承用の
#[\Override]
とマークすることが出来るのでそれを使いたい - そもそも継承は使わないようにしたい。。。
OPcache用のpreload処理を入れたが、file load時にしか動かないところに処理が記載されており、そちらが動かなくなった
関数とかに処理を書こう
CloudSignのサンドボックス環境用のIPアドレスが変わっており、IP制限があるSTG環境にCloudSignからのリクエストが来なかった
変更は2021年で、STGでは全然使わない機能だったので既存バグ?である。引きが悪い感はある。
URLのスラッシュが多かったり、リクエストjsonパラメータにゴミがついていたりしても、アップグレード前は動いていたが、アップグレード後は動かなくなった
後からチェックが厳しくなると、色々と問題が出ますな
既存バグが不具合で上がったので混乱した。既存バグはどこかにまとめておきたい
本番のログで、いつものエラーだと見逃したら、アップグレード起因のエラーでもあった
エラーは基本出ないようにしたい(大丈夫なものなら、ログレベルを下げたい)
開発加速のコツ
- 日々繰り返す回数が多いものが実行速度が遅いと、高速に開発できないため速度改善しておく
- 本番だけ再現するエラーなどをなくすため、本番とlocalを出来るだけ近づけておく
- エラーの再現を全部localでできたので、本当に大事だと再度実感した
- 自動テストを導入しておく
- カバレッジ4%でも、自動テストを実行するだけで、低コストでエラー確認ができて大変助かった
- 加速では無いが、処理が入り組んでいる場合、バックトレースを出してみると良い
- 処理の流れが把握できる
- エラースローでもいいが、それだと複数回実行される場所の流れが見れないとかある
アップグレード完了後の状態
-
PHP Version
- 7.0.33
- 7.1
- 7.2
- 7.3
- 7.4
- 8.0
- 8.1
- 8.2(7世代分進む)
-
Laravel Framework Version
- 5.5.3
- 5.6
- 5.7
- 5.8
- 6
- 7
- 8
- 9
- 10(8世代分進む)
- Node.js v12.22.12 -> v20(メジャーバージョン換算で4世代分進む)
- MySQL5.7(変更無し)
- その他ライブラリを全て最新化
- Apache -> nginx + php-fpmに移行
-
ElasticSearch -> OpenSearchに移行
- dockerも無かったので追加して、localで確認できるようにした
-
STG-DBのdumpをlocalにレストアする時間: 1.5時間 -> 5分(設定変えれば2分半)
- レストア速度を上げる設定をmy.cnfに追加
- RancherDesktopのVirtual Machine Type に、VZを使うように設定変更する
- M1系MacだとRosettaも使うように設定する
- MySQL5.7のイメージを
bitnami/mysql:5.7
に変更- MySQL5.7だと、公式のOracleからはx86しかなく、ARMビルドイメージが無いため
- Intel や AMDなどのx86CPUの場合は効果が無いはず
- 本当は、AWSのAuroraを使いたいところだが。。。
-
local環境と本番環境でバージョンのズレがあったが、今回は完全に合わせた
- PHPのdockerコンテナをdebian系からAmazonLinux2023(RedHat系)に変更(本番と合わせる)
- AmazonLinux2023のdockerイメージには、psやkillコマンドも入ってなかったが、
dnf install -y procps
で入る。 - AWSのGraviton3のインスタンスには最初から入っているので、なんなの?感はある
- AmazonLinux2023のdockerイメージには、psやkillコマンドも入ってなかったが、
- PHPのdockerコンテナをdebian系からAmazonLinux2023(RedHat系)に変更(本番と合わせる)
- 各種環境とバージョンが一覧できるようにスプレッドシートにまとめた
-
local環境でページを開くと10秒待たされたのを、0.7秒に短縮
- RancherDesktopの設定をチューニング(ついでにreadme.mdに追記)
- OPcacheの導入
- OPcacheのPreload導入
- 他、PHPなどのアップグレードにより最終的に0.7秒くらいになる
-
テストコードが増えた
- OpenSearchや、実装が必要だった部分をテスト駆動開発したので増えた
総括
- アップグレードに必須なことも、必須じゃないこともやったが、ほぼほぼ入れられたのはよかった
- 必須じゃない部分は削れるようにしておいたところもGood
- 必須じゃない部分の提案を聞き入れてくれたメンバーの皆様には感謝(良い会社だと思います)
- ある程度、自由にやらせてくれたから今回の成果が出せた部分もあったと思います
- フレキシブルに作業時間を変更できたのもよかった
- 残業多めで実装して、残業分を一気に休むのは、効率が良いと思われる
- PHPは敬遠されがちな言語だが、PHPじゃない部分に新技術を取り入れたり出来るなと
- 言語を変えるのは容易では無いが、周辺技術を変更してスキルアップというのはありかと
- ダンプのレストアや、ページを開くなど、繰り返す動作を高速化できた(重要!!)
最後に
「CODE COMPLETE」や、「ソフトウェア見積り」の技術書の作者であるSteve McConnell(スティーブ・マコネル)曰く、
マコネルは、採用する開発手法にかかわらず、プロジェクトが成功するか否かは究極的には能力が決定すると主張する
と言っており、エンジニアはスキルアップをしていくことを主眼に置くと、
プロジェクトは成功するし、自分も成長するし、良いと思います。
ただ、エンジニアはチームで働くため、
- 仕事の段取り
- 適切なメンバーの配置
- お互いの助け合い
- みんなが気分よく働けるように気を配る
など、人間関係や協力プレイにも配慮しつつ、作業していくことも肝要だと思っております。
Discussion