いものやま。

雑多な知識の寄せ集め

長瀞・秩父で桜を見てきた。

先週末の4/14(日)に長瀞秩父に行って桜を見てきた。

法善寺

まず向かったのは長瀞にある枝垂れ桜の法善寺。

時期が少し遅かったのもあり、けっこう散ってる感じはあったけど、まだ少し残ってた。

法善寺の枝垂れ桜

アップで

北桜通り〜長瀞駅宝登山神社

そこからは北桜通りを抜けて長瀞駅を通り、宝登山神社へ。

北桜通りもまだ桜自体は残っていたけど、だいぶ葉桜になっていた感じはあった。 車で通り抜けたので写真はなし。

そして長瀞駅を通過して、宝登山神社へ。

宝登山神社の鳥居

宝登山神社の本殿

いやー、やっぱり宝登山神社は何度見ても立派よね。

そして今回はロープウェイで上には行かず、駅前へ戻り。

宝登山神社への参道の桜も散り気味

たまにキレイに咲いている桜も

長瀞駅前から参道への入り口

長瀞

駅前に戻ってきてちょうどお昼時になったので、ご飯を食べることにした。

いつもの鮎めしもいいなと思いつつ、ご当地グルメとして豚みそ丼も有名で食べたことがなかったので、今回は豚みそ丼を食べようかなと。

事前に調べていて、長瀞駅前で有名だったのは「有隣」というお店。

ただ、すでに数組が待っていて、けっこう待つことになりそうだったので、どうしようかなぁと。

そして他に空いてそうな店がないかなと少し探してみると、すぐ近くの「長瀞屋」がすぐに入れそうだったので、入ってみた。

昔ながらの定食屋という感じで、豚みそ丼と蕎麦のセットを注文。

豚みそ丼と蕎麦のセット

この豚みそがすごく柔らかくてびっくり。 味付けはもうちょい濃くてもいいかなとか、もうちょいボリュームほしいなとは思いつつも、美味しかった。 運転がなければビールも飲みたかったなぁ。

あと、ちょっと暑かったので、追加でかき氷も頼んでみた。

あずきミルクのかき氷

