レガシープロジェクトを引き継いだ時、最初にするべき７つのこと

> 営業一課で使っている PHPアプリを保守してくれないかな？
> ○○さんが１人で作ってメンテしてたやつなんだけど

皆さんは上司からこんな仕事を振られたことはないでしょうか？私は過去に何度か経験した[^1]のですが、こういった仕事はなぜか：

[^1]: こう書くと、今の職場はレガシープロジェクトだらけ！！と思われるかもしれませんが、最近はルールを整備したり、プログラマのレベルを上げたりしたおかげで、遭遇することは無くなっています。

- 正確な仕様を知っている人はいない（知ってた人は辞めた）
- テスト計画書・デプロイ手順書・仕様書といったドキュメントは無い
- ソースコードはもちろんスパゲッティ
- でも、業務ではガッツリ使われているので廃止できない

という**レガシープロジェクト**ばかりでした。この記事では、レガシープロジェクトを引き継いでしまった時に、最初に何をするべきか書いていきたいと思います。

**なお、ここで最悪なのは「とりあえず、緊急の不具合から直してしまおう」と、いきなりコードの修正にかかることです。**

※**おことわり**： この記事では「遵法的な職場の」「PHPやRailsで書かれた」「数十〜数百人が使う」「内製アプリを」「プログラマー1人+αが専任で保守する」程度のプロジェクトを想定しています。「ウン千人のプログラマが参加し、多重下請け構造と社内承認フローでがんじがらめになった、COBOL製40年物の金融システム」に適用できるとは限りません。

# なぜ、いきなりコードを修正してはいけないのか？

いきなりコードを修正すると**誤った修正をしてしまう**可能性が大です。

プロジェクトを担当して日が浅いあなたは、まだ正確な仕様を理解できていません。 **あなたが修正しようとしている「不具合」は本当に不具合なのでしょうか？**

また、ユーザーから不具合修正を依頼される事がありますが、レガシープロジェクトはUIが難解だったりロクなマニュアルがなかったりするため、ユーザーは往往にして、

> 「〇〇さんにこうしろと言われたって、▲▲▲さんに聞きました」
> 「わかりません。私は前任者から引き継いだだけなんです」
> 「とにかく業務が回らないんです。ここを直せばいいだけなんですから」

・・・なんて、仕様を理解せずに使っていたり、目先のトラブルを解決したいがためにアドホックな修正を提案してきたりします[^2]。しかし、誤った修正をして後々矛盾に苦しむのはあなたです。

[^2]: ユーザーが仕様の無矛盾性を考えないのは、普通のプロジェクトでもよくあります。そもそも仕様を保つ責任はユーザーではなく開発者にあります。・・・とはいえ、基本的な用語すら通じないと文句の一つも言いたくなります。

そして、コードの修正方法を判断できたとしても、普通の作業に信じられないような危険が伴うことが、レガシープロジェクトではありえます。

- 例： 開発環境を作ろうと `rails db:setup` [^3]を実行したら会社中のエンジニアが悲鳴を上げた。（何故か）自分のローカルDBではなく、社内で共有している開発DBを初期化する設定になっていたからだ。
- 例： 開発版をテスト環境にデプロイしたら利用者から悲鳴が上がった。実はテスト環境を業務で使っていた。
- 例： デプロイしたら依存ライブラリのビルドが始まったが、なぜかビルドに失敗し、サービス停止５分の予定が結局３０分間サービス停止した。

[^3]: `rails db:setup`はRailsの開発用のコマンドで、普通はローカルマシンにSQLite3のファイルを作成します。

ただでさえ不具合山積みなのに、追加でトラブルを起こしては目も当てられません。また、このような環境では気が散ってコーディングのミスを犯す可能性も高まります。まず安全を確保することを優先してください。

# 最初にするべき７つのこと

とにかく、普通のことを普通にできる体制を整えましょう。

## 1. 協力者を確保する

プロジェクトを引き継いだら、まずプロジェクトに協力する人を確保してください。例えば以下のような人たちです：

* 前任者（まだ退職していなければ）
* コードレビュワー
* テストエンジニア
* インフラエンジニア
* ユーザーの代表者
* アプリの仕様を知っている人（ベテランユーザーなど）

