いものやま。

雑多な知識の寄せ集め

ソフト開発における「設計」とは何なのか。(まとめ)

これまでの各記事は、以下から。

だいぶ長くなったけど、現時点での自分の考えをある程度書き出せたかなと思う。

ポイントを少しまとめておくと、以下のような感じ:

  • ソフトウェアにおける開発とは、ソースコードの差分を生み出すこと
  • 開発はWhy → What → Howの順にブレークダウンしていく
    • Whyは要求で、「顧客が」何を望んでいるのか
    • Whatは要件で、顧客の要求を解決するために「開発者が」何を提供するのか
    • Howは設計で、要件を満たすためにソースコードの差分をどう生み出すか
  • 要求を満たす要件や要件を満たす設計は複数ありえる
    • 取捨選択するのが要件定義や設計というプロセス
    • 方向性を確認するためのコミュニケーションツールが要件定義書や設計書
  • 要件定義や設計においてユースケース記述は有用
    • イメージのすり合わせに使える
    • ドメイン知識の抽出に使える
  • ソースコードから作られる文書は設計書ではなく開発者向けドキュメント

せっかくだからもう少し整理して同人誌にしてもいいなぁ。

今日はここまで!

ソフト開発における「設計」とは何なのか。(その11)

前回はアジャイル開発に関する話や実現可能性の話を書いた。

今回はプロセスの話ではなく、ソフトウェアをどう考えて設計するといいかについて。

オブジェクト指向分析設計

今ソフトウェアの設計の話というと、ドメイン駆動設計(Domain-Driven Design, DDD)が流行っている感じはある。

ただ、どうにもDDDの本来の目的(ソフトが価値提供する領域(ドメイン)の知識をドメインモデルとして表現し中核に置くことで、価値提供の現場とのギャップをなくそうとしている)とはズレて、小手先のテクニックや紹介されているアーキテクチャの真似事に陥ってしまっている感じも。 根底にある思想の理解は難しいけど、やることのマネならなんとなくできてしまうからねぇ。

それと、一番肝心のドメイン知識をどうやって得るのかという部分についてはあまり語られていないというのも大きい。 「DDDやってみたいけど、ドメインモデルが抽出できない」というのもよく聞く。

そもそもの話として、オブジェクト指向的な考え方でなくフロー指向的な考え方をしてると、ドメインモデルをクラスとして表現するという発想ができないので、オブジェクト指向的な考え方ができてることは前提としてある (オブジェクト指向とフロー指向については『オブジェクト脳への一歩』というLTをしてみた。 - いものやま。や自分の同人誌『オブジェクト・ウォーズ』を参照)。 この前提がまずできてない人も多い印象・・・

それを抜きにしても、どうやってドメインモデルを抽出していくのかという「やり方」のは説明が必要だと思う。

実はそのあたりの「やり方」の話は、ソフト開発における「設計」とは何なのか。(その8) - いものやま。でも少し書いたけど、オブジェクト指向分析設計(Object-Oriented Analysis and Design, OOAD)で語られている。

オブジェクト指向分析設計について知りたい場合、次の本が楽しく読めると思う:

ただ、絶版なんだよなぁ・・・コード例が古いというのもちょっと難点。 デザインパターンは第2版が出たので、これもまた出るといいんだけど。

ユースケースから考える

さて、設計の出発点となるのは、ユースケース。 ここで、システムは何を顧客に提供するのか(そして何を提供しないのか)という境界を定めることになる。 また、システムが実際にどのように使われるのかや、その中で出てくる概念(ドメイン知識)を洗い出すことになる。

ドメイン駆動開発だと、このユースケースについて考えて分析するというプロセスが書かれてないんよね。

で、ユースケースを考えたあとにやりがちなのが、そのユースケースの流れをそのままコードにしてしまうということ。 これはとてもフロー指向的な考え方で、これをやってしまうとドメイン知識はどこにいるのか分からなくなってしまう。 トランザクションスクリプトと呼ばれるのは、これをやってしまった結果として生まれる。 とても自然な発想ではあるんだけど、ドメイン知識があちこちに散らばってしまったり、変更に弱いコードになりやすい。