これがとんでもない量だったw 食べても食べてもなくならない(^^;

ただ、味は美味しくて、氷も積もったばかりの雪みたいな柔らかさ。 せっかくだからと大きいサイズを頼んだけど、小さいサイズにしとけばよかったなw

岩畳

お昼を食べたあとは少し散策して岩畳を見たりお土産を買ったり。

荒川ライン下り

岩畳と荒川

駅前の花壇がキレイだった

美の山公園

長瀞をあとにして、次に向かったのは美の山公園。

秩父の東の方にある山をぐんぐん登った上にある公園なんだけど、ここは桜がちょうど見頃で、眺望もよく、とてもよかった。

美の山公園への入り口

駐車場で撮った桜

駐車場を見下ろして

駐車場から公園へ向かう階段

公園入り口の桜と展望台

ヤマザクラらしい

展望台からの眺め

ちなみに展望台から見えてるのは秩父の市街。

桜と東の山々を臨んで

東に広がる絶景

園内の桜

秩父市街と桜を臨む

いいところだったなぁ。

清雲寺

元々は美の山公園に行ったあとは帰るつもりだったんだけど、お昼を食べた長瀞屋に飾ってあったカレンダーの枝垂れ桜が気になり、調べると清雲寺という秩父のお寺さんで、時期は過ぎちゃってるけどまだ残っていそうだったので、少し足をのばして向かってみた。

ここでちょっと意外だったのが、お寺には駐車場がなかったということ。 周りのお家で数百円払って停めさせてもらう感じだった。

清雲寺

見ての通り、散っちゃってる桜もけっこう多かったんだけど、まだ残っている桜もあって、キレイだった。

清雲寺の枝垂れ桜

近くで撮影

墓地の方にも

キレイな枝垂れ桜

すごく古い桜もあったみたいで、ただそこはすでに散っちゃっていたので、残念。 いい時期を狙ってまた来てみたい。

浦山ダム

そのあとは、すぐそばに浦山ダムがあるということなので、せっかくだから行ってみた。

浦山ダム

ダム湖が広がる

ここでダムカードもゲット。 初めてもらったかも。


そんな感じで長瀞秩父の桜(&ダム)を堪能してきた。

少し時期が遅かったのもあってけっこう散っちゃってたのはあったけど、それでもいい桜が見れたのでよかった。 とくに美の山公園はよかったなぁ。

今日はここまで!

「日付」と「日時」の話。

プログラミングで時間を扱おうとすると出てくるのが「日付」と「日時」。 これらに関してちょっと考えたことがあるので、その話をしてみたい。

「日付」と「日時」を構成するデータ

「日付(date)」を構成する要素を考えると「年(year)」「月(month)」「日(day)」。

一方で、「日時(datetime)」を構成する要素を考えると、「年(year)」「月(month)」「日(day)」「時(hour)」「分(minute)」「秒(second)」(とミリ秒などもあるけど省略)。

なので、差分プログラミング的に考えると、「日付」クラスを継承して「日時」クラスを作るとするのが自然。

たとえば、(あまりいいコードではないけど)Pythonでデータクラスを使ってシンプルに書くと、次のようになりそう:

from dataclasses import dataclass

@dataclass
class Date:
    year: int
    month: int
    day: int

@dataclass
class DateTime(Date):
    hour: int = 0
    minute: int = 0
    second: int = 0

date = Date(2024, 3, 31)
datetime = DateTime(2024, 3, 31, 12, 0, 0)

実際、Pythonの標準ライブラリにあるdatetimeモジュールでは、日付はdatetime.dateクラス、日時はそれを継承したdatetime.datetimeクラスとして実装されている。

これはRubyでも同様で、標準ライブラリのdateライブラリで、日付はDateクラス、日時はそれを継承したDateTimeクラスとして実装されている。

ただし、面白いことにRubyDateTimeクラスはDeprecatedとされていて、代わりに組み込みライブラリのTimeクラスを使うことが推奨されている。 これがDateクラスもDeprecatedになってるならdateライブラリ自体がよくないのかなとなるけど、継承したDateTimeクラスだけがDeprecatedになってるのが不思議なところ。 で、実はこれは自分が気になったところが原因じゃないかと思ってる。

「日時」は「日付」なのか?

さて、そんな感じでデータ構造を考えると「日時」を「日付」のサブクラスとして作るのは自然なんだけど、疑問が出てくる。

はたして、「日時」は「日付」なのか?

クラスの継承では「is-a」関係があるのが望ましいとされている。

たとえば「動物」を継承して「犬」とか「猫」を作るという例が代表的だけど、この場合「『犬』は『動物』の一つ」だし「『猫』も『動物』の一つ」なので、たしかに「is-a」関係が成り立ってる。

これは集合関係として捉えることもできて、「『サブクラスのオブジェクトの集合』⊆『スーパークラスのオブジェクトの集合』」となっているのが妥当な継承関係と言える。

たとえば「『犬の集合』⊆『動物の集合』」だし「『猫の集合』⊆『動物の集合』」とこの関係が成り立っている。

それを踏まえたうえで、「日付」と「日時」でそういった妥当な関係が成り立ってるのかどうか?

「日時」は「日付」の一つかというと、ちょっと違う。 というのは、もしそうであるのなら、「日付」をたくさん集めてきた集合の中に「日時」も含まれることになるわけだけど、そこには日付しかなくて、日時というのは入っていない。 「日付」を集めた集合の中に、たとえば「2024-03-31」という日時は含まれていても、「2024-03-31 12:00:00」という日時は含まれてない。

「日付」と「日時」がどういう関係なのかを考えると、1対多の関係とみるのが妥当そう。 1つの「日付」の中に複数の「日時」が含まれている感じ。 これは逆に「日時」側からみると1つの「日付」が対応付くので、「has-a」関係ともいえる。 なので、継承ではなくコンポジションを使うのが設計としては妥当そう。

簡単なコードで書くと(あまりいいコードではないけど)以下:

from dataclasses import dataclass

@dataclass
class Date:
    year: int
    month: int
    day: int

@dataclass
class DateTime:
    date: Date
    hour: int = 0
    minute: int = 0
    second: int = 0

date = Date(2024, 3, 31)
datetime = DateTime(date, 12, 0, 0)

継承だと起きる問題

とはいえ、継承で何か問題があるの?ともなりそうなところ。

実は問題があって、引数で「日付」の型でくるとなってたときに、データが「日付」の場合も「日時」の場合もあるのが変なことを起こしたりする。

たとえば、「ある日時がある日付に含まれるかどうか」という関数を作りたいとする。 素直な実装は次のようになる:

import datetime as dt

def is_datetime_in_date(datetime: dt.datetime, date: dt.date) -> bool:
    return datetime.date() == date

is_datetime_in_date(
    dt.datetime(2024, 3, 31, 12), dt.date(2024, 3, 31)
)  # => True
is_datetime_in_date(
    dt.datetime(2024, 4, 1, 12), dt.date(2024, 3, 31)
)  # => False

何も問題なく動いてるように見えるけど、実は問題があって、次のようなコードが型として問題なく実行できてしまう:

is_datetime_in_date(
    dt.datetime(2024, 3, 31, 12), dt.datetime(2024, 3, 31, 9)
)  # => False

datetime.datetimedatetime.dateを継承してるので、datetime.dateが指定されるべき引数でdatetime.datetimeが指定されても、型としては何も問題がない。 そしてこれはdatetime.date(2024, 3, 31)datetime.datetime(2024, 3, 31, 9)の比較がされるので、結果はFalseとなる。 日付はどちらも2024-03-31であるにも関わらず。

一応、実装を次のようにすれば、結果をTrueに変えることはできる:

def is_datetime_in_date(datetime: dt.datetime, date: dt.date) -> bool:
    return (
        (datetime.year == date.year)
        and (datetime.month == date.month)
        and (datetime.day == date.day)
    )

is_datetime_in_date(
    dt.datetime(2024, 3, 31, 12), dt.date(2024, 3, 31)
)  # => True
is_datetime_in_date(
    dt.datetime(2024, 4, 1, 12), dt.date(2024, 3, 31)
)  # => False
is_datetime_in_date(
    dt.datetime(2024, 3, 31, 12), dt.datetime(2024, 3, 31, 9)
)  # => True

ただ、冷静に最後の式の意味を考えると「2024-03-31 12:00は2024-03-31 9:00に含まれるか?」となってるので、やっぱりおかしい。 日付しか指定できないところで日時も指定できてしまってるのが本質的な問題で、この問題が起きてるのは継承してるからに他ならない。

これが継承を使わない実装になってれば、「日付」を指定するところで「日時」を指定するのは型としておかしいと弾けるので、問題を防げる。 本来くるべきではない値は型として弾くのがいい。 で、継承を濫用してスーパークラスを型として指定すると、都合の悪いサブクラスが指定されてしまったときにおかしくなる、と。


これに似た話が直和型でもあったりする。 それについてはまた別に話したい。

今日はここまで!

OOC2024で登壇してみた。

3/24(日)にお茶の水女子大でObject-Oriented Conference 2024 (OOC2024)が開催された。

今までカンファレンスで発表したことはなかったんだけど、オブジェクト指向に関する話を発表できる機会ということで、せっかくだからとプロポーザルを出してみた。 そしたらなんと採択してもらえたので、登壇して発表してきた。

その発表の概要と感想とか。

発表の概要

タイトルは「オブジェクト指向の修得はなぜ難しいのか」。

スライドはこちら:

ざっくりと内容を紹介すると、プログラミングを学ぶと普通は「処理の流れ(フロー)」に注目して「トップダウンで処理を分解」してプログラムを書いていく(これを自分は「フロー指向」と呼んでいる)けど、オブジェクト指向だと実はそうしなくて、「データ」に注目して「ボトムアップで部品を組み上げ」てプログラムを書いていくので、考え方が180度違うから学ぶのが難しいんだよ、といった感じ。

ちなみにこれに近い内容が増田さんのセッションでも出てきてて、ちょっと嬉しかったw

p.16の「オブジェクト指向プログラミングの設計スタイル」で、「機能 vs データ型」で「データ型」が、「ブレークダウン vs ビルドアップ」で「ビルドアップ」が選ばれてるのがそうで、これは自分のオブジェクト指向の理解と同じになっていて、またここで「機能 & ブレークダウン」を選ぶと自分が呼ぶ「フロー指向」になっている。

感想とふりかえり

オンラインのLTで発表したことはあったけど、オフラインの場でそれなりの人を前にして発表するのはほぼ初めてということもあり、かなり緊張した。

ただ、今回は事前に原稿を用意したり少し練習しておいたのもあって、なんとか話し切れた気はする。

この原稿を用意したのがよかった点で、実際のところ、本番のときに原稿を見る余裕は全然なかったんだけど、説明の流れや言葉選びをあらかじめ推敲する機会が生まれてたので、詰まることが少なくスムーズに進められたと思う。 まぁ、もうちょい練習した方がよかった感じはあるけど(ただ本番のテンションで練習するはなかなか難しい・・・)。

あと、今回すごく助かったのが、すごく頷きながら聞いてくれる方が会場にいたこと。 その方を見つけられたことで、途中からは緊張も和らいで発表できたと思う。 リアクションしてくれる聴衆って大事なんだなぁと実感した。

ただ、それは本当にラッキーで、今思えばその方以外の聴衆の反応は全然見れてなかった。 もっと意識してリアクションしてくれる方を探さないとダメなんだろうなぁ。 次があったら意識してみたいと思った。

少し関連することで、タイムキーパーの方が残り時間を表示してくれていたんだけど、全然気づけてなかった。 やっぱり緊張してて視野が狭くなってるんだろうなぁ。

タイムキープに関しては別の反省点も。 iPhoneでストップウォッチを使って時間を見ようとしてたんだけど、気づいたらロックがかかってて見れなくなってることが何度か。 これはLTでも同じミスをしたことがあって、それを回避するために「おっかけプレゼンタイマー」というツールを導入してたんだけど、今回はネットワークに繋げられない環境だったので、このツールが使えずに同じ過ちを繰り返してしまった。

ネットワーク使えないときは気をつけるようにしないと。

とまぁ、初めてだったこともあっていろいろ反省点はあったけど、無事発表できたので、とてもいい経験になったと思う。 次があったらもうちょっと改善していきたいなぁ。

今日はここまで!

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

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

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

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

  • ソフトウェアにおける開発とは、ソースコードの差分を生み出すこと
  • 開発は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だけど。

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


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

今日はここまで!