この人たちとは、プロジェクトを回す上で遅かれ早かれ協力を仰ぐことになりますから、早い段階で渡りをつけておきましょう。具体的には：

- キックオフミーティングを開いて、問題意識・初期のタスクを共有する
- Slackチャンネルを作って、随時連絡を取れるようにする

などが、考えられます。

上司やユーザーと交渉をする時（「サーバー増設したい」「この機能を廃止したい」）にも、あなたが１人で交渉するよりも、関係者５人を引き連れて行った方が何かとスムーズにことが運ぶでしょう。

## 2. 仕様をドキュメント化する

ユーザーに聞いたり、コードを読んだりして、アプリの仕様をドキュメントに書き出しましょう。

ここでは完璧な仕様書を目指す必要はありません。正確性・網羅性よりも、**ともかくドキュメントを作ってしまうことが大事です。** というのも、ドキュメントが無い状態では、用語が通じなかったり、事実ではなく想像に基づいて物を言ったりして、議論が迷走しがちになるからです。

> ユーザー　　「カレンダー通知を直してくれないか？急いでるんだ」
> プログラマー「『カレンダー通知』って何ですか？僕の担当の『予定管理システム』関係ですか？」
> ユーザー　　「予約前日にユーザーにプッシュ通知するやつさ」
> プログラマー「ああ、『前日メッセージ』ですね。あれを送信してるのは、予定管理システムじゃなくてメッセージ画面ですよ？」
> ユーザー　　「えっ？メッセージ画面ってなに？」

なんて。

目に見えるドキュメントがあれば「ここを変えましょう」「この用語が分からない」と具体的で生産的な議論ができるようになります。

もちろん、引き継ぎ直後の理解が浅い段階ではドキュメントに間違った記述をしてしまうこともあるでしょう。しかし、ドキュメントならば、少なくとも誤りを見つけて訂正することはできます。各々の頭の中の想像を修正するのは不可能です。

## 3. テストやデプロイの手順書を作る

テストやデプロイは開発サイクル中に繰り返し行うものなので、手順書に書き起こして安全・確実に実施できるようにしましょう。

もちろん、テストやデプロイは最終的には自動化するのが望ましいところですが、レガシープロジェクトでは手動でのテスト・デプロイすらおろそかになりがちです。まず手動での手順を整備しましょう。

テストならTestRailなどで管理するのがベターですが、無ければExcelでも構いません。とにかく、「管理されている状態」に持っていくことが大事です。

デプロイ手順やテストについてもドキュメント化されていることで、

* この機能のテストが足りていない
* このテストケースは重複している
* このテストケースは誤りだ
* このデプロイ手順はキャッシュを使って高速化できる
* この手順は簡単に自動化できるのではないか？

といった、品質についての議論や、

* １日でテストを15件消化できたから、残り45件は3日で終わるはずだ
* A案はテストを50件実施する必要があるが、B案なら8件で済むから、B案にしよう
* Railsをアップデートすると、全テストに○人日かかるが、それだけの価値はある[^4]
* デプロイに1回30分もかかるのは問題だ。機能追加より先に、デプロイ高速化に取り組もう

といった、工数配分についての議論ができるようになります。

[^4]: テストが管理されていない状態では、テストを実施するのにかかる時間が過大に見積もられ、「○○○するなんて全機能テストが必要になるから無理！」といった感情的な反発が起きがちになります。

また、テスト手順がドキュメント化されていれば、必要に応じてテスターを増員して短期間でテストできるようになります。

## 4. 不具合・機能をチケット管理する

レガシープロジェクトでは、不具合も足りない機能も山盛りになっているはずです。それらはRedmineなどのチケット管理ツールで、目に見える形で管理するようにします。チケットにすれば、

- 優先度を決める
- 大きな課題を、細かいチケットに分割する
- 全チケットが完了するまでのスケジュールを見積もる

といったことがしやすくなるし、タスクを忘れることも無くなります。

また、全体像が見えない状態だと、

> 「これは重要なんだ！今すぐやってくれ！」