じゃあ、ユースケースを考えたあとに何をやるといいのかというと、ユースケースに出てくる名詞(と動詞)に注目するということ。 それらはドメインに登場してくる重要な「概念」なので、それらを抽出して、その概念がどういったものなのか、データ(プロパティ)として何を持つのか、どういったデータが妥当なのか(不正なデータの規定になる)、何ができるのか(メソッド)、他の概念との関係性はどうなっているのかなどを分析する。 そうやって分析した結果として表現されたクラスにはドメイン知識が詰め込まれることになる。

また、それらの概念を使った「処理」というのもある種の概念となるので、それもクラスとして表現しておくといい。 どういった処理を行うのかという具体的な内容、知識がクラスの中に詰め込まれて、外部からはその処理を実行しろというキックだけ行えば、オブジェクトが適切な処理をしてくれるようになる。

あとはそうやって用意した部品を組み上げてユースケースを実現すればいい。 こうすることで、ドメイン知識の詰まったコードを生み出せるようになる。

で、実際の開発の場合、生み出すのはソースコードの差分なので、こうやって分析するのは今回開発or変更する部分についてだけとなる。 そして新しく作るクラスや変更の入るクラスが見つかったら、それを設計書に書いて、方針の確認をするといい。 こうすると、処理の流れといった詳細をダラダラと書くのではなく、ファイル構成やそこで実装されるクラスといった概念レベルでのチェックができたりもする。

今日はここまで!

ソフト開発における「設計」とは何なのか。(その10)

前回は設計書に何を書いたらいいのかについて書いた。

今回は設計に関して少し補足をしたい。

アジャイル開発と設計の話

ここまででかなりじっくりと要件定義と設計の話をしてきているので、これはウォーターフォール開発向けの話だと思っている人も多そう。 あるいは「うちはアジャイル開発してるから設計なんてしないんだよね」なんていう人も。

まず、「アジャイル開発では設計をしない」というのはアジャイル開発に対する典型的な誤解。

次の図は『アジャイルサムライ』に出てくる図。

アジャイル開発でのプロセス

見ての通り、アジャイル開発でも分析(≒要件定義)や設計という工程は含んでいる。 なので、アジャイル開発だからといって要件定義や設計をしないというわけではない。

違うのは、前の工程が全部終わってから次の工程に行くというのではなく、段階的に何度も繰り返すというところ。 これはソフト開発における「設計」とは何なのか。(その4) - いものやま。で書いた図にも近い。 粒度の違いはあるかもしれないけど、繰り返し開発していき、その中で要件定義や設計していくというのは、ソフトウェア開発の基本的な考え方となっている。

ただし、設計はするけど、設計書を書くとは限らないというのは少し注意が必要。 これはソフト開発における「設計」とは何なのか。(その5) - いものやま。にも書いたけど、設計書はあくまでコミュニケーションのためのツールなので、別の手段でコミュニケーションが取れるなら、書かなくてもいいというのがあるから。

具体的には、アジャイル開発で使われることのあるプラクティスとして、ペアプロやモブプロというのがある。 これは担当者が一人で設計、実装していくのではなく、複数人でやっていくというもの。 みんなでワイワイと議論しながら作業を進めていくので、紙の上で方向性のコミュニケーションをする必要性が下がる。

あるいは、アジャイル開発だと同じ場所に集まって開発を進めた方がいいというプラクティスもあって、これも気軽にその場で相談や議論ができるので、方向性の確認が必要なら逐次その場でやっていけばいいというのがある。

あとは、とりあえず実装ができたら方向性がちょっと違っていてもOKとしてしまって、方向性の修正は次のイテレーションで行うという手もある。 繰り返しが短い期間で行われるので、ちょっと方向性が違った実装が出てきてしまっても、そこで学んだことを次の繰り返しで反映するのがやりやすい。 もちろん、開発に必要な工数自体は増えてしまうんだけど、間違った方向に進んだままリリースしてしまうというリスクをかなり抑えられる。

