ソフトウェア開発では、バグを前工程で検出すると被害が小さくて済むとよく言われます。様々な書籍やブログが同じことを言っています。一度経験してみればこれは当たり前のことなのですが、そうでない場合にそのようなことを見聞きしても「へえ、そんなものか」で終わってしまいます。そこで本記事は、ソフトウェアの運用開発が浅い人に向けて、「バグを前工程で検出すると被害が小さい」とはどういうことかについて、Aさんという開発者が大変な目に遭う架空の例を使って説明します。具体的にどう対策するかについてはスコープ外とします。
大変な目に遭うAさん
Aさんは、社員数30人程度の小さなSaaSプロバイダの開発、運用技術者です。プロダクトのソースコードはGitHubで管理されています。プロダクトはさまざまな他社サービス、オープンソースのツールに依存しています。サービスの新しいバージョンは次のような工程で本番環境にdeployされるとします。
- Aさんを含む複数の開発者がGitHubでPRを作り、レビューとCIを通せばmain branchにマージされる。CIは外部サービスには依存しない。従って、それぞれの開発者はこの工程では並列に開発できる。
- main branchは所定のタイミングで、本番環境と同じ構成のテスト環境にdeployされる。ここでユーザーのワークロードを模したテストを実行し、パスすれば本番環境にdeployされる。
- 本番環境でユーザーが新バージョンを使う。
Aさんは本番環境にdeployされたプロダクトを、サービスレベルを達成するよう運用します。そこで得たフィードバックをもとに機能開発、バグ修正、運用の改善をします。ユーザーから問い合わせが来たらサポートチームが一次対応し、技術的な問題の場合はAさんを含む開発、運用技術者にエスカレーションされます。
実世界でのサービスは、ほとんどの場合はもっと複雑な仕組みになっているでしょうが、ここではあくまで例なので気にしないことにします。
Aさんが仕込んでしまうバグ
AさんはPRの作成中に、うっかりバグを仕込んでしまいました。ユーザーが送信するフォームに特定の文字列が入っていると、変更前は問題なく成功していたのに、変更後はサービスを担うプロセスが異常終了するようになるというバグです。
このバグが顕在化すると、フォームを送信したユーザー、およびクラッシュしたプロセスを介してサービスを受けていた全ユーザーが、プロセスの再起動完了までの1分間、サービスにアクセスできないようになってしまうとします。
バグの検出と影響範囲
本節では、上述したバグをどのステップで検出したかによって、どのような影響になるかを紹介します。もちろん状況によってありうる展開はさまざまなのですが、ここではそのうちの一つを紹介します。
ステップ1のPR作成時点でバグが見つかるケース
AさんのPRに問題があったことが先輩によるレビューで明らかになったとします。どこが悪いかも指摘してもらえていたので、Aさんは自分がPRで書いたコードの指摘された箇所のロジックを見直しました。バグにより増えた時間的コストは延べ2時間でした。関係者はAさんと先輩の2人でした。
CIで問題が見つかった場合も、バグのありかを特定するのにもうすこし時間がかかるかもしれませんが、後続の節に比べると大したことはありません。
ステップ2のテスト環境でバグが見つかるケース
バグがあるAさんのPRがマージされてテスト環境にdeployされ、テスト環境でこのバグが見つかったらどうなるでしょうか。この場合はPR作成時に見つかるよりも対処が大変になります。
前回リリースされたバージョンとテスト環境にdeployされたバージョンの間には、一般に複数のPRがマージされています。このため、問題が顕在化した際には「どのPRにバグがあったのか」を最初に突き止めないといけません。問題発生箇所に関連するPRの数が多ければ多いほど、調査に時間がかかる傾向にあります。エラーログから「Aさんの修正が怪しい」とわかることもありますが、そうもいかずに調査が長引くこともあります。
このバグはサービス停止を引き起こしてしまうので、原因特定および修正が終わるまでリリースが止まってしまいました。何人もの開発者が原因究明にあたり、最終的にAさんのPRにバグがあると分かりました。修正が完了するまでの時間的コストは延べ20時間、関係者は5人でした。
今回はこの程度で済みましたが、タイミングが悪いと、アナウンス済の最新機能のリリースが遅れてビジネスに直接影響が出てしまうこともあるでしょう。
ステップ3の本番環境でバグが見つかるケース
PR時のテスト、テスト環境でのテストをすり抜け、このバグを抱えたサービスが本番環境にdeployされると、どうなるでしょうか。このバグの性質上、恐らく悲惨なことになります。
新バージョンのdeployをしてから1日後、たまたまバグを顕在化させる入力をしたユーザーがフォームを送信すると、そのユーザーはサービスにアクセスできなくなりました。別の多くのユーザーも、突然同じ状況になりました。さらに悪いことに、サービス復帰後にバグを顕在化させるフォーム送信が再試行されて、同じ問題が二度起きました。Aさんはこの問題の発生をアラートによって知り、調査を開始しました。また、数分後にサポートチームから大量の問い合わせが開発、運用チームに入りました。
事が事だけにのんびり原因究明しているわけにはいかず、まずは新バージョンを一旦古いバージョンに切り戻すことにしました。その後しばらくサポートチームはユーザー対応に追われ、開発者は全員で調査、修正、サポートチームへの説明に追われました。最終的に影響を受けたユーザーは1000人、社内的に対応に要した時間的コストは事後の再発防止策の立案、実行も含めると延べ200時間、社内の関係者は20人となりました。
影響はこれだけに留まりません。影響を受けたユーザーからのサービス、及びそれを提供するAさんの会社への印象は悪くなったことでしょう。一度毀損した信頼を取り戻すのは簡単ではありません。
実は今回切り戻しで問題を解消できたのは幸運でした。仮に同じバージョンでRDBのschema変更が入っていたような場合は切り戻しは難しかったでしょう。また、バグの顕在化が1ヶ月後で、その間に何度もリリースしていたら、バージョンを1つ戻した程度では解決せず、調査にもさらに時間がかかったでしょう。
最後に
かなり単純化しているとはいえ、それなりにありそうな例を使って、バグを前工程で検出するほど被害が小さくて済むということを説明しました。私は書いていて胃が痛くなってきました。
それぞれの工程でバグを検出した際の影響は次の通りでした。
| 検出段階 | 時間的コスト(延べ時間) | 関係者(人) | 影響を受けたユーザー(人) |
|---|---|---|---|
| ステップ1: PR作成時 | 2 | 2 | 0 |
| ステップ2: テスト環境 | 20 | 5 | 0 |
| ステップ3: 本番環境 | 200 | 20 | 1000 |
架空の例なので数値はもちろん適当なのですが、このようなことが起きたときの影響として、大外れはしていないと思います。
本記事の対象読者の意識が、少しでも「へえ、そんなものか」から「がんばって前工程でバグを潰すぞ」に変わることを願って、終わりにします。

