開発プロセスとソフトウェアの品質について

#目次
[1.はじめに](#1-はじめに)
[2.ソフトウェアの品質](#2-ソフトウェアの品質)
[3.開発プロセスと各工程における品質ポイント](#3-開発プロセスと各工程における品質ポイント)
[4.おわりに](#4-おわりに)

#1. はじめに
この記事は、[トラストバンク Advent Calendar 2021](https://qiita.com/advent-calendar/2021/trustbank)の16日目です。

トラストバンクの山本（@akiyoshiyamamoto）です。
トラストバンクに入社して3年ほど経ちますが、今はふるさとチョイスのAPI開発チームのリーダーを担当しています。実務としては、要件定義や設計、テスト・リリースの計画やシステム運用を担当することが多いです。

アドベントカレンダーも初めてですし、何を書けば良いのかと悩みながら、他のメンバーの記事を参考にしようかと思って読んでたら、どれも良い記事だらけで、さらに悩んでしまいギリギリまでかかりました。

現在担当しているAPI開発の技術的な話もありなのかと思いましたが、前職はSIer企業に勤めていたこともあり、トラストバンクでの開発経験も踏まえて、各開発工程でソフトウェア品質に関わるポイントや自分なりに開発において注意していることを書こうかと思います。

#2. ソフトウェアの品質
ソフトウェアにおける品質の定義は様々で一言では難しいですが、質の悪いシステムやサービスは以下のようなものが多いかと思います。

- バグ／不具合が多い（使いものにならない）
- 性能が悪い（遅くてイライラする）
- 要件が実現できてない（思ったのと違う）...etc

いずれの点も、利用するユーザであったり、サービスやシステムを企画している人であったり、誰かの価値観から生まれるものかと思います。
なので、何かを作る時は、「**なぜ作るのか**」「**誰のためのなのか**」「**作るモノの価値は何なのか**」を事前に整理しておくことが大切です。


#3. 開発プロセスと各工程における品質ポイント
開発プロセスは、会社やプロジェクトによって様々ですが、トラストバンクでは、大きめの開発案件は、ウォーターフォール型を採用しており、大まかには以下の流れで開発を進めています。

- 要件定義
- 工数見積もり
- 設計（基本設計レベル）
- コーディング
- テスト（単体試験～結合試験）
- QA試験（QAチームで実施）
- リリース

ただ、ガチガチのウォーターフォールではありません。
各工程のフェーズゲートを設けていなかったり、設計段階の仕様変更もカバーできる範囲は受け付けたり、以下に早く価値提供するかも要求されるため、ある程度柔軟にはやってきています。仕様変更については、言われたこと全てをやるのではなく、この変更が本当にサービス／システムとして今必要なのかどうかを判断して、優先順位を付けてリリースのフェーズを分ける等の対応を取っています（経験上、無理矢理ねじ込むと大抵は失敗します）。

ソフトウェアの品質は、上記のテスト工程で全てカバーできるかというとそうではなく、できるだけ**早い段階で問題／課題を見つける**ことが品質向上につながります。各工程で普段私が注意している点や開発を進める上でのポイントを書こうかと思います。
*※QA試験はQAチームで実施しているため記載対象外とします*

##3-1. 要件定義～設計
社内の開発案件は、色々な部署から企画や開発要望が出てくることが多く、開発を進める前に、まずは要件を整理することから始まります。

### 背景／目的とターゲット
要件定義を進める上で、背景や目的や誰をターゲットにしているかを一番最初に整理するのが大事かなと思っています。
開発者は、「何を(**What_**)」「いつまでに(**When**)」「どう作るのか(**How**)」等に重点を置きがちですが、「**なぜ作るのか(Why)**」が一番大事です。なぜならこれが要件の原点になるからです。この「なぜ作るのか」には、開発要望に至るまでの背景や目的が隠れており、そこが問題／課題解決の本質になります。そして、作るモノが「**誰が使うのか、誰への価値提供なのか(Who)**」を明確にすることが大切です。
開発が長期に渡ると様々な問題が発生し、目の前の問題解決に必死になり、本来の目的と逸れてしまうこともよくあります。**迷った時こそ原点に返る**必要があり、そのためには諸段階で本質的な課題や目的を明確にしておく必要があります。

また、この開発に至った「背景」や「目的」、そして「誰か使うもの」なのかは、開発メンバーにも共有すべきです。具体的なゴールや利用者をイメージ出来た方が、開発者も目的意識をもって取り組め、質の高い仕事に繋がります。具体的なイメージがあるからこそ、開発者同士で議論が生まれて、自分では見えてない改善点が新たに見つけられることも多々あります。

:::note info
・背景や目的、ターゲットは最初に明確にし、原点を決める
・開発メンバー全員と共有して開発を進める
:::

### 要望と要件の違い
企画者から開発相談を受け、その通りに開発を進めるのが要件定義ではありません。企画段階で挙がってくるものは「要望」で、「こんなことをしたい」「こんな機能が欲しい」等、コンセプトであったり理想像であることが多いです。当然、そこにシステム的な知見は、含まれていないので、内容に矛盾があったり、考慮すべき点が漏れていたりします。そこでエンジニア視点でも、「要望」を実現するために「本来はこんな機能であるべき」「挙がっている機能以外にこんな機能も必要になる」「要望する機能は別のツールや機能で代用可能」等、**「要望を実現するこを具体的に定義する」ことが「要件」**になります。
ただ、リソースにも限りがあるため、「要望」を全て聞き入れることは難しいこともありますが、企画者もできるだけ早くリリースしたいと考えています。それに間に合わせるためには、当然、優先順位を決める必要もあり、今回の開発では「やらない」「次フェーズで対応する」ということも重要な決定事項になります。

:::note info
・「要望」は「要件」ではない
・「やるべきこと」だけでなく「やらないこと」を決めるのも要件定義
:::


### 機能要件と非機能要件
「実装する機能機能は何か」を整理することが「機能要件」です。要件定義がちゃんとできていれば、「機能要件」は洗い出すことは比較的容易です。今回のスコープの要望／要件に対して、どの機能でどう対応するのか、マトリックスで整理すれば、漏れななく機能要件を洗い出すことはできます。
一方、「非機能要件」とは、「機能要件以外のすべて」という定義になります。それ以外というとかなり幅広いですが、一般的には、システムの「可用性」「性能/拡張性」「運用/保守性」「移行性」「セキュリティ」等が挙げられます。
これらを毎回全てを決める必要性はなく、既に「可用性」を考慮したシステム上での開発であれば考慮は不要ですし、データ移行を伴わないのであれば「移行性」も考慮は不要です。ただ、「性能」や「セキュリティ」は、考慮すべきことが多いかと思います。当然、これらは企画者から挙がってるわけではなく、**言われてないから考慮してません／対応していませんは、エンジニア失格**です。エンジニアとして必要なシステム要件は提示すべきですし、それ踏まえて全体の要件／スコープを考慮する必要があります。

:::note info
見えない要件こそエンジニアが見つける
:::

### 見積もり
見積もりの工数算出と実体が大きく乖離があるとプロジェクトの進行に多きな影響が出てきます。ただ、この見積もりも開発において難しいテーマで、神の方程式みたいなものはなく、四苦八苦する方も多いかと思います。見積もりをするタイミングはある程度、要件定義が進んだ段階で行うため、設計～テストまでの工数見積もりとなります。
見積もりをぶれなく実施する上での前提としては、「**開発プロセスを熟知しておくこと**」だと思います。「開発する」ということは「プログラムを作る」だけではなく、実際は様々なアウトプットをしています。開発プロセスによって必要なアウトプットも変わり、アウトプットに応じて当然工数も比例していきます。例えば、ドキュメント作成、ユニットテスト（テストコード）、テスト項目書作成とテストの実施等があり、さらにそのドキュメントやテスト項目書のレビューも今の開発プロセスでは必須としています。これらの全ての工程で必要な工数を算出してトータルの工数を出す必要があります。
コーディング工数もフレームワークやアーキテクチャにも依存して変わってくるケースもあるため、一概に決まったやり方はないですが、FP(ファンクションポイント)法でえいやで一旦出してみて、その後、実際かかった工数と照らし合わせて、実態に近い重みでFPを改良してくのが一番精度が高いかなと思っています（時間はかかりますが）。

:::note info
開発プロセスを考慮して必要なアウトプットに応じた見積もりをする
:::

### 運用設計
新しくサービス／システムを作ると必ずと言って良いほどシステムの運用が発生します。場合によっては、それに伴った運用機能の開発も必要です。例えば、新しい機能を追加したことでそのユーザ設定に関する問い合わせがあった場合に、カスタマーサポートがユーザの利用状況が分かる機能が必要だったり、誰が/いつ/どのタイミングで/どの機能で/どういう運用をするのかを整理しておく必要があります。
何かを作る時は、作って終わりではなく、システム運用のサイクルが的確に回せられることも重要です。

:::note info
要件を実現する上で、それをカバーできる運用の考慮ができているのか
:::


## 3-2. コーディング

### コーディングレビュー
私自身コーディングを今はほぼやっていないので、コーディングレビューについて書くのは大変恐縮なのですが、以下の観点で見ることが多いです。

1. 読んですぐ分かるコードになっているか
2. データ量や性能を意識した作りになっているか

1点目は、1メソッドで膨大な処理を書いてるコード等、複雑なコードはバグを生みやすいです。バグを修正しようにも手が付けられず、修正するとデグレが発生するという**品質低下の悪循環**の原因となるからです。可読性は個人的には重要視したいです。ただ、ある程度作り込まれた段階で、コードの可読性を指摘するのは、逆に手間がかかり過ぎるので、改善できる範囲で妥協点を見つける必要はあるかと思います。
2点目は、性能です。DBからデータを取得して、ループ処理をするようなケースで、対象テーブルのデータ量を考慮できているのか、計算量は考慮出来ているのか等です。開発者は本番環境のデータが見れず、開発環境の少ないデータ量でしか認識していないケースもあるため、どれだけのデータ量を考慮して作っているのかは確認する必要があります。

## 3-3. テスト

### テスト項目書のレビュー
テストは最終的なアウトプットをチェックする重要な工程です。
今の開発プロセスのテスト工程では、ユニットテストと結合試験レベルのテストを実施してます。ユニットテストはPHPUnitを使ってテストコードを書いていますが、結合試験では試験項目書を作成して、それをレビューするようにしています。普段は以下の観点でレビューをしています

- 要件の実現が確認できる試験になっているか
- データパターンや判定条件が網羅できているか
- 異常系のテストパターンが考慮てきているか

上記に加え、既存機能の改修であったり、不具合によある改修の場合は、「デグレ防止」の試験が含まれているかを重視しています。デグレとはプログラムの修正によって本来動くべき機能が正常に動かなくなることですが、**「デグレ」が発生するで良いことは何一つない**です。今までの開発作業に不信感が生まれたり、再テストの実施など本来予定していなかった工数が発生したり、エンジニアとして罪が深いことの1つでもあります。

:::note info
デグレ防止試験は含まれているか
:::

## 3-4. リリース
作ってテストが完了したらそれで開発が終わりではありません。**リリースして稼働させるまでが開発**です。
リリース時にトラブルが発生するのは、よくあることなのですが、リリース時の影響を加味せずに、設計／開発を進めると、痛い目を見ることが多いです。リリース時に注意するというよりは、リリース作業も意識した設計や準備ができているかを設計～テスト工程で注視しています。
例えば、あるテーブルにカラムを追加する必要が出てきた場合、対象テーブルのデータ量が多すぎて、MySQLの場合だとテーブルロックが発生し、オンラインでは変更できないケースがあります。カラム追加ではなく、リレーションテーブルを1つ追加する等の対応を取るか、最悪サービスを一時停止してカラム追加する等の対応を取る必要があります。
*※現在は、percona-toolkit(pt-online-schema-change)というツールを使ってある程度大きなテーブルでもオンラインで定義変更ができるようにしています*

:::note info
・リリース時の作業まで想定した設計になっているか
・リリースまでの準備が十分にできているか
:::

#4. おわりに

文章ばかりで少し抽象的な記事で恐縮ですが、10年以上システム・サービス開発に携わって品質を常に意識しても、バグのないシステムやサービスは存在しません。

:::note info
バグがないソフトウェアがあるとすれば、それは誰にも使われていないモノだ
:::

リリースまでにここまで品質を意識して頑張っても、リリース後もバグは出てくるもので、バグの報告を受ける度に、心が痛むものです。
しかし、誰かが使ってくれてるから・関心があるから、見つかった不具合だと思えば、少しは心は和らぎます。
**最高のソフトウェア**を目指してもその達成は中々難しいですが、**最善を尽くす**という気持ちは肝に銘じて今後も開発を進めていきたいな、と思います。

技術的な知見やノウハウの記事ではないですが、開発を進める上で少しでも参考になれば、と思います。