実際、ウォーターフォール開発で設計をしっかりと考えてみたけどコードを書き進めてみたら思ったよりも感触が悪いというのもよくある話。 そのときは、よく練られた設計を考えるよりもとりあえずの設計で動く実装を用意してみて、そこからのフィードバックを踏まえていい設計を考えて実装を直す方がよかったりする。

実現可能性との付き合い方

上記のアジャイル開発の方法と関連して、実現可能性の問題というのは常にある。

何かというと、設計を考えてみたけど、実際にやってみたらうまいこと動かなかったみたいな問題。 設計をするときはこれとどう付き合っていくかというのを考えておかないといけない。

一つの対策は、上記の通りアジャイル開発的な方法を使うというもの。 つまり、まずは動くものを作っておいて、繰り返しの中で必要に応じて改善していく。 こうしておけばとりあえず動くものがあるので企画倒れで終わることはないし、最悪そのままリリースもできる。

他の対策は、アジャイル開発の方法に近いんだけど、実際に手元で仮の実装を作って実現可能性を確認しておくという方法。 手元で試行錯誤した結果を踏まえて「この方法だとこういうメリットがある」「この方法だと問題があるからやらない」というのを書けると、設計書に説得力を持たせられる。

ただ、気をつけないといけないのは、手元で試した実装をそのまま「できました」と出しちゃうのはダメということ。 あくまで実現可能性を確認するためのコードなので品質に問題があるし、方向性が違って「なんじゃこりゃ」となったり、あるいは「じゃあそれで」と仮の実装なのにそのまま使われてあとで苦労することになりがち。 なので、方向性の確認は設計書でちゃんとやった方がいい。


一応これで設計のプロセスに関する話は終わり。

あとは実際にソフトウェアの設計をどう考えて作るといいかについて少し書きたい。

今日はここまで!

ソフト開発における「設計」とは何なのか。(その9)

前回はユースケース記述について書いた。

今回は設計書に書いた方がいいことについて。

設計書に書くといいこと

実際のところ、設計書に何を書いたらいいのかを画一的に述べるのは、かなり難しい。 というのも、ソフト開発における「設計」とは何なのか。(その5) - いものやま。にも書いたとおり、設計書というのはコミュニケーションのためのツールにすぎないので、十分なコミュニケーションを取れるかどうかで書く内容や量も変わってきてしまうから。 あるチームだと本当に簡潔な内容で済むかもしれないし、別のチームだと綿密に書く必要があるかもしれない。 開発の領域によっても書くべき内容は変わってくるしね。

ただ、とはいっても原則的なところはあるので、その原則を抑えたうえでチーム内でテーラリングしていけるといいんじゃないかと思う。

  • 概要から始め詳細へ分解する
  • 境界に注目する
  • 検討内容と選択理由を書く
  • ファイル一覧を書く
  • 影響範囲を書く
  • テスト設計する

概要から始め詳細へ分解する

設計書の大きな目的は、やろうとしている変更の方向性がちゃんとあってるかを確認すること。 なので、大きな方向性がまず間違ってないかを確認し、それをさらに小さく分解して、それぞれの方向性が間違ってないかを確認していくといい。

たとえば、まずシステム全体の構成を考えて、各モジュールの役割を考えて、その中でどういったクラスを用意するかを考えて、さらにはデータ構造はどうするのか、メソッドはどうするのか、どんなアルゴリズムを使うのかと、詳細へと落とし込んでいく。

もちろんこれは、開発の段階や内容、そして開発するエンジニアのレベル感によって、どこを書くのか、どこまで書くのかは変わってくる。

