レガシーシステムをリプレイスして古の遺産を浄化した話
まえがき
タイトルの通り、少し前に 8 年近く(正確な歴史は不明)動いていたシステムをリプレイスしました。
かかった期間は約 2 年、発行した課題チケット数は 6000 超え、リリース直前に直属上司が退職など紆余曲折ありました。
良かった点や反省点が多くあったので、記憶が新しいうちにアウトプットしようと思います。
なぜリプレイスするのか
単純にエンジニアが「レガシーだからモダンな環境にリプレイスしたい」と言っても、事業責任者や経営層にメリットを理解してもらえないと実施できません。
今回は後述する問題によって、事業の足を引っ張っているという結論が出たためリプレイスに至ったと認識しています。
セキュリティの問題
システムの問題
- フレームワークのコア部分を魔改造
- 一度リプレイスしようとして失敗した昔の残骸(Python のコード)が残っている
- テストコードがない
- バージョン管理が Subversion
- 密結合
- 1 つのリポジトリを別部署の別サービスと共有している
- サービスA のバリデーション定義を変更したら、サービスB のバリデーション定義も変更されてしまう
- エラーに気づけない(ユーザーからの問い合わせで初めて気づくなど)
- サイトが http
DBの問題
- 1 テーブルのカラムが大量に存在する
- 使用していないテーブルが混ざっている
- 何でも DB で管理しようとしているためデータが複雑化している(〇〇カテゴリーのネスト構造をマスターとして管理など)
- スロークエリ多数
- 命名に一貫性がない
***_table2
が存在するm_***_table
のようにマスターテーブルを想起させるプレフィックスが付いていたりするが、実際はマスターではなく頻繁に更新される普通のテーブルだったりする
事業的な問題
- 上述の理由によりデリバリーのスピードが出せず、施策が実行できない(時代の変化についていけない)
- テストが人力のみなのでシステムのクオリティが担保できない
- 枯れた技術を使用しているため、エンジニア採用時の足かせになる(または退職される)
どうリプレイスしたか
ここは書こうと思えばいくらでも具体的にかけますが、キリがないのでざっくりポイントだけ書きます。
システム構成
- オンプレからクラウド(AWS)に移行
- マイクロサービスアーキテクチャ導入
- Frontend ⇔ API Gateway ⇔ API
- API 実装時はテストコード必須に
- PHP は 7 系にアップデートし、フレームワークは Laravel に変更
- MySQL は 5.7 互換の Aurora に変更
- DB は全て再設計し、新テーブル用にデータをコンバート
- 画像系と静的コンテンツは S3 で管理
- インフラ周りは Terraform と Ansible で管理
- フロントエンドは Vue や React で作成
開発方法
- 2 週間ごとにスプリントを切って回していくスクラムを導入
- 課題の管理や進捗の把握は JIRA で統一
- 1 人でフロントエンドからバックエンドまでを担当
- システムとしてどうあるべきかということを先に提案し、なるべく既存システムに近づけてひたすら機能を開発していく
体制
開始時
- エンジニア: 2 名
- プロダクトオーナー(直属上司) 1 名
- 部長: 1 名
終了時
- エンジニア: 5 名
- 部長: 1 名
やらなかったこと
- デザインのリプレイス(リニューアル)
- 単純にデザイナーがいなかったというのと、システムに比べてスタイルガイドなどはかなり丁寧に作られていたのでデザインは踏襲することにしました
- ただ、純粋なコーディング部分は Webpack で管理できるように書き換えました
リプレイスするにあたり追い風だったこと
- リリース当日はサイト停止 OK
- 廃止できる機能が多かった
大変だったこと
旧システムの有識者が 0 の状態からのリプレイス
プロジェクトにアサイン後、唯一の有識者だった派遣社員エンジニアが退職してしまい、手を動かすエンジニアがアサインされたばかりの自分と、入社したばかりの業務委託の方のみになってしまいました。
そのため、かなり手探りの状態からリプレイスプロジェクトが始まり、スタートダッシュができませんでした。
データの移行
データを移行するためにデータコンバーターを作成しました。
「旧DBからデータ取得→バリデーション→新DB用にデータを加工→新DBにインサート」という流れです。
移行するテーブル 1 つひとつに、このロジックとテストコードを作成しなくてはいけないため、かなり時間を費やしました。
また、データの移行を失敗すると取り返しがつかないことが多いので、かなり慎重に行う必要がありました。
パスワードの移行
可逆だったパスワードを不可逆の値にするために一旦復号化する必要がありました。
旧パスワードが pear の Crypt_Blowfish を使用していたため、composer に pear のライブラリを追加しました。。
そして、そのままでは PHP 7 系では動かないので、/pear-pear.php.net/Crypt_Blowfish/Crypt/Blowfish.php
を改修することに。。
※ そのときの調査ログ。
工夫したこと
フロントエンドはなるべくサーバーレスに
EC2 インスタンスは結構料金がかかるので、可能な限り SPA で構築することにしました。
SEO 対策が必要なサービスは SSR できる Next と Nuxt を導入して対応しました。
反省点
SEO の影響をなめていた
そこまで SEO に依存していないコンテンツでしたが、それでも目に見えて集客力が下がったため、もう少し真剣に考慮するべきだったと思います。
ちなみに SPA にした影響というよりは、旧 URL からのリダイレクトやエラーページのステータスコード、ページ構成などが主な理由です。
サービス間の連携テストの実施が遅かった
これはマイクロサービスならではですが、各サービス間をまたいだテストをするのが少し遅かったです。
連携テストをして初めてログイン周りの不具合などが見つかりました。
そして、結果的にリリース日を 2 週間ずらすことになってしまいました。。
サービスが属人化してしまった
スピードを重視していたため、1 人 1 サービス(場合によっては複数)を実装してもらうことにしたのですが、そのサービスについて詳しい人物が限定されてしまいリスクを生んでしまいました。
ある程度はスピードとのトレードオフなので仕方ないかもですが、もう少し横断的に開発していくべきだったなと思っています。
ただ、メンバー全員が責任を持って取り組んでくれたおかげで、誰もメンバーが欠けることなくリプレイスを終えることができました。
あとがき
0 → 1 というより、マイナス → 0 にするフェーズだったのでかなり貴重な経験をさせてもらいました。
エンジニアとしても成長できました。
事業責任者と関わることも多かったので、浮かび上がる課題に対してエンジニアリング以外で解決する方法を学べました。
また、エンジニアという役割上、課題をどう解決していくかという How に目が行きがちですが、そもそも課題や目的は何なのか、はたまたそれは本当に課題なのかという Why の視点を持てるようになったことが良かったです。
おまけ
新旧リプレイス表
環境 | 旧 | 新 |
---|---|---|
全体構成 | モノリシック | マイクロサービス |
サーバー | オンプレ | クラウド(AWS) |
インフラ構成管理 | 手動 | Terraform, Ansible |
バックエンド言語 | PHP 5.1 | PHP 7.1 |
バックエンドフレームワーク | Ethna | Laravel |
フロントエンド | jQurey | Vue, React |
DB | MySQL 5.5 | MySQL 5.7(Aurora) |
バージョン管理 | Subversion | Github |
メール | オンプレ | SendGrid API |