と言われたとき、反論できず困ったことになります（言ってくる方も自身の職務上の責任があるわけですし）。一方、チケットが一覧されていれば、

> 「すみません。優先度：高のチケットが○件あるので、その後になります」
> 「〇〇さんの〇〇〇〇案件を後回しにすることになりますけど、交渉してきてもらえますか？」

といった妥協をしやすくなります。

なお、チケット管理をExcelで行うことも可能ではありますが、専用のチケット管理ツールを使用することをお勧めします。Excelでチケット管理するのは、チケット番号を手動で振らなければならなかったり、チケットにコメントを付けにくかったり、更新時に関係者に共有する手間があったりして、不便が多いからです。

## 5. まともなテスト環境を用意する（できれば複数）

そのレガシープロジェクトにテスト環境はあるでしょうか？まれによくあることですが、

- 「テスト環境」と称する環境はあるが、本番環境とサーバー構成や設定が違ってしまっているので、テストで不具合を見のがしてしまう。
- テスト環境が無く、開発者のマシンで適当にテストした後いきなり本番適用するフローになっている（そしてバグる）

といった状況ではないでしょうか？ともかく「テスト環境が無い」では話にならないので、本番環境とできるだけ同じ設定のテスト環境を作りましょう。

なお、テスト環境を作る際には、サーバーを手動変更するのではなく、Ansibleなどの設定ツールを使うことをお勧めします。後々、複数案件を同時にテストしたり、自動テストを並列実行したりするのに、テスト環境が複数必要になることがあるからです。

## 6.CIツールを導入する（JenkinsやGitlab-CI）

レガシープロジェクトでは常に時間が足りなくなります。ボタン１個で、ビルド、ユニットテスト、リリース、コーディング規約チェックなどの定形作業をできるようにしてください。また、CIの形にしておくと、プロジェクトに新メンバーが参加したときの説明もしやすくなります（説明が「Jenkinsでビルドして」で済む）。

なお、Jenkinsは好きな場所に単独で建てることができる上、シェルスクリプトをコピペするだけでジョブが作れるので、おすすめです。

## 7. コーディングを楽にする

さて、ようやくコーディングに集中できる環境が整ってきました。しかし、レガシープロジェクトではソースコードが汚かったり、当たり前のツールが導入されていないことが多いです。人間の注意力は限られているので、本質的でない所で苦労していると、つまらないミスを犯す可能性が上がります。可能な限りのツールを導入しましょう：

- コーディング規約を自動チェックする（rubocop など）
- 脆弱性につながりうる書き方を自動チェックする（brakeman など）
- シンタックスシュガー言語を導入する（TypeScript, SCSS など）
- パッケージ管理する（bundler など）
- viではなくIntelliJなどのIDEを使う
- Dockerで開発用のDBを個人ごとに作れるようにする
- etc...

なお、ツールを導入するときは、

> 既存コードを変更せずに使えるか？（既存の問題点を一時的に無視できるか？）

という点に注意してください。既存のコードを変更してしまうと、それによって回帰テストが必要になってしまうからです。

例えば rubocop では `.rubocop_todo.yml` というファイルを生成すると、既存コードの問題点については一旦無視し、新規コードにのみチェックを行うようにできます。

# 追記

思いがけず、「いいね」やはてなブックマークをたくさん頂けました。

ここで、[はてなブックマークのコメント](https://b.hatena.ne.jp/entry/s/qiita.com/tonluqclml/items/a7ed2f94225ef8a37b8b)で気になったものについて追記いたします。

## 「捨てて新しく作ってもいいんじゃない？」

これについては[Rub a dub dub](https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/)（無理やり和訳すれば、**ゴシゴシ作戦**？）をお読みください。ジョエルテストで有名なジョエル・スポルスキーさんのエッセイです。

・・・読みましたか？

再実装には、

- 旧バージョンの経験が活かされず、同じバグを再び作りこむ恐れがある
- 新バージョンを作る間、旧バージョンの開発が止まる（あるいは、平行開発しなければならない）

といったリスクがあります。しかも、リスクを取ったとしても、利用者から見れば「旧バージョンと同じ物」が出来るだけです。

なので、ごく小規模な場合を除けば、再実装はお勧めしません。