たとえば、一番最初の開発ならシステム全体のアーキテクチャから考え始めないといけないわけだけど、その後の開発なら全体像はもう決まっているので、今回開発する部分にだけ絞って記述すればいいとなる。 あるいは、ライブラリ開発だとシステム全体のことは気にしないでライブラリ内の構成だけ考えればいいし、逆にマイクロサービスを組み合わせて大きなサービスを作り上げるとかだと全体の構成や他のサービスへの影響とかも考える必要が出てくる。 あと、シニアなエンジニアなら設計では概要レベルだけ書いておけばあとはお任せでOKとなるけど、ジュニアなエンジニアに任せる場合は設計段階でより詳細まで詰めておいた方が安心できる。

このあたりの調整は必要。

で、その調整をしやすくする意味でも、設計書も何回かに分けて途中で確認しながら書いていくといいところはある。

まずは書こうと思っていることの目次だけ用意してチェック。 次に概要レベルの部分を書いてチェック。 それもOKならそれより少し詳細に入ったところまで書いてチェック。 みたいのを繰り返しながら、もうあとは大丈夫かなというところで設計書は完成にして、その方針にしたがって実装するみたいな。

境界に注目する

上記とも少し関係するところだけど、システムというのは完全な一枚岩でできているわけではなく、いくつかの部品が組み合わさってできあがっている。 で、部品内の作りはいくらでも変えられるんだけど、部品と部品をつなぐ部分、つまり境界については、簡単に変えることができない。 なぜなら、そこを変えると両方の部品に変更が入ってしまうから。 なので、部品と部品の境界、すなわちインタフェースについては、しっかりと考えた方がいい。

なので、まずシステムのどこに境界があるのかをちゃんと把握するといい。 そして、その境界のインタフェースをしっかりと定める。

さらにいうと、部品は普通入れ子構造になっていて、部品の中にはさらに小さな部品が集まっていたりするはず。 なので、必要なところまでブレークダウンしていく必要がある。

たとえば、フロントエンドとバックエンドが協調して動くなら、まずそこに境界があり、インタフェースが存在することになる。 これは普通はWebAPIとして規定されるはず。 さらにはバックエンドの中にも複数の層があったりして、そうなるとその間にも境界があり、やはりインタフェースを規定することになる。 その各層にも複数のモジュールが含まれていたり、さらには複数のクラスがあったり、みたいな。

これもどこまで掘っていくかというのはあるんだけど、そうやって境界を意識することで、かっちりと決めておかないといけない部分が見えてくると思う。

検討内容と選択理由を書く

設計というのは「How」すなわち「どうやるのか」の取捨選択だということを書いた。 なので、要件定義書と同じように、「なんでその選択をしたのか」というのを書いておくといい。

とくに「この方法ではやらない」というのを書いておくといい。 理由についてはこれも要件定義書と同じで、選ばれなかった方法についてはソースコードに何も情報が残らないから。

ファイル一覧を書く

そんな感じで検討しながら詳細に詰めていくわけだけど、ついでにこれを書いておくといいかなというのがファイル一覧。 今回の開発で、このファイルが新規に増える、このファイルに変更が入る、このファイルは削除する、というのをツリーで書いておくイメージ。

- project_dir/
  - hoge/
    - hoge.py  # 新規;XXクラスを実装
    - huga.py  # 変更;XXクラスに対応、piyo.pyと統合
  - (piyo.py)  # 削除 -> huga.pyへ統合
  - ...

みたいな。

これを書いておくと以下のようないいことがある:

  • どのあたりに変更が入るか分かりやすい
    • 変更する場所や内容が適切かがこれでけっこう分かる
  • 新規ファイルの場所や名前を適切にしやすい
    • バージョン管理でファイルの位置や名前が変わると変更を追跡しづらくなるので、作ってしまってから変えるのではなく、あらかじめ合意をとっておいた方がいい
  • 変更の影響が分かりやすい
    • 意外なところにも変更が入ると気づけると、影響範囲の推測でミスしづらくなる

ちなみに、ファイル全部を書くのは大変なので、今回の開発に注目したところだけ書くのでOK。 書く量が多すぎるならディレクトリレベルで「このディレクトリ以下に変更が入る」とかでもいい。 まぁ、書く量が多すぎるとしたら、ちょっと設計として問題ありなのかもみたいな嗅覚も働かせたいところだけど。

