3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

なぜあなたの仕様書は"伝わらない"のか ── 一意性という落とし穴

3
Last updated at Posted at 2026-06-21

なぜあなたの仕様書は"伝わらない"のか ── 一意性という落とし穴

この記事の対象:仕様書・設計書を書く/読む立場にいる、組込・通信系のエンジニア。設計レビューやテスト設計に関わる人。
この記事のゴール:「受信時、一定時間以内にレスポンスする」のような一文が、なぜ仕様定義者・設計(実装)・テストで別物になるのかを具体的に分解し、用語を一意に定義して書き直す観点を持ち帰る。
前提知識:UART などシリアル通信の基本(フレーム、ボーレートの概念)、ソフトウェア設計・テストの一般的な流れ。
この記事で扱わないこと:仕様書全体の構造化、仕様の正規化(重複定義の排除)。本稿は「仕様書・設計書など技術文書の一意性について」を範囲とします


1. はじめに

ある通信仕様書に、こう書かれていました。

受信時、一定時間以内にレスポンスする。

仕様定義のレビューで、この一文に反対した人はいませんでした。みな「わかる」と頷いて、先へ進みました。

それから時間が経ち、結合の段になって、応答が間に合わないケースが、出たり出なかったりし始めます。関係者を集めて確認すると、こうなりました。

仕様を書いた本人は言います。「一定時間以内にレスポンスする、と書いたとおりです。それ以上でも、それ以下でもありません。」

設計・実装を担当した側は言います。「受信してから応答を出し始めるまで、ちゃんと時間内に収めています。」

テストを担当した側は言います。「いえ、波形で測ると時間内に収まっていません。仕様違反です。」

三人とも、嘘はついていません。それぞれが、あの一文を素直に読み、自分の読みに忠実に仕事をしています。そして三人の言い分は、あの一文と、どれも矛盾しません。

では、何が問題だったのでしょうか。

これが、開発の現場で恐れられる「伝言ゲーム」です。同じ一文が、人を経るたびに別の意味になり、最後に突き合わせると食い違う。そして、この食い違いこそが、不具合の温床になります。

この記事は、たった一文のタイミング要求が、なぜ仕様定義者・設計(実装)・テストで別物になるのかを、一緒に解きほぐしていく読み物です。おそらくあなたも、これとそっくりな一文を、書いたか、レビューで通したことがあるはずです。

2. 「受信時、一定時間以内にレスポンスする」を分解する

まず、あの一文を品詞で見ます。名詞は「受信時」と「一定時間」。動詞は「レスポンスする」。一見、なにも難しくありません。しかしこの三つは、どれも複数の意味に割れます。割れ方を見る前に、今回の題材をはっきりさせておきます。

題材は、機器内の CPU 間(ユニット間)通信です。物理層は UART のようなバイト列で、その上に独自の通信フレームを載せます。流れはこうです。

  • 受信側はバイトを 1 つずつ受け取る。
  • 先頭バイトに、そのフレームの長さ(DLC:データ長)が数ビットで入っている。受信側はその数だけバイトを受け続け、数がそろったらフレーム完成とみなす。
  • 完成したフレームの CRC(チェックサム)を検証する。
  • 検証に合格したら、フレームから信号(コマンド)を取り出し、制御へ反映する。

フレームの終わりを「一定時間バイトが来なかったら完了」とするタイムアウト方式は採りません。バスの時間効率が悪くなるからです。先頭の DLC で長さを宣言し、その数で完了を判定する。インターフレームのバイト間隔が規定値を超えたら、それは正常な完了ではなく、タイムアウトエラー(エラーフレーム受信)として扱う。これが筋のいい設計です。

この通信を、三つの層で見ます。

  • L1 物理層(バイト/ビット):スタートビット、1 バイト受信、STOP ビット、パリティ、フレーミングエラー、オーバーラン。
  • L2 データリンク層(フレーム):DLC ぶんのバイトを連結してフレーム完成、CRC 検証。
  • L3 アプリ層(信号/制御):検証済みフレームから信号を抽出し、制御へ反映。

この層を物差しに、三つの語を見ていきます。

2.1 「受信時」── どの層の話か

「受信時」が指しうる瞬間は、層ごとに違います。

  • L1:1 バイトを受信した瞬間(あるいはスタートビットを検出した瞬間)
  • L2:DLC ぶんのバイトがそろってフレームが完成した瞬間/CRC 検証に合格した瞬間
  • L3:信号として認識し、制御へ反映した瞬間

