自己検査つきの画面を作るとき、私は「期待値は画面の外で・別の言語で数える」と決めています。同じ日に、その画面が正常なのに赤を 2 回出しました。原因はどちらも被検査物ではなく、期待値を作った自分の癖でした。
この記事は、その 2 件と、直すときに何を根拠にしたか、そして「別言語=独立」ではないという話です。試作のデータはすべて架空です。
この記事の位置づけ(2026-09-26 追記)
- この記事で新しく示すこと: 期待値を別の言語で作っても、作る人の丸め方と語彙の癖は同じなので「正常なのに赤」が 2 回出たこと=別言語は独立の十分条件ではない。
- 当てはまる範囲: 当方の試作(データは架空)で、同じ日の 2 件です。
- 言葉: 本文の「器」=点検や処理のための自作スクリプト、「毒」=チェックが効くか確かめるためにわざと壊した入力のことです。
先に結論
- 赤の原因は 2 回とも被検査物ではなかった——①Python の
round(62.5)は 62(偶数丸め)、JavaScript のMath.round(62.5)は 63。丸めの仕様を書いておらず、両側が別の解釈をしていた ②削除語の一覧に、整えた文にも残る語(「その」「ですね」)を入れていた。 - 差が出たら、それは修正指示ではなく調査開始の信号。独立オラクルは実装のバグだけでなく、「仕様を書かずに双方が別解釈していた」ことまで赤にする。
- 直すときの根拠は画面ではなく、画面の外に置いた仕様・定義。「画面が 63 だから 63」にしたら自己比較になる。
- 独立オラクルの本体は「別言語」ではなく、期待値を被検査物とは別の根拠・別のロジックから導くこと。別言語は実装の相関を減らす一段にすぎない。
- 出す前に見るのは 3 点——正常で緑・毒で赤・毒が被検査物に届いている(自己検査の表示を除いた DOM に差分がある)。
試作の作り
ブラウザで開くだけで動く HTML 1 本。データ(架空)と期待値は Python 側で持ち、期待値はデータから別に数えて JSON に凍結。画面の JavaScript は描画した表(DOM)から数え直し、凍結値と突き合わせて右上に「8 件すべて通過」か「壊れています」を出します。URL の末尾に #selftest-poison を付けると描画前に 1 行を落とす(毒)。共通の器がそれを機械で確かめます——正常で緑/毒で赤/毒あり・毒なしで自己検査の表示を除いた DOM が違うか。
1 件目: 62 と 63——どちらも間違っていなかった
4 択の練習と記録の見本。架空の児童 B は 8 問中 5 問正解=62.5%。
- Python 側の期待値:
round(100 * 5 / 8)→ 62(偶数丸め) - 画面側:
Math.round(62.5)→ 63
器の出力は「毒なしで赤になっている(正常時に緑が出ない): 1 件が通らない」。
ここで「Python が間違い、JS が正しい」とはまだ決められません。この時点で「正答率をどう丸めるか」という仕様を書いていなかったからです。両方とも自分の言語の既定どおりに動いていて、食い違ったのは仕様が未定義で、両側が別の解釈をしていたことでした。独立オラクルが露出させたのは実装のバグではなく、この未定義です。
直した手順: 「正答率は一般的な四捨五入(0.5 を上へ)で表示する」と仕様を決めて文字にし、Python 側を int(x + 0.5) に揃えた(今回の 0〜100% の非負の値に限った処理。負の値や汎用の丸めには使わない)。画面側の検査文は触っていません。仕様を先に 1 行書いておけば、この差は「仕様違反」として最初から見えたはずです。
2 件目: 「その」と「ですね」
会話の文字起こしを整える見本。生の起こしからフィラー(えー・あの・まあ…)を除いた整文を並べ、除いた数を数えます。削除語の一覧に「その」「ですね」を入れていました。
整えた文には「その集まり」「三年目くらいですね」が残ります。検査「整文側にフィラーが残っていない」が落ちた。
直した根拠: 削除対象の定義は「意味を持たない間投詞・言い淀み」。指示語の「その」と語尾の「ですね」は意味を持つ語で、定義に照らして対象外。一覧から 2 語を外し、期待値(除いた数)は 48→39 に変わりました。画面側は触っていません。
「画面に合わせる」と「根拠に照らす」は別物
どちらの修理も、結果として期待値が画面と同じ値になりました。だから危ない。画面と違った→期待値を画面に合わせた→緑なら、それは自己比較で、検査は何も証明しません。
許されるのは、画面の外に置いた根拠(仕様・定義・別データ)に照らして、期待値の作り方の誤りを直すことだけ。今回は「四捨五入で表示する」という仕様と、「意味を持たない語」という定義がその根拠です。根拠は先に 1 行書いておく。
独立性には段階がある
| 段階 | 期待値の作り方 | 何を捕まえるか | 費用 |
|---|---|---|---|
| 弱 | 同じコードから期待値を生成 | ほぼ何も(自己比較) | 0 |
| ↓ | 別関数で再計算 | 実装の打ち間違い | 小 |
| ↓ | 別プロセス・別実装(別言語を含む)で再計算 | 実装の癖(丸め・文字列処理・境界) | 中 |
| 強 | 仕様・手ラベル・別データ源など別根拠から凍結 | 仕様の誤解・定義の混入 | 大 |
1 件目は「別言語」の段で実装の癖(丸め)を捕まえ、2 件目は「別根拠(定義)」の段で定義の混入を捕まえました。別言語は途中の一段で、独立性そのものではありません。同じ誤った仕様から Python と JavaScript の両方を作れば、同じ間違いを両方でします。毎回別言語まで用意する必要もなく、必要な独立性だけ買う判断でよい。
正常時に赤が出たときの手順
- 期待値は被検査物と別の根拠・別のロジックで作り、ファイルに凍結する。根拠(仕様・定義)を先に 1 行書く。
- 出す前に 3 点を機械で見る: 正常で緑/毒で赤/毒が被検査物(自己検査の表示を除いた DOM)に届いている。
- 正常で赤が出たら、期待値の作り方(丸め・語彙・境界)を先に疑い、画面の外の根拠に照らして片側だけ直す。画面に合わせない。
- 言語をまたぐ丸めは境界値(x.5)で 1 本テストする。
- 独立性は段階から必要な分だけ選ぶ。
今回の 2 試作は、正常時 8 件の検査と毒 1 種で運用しました。それで足りたのはこの 2 本の話で、一般に足りるとは言いません。
Lab 版(何を疑い、何で決めたかの記録): https://sumitsuke.jp/via/qiita/lab/oracle-independence-caught-my-habits/
AI の利用について
試作の実装と記事の下書きには AI(Claude)を使いました。数字(62/63・48→39・8 件)は器の出力と確かめた記録から写し、判断と公開は自分で行っています。試作のデータは架空で、どの依頼に使ったかは書いていません。
受託でも同じ運用をしています
期待値を被検査物から作らず、出す前に「毒で赤になるか」まで確かめます。相談はテキストだけで完結します。
▶ Sumitsuke / 依頼から納品までの流れ
次に読む: 作った証拠を第三者に出す形——テスト件数を 3 通りに数えたら 453/3,160/4,878
この記事は Zenn にも同じ内容を掲載しています。