影響範囲を書く

開発で怖いのはデグレ。 なので、今回の変更でどこら辺のコードにも影響が発生するのかは書いておいた方がいい。 これを複数人でレビューしておくことで、影響範囲の考慮漏れがないかとかを考えられる。

このときに注意したいのは、影響を受けるのはコードだけじゃなくて、ドキュメントやテストもあるということ。 今回の開発によって新たにドキュメントを作る必要が出てくるとか、ドキュメントのここの記載が変わってくるとか、今まで通ってたこのテストが通らなくなるとか。

そのあたりを抑えられてないと、あとになってドキュメントの記載が間違ってるという話がきたり、テストが急に通らなくなったんだけどとかが出やすい。

ちなみに、ドキュメントやテストのソースもあるなら、上記のファイル一覧を書くときにそれらに対しても「このファイルに変更が入る」と書いておくことになる。 そういった意味でもファイル一覧を書いておくのは影響範囲の考慮漏れを防ぐのによかったり。

テスト設計する

あとはテスト設計も書いておくと安心なところがある。 もちろん、厳密に書くと大変なので、基本的には「こんな感じのテストをしようと考えてる」くらいでOKだけど。

ただ、データの境界値とかが明確に決まっているのなら、その仕様についても書いておくといいかも。 あとでテストレビューをするときに、境界値に対してちゃんとチェックできてるかが確認しやすいので。


以上が抑えられていれば、コードが出来上がってきてから「思ってたのと違った」となるリスクをだいぶ抑えられると思う。 かつ、設計書自体のボリュームもそれなりに抑えられるかなと。

今日はここまで!

ソフト開発における「設計」とは何なのか。(その8)

前回は要件定義のやり方について書いた。

今回はそれに付け加える感じで、ユースケース記述の話。

ユースケース記述

ユースケース記述というのは、システムがどのように使われるのかという一連の流れを書いたもの。

一例を挙げると、たとえば「ユーザがログインする」という場合なら、

  1. ユーザがブラウザで特定のURLにアクセスする。
  2. システムはログイン画面を表示し、ユーザにメールアドレスとパスワードの入力を求める。
  3. ユーザはメールアドレスとパスワードを入力し、「ログイン」ボタンを押す。
  4. システムは入力されたメールアドレスとパスワードで認証を行い、認証ができたらユーザのマイページを表示する。

みたいな。

このユースケース記述というのはシステムを外部から見たときの振る舞いを規定するものなので、要件定義というよりかは基本設計で考えるものではあるんだけど、できれば要件定義の中で追加でやっておいた方がいいと思っている。 なんでかというと、このユースケース記述をお客さんに共有することで、実際に自分がそのシステムを使うときの様子をイメージしてもらうことができるから。 この段階で「思ってたのと違う」というのが分かれば、システムを作り出す前に方向性の修正ができる。

このユースケース記述はシステムの内部の動きについては細かく書かないので、お客さんも理解できるギリギリのところにある設計となっている。 なので、ユースケース記述を共通言語してシステムの振る舞いを積極的に議論しておくといい。

ちなみに、このユースケース記述をちゃんと作れるかどうかが設計の肝なところがある。 というのは、近頃ドメイン駆動設計(DDD)が声高に叫ばれてたりするけど、このユースケース記述の中に出てくる用語や概念、その適切な値、振る舞いといったものが、まさにドメイン知識だから。

ドメイン駆動設計をやろうとして、でも何がドメイン知識なのか分からないみたいな話を聞くことがあるけど、それはドメイン知識を抽出する方法を知らないからだと思っている。 ドメイン知識というのはユースケース記述から抽出するものなんよね。 そして、ユースケース記述から抽出するからこそ、お客さんとの共通言語としての機能を果たせるというのがある。 オブジェクト指向分析設計でこのあたりの知識は語られてるはずなんだけど、なんか抜け落ちてしまっているせいで、テクニックに走りすぎて実際のドメイン知識はスカスカの軽量DDD(というか形骸DDD)が生まれてしまってると思うんだよなぁ・・・オブジェクト指向分析設計がちゃんと復権してくれるといいんだけど。