「受信時」とだけ書けば、読み手はこのどれを指すかを、自分で選ぶことになります。L1 で読む人と L3 で読む人では、起点が数十ミリ秒ずれます。

2.2 「一定時間」── どこからどこまでか

「一定時間」には、数値(何 ms 以内か)の問題と、区間(どこからどこまでか)の問題があります。割れるのは後者です。

起点の候補:受信開始(要求の最初のバイト)/受信完了(フレーム完成)。
終点の候補:送信開始(応答の最初のバイト)/送信完了(応答の最後のバイト)。

起点 2 通り × 終点 2 通りで、最低 4 通りの区間があります。数値が同じ「100ms 以内」でも、どの区間を指すかが決まっていなければ、要求は確定していません。

2.3 「レスポンス」── 何をもって応答したか

「レスポンスする」も、完了の定義が割れます。応答フレームの先頭バイトを送り始めた時点/全バイトを送り終えた時点/さらに踏み込めば相手が受信し終えた時点。フレーム単位で見るか、バイト単位で見るかでも変わります。

2.4 そして「誰の時計で測るのか」

最後に、見落とされやすい軸です。この通信には相手(要求側の CPU)がいて、相手も自分のタイムアウトを持っています。相手は「自分が要求を送り終えてから、応答を受け終わるまで」で測るかもしれません。すると、こちら(応答側)が「受信完了から送信開始まで」を守っても、応答の送信時間ぶんは相手のタイムアウトに含まれます。応答側のローカルな 100ms と、要求側が許す 100ms は、同じ 100ms ではありません。

タイミング要求は、二つの観測点と「誰の視点か」を決めて、はじめて意味が一つに定まります。

3. 同じ回路、同じ実装、それでも判定が割れる

抽象論だと飲み込みにくいので、具体的な数値で見ます。以下はあくまで例示で、特定の実機の実測値ではありません[注1]。自分の条件に置き換えて読んでください。

条件(例):

  • 物理層:UART、9600 bps、8N1(1 バイト = 10 ビット → 約 1.04ms / バイト)
  • 要求フレーム 20 バイト → 約 20.8ms
  • 応答フレーム 20 バイト → 約 20.8ms
  • 応答側の処理時間(CRC 検証〜信号化〜応答生成)40ms
  • 伝搬遅延は短距離バス前提で無視できる程度

要求の先頭バイトがバスに出た瞬間を t = 0 とします。先頭バイトで DLC が分かるので、受信側は 20 バイト目で「フレーム完成」と判定できます。時間軸はこうなります。

時刻 出来事
0 ms 要求の先頭バイト(受信開始 / DLC 判明)
20.8 ms 要求の最終バイト = DLC ぶん受信完了(フレーム完成)
60.8 ms 応答の先頭バイト送信開始(送信開始)
81.6 ms 応答の最終バイト送信完了(送信完了)

この一回のやり取りを、三者がそれぞれの読みで測ると、こうなります。

  • 仕様定義者の意図(受信完了 → 送信完了):本当に欲しかったのは「要求を受けてから、応答を最後まで返し終えるまで」。81.6 − 20.8 = 60.8ms
  • 設計(実装)の読み(受信完了 → 送信開始):「受けてから、応答を出し始めるまで」と読んだ。60.8 − 20.8 = 40.0ms
  • テストの読み(受信開始 → 送信完了):バス上で観測できる、端から端まで。81.6 − 0 = 81.6ms

要求値が「100ms 以内」なら、三者とも合格です。問題は表面化しません。

では、要求値が「50ms 以内」だったら、どうなるか。

  • 設計(実装)の読みでは 40ms → 合格
  • 仕様定義者が本当に欲しかった 60.8ms → 不合格
  • テストの読みでは 81.6ms → 不合格

回路は同じ、実装も同じ、波形も同じです。それでも、読みが割れているだけで、判定が割れます。しかも厄介なのは、設計(実装)は「仕様書どおり」に作って、自分の読みでは合格していることです。仕様定義者が本当に実現したかった時間は満たせていないのに、書かれた文には違反していない。テストは観測点で測って不合格を出す。三者の判定が割れ、そして誰も嘘をついていません。

さらに §2.4 の「誰の時計か」を重ねると、もう一段崩れます。要求側のタイムアウトが「要求を送り終えてから応答を受け終わるまで」で、その値が 70ms だったとします。この例では 81.6 − 20.8 = 60.8ms なので間に合います。しかし要求側が「要求を送り始めてから応答を受け終わるまで」で測り、70ms で切るなら、81.6ms > 70ms で、応答側が "100ms 以内" を守っていても、要求側のほうが先に諦めます。しかも応答側のログには異常が残りません。これが、視点を決めずに書いたときの、最もたちの悪い不具合です。

