営業一課で使っている PHPアプリを保守してくれないかな?
○○さんが1人で作ってメンテしてたやつなんだけど
皆さんは上司からこんな仕事を振られたことはないでしょうか?私は過去に何度か経験した1のですが、こういった仕事はなぜか:
- 正確な仕様を知っている人はいない(知ってた人は辞めた)
- テスト計画書・デプロイ手順書・仕様書といったドキュメントは無い
- ソースコードはもちろんスパゲッティ
- でも、業務ではガッツリ使われているので廃止できない
というレガシープロジェクトばかりでした。この記事では、レガシープロジェクトを引き継いでしまった時に、最初に何をするべきか書いていきたいと思います。
なお、ここで最悪なのは「とりあえず、緊急の不具合から直してしまおう」と、いきなりコードの修正にかかることです。
※おことわり: この記事では「遵法的な職場の」「PHPやRailsで書かれた」「社員25人が使う」「業務用の内製アプリを」「プログラマー1人+αが専任で保守する」程度のプロジェクトを想定しています。「ウン千人のプログラマが参加し、多重下請け構造と社内承認フローでがんじがらめになった、COBOL製40年物の金融システム」に適用できるとは限りません。
なぜ、いきなりコードを修正してはいけないのか?
いきなりコードを修正すると誤った修正をしてしまう可能性が大です。
プロジェクトを担当して日が浅いあなたは、まだ正確な仕様を理解できていません。 あなたが修正しようとしている「不具合」は本当に不具合なのでしょうか?
また、ユーザーから「こういう不具合があるので直してください」と依頼される事がありますが、レガシープロジェクトはUIが難解だったりロクなマニュアルがなかったりするため、ユーザーは往往にして、
「〇〇さんにこうしろと言われたって、▲▲▲さんに聞きました」
「わかりません。私は前任者から引き継いだだけなんです」
「とにかく業務が回らないんです。ここを直せばいいだけなんですから」
・・・なんて、仕様を理解せずに使っていたり、目先のトラブルを解決したいがためにアドホックな修正を提案してきたりします2。しかし、誤った修正をして後々矛盾に苦しむのはあなたです。
そして、コードの修正方法を判断できたとしても、普通の作業に信じられないような危険が伴うことが、レガシープロジェクトではありえます。
- 例: 開発環境を作ろうと
rails db:setup
3を実行したら、全エンジニアで共有している開発DBが初期化された - 例: 開発版をテスト環境にデプロイしたら、利用者からクレームが来た。実はテスト環境を業務で使っていた。
- 例: デプロイしたら依存ライブラリのビルドが始まったが、なぜかビルドに失敗し、サービス停止5分の予定が結局30分間サービス停止した。
ただでさえ不具合山積みなのに、追加でトラブルを起こしては目も当てられません。また、このような環境では気が散ってコーディングのミスを犯す可能性も高まります。まず安全を確保することを優先してください。
最初にするべき7つのこと
とにかく、普通のことを普通にできる体制を整えましょう。
1. 協力者を確保する
プロジェクトを引き継いだら、まずプロジェクトに協力する人を確保してください。例えば以下のような人たちです:
- 前任者(まだ退職していなければ)
- コードレビュワー
- テストエンジニア
- インフラエンジニア
- ユーザーの代表者
- アプリの仕様を知っている人(ベテランユーザーなど)
この人たちとは、プロジェクトを回す上で遅かれ早かれ協力を仰ぐことになりますから、早い段階で渡りをつけておきましょう。具体的には:
- キックオフミーティングを開いて、問題意識・初期のタスクを共有する
- Slackチャンネルを作って、随時連絡を取れるようにする
などが、考えられます。
上司やユーザーと交渉をする時(「サーバー増設したい」「この機能を廃止したい」)にも、あなたが1人で交渉するよりも、関係者5人を引き連れて行った方が何かとスムーズにことが運ぶでしょう。
2. 仕様をドキュメント化する
ユーザーに聞いたり、コードを読んだりして、アプリの仕様をドキュメントに書き出しましょう。
ここでは完璧な仕様書を目指す必要はありません。正確性・網羅性よりも、**ともかくドキュメントを作ってしまうことが大事です。**というのも、ドキュメントが無い状態では、用語が通じなかったり、事実ではなく想像に基づいて物を言ったりして、議論が迷走しがちになるからです。
ユーザー 「カレンダー通知を直してくれないか?急いでるんだ」
プログラマー「『カレンダー通知』って何ですか?僕の担当の『予定管理システム』関係ですか?」
ユーザー 「予約前日にユーザーにプッシュ通知するやつさ」
プログラマー「ああ、『前日メッセージ』ですね。あれを送信してるのは、予定管理システムじゃなくてメッセージ画面ですよ?」
ユーザー 「えっ?メッセージ画面ってなに?」
なんて。
目に見えるドキュメントがあれば「ここを変えましょう」「この用語が分からない」と具体的で生産的な議論ができるようになります。
もちろん、引き継ぎ直後の理解が浅い段階ではドキュメントに間違った記述をしてしまうこともあるでしょう。しかし、ドキュメントならば、少なくとも誤りを見つけて訂正することはできます。各々の頭の中の想像を修正するのは不可能です。
3. テストやデプロイの手順書を作る
テストやデプロイは開発サイクル中に繰り返し行うものなので、手順書に書き起こして安全・確実に実施できるようにしましょう。
もちろん、テストやデプロイは最終的には自動化するのが望ましいところですが、レガシープロジェクトでは手動でのテスト・デプロイすらおろそかになりがちです。まず手動での手順を整備しましょう。
テストならTestRailなどで管理するのがベターですが、無ければExcelでも構いません。とにかく、「管理されている状態」に持っていくことが大事です。
デプロイ手順やテストについてもドキュメント化されていることで、
- この機能のテストが足りていない
- このテストケースは重複している
- このテストケースは誤りだ
- このデプロイ手順はキャッシュを使って高速化できる
- この手順は簡単に自動化できるのではないか?
といった、品質についての議論や、
- 1日でテストを15件消化できたから、残り45件は3日で終わるはずだ
- A案はテストを50件実施する必要があるが、B案なら8件で済むから、B案にしよう
- Railsをアップデートすると、全テストに○人日かかるが、それだけの価値はある4
- デプロイに1回30分もかかるのは問題だ。機能追加より先に、デプロイ高速化に取り組もう
といった、工数配分についての議論ができるようになります。
また、テスト手順がドキュメント化されていれば、必要に応じてテスターを増員して短期間でテストできるようになります。
4. 不具合・機能をチケット管理する
レガシープロジェクトでは、不具合も足りない機能も山盛りになっているはずです。それらはRedmineなどのチケット管理ツールで、目に見える形で管理するようにします。チケットにすれば、
- 優先度を決める
- 大きな課題を、細かいチケットに分割する
- 全チケットが完了するまでのスケジュールを見積もる
といったことがしやすくなるし、タスクを忘れることも無くなります。
また、全体像が見えない状態だと、
「これは重要なんだ!今すぐやってくれ!」
と言われたとき、反論できず困ったことになります(言ってくる方も自身の職務上の責任があるわけですし)。一方、チケットが一覧されていれば、
「すみません。優先度:高のチケットが○件あるので、その後になります」
「〇〇さんの〇〇〇〇案件を後回しにすることになりますけど、交渉してきてもらえますか?」
といった妥協をしやすくなります。
なお、チケット管理をExcelで行うことも可能ではありますが、専用のチケット管理ツールを使用することをお勧めします。Excelでチケット管理するのは、チケット番号を手動で振らなければならなかったり、チケットにコメントを付けにくかったり、更新時に関係者に共有する手間があったりして、不便が多いからです。
5. まともなテスト環境を用意する(できれば複数)
そのレガシープロジェクトにテスト環境はあるでしょうか?まれによくあることですが、
- 「テスト環境」と称する環境はあるが、本番環境とサーバー構成や設定が違ってしまっているので、テストで不具合を見のがしてしまう。
- テスト環境が無く、開発者のマシンで適当にテストした後いきなり本番適用するフローになっている(そしてバグる)
といった状況ではないでしょうか?ともかく「テスト環境が無い」では話にならないので、本番環境とできるだけ同じ設定のテスト環境を作りましょう。
なお、テスト環境を作る際には、サーバーを手動変更するのではなく、Ansibleなどの設定ツールを使うことをお勧めします。後々、複数案件を同時にテストしたり、自動テストを並列実行したりするのに、テスト環境が複数必要になることがあるからです。
6.CIツールを導入する(JenkinsやGitlab-CI)
レガシープロジェクトでは常に時間が足りなくなります。ボタン1個で、ビルド、ユニットテスト、リリース、コーディング規約チェックなどの定形作業をできるようにしてください。また、CIの形にしておくと、プロジェクトに新メンバーが参加したときの説明もしやすくなります(説明が「Jenkinsでビルドして」で済む)。
なお、Jenkinsは好きな場所に単独で建てることができる上、シェルスクリプトをコピペするだけでジョブが作れるので、おすすめです。
7. コーディングを楽にする
さて、ようやくコーディングに集中できる環境が整ってきました。しかし、レガシープロジェクトではソースコードが汚かったり、当たり前のツールが導入されていないことが多いです。人間の注意力は限られているので、本質的でない所で苦労していると、つまらないミスを犯す可能性が上がります。可能な限りのツールを導入しましょう:
- コーディング規約を自動チェックする(rubocop など)
- 脆弱性につながりうる書き方を自動チェックする(brakeman など)
- シンタックスシュガー言語を導入する(TypeScript, SCSS など)
- パッケージ管理する(bundler など)
- viではなくIntelliJなどのIDEを使う
- Dockerで開発用のDBを個人ごとに作れるようにする
- etc...
なお、ツールを導入するときは、
既存コードを変更せずに使えるか?(既存の問題点を一時的に無視できるか?)
という点に注意してください。既存のコードを変更してしまうと、それによって回帰テストが必要になってしまうからです。
例えば rubocop では .rubocop_todo.yml
というファイルを生成すると、既存コードの問題点については一旦無視し、新規コードにのみチェックを行うようにできます。
追記
思いがけず、「いいね」やはてなブックマークをたくさん頂けました。
ここで、はてなブックマークのコメントで気になったものについて追記いたします。
「捨てて新しく作ってもいいんじゃない?」
これについてはRub a dub dub(無理やり和訳すれば、ゴシゴシ作戦?)をお読みください。ジョエルテストで有名なジョエル・スポルスキーさんのエッセイです。
・・・読みましたか?
再実装には、
- 旧バージョンの経験が活かされず、同じバグを再び作りこむ恐れがある
- 新バージョンを作る間、旧バージョンの開発が止まる(あるいは、平行開発しなければならない)
といったリスクがあります。しかも、リスクを取ったとしても、利用者から見れば「旧バージョンと同じ物」が出来るだけです。
なので、ごく小規模な場合を除けば、再実装はお勧めしません。
-
こう書くと、今の職場はレガシープロジェクトだらけ!!と思われるかもしれませんが、最近はルールを整備したり、プログラマのレベルを上げたりしたおかげで、遭遇することは無くなっています。 ↩
-
ユーザーが仕様の無矛盾性を考えないのは、普通のプロジェクトでもよくあります。そもそも仕様を保つ責任はユーザーではなく開発者にあります。・・・とはいえ、基本的な用語すら通じないと文句の一つも言いたくなります。 ↩
-
rails db:setup
はRailsの開発用のコマンドで、普通はローカルマシンにSQLite3のファイルを作成します。 ↩ -
テストが管理されていない状態では、テストを実施するのにかかる時間が過大に見積もられ、「○○○するなんて全機能テストが必要になるから無理!」といった感情的な反発が起きがちになります。 ↩