ユースケース記述のポイント

さて、ユースケース記述を書く上で、いくつか気をつけたいことを書いておきたい。

まずは、個々のユースケース記述をやっていく前に、ユースケースの一覧を用意しておくこと。 一覧で見れるようにしておくことで、ユースケースに抜け漏れがないかを確認しやすくなる。

一つ注意したいのは、このユースケース一覧というのは、システム全体のユースケースじゃなくて、今回の開発対象のユースケースということ。 全部のユースケースを書いてたら、いくら時間があっても足りないので。

そして、ここのユースケース記述をするときに気をつけたいのは、「主語」。 ここでもやっぱり主語が重要。 日本語だとついつい省略しちゃいがちなんだけど、「誰が」何をやるのかをハッキリさせておかないと、あとで「あれ?」ってなることが出てきやすい。

たとえば「毎日9時に在庫情報に矛盾がないか確認する」みたいな記述があると、誰がこの確認をするのかがハッキリしない。 決まった時間という意味ではシステムがやりそうな感じがあるけど、9時という時間だと管理者がやる可能性もワンチャンありそうな。 ましてやこのあとに「在庫情報に矛盾があった場合、修正を行う」みたいな記述が続いてたら、管理者が確認してシステムに修正情報を打ち込むのか、システムが確認して修正できるところを自動で行うのか、みたいになってくる。 いずれにせよ、解釈に揺れが生じて、「思ってたのと違った」となる危険性が高い。

あと、主語を書くときに、ユーザ側については「ロール(役割)」も意識した方がいい。 同じシステムであっても、管理者ができることと一般ユーザができることというのは違ったりする。 なので、単に「ユーザは顧客情報を入力する」「ユーザは使用明細を見る」のように書くのではなく、「管理者は顧客情報を入力する」「一般ユーザは自分の使用明細を見る」のように、どのロールで振る舞った場合のユースケースなのかが分かるような主語になっているといい。

他には、システムがバッチで動くとかなら「いつ」動くのかとかもちゃんと書いておきたいかな。 まぁそんな感じで、システムの外側から見た振る舞いに関して、できるだけ曖昧なところが生じないように書いて合意をとっておけるといい。

一方で、ついやってしまいがちなのが、システム内部の処理を細かく書きすぎてしまうこと。 この条件ではこう計算してとか、ループをどう回してだとか。 ユースケース記述を書くのって、フローチャートを書いてるのに近い感じはあるからね。

ただ、そういった内部の動きはお客さんも言われても困るだけから、書いてもしょうがない。 なので、システム内部の細かい動きには触れず、ユーザとシステムのちょうど境界の部分について書けるといい。

要件定義書に書くといいこと

ということで、要件定義書に書いておくといいことは、以下のような感じ:

  • 開発の目的
    • 顧客が何を望んでいるか
      • 深掘りして、要件に繋げられるレベルにしておく
    • リファクタリングなら、開発者が何を望んでいるのか)
  • 要件定義
    • 要求を満たすために、開発者は何を行うのか
      • 〜〜機能を新しく作る
      • 〜〜機能を修正する
      • (〜〜機能を廃止する)
      • (開発後にシステムが満たしているべき条件(非機能要件))
    • 検討したけど実施しないことは何か、どうして実施しないのか
  • ユースケース

このレベルでお客さんと合意が取れていると「思ってたのと違った」というのが起こりにくいし、開発者側もドメイン知識の抽出やシステムの作りをイメージしやすくなる。

今日はここまで!

ソフト開発における「設計」とは何なのか。(その7)

前回は、要求と要件の違いは「主語」で、それぞれを考えるときには視点を切り替える必要があることを書いた。