4. 誰も悪くない、そして「工学的な想像力」

ここで、はっきりさせておきたいことがあります。三者の誰も、悪いことはしていません。

仕様定義者は、自分の頭の中では一意なつもりで書きました。「受けてから返すまで」を時間内に、という当たり前のことを書いたつもりだった。設計(実装)は、その文を素直に読み、自分が制御する区間をきっちり守った。テストは、誰の目にも見える観測点で測った。三者とも、自分の責務に忠実です。手を抜いた人はいません。

では、どこで止めるべきだったか。本来は、仕様定義のレビューです。そしてそのレビューには、仕様定義者自身を出席させることが要ります。文を書いた人がその場にいて、「受信時とはどの瞬間か」「一定時間はどこからどこまでか」を問われて答える。それだけで、幅の多くは潰せます。

ただ、問う側にも力が要ります。私はそれを 「工学的な想像力」 と呼んでいます。「受信後」という抽象を、具体へ演繹的に展開できる力のことです。

ミクロには、こう展開できます。STOP ビットを検出した瞬間か。パリティエラーが出たときはどう扱うのか。STOP ビットの時刻に回線がまだアクティブ(スペース)のままだったら、それはフレーミングエラーなのか、ノイズと見なすのか。オーバーランは。

マクロには、こう展開できます。プロトコル上、相手と疎通できたとはどの状態か。フレームの整合が取れて、信号として認識できたのはどこか。

そして、ここが習熟したエンジニアの差が出るところですが、信号レベルの一つの事象が、そのユニットの挙動に、さらにそのユニットが属するシステムの整合に、どう波及するかを、再現可能な形で頭の中に立ち上げられる。この力があれば、レビューで「その『受信時』は、L1 ですか、L2 ですか、L3 ですか」を、不具合が出る前に問えます。

📝【執筆メモ・公開前に処理】〔要・実体験〕
ここに、自分が現場で見てきた具体例を入れる(顧客名・案件名・量産車種名は出さない。一般化する)。
候補:

  • 一生懸命やっているのに噛み合わない場面を、どの立場で、どう観測し、どう切り分けたか。
  • 「実装が仕様の意図を汲んでいない」のではなく、「文章に意図を一意に書けていなかった」と気づいた瞬間。
  • レビューに仕様定義者を入れて、定義をそろえる前と後で何が変わったか。
    ※ ここは一般論で埋めない。一次経験が差別化の核。

私自身、こうした場面を数多く見てきました。指摘し、定義をそろえ、ソフトウェア、ひいてはシステム全体を成立させるところまで持っていく。その繰り返しでした。そして毎回、根は同じでした。人の能力や熱意ではなく、意思を共有するための文章に、最低限の一意性がなかったことです。

5. 読み手の認識に、書き手が責任を持つ

ここまでをひと言でまとめます。

一意性のない文章は、意味に幅を持たせ、その幅の中で、読み手に誤読の余地を与えます。

しかも、その誤読はたいてい善意です。読み手は手を抜いて誤読するのではなく、一生懸命に読んだうえで、自分の責務にとって自然な一点を、幅の中から選びます。

やっかいなのは、コンテキストを共有している間は、この幅が表に出ないことです。同じチーム、同じ時期、口頭の補足がある間は、全員が暗黙のうちに同じ一点を見ています。だから仕様定義のレビューでも問題になりません。通ってしまう。

幅が露出するのは、時間が経ったとき、人が変わったとき、別のエンジニアがその文章だけを頼りに作業したときです。口頭の補足は残りません。残るのは文章だけで、その文章には幅があります。

私は、こう考えています。わかりにくさや誤読の余地は、読み手の落ち度ではなく、書き手の落ち度です。 読み手の認識に、書き手が責任を持つ。そして、技術を実装する力と、それを相手に一意に伝える力は、別物です。後者を欠いたまま前者だけを磨いても、一人前のエンジニアとは言いにくい。一意に書くことは、文才ではなく、エンジニアリングのスキルです。

システムが複雑になるほど、文章への要求は厳しくなります。品質を管理する責務を持つ者ほど、この一意性が保たれている状態を、維持し続ける努力が要ります。

6. どう書けば伝わるか ── 用語を定義し、一貫させる