今回はそれを踏まえて要件定義のやり方を書いてみたい。

要求の深掘り

開発はお客さんから「〜〜がしたい」とか「〜〜機能がほしい」とか言われてスタートすることが多いと思う。 ただ、このときそのまま「〜〜できるようにする」とか「〜〜機能を追加する」のように要件にしてしまうのは避けた方がいい。

これにはいくつか理由がある。

まず、本当にほしいものとズレている可能性があるということ。 「顧客が本当に必要だったもの」の風刺画にあるように、本当に必要なものをちゃんと分析して自分で規定することはとても難しい。 もちろん、それはお客さんではなくこちらで考えても難しいことなんだけど。 ただ、お客さんと会話して深掘りしたりモックを見せたりすることで、「思ってたのと違った」というのを早めに潰しておけるならそれに越したことはない。

そして、要求は深掘りしてより深い要求として理解した方が、その要求を満たせる手段の幅も広がるというのがある。 「ドリルの穴理論」の話にあるように、「ドリルがほしい」というレベルの要求の理解だと、それを満たす手段は「ドリルを売る」としかならない。 けど、「なんでドリルがほしいんだろう?」というのを深掘りして「壁に穴を空けたい」という要求が分かれば、「ドリルを売る」以外にも「ドリルを貸し出す」「人を派遣して穴を空ける」などの手段も考えられる。 さらに深掘りして「どうして壁に穴を空けたいんだろう?」というのを聞いてみたら「壁に絵画を飾りたいから」という要求が分かったりするかもしれない。 もうそうなると本当に必要だったのは壁の穴ですらない可能性も出てくる。

あと割とあるのが、単なる思いつきだったという話。 そういった思いつきをそのまま作って提供すると、作ったけど結局使われないままということがよくある。 それでもお金がもらえるならいいんじゃない?という意見もありそうだけど、実は落とし穴がある。 それは作った機能は使われてなくてもメンテし続けないといけないということ。 そうやって使われないゴミ機能が増えてしまうと、他の機能を追加するときの足枷になるし、やる価値のないバグ修正やテストで工数が膨れ上がることになる。

そんな感じで、要求の深掘りは重要。

そして、要求の深掘りをするときに重要なのが、顧客視点になるということ。 要求は主語が「顧客」なので、自分が開発者であることは一旦忘れて、顧客の立場になって、どうして〜〜したいのか、〜〜機能がほしいのかということを深掘っていく。

このとき、とくに意識したいのが「顧客が困っていること」。 困っていることを解消するのはすごく価値が高いというのがまずあるし、困ってることは明確なのでそれを解消するのは要求としてズレにくいというのもある。

逆に「あれば嬉しいこと」は難しくて、実際に作ってみても意外と使われないことも多い。 なくても別に困らない機能って、よほどの嬉しさがないと使わないんよね。

要件の検討

こうして顧客の要求を分析したら、それを満たすための要件を検討する。

ここからは顧客視点で考えていたのを一転させ、開発者視点で考えていくことになる。 顧客の要求を満たせるような手段を開発者として考えて、妥当な点を見つけていく。

この妥当な点というのは、「やりたいこと」と「できること」の均衡点。

「やりたいこと」は顧客が要求するもので、顧客視点では当然最大限満たされることが望ましい。 けど、開発ではそれを完全に満たすことは難しい。 技術的に実現可能かどうかがまずあるし、可能だとしてもコストがとてもかかってしまって現実的ではない場合もある。 なので開発者視点では「できること」に限りが出てくる。 そこで、「やりたいこと」ーー上(顧客)からの力学と、「できること」ーー下(開発者)からの力学とで綱引きが発生して、コストや納期なども踏まえてその綱引きが均衡する点が要件として定義されることになる。

そんな感じで「開発者が」やること、満たすべき条件を要件としてまとめる。 これはその開発でのスコープになる。