では、どう書けば一意になるのか。文才の話ではないので、手順にできます。考え方は、文を 形態素解析 するのに似ています。

  1. 要求文を品詞で分解する。 少なくとも、名詞(S)と動詞(V)を洗い出す。「受信時」「一定時間」「レスポンス」を、まず取り出す。
  2. 相手と共有する用語・動作を、事前に定義する。 「受信」「レスポンス」が、このシステムで何を指すのかを決める。名詞が汎用的すぎるときは、形容詞を付けるか、複合名詞を新しく作って限定する(例:「受信」→「フレーム受信完了」)。
  3. 定義した語は、文書全体で、その意味以外に使わない。 そして、本当に守られているかを、文書を検査して確かめる。同じ「受信」が、ある章では L1、別の章では L3、になっていないか。
  4. 一つひとつの語に、システム上の意味を与える。 例えば「受信時」なら、こう詰めます。フレーム受信のことか。スタートビット検出を含むのか。エラー検出時はどう扱うのか。曖昧なまま地の文に溶かさず、定義として一意に決める。

タイミングを表す語については、定義のときに最低限これだけは固定します。

  1. トリガ事象(どの瞬間か。観測できる点で)
  2. 計測の起点
  3. 計測の終点
  4. 上限値(数値と単位)
  5. 視点(応答側ローカルか、要求側ラウンドトリップか)
  6. 観測点(三者が同じ点を測れるよう、物理的にどこを見るか)

これを使って、最初の一文を書き直します。まず用語を定義します。

システム上の意味
受信完了 要求フレームを DLC ぶん受信し、CRC 検証に合格した時点
応答開始 応答フレームの先頭バイトの送信を開始する時点
応答時間 受信完了から応答開始までの区間。バス上の信号で計測する

書き直し前:

受信時、一定時間以内にレスポンスする。

書き直し後(例):

応答時間(受信完了から応答開始まで、バス上の信号で計測)を 100ms 以内とする。なお、要求側のタイムアウト(要求送信完了から応答受信完了まで 200ms)は別に管理する。

長くなったように見えます。しかし増えたのは文字数であって、解釈の幅は消えています。「一意」は「冗長」とは違います。目的は文を長くすることではなく、解釈を一つに定めることです。文章で書ききるのが重いなら、観測点を 1 枚のタイミングチャートで示すのが、たいてい最速です。バス上のエッジに矢印を引いてしまえば、仕様定義者も、設計(実装)も、テストも、同じ点を測れます。ソフトウェアの仕様を、物理的に観測できる点へ接地させる、ということです。

7. この記事で扱わなかったこと

本稿は「一文の意味の幅」だけに絞りました。次の二つは、関連はしますが、別の問題なので扱っていません。

  • 構造化:仕様書全体をどう構成し、章立て・粒度・参照関係をどう設計するか。
  • 正規化:必須の仕様が複数箇所に重複定義されることを許さず、一箇所に「正」を置くか。

一意性は「一つの記述の意味が一つに定まるか」、構造化と正規化は「記述の集合をどう配置するか」の話です。レイヤが違います。それぞれ単独で扱う価値があるので、別の機会に分けて書きます。

8. まとめ

  • 一文の中の名詞・動詞は、しばしば隠れた分岐点です。「受信時」「一定時間」「レスポンス」は、L1 / L2 / L3 のどこを指すか、どこからどこまでか、何をもって完了か、で割れます。
  • 同じ仕様書から、仕様定義者・設計(実装)・テストが、それぞれ違う一点を読みます。突き合わせると食い違う。これが伝言ゲームであり、不具合の温床です。
  • 読みが割れていれば、同じ回路・同じ実装でも、判定が割れます。設計(実装)が「仕様書どおり」に作ってなお、仕様定義者の意図を外すことすらあります。
  • 誤読の余地は、読み手ではなく書き手の責任です。一意に書くことは、エンジニアリングのスキルです。
  • 書き方は手順にできます。用語を品詞で分解し、事前に定義し、文書全体で一貫させ、検査する。
  • 明日からできること:自分が書いた、あるいはレビューで通したタイミング要求を一つ取り出し、本稿の六項目で点検してみてください。一つでも欠けていたら、それは読み手が誤読できる余地です。

注記

[注1](3章「同じ回路、同じ実装、それでも判定が割れる」)本文のバイト時間は、UART 8N1 で 1 バイト = 10 ビット(スタート 1 + データ 8 + ストップ 1)として、ボーレートで割った算術上の例示です。9600 bps なら 10 / 9600 ≈ 1.04ms / バイト。記載の数値は特定の実機の実測値ではありません。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?