気をつけたいのは、これはソフトウェアの要件ではなく、開発の要件ということ。 すなわち、開発で作るソフトウェアの差分についての要件を書くことになる。 なので、「新規に〜〜機能を作る」「従来機能を〜〜に変更する」(場合によっては「〜〜機能を廃止する」)といったもののが要件となる。

ちなみに、ここでは主に機能要件を挙げてるけど、他にも性能やリソースなどの非機能要件もあるので、それらも必要に応じて定義することになる。

「やらないこと」の記録の重要性

あと、要件定義では「やること」だけではなく、「やらないこと」とその理由も実は書いておいた方がいい。

これは、「やること」は実際に開発が進むので明確に残るのに対し、「やらないこと」は開発が行われないので、文書に残しておかないとそもそも検討のテーブルに上がってたのかどうかすら分からなくなってしまうから。

あとになって「現状こうしてるけど、なんでこうしなかったんだろう?」みたいな疑問が浮かぶことはよくあるんだけど、やらなかった理由が文書として残っていれば、その疑問をすぐに解消できる。 そもそも検討のテーブルに上がってなかった(開発当時よりあとに出てきた手法とかだとよくある)のであれば、その方法を新しく検討してみる価値はあるだろうし、検討のテーブルに上がってはいたけど明確な理由があって採用されなかったというのであれば、それを知らずに再検討してやっぱり使えないとなる無駄を避けることができる。 あるいは、上記の力学の都合で採用されなかったり改善が先延ばしになっている場合もあるので、それであれば優先度に応じてその方法に切り替える開発を行うことも考えられる。

そんな感じで、どうしてやらなかったのかの理由はちゃんと書いておいた方がいい。

今日はここまで!

ソフト開発における「設計」とは何なのか。(その6)

前回は以下のようなことを書いた:

  • 開発は「Why(要求)→What(要件)→How(設計)」の順にブレークダウンしていく必要がある
  • ブレークダウンでは複数の選択肢が考えられるので取捨選択が必要
  • 選択した方針が間違ってないかのコミュニケーションが必要で、そのために要件定義書や設計書を書くといい
  • コミュニケーションが不要なら文書はなくてもいい(ただし要件定義や設計自体は必要)

今回は、要件定義を考えていくために、要求と要件について深く考えてみる。

要求と要件の違い

要件定義について考える前に、そもそもの話として「要求」と「要件」の違いが何なのかをハッキリさせておいた方がいい。 「要求と要件の違いって何?」ってあらためて聞かれると、ちゃんと答えられない人は多いと思う。

かく言う自分も、昔はこの違いでかなり悩んだ。 というのも、会社で用意されていた要件定義書のテンプレートで、要求を書く章と要件を書く章が分かれてたから。 要求も要件も同じじゃね?ってなってると、それぞれに何を書いたらいいのか分からないとなる。

要求と要件の違い、それは何かというと、「主語」が違う。

要求の主語は基本的に「顧客」となる。 「顧客は〜〜したい」「顧客は〜〜がほしい」のように、「顧客が」望んでいることが要求であり、その要求を満たすために開発は行われる。

一方で、要件の主語は常に「開発者」となる。 要求を満たすために「開発者は〜〜する」のように、「開発者が」やること(および守るべき条件)が要件となる。

ここで少し補足しておくと、要求の主語は「顧客」以外の場合もある。 たとえば「リファクタリングしたい」とか「ログを出せるようにしたい」とかは顧客ではなく開発者の要求なので。 もちろん、そのさらに奥には「顧客は新規機能の開発速度を上げてほしい」「顧客はバグを踏みたくない」「顧客は問題が起きたときに早く修正が入ってほしい」などの顧客の要求があるわけだけど。

それに対して、要件の主語は常に「開発者」であることに気をつけたい。 要求が何であれ、それを満たすために手を動かすのは「開発者」以外にありえないから。

いずれにせよ、「主語」を意識することがすごく重要。 そしてそれは、要求を考えるときと要件を考えるときでは、視点を変える必要があるということにも繋がる。

その話はまた次にしたい。

今日はここまで!