※小説ではない※専門書 要約資料集 為替(換算)3.9万円でもらう 紐解集生成 専門 初入門 資料 作:{作者名}
> 「学問の宇宙」PC創造大全・分冊0358n(関数ノウハウ500・完結巻)
> 分類記号: KNOW-F-0376〜KNOW-F-0500(合成と堅牢化の型・125項目・関数ノウハウ500完結)
> **PC創造大全 目録シリーズ索引(関数ノウハウ・分冊)**: 目録I BOOK-0358k(関数設計の基本形・KNOW-F-0001〜0125) → 目録II BOOK-0358l(制御と分岐の型・KNOW-F-0126〜0250) → 目録III BOOK-0358m(データ変換の型・KNOW-F-0251〜0375) → **目録IV BOOK-0358n(本冊・合成と堅牢化の型・KNOW-F-0376〜0500)**。
> 接続: →脊椎BOOK-0358f(関数を正式に組む・「段落を崩しても通る」の解釈表の出典)/型見本=BOOK-0184(五点セット形式)/前巻BOOK-0358m(データ変換の型)。
> 設計書: CATALOG_PC創造大全.md §1(ID帯)・§2(部構成表)・§5(執筆規格)に準拠。
**§16.22完全性優先・目安80,000〜120,000字(空白除)の範囲内・実測: 空白除92,739字**。KNOW-F-0376〜0500の全125件に五点セット(①名称 ②構造の型〈骨子・図または式〉 ③使いどころ ④組合せ例 ⑤確認事項〈罠と検算・安全注意〉)を完備する(白カードなし・自己機械検査は巻末「参考: 本冊の構成情報」に記載)。
---
# BOOK-0358n PC創造大全 目録IV 合成と堅牢化の型
> 「学問の宇宙」PC創造大全・分冊0358n(関数ノウハウ500・完結巻)
> 分類記号: KNOW-F-0376〜KNOW-F-0500(合成と堅牢化の型・125項目・関数ノウハウ500完結)
> **PC創造大全 目録シリーズ索引(関数ノウハウ・分冊)**: 目録I BOOK-0358k(関数設計の基本形・KNOW-F-0001〜0125) → 目録II BOOK-0358l(制御と分岐の型・KNOW-F-0126〜0250) → 目録III BOOK-0358m(データ変換の型・KNOW-F-0251〜0375) → **目録IV BOOK-0358n(本冊・合成と堅牢化の型・KNOW-F-0376〜0500)**。
> 接続: →脊椎BOOK-0358f(関数を正式に組む・「段落を崩しても通る」の解釈表の出典)/型見本=BOOK-0184(五点セット形式)/前巻BOOK-0358m(データ変換の型)。
> 設計書: CATALOG_PC創造大全.md §1(ID帯)・§2(部構成表)・§5(執筆規格)に準拠。
**§16.22完全性優先・目安80,000〜120,000字(空白除)の範囲内・実測: 空白除92,739字**。KNOW-F-0376〜0500の全125件に五点セット(①名称 ②構造の型〈骨子・図または式〉 ③使いどころ ④組合せ例 ⑤確認事項〈罠と検算・安全注意〉)を完備する(白カードなし・自己機械検査は巻末「参考: 本冊の構成情報」に記載)。
---
## 序章 「合成」と「堅牢化」を分けて考える
目録I(BOOK-0358k)は一つの関数の内側(命名・引数・戻り値・エラー処理)を、目録II(BOOK-0358l)は一つの関数の中の分岐と反復を、目録III(BOOK-0358m)は一つの関数が扱うデータの変換を扱ってきた。本冊(目録IV)が扱うのは、その「一つの関数」を何十・何百と束ねて一つの動くシステムに組み上げたとき、なぜ崩れるのか、どうすれば崩れないのかという一段上の視点である。
本冊は五章に分かれる。第一章「モジュール分割の型」は、関数と関数の集まり(モジュール)をどう切り分け、どちらからどちらへ依存させるかという設計の型を扱う。第二章「純化の型」は、副作用(ファイルへの書込・画面表示・通信など、戻り値以外に世界へ影響を与える処理)を関数の端に押し出し、計算の芯を「同じ入力なら常に同じ出力」に保つ技を扱う。第三章「テストの型」は、書いた関数が本当に正しく動くことを機械的に確認し続ける仕組みを扱う。第四章「デバッグの型」は、動かなくなったときに原因へ最短距離でたどり着く探索の型を扱う。第五章「計測の型」は、速さや資源消費を勘でなく数値で語るための計測の作法を扱う。
**用語の初出定義**: 本冊で「モジュール」とは、関連する関数・データをひとまとめにし、外部に対して限られた窓口(公開インターフェース)だけを見せる単位を指す。「純粋(pure)」とは、同じ入力に対して常に同じ出力を返し、かつ外部の状態を変更しない性質を指す。「冪等(idempotent)」とは、同じ操作を1回行っても複数回行っても結果が変わらない性質を指す。「回帰(regression)」とは、過去に直した不具合や過去に正しく動いていた挙動が、後の変更で再び壊れることを指す。
**「段落を崩しても通る」の解釈(CATALOG §0.5準拠の要約)**: 詳しい解釈表は脊椎BOOK-0358fに置くが、本冊での実務上の意味は次の三点に集約される。(a)順序独立性 — どの順で読んでも/呼んでも成立する構造にする、(b)参照透過性 — 同じ入力からは同じ結果しか出ない構造にする、(c)冪等性 — 繰り返しても壊れない構造にする。本冊第二章はこの三点をコードの型として具体化する章であり、第一章(モジュール分割)・第三章(テスト)・第四章(デバッグ)・第五章(計測)はいずれもこの三点が成立していることを前提に組み立てられる、あるいはこの三点を検証するための道具になっている。
**コード表記について**: 本冊のコード例はJavaScript風の疑似コードで統一する(特定の言語機能に依存しない骨子として読むこと)。`//`から行末までは注釈である。
**安全に関する注意**: 本冊はテスト・デバッグ・計測の実務を扱うため、実在のシステムに対して行う際の注意を各所の確認事項に記す。特に、本番環境(実際に利用者が使っている環境)のデータを直接書き換えるテストや、本番ログに個人情報を平文で残すログ設計は、いずれも本冊が推奨する型ではない。安全のための一般原則として「破壊的な操作の練習は複製環境で行う」ことを本冊全体の前提とする(CATALOG §3の安全枠と同じ姿勢を関数実務に適用したもの)。
---
## 第一章 モジュール分割の型(KNOW-F-0376〜0400)
一つ一つは正しく書けた関数も、束ね方を誤ると全体としては壊れやすいシステムになる。第一章は「どこで切るか」「どちらを向かせるか」「輪にしないか」という、関数の集合を扱うための25の型を扱う。
---
### KNOW-F-0376 単一責務の型(Single Responsibility)
**①名称**: 単一責務の型(Single Responsibility Principle, SRP)
**②構造の型(骨子)**:
```
// 悪い例: 一つの関数が計算・整形・保存を全部背負う
function processOrder(order) {
const total = order.items.reduce((s,i)=>s+i.price*i.qty, 0) // 計算
const text = `合計: ${total}円` // 整形
saveToFile(text) // 保存(副作用)
return total
}
// 良い例: 変更理由ごとに関数を分ける
function calcTotal(order) { return order.items.reduce((s,i)=>s+i.price*i.qty, 0) }
function formatTotal(total){ return `合計: ${total}円` }
function saveReceipt(text) { saveToFile(text) } // 副作用はここだけに閉じ込める
```
**③使いどころ**: 「この関数を直す理由は何か」を2つ以上言えてしまうとき。計算式の変更と保存先の変更が別々の事情で起きるなら、その時点で責務が2つ以上混ざっている合図である。
**④組合せ例**: KNOW-F-0402(副作用分離の型)と組み合わせると、calcTotal/formatTotalは純関数、saveReceiptだけが副作用を持つ関数として境界がさらに明確になる。KNOW-F-0389(公開境界の型)と組み合わせ、calcTotalだけを外部公開しformatTotal/saveReceiptを内部実装に留める設計もできる。
**⑤確認事項(罠と検算・安全注意)**: 責務を細かく分けすぎると呼び出し側で3関数を毎回手で繋ぐ手間が増える(KNOW-F-0400の粒度調整で判断する)。検算: calcTotal(order)を単体で呼び、reduceの初期値0と各itemのprice×qtyの合計が手計算と一致するかを確かめる。
---
### KNOW-F-0377 高凝集の型(Cohesion)
**①名称**: 高凝集の型(関連するものを近くに置く)
**②構造の型(骨子)**:
```
// 低凝集: 注文にも在庫にも関わる処理が1ファイルに雑居
// order_and_stock.js: processOrder(), checkStock(), sendMail() ...
// 高凝集: 「注文」に関する処理だけをまとめる
// order.js
export function calcTotal(order) { /* ... */ }
export function validateOrder(order) { /* ... */ }
export function applyDiscount(order, rate) { /* ... */ }
```
**③使いどころ**: 一つのファイル/モジュールを開いたとき、上から下まで「同じ名詞(この例では注文)」について読んでいる状態を目指すとき。読んでいる途中で話題が飛ぶファイルは凝集度が低い。
**④組合せ例**: KNOW-F-0378(疎結合の型)と対で使う。高凝集(内側をまとめる)と疎結合(外側との関係を薄くする)は同時に達成できる別軸の目標であり、片方だけを追うと歪む。KNOW-F-0393(垂直分割の型)は高凝集を機能単位で実現する具体手段になる。
**⑤確認事項(罠と検算・安全注意)**: 「名前が似ている」だけでまとめると偽の凝集になる(例: calcTotalとformatDateは名前の並びは近くても関連しない)。検算: モジュール内の各関数が同じデータ構造(この例ではorder)を引数に取っているかを表にして確認し、無関係な引数の関数が紛れていないかを見る。
---
### KNOW-F-0378 疎結合の型(Loose Coupling)
**①名称**: 疎結合の型(モジュール間の知りすぎを減らす)
**②構造の型(骨子)**:
```
// 密結合: 呼び出し側がPaymentモジュールの内部構造まで知っている
function checkout(cart) {
const p = new Payment()
p.gateway.connection.retryCount = 3 // 内部の内部まで触っている
p.gateway.charge(cart.total)
}
// 疎結合: 公開された1つの窓口だけを知っていればよい
function checkout(cart, paymentService) {
paymentService.charge(cart.total) // 内部実装は知らない
}
```
**③使いどころ**: あるモジュールを直したら無関係に見える別のモジュールが壊れた、という経験をしたとき。呼び出し側が相手の内部構造(プロパティの階層など)まで知っていないかを疑う。
**④組合せ例**: KNOW-F-0383(インターフェース分離の型)・KNOW-F-0386(ファサードの型)は疎結合を実現する具体的な部品になる。KNOW-F-0381(コンストラクタ注入の型)と組み合わせると、paymentServiceを外から渡す形が自然にでき、テスト時にKNOW-F-0431(スタブの型)へ差し替えやすくなる。
**⑤確認事項(罠と検算・安全注意)**: 疎結合を求めすぎて抽象化の層(インターフェース)を増やしすぎると、逆にコードを追うのが難しくなる罠がある(KNOW-F-0400参照)。検算: checkout関数の引数と戻り値だけを見て、Paymentの内部実装を知らなくても動作が理解できるかを確認する。
---
### KNOW-F-0379 依存の向きを揃える型(Dependency Direction)
**①名称**: 依存の向きを揃える型
**②構造の型(骨子)**:
```
方向が揃った依存(一方向):
UI層 → アプリケーション層 → ドメイン層
(UIはアプリを知ってよいが、ドメインはUIを知らない)
方向が乱れた依存(危険信号):
UI層 → ドメイン層 → UI層の関数を呼び返す (逆流)
```
**③使いどころ**: 設計図(依存グラフ)を描いたときに矢印がすべて同じ方向を向いているかを確認する場面。矢印が逆流している箇所は変更の影響が両方向に伝播する危険地帯である。
**④組合せ例**: KNOW-F-0385(レイヤー化の型)は方向を「層」という単位で強制する手段。KNOW-F-0380(依存性逆転の型)は、どうしても方向を揃えられない場合に「向きだけ」を反転させる高度な技として接続する。
**⑤確認事項(罠と検算・安全注意)**: 「依存の向き」は import/require の向きと必ずしも一致しない場合がある(インターフェースを介した逆転など)ため、KNOW-F-0398(依存グラフ可視化の型)で実際の呼び出し方向を確認するのが確実。検算: モジュール一覧を紙に書き出し、矢印を引いて輪(サイクル)ができていないかを目視で辿る。
---
### KNOW-F-0380 依存性逆転の型(Dependency Inversion, DIP)
**①名称**: 依存性逆転の型(DIP)
**②構造の型(骨子)**:
```
// 逆転前: 上位モジュール(注文処理)が下位モジュール(具体的なDB)に直接依存
function saveOrder(order) { mysqlSave(order) }
// 逆転後: 上位も下位も「インターフェース(契約)」に依存する
function saveOrder(order, repository) { repository.save(order) } // repositoryは契約
const mysqlRepository = { save: (o) => mysqlSave(o) } // 契約を満たす実装の一つ
const memoryRepository = { save: (o) => memoryStore.push(o) } // 差し替え可能な別実装
```
**③使いどころ**: 上位の(方針を決める)モジュールが、下位の(具体的な手段を実装する)モジュールの都合に振り回されているとき。DBの種類を変えたら注文処理まで書き換わる、という状況がDIPの適用対象である。
**④組合せ例**: KNOW-F-0381(コンストラクタ注入の型)はDIPを実現する具体的な配線手段。KNOW-F-0396(モジュール契約の型)はrepositoryが満たすべき契約(saveを持つこと)を明文化する型として対になる。
**⑤確認事項(罠と検算・安全注意)**: DIPは「抽象に依存させる」ことが目的であり、単にファイルを分けただけでは達成できない罠がある。検算: saveOrder関数を、mysqlRepositoryとmemoryRepositoryのどちらを渡してもsaveOrder自身のコードを1行も変えずに動くかを実際に両方渡して確認する。
---
### KNOW-F-0381 コンストラクタ注入の型(Constructor Injection)
**①名称**: コンストラクタ注入の型(依存性注入の基本形)
**②構造の型(骨子)**:
```
function createOrderService(repository, mailer) { // 依存を生成時に渡す(注入)
return {
place(order) {
repository.save(order)
mailer.send(order.customerEmail, "注文を受け付けました")
}
}
}
const service = createOrderService(mysqlRepository, smtpMailer) // 実運用
const testService = createOrderService(memoryRepository, fakeMailer) // テスト用
```
**③使いどころ**: 関数やオブジェクトが動くために必要な外部の道具(DB接続・メール送信など)を、関数の内部でnewせず、生成のタイミングで外から渡したいとき。CATALOG §0.1「循環回避=依存性注入」の実装形そのものである。
**④組合せ例**: KNOW-F-0384(循環依存検出の型)で見つかった循環は、多くの場合このコンストラクタ注入に置き換えることで解消できる(直接importする代わりに引数として受け取る)。KNOW-F-0431〜0434(テストダブル群)への差し替え口として機能する。
**⑤確認事項(罠と検算・安全注意)**: 依存を注入しすぎると引数(コンストラクタの受け取り数)が肥大化する罠がある。5個を超えたら関連する依存をひとまとめの契約オブジェクトにできないか検討する。検算: createOrderServiceにfakeMailerを渡し、実際にsmtpへ通信せずにplace()が完走することを確認する(実測: 外部通信0件)。
---
### KNOW-F-0382 セッター/プロパティ注入の型(Setter Injection)
**①名称**: セッター/プロパティ注入の型
**②構造の型(骨子)**:
```
function OrderService() { this.repository = null } // 生成時は空
OrderService.prototype.setRepository = function(r) { this.repository = r } // 後から注入
OrderService.prototype.place = function(order) {
if (!this.repository) throw new Error("repository未設定") // 必須確認
this.repository.save(order)
}
```
**③使いどころ**: 依存が生成時には確定しない(設定画面や後段の初期化フェーズで決まる)場合や、任意(なくても動く)の依存を後から差し込みたい場合。
**④組合せ例**: KNOW-F-0381(コンストラクタ注入の型)と対比される型で、「必須の依存はコンストラクタ注入、任意の依存はセッター注入」と使い分けるのが定石。KNOW-F-0422(事前条件の型)のnullチェックと組み合わせて未設定時の早期検知を行う。
**⑤確認事項(罠と検算・安全注意)**: セッター注入は「注入し忘れたまま使ってしまう」罠が起きやすい(コンストラクタ注入より安全性が下がる)。検算: repositoryを設定せずにplace()を呼び、意図通り例外(または明示的なエラー)が発生することを確認する(実測: 未設定時は必ず停止する)。
---
### KNOW-F-0383 インターフェース分離の型(Interface Segregation, ISP)
**①名称**: インターフェース分離の型(ISP)
**②構造の型(骨子)**:
```
// 分離前: 1つの巨大な契約(使わないメソッドまで実装を強制される)
// Repository = { save, delete, archive, exportCsv, migrate, ... }
// 分離後: 用途ごとに小さな契約へ分ける
const Readable = { findById(id) {} }
const Writable = { save(item) {} }
// 読み取りだけでよい関数はReadableだけを要求する
function showOrder(id, reader) { return reader.findById(id) }
```
**③使いどころ**: あるモジュールを使うために、実際には使わない大量のメソッドまで実装・モック化しなければならないとき。巨大な契約は変更の影響範囲を不必要に広げる。
**④組合せ例**: KNOW-F-0396(モジュール契約の型)で契約を明文化したあと、本型で契約を用途別に分割する、という順序で使うと効果的。KNOW-F-0433(フェイクの型)を作る際、分離された小さな契約の方が偽実装を書く手間が小さくて済む。
**⑤確認事項(罠と検算・安全注意)**: 分離しすぎると契約の数が爆発し、全体像が逆に追いにくくなる罠がある。検算: showOrder関数が要求する契約(Readable)のメソッド数を数え、実際に使っているメソッド数と一致するか(過不足がないか)を照合する。
---
### KNOW-F-0384 循環依存検出の型(Circular Dependency Detection)
**①名称**: 循環依存検出の型
**②構造の型(骨子)**:
```
// A.js が B.js を import し、B.js が A.js を import している状態(循環)
// A.js: import { b } from "./B.js"
// B.js: import { a } from "./A.js"
//
// 検出の骨子(深さ優先探索):
function hasCycle(graph, start, visiting = new Set(), visited = new Set()) {
if (visiting.has(start)) return true // 探索中の頂点に戻ってきた=循環
if (visited.has(start)) return false
visiting.add(start)
for (const next of graph[start] || []) {
if (hasCycle(graph, next, visiting, visited)) return true
}
visiting.delete(start); visited.add(start)
return false
}
```
**③使いどころ**: ビルド時に「循環参照」の警告が出たとき、あるいは意図せず初期化順序に依存したバグ(片方がundefinedのまま使われる)が起きたときの原因調査。
**④組合せ例**: 循環が見つかったら、KNOW-F-0381(コンストラクタ注入の型)またはKNOW-F-0399(イベント駆動循環回避の型)で片方の直接参照を断ち切るのが定石の対処。KNOW-F-0398(依存グラフ可視化の型)は本型の検出結果を人間が読める図に変換する後段の型。
**⑤確認事項(罠と検算・安全注意)**: 循環は2ファイル間だけでなく、A→B→C→Aのように3つ以上を経由する場合もあるため、2ファイルだけを見て「循環なし」と即断しない。検算: hasCycleに実際の依存グラフ(各モジュールが import するモジュール名の一覧)を与え、既知の循環ペアに対してtrueを返すことを確認する(実測: 既知循環で検出成功)。
---
### KNOW-F-0385 レイヤー化の型(Layered Architecture)
**①名称**: レイヤー化の型(層状アーキテクチャ)
**②構造の型(骨子)**:
```
第4層: UI(画面表示・入力受付)
第3層: アプリケーション(ユースケースの手順)
第2層: ドメイン(業務ルールの計算・純関数中心)
第1層: インフラ(DB・外部API・ファイルI/O)
規則: 上位層は下位層を呼んでよいが、下位層は上位層を呼んではならない
```
**③使いどころ**: 「業務ルールの計算」と「画面の都合」と「保存先の都合」が1つの関数の中で混線しているとき。層に分けることで、DBを変えても業務ルールのコードは1行も変わらない、という状態を目指す。
**④組合せ例**: KNOW-F-0379(依存の向きを揃える型)を層という単位で具体化したものが本型。KNOW-F-0407(純粋コアと不純な殻の型)は第2層(ドメイン)を純関数だけで構成する、というさらに厳しい版として接続する。
**⑤確認事項(罠と検算・安全注意)**: 小規模な処理に4層すべてを強制すると、単純な処理でもファイルを4つ渡り歩く必要が生じ、かえって読みにくくなる罠がある(規模に応じて層数を調整する)。検算: ドメイン層のファイルをgrepし、DBやHTTP通信の関数呼び出しが1件も含まれていないことを確認する。
---
### KNOW-F-0386 ファサードの型(Facade)
**①名称**: ファサードの型(窓口関数)
**②構造の型(骨子)**:
```
// 内部は3つのサブシステムに分かれて複雑だが…
function validate(order) { /* ... */ }
function reserveStock(order) { /* ... */ }
function chargePayment(order) { /* ... */ }
function notify(order) { /* ... */ }
// 外部には1つの窓口だけを見せる(ファサード)
function checkout(order) { // これだけを呼べば内部の順序を知らなくてよい
validate(order); reserveStock(order); chargePayment(order); notify(order)
return { status: "ok" }
}
```
**③使いどころ**: あるまとまった処理を使うために、呼び出し側が「まず何を呼び、次に何を呼び…」という手順まで覚えなければならないとき。手順そのものを1つの関数の中に閉じ込め、呼び出し側の負担を減らす。
**④組合せ例**: KNOW-F-0378(疎結合の型)を実現する代表的な部品。KNOW-F-0397(ストラングラー分離の型)で古いモジュールを段階的に置き換える際、まずファサードを立てて内部だけを差し替える、という順序で使われることが多い。
**⑤確認事項(罠と検算・安全注意)**: ファサードが内部の全メソッドをそのまま素通しするだけの「薄すぎるファサード」になると、単なる別名づけに過ぎず利点が薄い罠がある。検算: checkout()を呼んだときの副作用の順序(在庫確保→課金→通知)が、内部を直接個別に呼んだ場合と一致するかをログで突き合わせる。
---
### KNOW-F-0387 アダプタの型(Adapter)
**①名称**: アダプタの型(異なるインターフェースの橋渡し)
**②構造の型(骨子)**:
```
// 外部ライブラリの形(自分たちの契約と形が違う)
// externalPay.doCharge(amountInCents, currencyCode)
// 自分たちの契約(Payable)に合わせる橋渡し役
function makePayableAdapter(externalPay) {
return {
charge(amountInYen) { // 自分たちの語彙で呼べる
return externalPay.doCharge(amountInYen * 100, "JPY")
}
}
}
```
**③使いどころ**: 外部ライブラリやレガシーコードのインターフェースが、自分たちのモジュール契約(KNOW-F-0396)と食い違っているとき。外部の形に自分たちのコード全体を合わせるのではなく、橋渡し役だけを1箇所作る。
**④組合せ例**: KNOW-F-0380(依存性逆転の型)と組み合わせると、アプリケーション本体は自分たちのPayable契約だけを知り、外部ライブラリの詳細はアダプタの中に閉じ込められる。KNOW-F-0433(フェイクの型)は、このアダプタと同じ形(Payable契約)を満たすテスト用の偽実装として対になる。
**⑤確認事項(罠と検算・安全注意)**: 単位変換(円↔セント)のような変換ミスがアダプタ内に紛れ込みやすい罠がある。検算: makePayableAdapterに金額1000円を渡し、externalPay.doChargeへ100000(セント)が渡っていることをログまたはスタブの記録で確認する(実測: 単位変換が100倍で一致)。
---
### KNOW-F-0388 名前空間分割の型(Namespace Partitioning)
**①名称**: 名前空間分割の型
**②構造の型(骨子)**:
```
// 分割前: グローバルに同名の関数が衝突する危険
function save() { /* 注文の保存 */ }
function save() { /* ユーザーの保存 */ } // 上書きしてしまう
// 分割後: 名前空間(モジュール/オブジェクト)で隔離する
const Order = { save(o) { /* ... */ } }
const User = { save(u) { /* ... */ } }
Order.save(order); User.save(user) // 衝突しない
```
**③使いどころ**: 複数のチームや複数のファイルが同じ一般的な単語(save, validate, formatなど)を使いたいとき。名前空間で区切ることで、同じ単語を安全に再利用できる。
**④組合せ例**: KNOW-F-0389(公開境界の型)と組み合わせ、名前空間の中でも「公開してよい名前」と「内部だけの名前」をさらに分ける二段構えにできる。KNOW-F-0390(バレルエクスポートの型)は複数の名前空間をまとめて外部へ見せる際の窓口として接続する。
**⑤確認事項(罠と検算・安全注意)**: 名前空間を深くしすぎる(App.Domain.Order.Service.save のような多段)と、呼び出しの記述が冗長になる罠がある。検算: プロジェクト全体をgrepし、同名関数が異なる名前空間に属さずグローバルに重複定義されていないかを確認する。
---
### KNOW-F-0389 公開境界の型(Public/Private Boundary)
**①名称**: 公開境界の型(公開APIと内部実装の分離)
**②構造の型(骨子)**:
```
// order.js
function _calcTax(amount) { return amount * 0.1 } // 先頭_で内部専用を示す慣習
function _validateInternal(order) { /* ... */ }
export function calcTotal(order) { // 公開する窓口だけexport
_validateInternal(order)
const sub = order.items.reduce((s,i)=>s+i.price*i.qty, 0)
return sub + _calcTax(sub)
}
```
**③使いどころ**: モジュールの利用者に「これだけ使えばよい」という最小限の窓口を示し、内部の実装詳細を自由に変更できる余地を確保したいとき。
**④組合せ例**: KNOW-F-0396(モジュール契約の型)は公開境界に「何を公開するか」という約束を明文化したもの。KNOW-F-0390(バレルエクスポートの型)は複数モジュールの公開境界をひとつの入口にまとめる型として接続する。
**⑤確認事項(罠と検算・安全注意)**: 内部専用のつもりの関数が別ファイルからimportされてしまう(慣習に頼った隠蔽は破られやすい)罠がある。可能な言語機能ではexport/importの仕組みそのもので強制する方が安全。検算: プロジェクト全体をgrepし、_始まりの関数名が定義ファイルの外から参照されていないことを確認する。
---
### KNOW-F-0390 バレルエクスポートの型(Barrel Export)
**①名称**: バレルエクスポートの型(まとめexport)
**②構造の型(骨子)**:
```
// order/calc.js, order/validate.js, order/format.js に分かれた実装を…
// order/index.js (バレル)
export { calcTotal } from "./calc.js"
export { validateOrder } from "./validate.js"
export { formatTotal } from "./format.js"
// 利用側は1箇所からまとめて読み込める
import { calcTotal, validateOrder, formatTotal } from "./order/index.js"
```
**③使いどころ**: 1つの機能(この例では注文)が複数ファイルに分かれているとき、利用側が個々のファイルパスを覚えなくても済むようにしたいとき。
**④組合せ例**: KNOW-F-0389(公開境界の型)で選んだ「公開してよいもの」だけをバレルに列挙するのが定石。KNOW-F-0393(垂直分割の型)で機能単位に分けたモジュール群の入口として使われることが多い。
**⑤確認事項(罠と検算・安全注意)**: バレルファイルが内部の全ファイルをimportするため、循環依存(KNOW-F-0384)を誘発しやすい罠がある(特に大きなプロジェクトでビルド時間が悪化する例が知られているとされる)。検算: バレル経由の読み込みと個別ファイル直接読み込みとで、実際にexportされる関数の集合が一致するかを比較する。
---
### KNOW-F-0391 サービスロケータの型と罠(Service Locator)
**①名称**: サービスロケータの型と罠
**②構造の型(骨子)**:
```
const registry = {} // 中央のレジストリ
function register(name, service) { registry[name] = service }
function locate(name) { return registry[name] }
register("mailer", smtpMailer)
function placeOrder(order) {
const mailer = locate("mailer") // 関数の外から勝手に取ってくる
mailer.send(order.email, "ok")
}
```
**③使いどころ**: 依存が非常に多い大規模システムで、コンストラクタ注入(KNOW-F-0381)による引数の連鎖(バケツリレー)を避けたいとき。ただし本冊は次点の型として推奨し、まずは注入を優先する。
**④組合せ例**: KNOW-F-0381(コンストラクタ注入の型)と比較対象になる。サービスロケータは「何に依存しているか」がplaceOrderの引数から見えなくなる(locate呼び出しの中に隠れる)ため、依存関係を明示したい章の趣旨(CATALOG §0.5)とは相性が悪い。
**⑤確認事項(罠と検算・安全注意)**: 依存が引数に現れないため、テスト時に「どのサービスを差し替えればよいか」がコードを読むだけでは分からなくなる罠がある(隠れた入力、KNOW-F-0412参照)。検算: placeOrder関数のシグネチャ(引数一覧)だけを見て、必要な依存がすべて分かるかを確認する — サービスロケータではこの検算に失敗することが型の限界として記録される。
---
### KNOW-F-0392 プラグイン拡張点の型(Plugin Extension Point)
**①名称**: プラグイン拡張点の型
**②構造の型(骨子)**:
```
function createPipeline() {
const steps = []
return {
use(step) { steps.push(step); return this }, // 拡張点: 誰でも工程を追加できる
run(input) { return steps.reduce((acc, step) => step(acc), input) }
}
}
const pipeline = createPipeline()
.use(x => x.trim())
.use(x => x.toLowerCase())
.use(x => x.replace(/\s+/g, "_"))
pipeline.run(" Hello World ") // "hello_world"
```
**③使いどころ**: 本体のコードを変更せずに、後から機能(工程・検証ルール・出力形式など)を追加できるようにしたいとき。use()のような差込口を用意することで、本体は「差込口を順番に実行する」ことだけを知ればよくなる。
**④組合せ例**: KNOW-F-0378(疎結合の型)の応用形。KNOW-F-0420(遅延評価の型)と組み合わせると、拡張点に登録した関数を実行時まで評価しない設計にでき、順序変更やスキップが柔軟になる。
**⑤確認事項(罠と検算・安全注意)**: 拡張点を経由する処理の順序が実行時に決まるため、デバッグ時に「どの工程で値がおかしくなったか」が追いにくくなる罠がある。検算: run()の途中でacc(中間結果)を各工程ごとに記録し、pipeline.runの最終結果が各工程を手で1つずつ適用した結果と一致するかを確認する。
---
### KNOW-F-0393 垂直分割の型(Vertical Slicing)
**①名称**: 垂直分割の型(機能単位の分割)
**②構造の型(骨子)**:
```
垂直分割(機能ごとにフォルダを縦に切る):
features/
order/ (order.js, order.test.js, order.repository.js)
user/ (user.js, user.test.js, user.repository.js)
各フォルダは「注文」「ユーザー」という機能に必要な全層(計算・保存・検証)を内包する
```
**③使いどころ**: 「注文機能を1つ削除・追加したい」というような、機能単位の変更が頻繁に起きるプロジェクトのフォルダ構成を決めるとき。
**④組合せ例**: KNOW-F-0394(水平分割の型)と対をなす。両者はどちらか一方だけが正しいわけではなく、プロジェクトの規模や変更頻度によって使い分ける(小規模〜中規模は垂直分割が読みやすいとされることが多い)。KNOW-F-0377(高凝集の型)を実現する具体的な構成手段になる。
**⑤確認事項(罠と検算・安全注意)**: 機能間で共通するロジック(例: 日付整形)が各フォルダに重複して書かれやすい罠がある。共通部分はKNOW-F-0395(共有カーネルの型)として別枠に切り出す。検算: フォルダを跨いだ重複コードをgrepし、3回以上同じ処理が現れていないかを確認する(3回目で共有化する、CLAUDE.mdの「3回使ったらレシピ化」原則と同型)。
---
### KNOW-F-0394 水平分割の型(Horizontal Slicing)
**①名称**: 水平分割の型(層単位の分割)
**②構造の型(骨子)**:
```
水平分割(層ごとに横に切る):
controllers/ (order.controller.js, user.controller.js)
services/ (order.service.js, user.service.js)
repositories/(order.repository.js, user.repository.js)
各フォルダは「層」という技術的な役割ごとに全機能をまたいでまとめる
```
**③使いどころ**: 「保存の仕組みを全機能まとめてDBからクラウドストレージへ移行したい」というような、層単位の変更が頻繁に起きるプロジェクトの構成を決めるとき。
**④組合せ例**: KNOW-F-0393(垂直分割の型)と対比。KNOW-F-0385(レイヤー化の型)が定める層の区切りを、そのままフォルダ構成へ落とし込んだものが本型にあたる。
**⑤確認事項(罠と検算・安全注意)**: 1つの機能(注文)を直すのに controllers/services/repositories の3フォルダを毎回横断しなければならず、機能単位の変更が多いプロジェクトでは読解コストが上がる罠がある。検算: 直近の変更履歴を振り返り、機能単位の変更(縦方向)と層単位の変更(横方向)のどちらが多いかを数え、多い方向に合わせた分割を選べているかを確認する。
---
### KNOW-F-0395 共有カーネルの型(Shared Kernel)
**①名称**: 共有カーネルの型(共通基盤モジュール)
**②構造の型(骨子)**:
```
// shared/date.js — どの機能からも参照される共通基盤
export function formatDate(d) { return d.toISOString().slice(0,10) }
export function isWeekend(d) { const w=d.getDay(); return w===0 || w===6 }
// order/order.js と user/user.js の両方が shared/date.js を使う
```
**③使いどころ**: KNOW-F-0393(垂直分割の型)で機能ごとに分けたモジュール間で、明らかに共通する処理(日付整形・通貨計算など)が3回以上重複して現れたとき。
**④組合せ例**: KNOW-F-0393(垂直分割の型)の重複解消策として直接接続する。共有カーネルは全機能から依存されるため、KNOW-F-0379(依存の向きを揃える型)における「最も下位」の層に置くのが安全。
**⑤確認事項(罠と検算・安全注意)**: 共有カーネルが肥大化すると「なんでも置き場」になり、変更が全機能に波及する危険なモジュールに変質する罠がある(俗に言う神モジュール化)。検算: shared/以下のファイル数と行数を定期的に数え、増加が続く場合は中身を機能別に再分割できないか棚卸しする。
---
### KNOW-F-0396 モジュール契約の型(Module Contract)
**①名称**: モジュール契約の型(公開インターフェース定義)
**②構造の型(骨子)**:
```
/**
* 契約: OrderRepository
* - save(order): Promise<void> — orderを永続化する。失敗時はエラーを投げる。
* - findById(id): Promise<Order|null> — 存在しなければnullを返す(例外にしない)。
*/
// 実装は契約さえ満たせば自由(mysql版・memory版・mock版すべて可)
```
**③使いどころ**: 複数人・複数モジュールが協業する箇所で、「何を渡せば何が返るか」を実装コードだけに頼らず明文化しておきたいとき。KNOW-F-0383(インターフェース分離の型)の前段として、まず契約全体を書き出す。
**④組合せ例**: KNOW-F-0380(依存性逆転の型)が依存する「抽象」の実体はこの契約である。KNOW-F-0447(契約テストの型)は、この契約を実際に満たしているかを自動テストで検証する後段の型として直接対応する。
**⑤確認事項(罠と検算・安全注意)**: 契約に書いた前提(例: findByIdは例外を投げない)を実装が守らないと、契約を信じた呼び出し側で予期しない例外が発生する罠がある。検算: 契約の各項目(save/findByIdの入出力・エラー時の挙動)について、実装が1項目ずつ満たしているかをチェックリストにして照合する。
---
### KNOW-F-0397 ストラングラー分離の型(Strangler Pattern)
**①名称**: ストラングラー分離の型(段階的な置き換え)
**②構造の型(骨子)**:
```
// 旧: legacyProcessOrder(order) を一括で新実装へ置換したいが危険が大きい
function processOrder(order) {
if (isMigratedFeature(order.type)) {
return newProcessOrder(order) // 新実装(移行済み部分)
}
return legacyProcessOrder(order) // 旧実装(未移行部分)
}
// isMigratedFeatureの対象を少しずつ広げ、最終的にlegacyを削除する
```
**③使いどころ**: 巨大で複雑な既存モジュール(モノリス)を一度に書き換えるとリスクが大きすぎるとき。窓口(ファサード、KNOW-F-0386)を立てて、内部だけを機能ごとに少しずつ新実装へ置き換える。
**④組合せ例**: KNOW-F-0386(ファサードの型)を置き換えの窓口として使う。KNOW-F-0428(回帰テストの型)を各段階の置き換え前後で必ず走らせ、旧実装と新実装で挙動が一致することを確認しながら進める。
**⑤確認事項(罠と検算・安全注意)**: isMigratedFeatureのような分岐条件自体が複雑化し、いつまでも旧実装を消せなくなる罠がある(移行の終了条件を最初に決めておく)。検算: 同じorderをlegacyProcessOrderとnewProcessOrderの両方に通し、移行対象範囲では結果が一致することを確認してから分岐を切り替える。
---
### KNOW-F-0398 依存グラフ可視化の型(Dependency Graph Visualization)
**①名称**: 依存グラフ可視化の型
**②構造の型(骨子)**:
```
依存関係を矢印の一覧として書き出す(骨子):
order.js -> shared/date.js
order.js -> payment.js
payment.js -> shared/date.js
user.js -> shared/date.js
矢印の一覧から輪(循環)や、矢印が集中しすぎるモジュール(神モジュール)を見つける
```
**③使いどころ**: モジュール数が増えて頭の中だけでは依存関係を把握できなくなったとき。定期的に一覧・図として書き出し、KNOW-F-0384(循環依存)やKNOW-F-0395(共有カーネルの肥大化)の兆候を早期に見つける。
**④組合せ例**: KNOW-F-0384(循環依存検出の型)のアルゴリズムに、実際のimport文一覧を読み込ませることで本型を機械化できる。KNOW-F-0379(依存の向きを揃える型)の検証にもそのまま使える。
**⑤確認事項(罠と検算・安全注意)**: 動的に文字列でモジュール名を組み立てて読み込む(動的import)場合、静的な解析だけでは矢印を見落とす罠がある。検算: 可視化した矢印の本数と、実際のソースコード中のimport文の本数を数えて一致するかを突き合わせる。
---
### KNOW-F-0399 イベント駆動循環回避の型(Event-Driven Decoupling)
**①名称**: イベント駆動循環回避の型
**②構造の型(骨子)**:
```
// 直接呼び合うと循環しやすい: Order <-> Notification
// order.js: notifyModule.send(...) notification.js: orderModule.getStatus(...)
// イベント駆動に変更: 互いを直接知らない
const bus = createEventBus()
// order.js
bus.emit("order.placed", { orderId })
// notification.js (orderを一切importしない)
bus.on("order.placed", ({ orderId }) => sendMail(orderId))
```
**③使いどころ**: KNOW-F-0384(循環依存検出の型)で見つかった循環のうち、「AがBの結果を使い、BがAの発生をきっかけに動く」という相互関係が本質的な場合。直接の呼び出しをイベント(通知)に置き換えて依存の矢印そのものを消す。
**④組合せ例**: KNOW-F-0378(疎結合の型)の強力な実現手段。KNOW-F-0404(冪等性の型)と組み合わせ、同じイベントが2回配送されても安全に処理できるようにしておくのが定石(イベント配送は重複しうる前提で設計する)。
**⑤確認事項(罠と検算・安全注意)**: イベント駆動は「どの処理がどのイベントに反応するか」がコードを読むだけでは追いにくくなる罠がある(依存が消えた代わりに見通しが下がる)。検算: emitされるイベント名の一覧と、onで購読されているイベント名の一覧を突き合わせ、発火されても誰も購読していないイベント(死んだイベント)がないかを確認する。
---
### KNOW-F-0400 モジュール粒度調整の型(Module Granularity)
**①名称**: モジュール粒度調整の型(分割しすぎ・しなさすぎの判断)
**②構造の型(骨子)**:
```
判断の目安表:
| 兆候 | 対処 |
|-----------------------------------------|-------------------------------|
| 1ファイルが1000行を超え話題が複数混在 | 分割する(KNOW-F-0393/0394) |
| 3関数呼ぶだけの処理に5ファイルを渡り歩く | 統合する(過剰分割の解消) |
| 変更のたびに2〜3ファイルが同時に変わる | 凝集度を見直す(KNOW-F-0377) |
```
**③使いどころ**: 「そろそろ分割すべきか、それとも分割しすぎているか」の判断に迷ったとき。感覚ではなく、変更履歴(同時に変更されたファイルの組)という実測データを根拠にする。
**④組合せ例**: 第一章の全型(KNOW-F-0376〜0399)の適用度合いを事後的に調整する仕上げの型として、章の最後に置かれている。KNOW-F-0377(高凝集の型)・KNOW-F-0378(疎結合の型)の両方が行き過ぎていないかを本型で点検する。
**⑤確認事項(罠と検算・安全注意)**: 「分割は常に善」という思い込みで機械的に分け続けると、KNOW-F-0391(サービスロケータの罠)と同様に見通しの悪化を招く。検算: 直近10回のコミット(変更のまとまり)を振り返り、1回のコミットで平均何ファイルが変更されたかを数え、極端に多い(分割不足)か極端にファイル間の行き来が多い(過剰分割)かを判定する。
---
## 第二章 純化の型(KNOW-F-0401〜0425)
第一章がモジュール同士の関係を扱ったのに対し、第二章は個々の関数の「中身」を扱う。副作用(戻り値以外の形で世界に影響を与える処理)を関数の端へ押し出し、計算の芯を「同じ入力なら常に同じ出力」に保つと、テスト・デバッグ・並行処理のすべてが単純になる。これがCATALOG §0.5「段落を崩しても通る」の実装形である25の型を、この章に集約する。
---
### KNOW-F-0401 純関数の型(Pure Function)
**①名称**: 純関数の型
**②構造の型(骨子)**:
```
// 純関数: 入力だけから出力が決まり、外の世界に触れない
function add(a, b) { return a + b }
// 純関数でない例: 外の変数を読む/書く
let total = 0
function addToTotal(x) { total += x; return total } // 呼ぶたびに結果が変わりうる
```
**③使いどころ**: ある関数を「同じ引数で2回呼んだら同じ結果になるか」と自問し、答えが「いいえ」だったとき。純関数にできる部分とできない部分を仕分ける出発点になる。
**④組合せ例**: KNOW-F-0402(副作用分離の型)は、純関数にできない部分(addToTotalのtotal更新)を関数の外(呼び出し側)へ追い出す具体手順。KNOW-F-0429(決定性テストの型)は純関数のテストが特に書きやすい(入力と期待出力のペアを並べるだけでよい)ことと直接つながる。
**⑤確認事項(罠と検算・安全注意)**: 「関数の中でconsole.logするだけだから純関数扱いでよい」という判断は罠である。ログ出力も画面(標準出力)という外の世界への副作用である。検算: add(2,3)を100回連続で呼び、すべて5を返すこと、かつ外部の変数が一切変化していないことを確認する(実測: 100回とも5・外部状態不変)。
---
### KNOW-F-0402 副作用分離の型(Side-Effect Isolation)
**①名称**: 副作用分離の型(端に押し出す)
**②構造の型(骨子)**:
```
// 分離前: 計算と保存が1関数に同居
function updateStock(itemId, qty) {
const newQty = getStock(itemId) - qty // 計算
db.update(itemId, newQty) // 副作用
return newQty
}
// 分離後: 計算(純粋)と実行(副作用)を分ける
function calcNewStock(current, qty) { return current - qty } // 純関数
function updateStock(itemId, qty) { // 副作用はここだけに集約
const newQty = calcNewStock(getStock(itemId), qty)
db.update(itemId, newQty)
return newQty
}
```
**③使いどころ**: KNOW-F-0401(純関数の型)で「純粋にできない」と判定された関数に対して、計算部分だけでも切り出したいとき。全体を純粋にできなくても、計算ロジックの正しさだけは純関数として独立にテストできるようになる。
**④組合せ例**: KNOW-F-0407(純粋コアと不純な殻の型)は本型をシステム全体の設計原則にまで拡張したもの。KNOW-F-0423(副作用ログ化の型)は、分離しきれなかった副作用を記録として残す型として接続する。
**⑤確認事項(罠と検算・安全注意)**: 「計算」だと思っていた部分に隠れた副作用(乱数・現在時刻の参照など)が紛れていないかを疑う(KNOW-F-0412参照)。検算: calcNewStock(10, 3)を呼び、db.updateが一切呼ばれていない(モックの呼び出し回数が0)ことを確認する。
---
### KNOW-F-0403 参照透過性の型(Referential Transparency)
**①名称**: 参照透過性の型
**②構造の型(骨子)**:
```
// 参照透過: 式をその値で置き換えても全体の意味が変わらない
const x = add(2, 3) // add(2,3) は常に5
const y = x + x // 5 + 5 と書き換えても同じ結果(10)
// 参照透過でない例: 式を値に置き換えると結果が変わる
const now1 = Date.now() // 呼ぶたびに違う値
const z = now1 + now1 // Date.now() + Date.now() には置き換えられない
```
**③使いどころ**: ある式を「一時変数に入れてから使う」のと「そのまま2回書く」のとで結果が変わらないかを確認したいとき。変わってしまうなら参照透過ではない(=純粋でない要素が混ざっている)。
**④組合せ例**: KNOW-F-0401(純関数の型)の性質を式のレベルで言い換えたものが本型であり、事実上の対になる型。KNOW-F-0419(参照透過なキャッシュの型)は、参照透過な式だけが安全にキャッシュ(メモ化)できるという直接の応用。
**⑤確認事項(罠と検算・安全注意)**: 参照透過性は「関数」だけでなく「式全体」に対して問う概念であるため、純関数を組み合わせた式でも、途中に1つでも非純粋な呼び出し(現在時刻など)が混じると全体が参照透過でなくなる。検算: 同じ式を2回評価し、結果が完全に一致するかを実際に2回実行して比較する(実測: 2回とも同一値なら参照透過)。
---
### KNOW-F-0404 冪等性の型(Idempotency)
**①名称**: 冪等性の型
**②構造の型(骨子)**:
```
// 冪等でない例: 呼ぶたびに残高が減り続ける
function withdraw(accountId, amount) { balances[accountId] -= amount }
// 冪等な例: 同じ操作を何度行っても結果が同じ状態に収束する
function setBalance(accountId, targetAmount) { balances[accountId] = targetAmount }
// setBalance("A", 100) は1回でも3回でも残高は100のまま
```
**③使いどころ**: 通信の再送(タイムアウトでリトライした結果、同じ操作が2回届く可能性がある処理)を扱うとき。「引き算」ではなく「値の設定」の形にできないかを検討する。
**④組合せ例**: KNOW-F-0417(冪等なリトライの型)・KNOW-F-0418(冪等キーの型)は、通信が信頼できない環境で冪等性を実運用に落とし込む直接の後続の型。CATALOG §0.5の「段落を崩しても通る」の三要件(順序独立・参照透過・冪等)のうち、冪等の部分を担う中核の型である。
**⑤確認事項(罠と検算・安全注意)**: すべての操作を冪等にできるわけではない(例: 「1件追加する」という操作は本質的に累積的)。累積操作を冪等にするには、KNOW-F-0418の冪等キーで「同じ要求は1回しか適用しない」という仕組みを別途足す必要がある。検算: setBalance("A", 100)を3回連続で呼び、balances["A"]が3回とも100のままであることを確認する(実測: 3回呼んでも100のまま=冪等成立)。
---
### KNOW-F-0405 イミュータブルデータの型(Immutable Data)
**①名称**: イミュータブルデータの型
**②構造の型(骨子)**:
```
// ミュータブル(書き換え可能): 元の配列そのものを変更する
function addItem(cart, item) { cart.items.push(item); return cart } // 元のcartも変わる
// イミュータブル(不変): 新しいコピーを返し、元は変更しない
function addItem(cart, item) {
return { ...cart, items: [...cart.items, item] } // 新しいオブジェクトを生成
}
```
**③使いどころ**: 「この変数、いつの間にか中身が変わっていた」というバグに遭遇したとき。関数が受け取ったデータをその場で書き換えず、常に新しいコピーを返す形にすることで、呼び出し元のデータが意図せず変化することを防ぐ。
**④組合せ例**: KNOW-F-0405は第一・第二章を通じて多くの型の前提になる基礎技術であり、特にKNOW-F-0421(不変条件の型)・KNOW-F-0429(決定性テストの型)と相性がよい(不変なデータはテストの「与えたデータが後で壊れていないか」の心配を排除する)。
**⑤確認事項(罠と検算・安全注意)**: 「スプレッド構文({...obj})でコピーしたから安全」と思い込むのは罠である。ネストしたオブジェクト(cart.items[0].price など2階層目以降)は浅いコピーでは共有されたままになる(KNOW-F-0406参照)。検算: addItem呼び出し後、元のcart変数のitems配列の長さが変化していない(元のcart !== 戻り値のcart)ことを確認する。
---
### KNOW-F-0406 コピーオンライトの型(Copy-on-Write)
**①名称**: コピーオンライトの型
**②構造の型(骨子)**:
```
function updateItemPrice(cart, itemId, newPrice) {
return {
...cart, // 変更しない部分は参照を使い回す(コピーしない)
items: cart.items.map(i => // 書き換える部分だけ新しく作る
i.id === itemId ? { ...i, price: newPrice } : i // 対象外はそのまま使い回す
)
}
}
```
**③使いどころ**: KNOW-F-0405(イミュータブルデータの型)を実践する際、「変更が必要な箇所だけを新しく作り、それ以外は元のまま使い回す」ことで、丸ごと全部コピーするコストを避けたいとき(データ量が大きい場合に重要)。
**④組合せ例**: KNOW-F-0405(イミュータブルデータの型)の実装最適化として直接接続する。KNOW-F-0486(キャッシュ効果測定の型)と組み合わせ、コピーオンライトによって「変更されていない部分の参照が同一のままか」を検算すると、意図しない全体コピーが起きていないかを計測で確認できる。
**⑤確認事項(罠と検算・安全注意)**: 「変更しない部分は使い回す」つもりが、実際には毎回全コピーしてしまっている実装ミスが起きやすい罠がある(mapは常に新しい配列を作る点に注意)。検算: 変更対象外のアイテム(id違い)について、更新前後で同一の参照(===で真)になっているかを確認する。
---
### KNOW-F-0407 純粋コアと不純な殻の型(Functional Core, Imperative Shell)
**①名称**: 純粋コアと不純な殻の型
**②構造の型(骨子)**:
```
// 殻(不純): 入出力だけを担当
async function handleCheckout(req) {
const order = await db.loadOrder(req.orderId) // 入力(副作用)
const result = decideCheckout(order, Date.now()) // コア(純粋)呼び出し
await db.saveResult(result) // 出力(副作用)
return result
}
// コア(純粋): 判断ロジックのみ、時刻は引数で受け取る
function decideCheckout(order, now) {
if (order.expiresAt < now) return { status: "expired" }
return { status: "ok", total: order.items.reduce((s,i)=>s+i.price, 0) }
}
```
**③使いどころ**: システム全体を「入出力を担当する薄い殻」と「判断・計算を担当する分厚い純粋な核」の2層に整理したいとき。KNOW-F-0385(レイヤー化の型)のドメイン層を、より厳密に純関数だけで構成する版にあたる。
**④組合せ例**: KNOW-F-0412(隠れた入力排除の型)がこの型を可能にする鍵となる技術(Date.now()を殻で取得しコアには引数として渡す、という部分がまさにそれ)。KNOW-F-0429(決定性テストの型)は、コア(decideCheckout)だけを対象にすればDBもネットワークも使わずテストできるという恩恵を直接受ける。
**⑤確認事項(罠と検算・安全注意)**: 殻が薄いつもりでも、うっかり判断ロジック(if文)を殻の中に書いてしまう「侵食」が起きやすい罠がある。検算: コア関数(decideCheckout)の中にawait・db・fetchなどの語が1件も出現しないことをgrepで確認する。
---
### KNOW-F-0408 早期I/O分離の型(Early I/O Separation)
**①名称**: 早期I/O分離の型
**②構造の型(骨子)**:
```
// I/Oを先にまとめて済ませ、以降は純粋な計算だけにする
async function generateReport(userId) {
// ステップ1: I/Oをすべて先に済ませる
const [user, orders, prefs] = await Promise.all([
db.getUser(userId), db.getOrders(userId), db.getPrefs(userId)
])
// ステップ2: 以降はメモリ上のデータだけを使う純粋な計算
return buildReport(user, orders, prefs) // 純関数
}
```
**③使いどころ**: 計算の途中で何度もI/O(DB問い合わせなど)を挟むコードを書いてしまい、テストのたびに大量のモックが必要になっているとき。I/Oを関数の先頭にまとめることで、以降のロジックを純粋に保てる。
**④組合せ例**: KNOW-F-0407(純粋コアと不純な殻の型)を実装する際の具体的な手順(まず殻でI/Oをまとめて取得し、その後コアへ渡す)として直接対応する。KNOW-F-0430(フィクスチャ設計の型)は、まとめて取得したデータ(user, orders, prefs)を模擬データとして用意する後段の型になる。
**⑤確認事項(罠と検算・安全注意)**: 「先にまとめて取得する」ことで、実は使わないデータまで取得してしまう無駄が発生する罠がある(過剰な事前取得)。検算: buildReport関数が実際に使っている引数(user/orders/prefsの各フィールド)を洗い出し、取得したデータのうち未使用のフィールドがないかを確認する。
---
### KNOW-F-0409 グローバル状態排除の型(Eliminate Global State)
**①名称**: グローバル状態排除の型
**②構造の型(骨子)**:
```
// 排除前: グローバル変数がどこからでも書き換えられる
let currentUser = null
function login(u) { currentUser = u }
function getGreeting() { return `こんにちは、${currentUser.name}` } // 暗黙の依存
// 排除後: 状態を引数として明示的に受け渡す
function getGreeting(user) { return `こんにちは、${user.name}` }
```
**③使いどころ**: 「このテストは他のテストの後に実行すると失敗する」という現象(テスト間の状態汚染)に遭遇したとき。原因の多くはグローバル変数(モジュールスコープの可変な値)である。
**④組合せ例**: KNOW-F-0410(状態明示渡しの型)が本型の直接の代替手段。KNOW-F-0442(テスト独立性の型)は本型が守られていないと成立しない前提条件の関係にある。
**⑤確認事項(罠と検算・安全注意)**: 「定数だから大丈夫」という思い込みは罠である。constで宣言されていても中身がオブジェクト/配列であれば、プロパティやフィールドは書き換え可能である(KNOW-F-0405のイミュータブル化で別途対処が必要)。検算: グローバルスコープのlet/varで宣言された変数を一覧化し、テスト実行の前後で値がリセットされずに残っていないかを確認する。
---
### KNOW-F-0410 状態明示渡しの型(Explicit State Passing)
**①名称**: 状態明示渡しの型
**②構造の型(骨子)**:
```
// 暗黙: 関数の外の状態に依存
let counter = 0
function increment() { counter++; return counter }
// 明示: 状態を引数として受け取り、新しい状態を返す
function increment(counter) { return counter + 1 }
let counter = 0
counter = increment(counter) // 呼び出し側が状態の受け渡しを担う
```
**③使いどころ**: KNOW-F-0409(グローバル状態排除の型)の具体的な実装手段として、状態を「関数の外」ではなく「引数と戻り値」の形で表現したいとき。
**④組合せ例**: KNOW-F-0405(イミュータブルデータの型)と組み合わせると、増分後のcounterは新しい値(元の値を変更しない)として返される。KNOW-F-0416(段落順序独立の型)は、状態が明示的に受け渡される関数ほど呼び出し順序への依存が薄くなるという性質を通じて接続する。
**⑤確認事項(罠と検算・安全注意)**: 状態を毎回引数で渡す設計は、呼び出し側のコードが冗長になる罠がある(counter = increment(counter)の書き方を全呼び出し箇所で徹底する必要がある)。検算: increment(5)を呼び、戻り値が6であること、かつ元の引数5を渡した変数自体は変更されていないことを確認する。
---
### KNOW-F-0411 デフォルト引数の罠回避の型(Default Argument Pitfalls)
**①名称**: デフォルト引数の罠回避の型
**②構造の型(骨子)**:
```
// 罠: デフォルト引数にミュータブルな値(配列/オブジェクト)を使う
function addTag(tag, tags = []) { tags.push(tag); return tags }
// 言語によっては、デフォルト値が呼び出し間で共有され蓄積し続ける実装がある
// 安全: 毎回新しく生成する、または明示的にnullを渡してもらう
function addTag(tag, tags) {
const base = tags ? [...tags] : [] // 呼び出しごとに新しい配列
base.push(tag)
return base
}
```
**③使いどころ**: 「引数を省略して呼んだときだけ、前回呼んだときのデータが残っている」という不可解なバグに遭遇したとき。デフォルト引数に配列やオブジェクトを直接書いていないかを疑う。
**④組合せ例**: KNOW-F-0405(イミュータブルデータの型)の考え方を関数のシグネチャ設計にまで適用したもの。KNOW-F-0429(決定性テストの型)で、同じ関数を複数回呼ぶテストケースを並べると本型の不具合を検出しやすい。
**⑤確認事項(罠と検算・安全注意)**: この罠は特に「デフォルト値が定義時に1回だけ生成され、以後使い回される」言語仕様を持つ環境で顕在化しやすい(挙動は言語ごとに異なるため、使用言語の仕様を必ず確認する)。検算: addTag("a")を2回連続で呼び、2回目の戻り値の長さが1(前回の"a"を引き継いでいない)であることを確認する。
---
### KNOW-F-0412 隠れた入力排除の型(Hidden Input Elimination)
**①名称**: 隠れた入力排除の型(時刻・乱数の注入)
**②構造の型(骨子)**:
```
// 隠れた入力: 引数に現れないのに結果を左右する
function isExpired(order) { return Date.now() > order.expiresAt } // 呼ぶ時刻で結果が変わる
// 排除後: 時刻を引数として明示する
function isExpired(order, now) { return now > order.expiresAt }
isExpired(order, Date.now()) // 呼び出し側が現在時刻を注入する
```
**③使いどころ**: 「昨日は成功したテストが今日は失敗する」という現象に遭遇したとき。関数の内部でDate.now()やMath.random()を直接呼んでいないかを疑う。これらは引数に現れない「隠れた入力」である。
**④組合せ例**: KNOW-F-0407(純粋コアと不純な殻の型)を成立させる鍵となる技術。KNOW-F-0429(決定性テストの型)・KNOW-F-0446(シード固定の型)は、この隠れた入力を明示化した後に「テストではどの値を注入するか」を決める直接の後続の型である。
**⑤確認事項(罠と検算・安全注意)**: UUID生成・環境変数の読み取り・ファイルの存在確認なども同じ意味で「隠れた入力」になりうる(見落としやすい)。検算: 関数の中でDate.now()、Math.random()、環境変数読み取りなどの語をgrepし、1件でも見つかれば引数化を検討する。
---
### KNOW-F-0413 隠れた出力排除の型(Hidden Output Elimination)
**①名称**: 隠れた出力排除の型(戻り値への一本化)
**②構造の型(骨子)**:
```
// 隠れた出力: 戻り値以外の経路で結果を外部に伝える
function validate(order) {
if (order.total < 0) { console.error("不正な合計"); return false } // ログという隠れた出力
window.lastValidatedOrder = order // グローバルへの書き込みという隠れた出力
return true
}
// 排除後: 結果はすべて戻り値にまとめる
function validate(order) {
if (order.total < 0) return { ok: false, reason: "不正な合計" }
return { ok: true }
}
```
**③使いどころ**: 関数の結果を確かめるために、戻り値だけでなくログやグローバル変数まで確認しなければならないとき。関数が伝えたい情報をすべて戻り値の構造(オブジェクト)にまとめる。
**④組合せ例**: KNOW-F-0414(Result型による例外回避の型)は、隠れた出力の代表格である「例外」を戻り値として扱う具体形。KNOW-F-0423(副作用ログ化の型)は、それでも必要なログ出力を「戻り値として記録を返す」形に変換し隠れた出力を減らす技として接続する。
**⑤確認事項(罠と検算・安全注意)**: 「一応ログにも出しておけば安心」という発想が隠れた出力を増やす典型的な罠である(戻り値とログの2箇所を見なければ全体像が分からなくなる)。検算: validate関数の呼び出し前後で、戻り値以外に変化した外部の状態(グローバル変数・コンソール出力)がないかを確認する。
---
### KNOW-F-0414 Result型による例外回避の型(Result Type)
**①名称**: Result型による例外回避の型
**②構造の型(骨子)**:
```
// 例外方式: 呼び出し側は必ずtry/catchで囲む必要がある(囲み忘れがバグになる)
function parseAge(input) {
const n = Number(input)
if (Number.isNaN(n)) throw new Error("数値でない")
return n
}
// Result型: 成功/失敗を戻り値の型として表現する
function parseAge(input) {
const n = Number(input)
if (Number.isNaN(n)) return { ok: false, error: "数値でない" }
return { ok: true, value: n }
}
const result = parseAge("abc")
if (result.ok) { /* result.value を使う */ } else { /* result.error を扱う */ }
```
**③使いどころ**: 「例外を投げる関数」を呼ぶ側がtry/catchを書き忘れ、想定外の場所でプログラム全体が停止してしまう事故を防ぎたいとき。失敗が「起こりうる通常の結果」である場合(入力検証など)は、Result型で明示するほうが呼び出し側に対応を強制できる。
**④組合せ例**: KNOW-F-0413(隠れた出力排除の型)の直接の実装形。KNOW-F-0415(Optionパターンの型)は「値があるかないか」だけを表す近い型であり、Result型は「なぜ失敗したか」まで表す点で一段情報量が多い。
**⑤確認事項(罠と検算・安全注意)**: すべての例外をResult型に置き換えるべきとは限らない(プログラムのバグに近い異常事態は例外のままにし、想定される業務上の失敗だけをResult型にする、という使い分けが実務的)。検算: parseAge("abc")の戻り値がok:falseであり、呼び出し側がresult.okを確認せずにresult.valueへ直接アクセスするとundefinedになる(壊れた使い方が型として発見しやすいか)を確認する。
---
### KNOW-F-0415 Optionパターンの型(Option/Maybe)
**①名称**: Optionパターンの型
**②構造の型(骨子)**:
```
// 素朴な方式: nullを直接返す(呼び出し側がnullチェックを忘れやすい)
function findUser(id) { return users.find(u => u.id === id) || null }
// Optionパターン: 「値がある/ない」を型で表現し、中身へのアクセスを関数越しにする
function some(value) { return { hasValue: true, value } }
function none() { return { hasValue: false } }
function findUser(id) {
const u = users.find(u => u.id === id)
return u ? some(u) : none()
}
function getName(option, fallback) { return option.hasValue ? option.value.name : fallback }
```
**③使いどころ**: 「nullかもしれない値」をコードのあちこちで無警戒に使ってしまい、実行時に「undefinedのプロパティは読めない」という種類の実行時エラーが多発するとき。
**④組合せ例**: KNOW-F-0414(Result型による例外回避の型)の姉妹型(Resultは失敗理由も持つが、Optionは有無だけを持つ点が違う)。KNOW-F-0422(事前条件・事後条件の型)と組み合わせ、Optionを返す関数は「関数の戻り値には必ずhasValueの確認が要る」という契約として明文化できる。
**⑤確認事項(罠と検算・安全注意)**: Optionでラップしたつもりが、ラップされた値の中でさらにnullを直接返してしまう二重管理の罠がある(ラップの外側と内側でnullの扱いを混在させない)。検算: findUser(存在しないid)を呼び、戻り値がnullそのものではなくhasValue:falseを持つオブジェクトであることを確認する。
---
### KNOW-F-0416 段落順序独立の型(Order Independence)
**①名称**: 段落順序独立の型(「段落を崩しても通る」の実装形)
**②構造の型(骨子)**:
```
// 順序依存: 呼ぶ順番を間違えると壊れる
let cart = []
function initCart() { cart = [] }
function addItem(item) { cart.push(item) } // initCartを先に呼ばないと未定義動作の恐れ
// 順序独立: どの順で呼んでも(あるいは呼ばなくても)安全
function addItem(cart, item) { return [...(cart || []), item] } // cart省略時も安全に動く
```
**③使いどころ**: CATALOG §0.5「段落を崩しても通る」を実装レベルで検証したいとき。関数群を任意の順序で呼び替えたり、一部を省略したりしても、結果が予測可能な形で得られるかを確認する。
**④組合せ例**: KNOW-F-0403(参照透過性の型)・KNOW-F-0404(冪等性の型)・KNOW-F-0409(グローバル状態排除の型)の3つが揃うと、この順序独立性が自然に達成される。すなわち本型は第二章の他の型を統合した「章の到達点」として位置づけられる。
**⑤確認事項(罠と検算・安全注意)**: 「完全に順序独立」は理想であり、現実には初期化(setup)のように順序が必要な処理も存在する。順序が必要な箇所は本型の対象外として明示的に区別することが誠実な適用である(誇大に「全部が順序独立」と主張しない)。検算: addItemを異なる順序(A→B→C、C→A→B)で複数パターン呼び出し、最終的なcartの中身(集合として)が一致するかを確認する。
---
### KNOW-F-0417 冪等なリトライの型(Idempotent Retry)
**①名称**: 冪等なリトライの型
**②構造の型(骨子)**:
```
async function sendWithRetry(request, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await send(request) // requestが冪等な操作である前提
} catch (e) {
if (i === maxRetries - 1) throw e
await sleep(2 ** i * 100) // 指数バックオフ
}
}
}
```
**③使いどころ**: ネットワーク通信のようにタイムアウトが起こりうる処理を、失敗時に自動で再試行したいとき。再試行が安全であるためには、send自体がKNOW-F-0404(冪等性の型)を満たしている必要がある。
**④組合せ例**: KNOW-F-0404(冪等性の型)は本型が成立するための前提条件。KNOW-F-0418(冪等キーの型)は、send自体が本質的に冪等でない操作(新規作成など)であっても、外側から冪等性を付与する補完手段として組み合わせる。
**⑤確認事項(罠と検算・安全注意)**: 冪等でない操作(例: 「注文を1件作成する」)にそのままリトライを付けると、通信が成功していたのに応答が届かなかっただけの場合に二重注文が発生する重大な罠がある。検算: sendが1回目で失敗・2回目で成功する状況を模擬し、最終的な副作用(注文件数など)が1回分しか記録されていないことを確認する。
---
### KNOW-F-0418 冪等キーの型(Idempotency Key)
**①名称**: 冪等キーの型(Idempotency Key)
**②構造の型(骨子)**:
```
const processedKeys = new Set() // 実運用ではDB等の永続領域に保存する
function createOrder(request, idempotencyKey) {
if (processedKeys.has(idempotencyKey)) {
return getExistingResult(idempotencyKey) // 既に処理済みなら同じ結果を返す
}
const result = actuallyCreateOrder(request)
processedKeys.add(idempotencyKey)
saveResult(idempotencyKey, result)
return result
}
```
**③使いどころ**: 「注文の作成」のように本質的に冪等でない操作を、KNOW-F-0417(冪等なリトライの型)で安全に再送できるようにしたいとき。呼び出し側が一意なキー(冪等キー)を発行し、同じキーでの再送は「初回と同じ結果を返すだけ」にする。
**④組合せ例**: KNOW-F-0417(冪等なリトライの型)と対で使う中核の型。KNOW-F-0404(冪等性の型)が原理、本型がその原理を「本質的に非冪等な操作」へ適用するための具体的な補完技術という関係にある。
**⑤確認事項(罠と検算・安全注意)**: 冪等キーの保存領域が揮発性(メモリのみ)だと、サーバー再起動をまたぐ再送で重複が発生する安全上の罠がある(永続化された領域に保存する必要がある)。検算: 同じidempotencyKeyでcreateOrderを2回呼び、actuallyCreateOrderが呼ばれる回数が1回だけであることを確認する(実測: 2回目は既存結果を再利用)。
---
### KNOW-F-0419 参照透過なキャッシュの型(Memoization)
**①名称**: 参照透過なキャッシュの型(メモ化)
**②構造の型(骨子)**:
```
function memoize(fn) {
const cache = new Map()
return function(...args) {
const key = JSON.stringify(args)
if (cache.has(key)) return cache.get(key) // 2回目以降は計算せず返す
const result = fn(...args)
cache.set(key, result)
return result
}
}
const fastAdd = memoize(add) // addが純関数であることが前提
```
**③使いどころ**: 同じ引数で何度も呼ばれる計算量の大きい純関数の速度を上げたいとき。KNOW-F-0403(参照透過性の型)を満たす関数にしか安全に適用できない。
**④組合せ例**: KNOW-F-0401(純関数の型)・KNOW-F-0403(参照透過性の型)が本型の前提条件。第五章KNOW-F-0486(キャッシュ効果測定の型)は、メモ化が実際に速度改善に寄与しているかを計測で裏づける後段の型として直接接続する。
**⑤確認事項(罠と検算・安全注意)**: 参照透過でない関数(現在時刻を使うなど)にメモ化を適用すると、古い結果が返り続ける重大なバグになる罠がある。またキャッシュが際限なく増え続けるメモリリーク(KNOW-F-0470参照)にも注意する。検算: fastAdd(2,3)を2回呼び、1回目と2回目でfn(add)の内部が実際に実行された回数が1回だけであることを確認する。
---
### KNOW-F-0420 遅延評価の型(Lazy Evaluation)
**①名称**: 遅延評価の型
**②構造の型(骨子)**:
```
// 即時評価: 定義した瞬間に計算してしまう(使わなくても計算コストがかかる)
const heavyResult = computeHeavy() // ここで即座に実行される
// 遅延評価: 実際に必要になった瞬間まで計算を先送りする
function lazy(fn) {
let cached, evaluated = false
return () => { if (!evaluated) { cached = fn(); evaluated = true }; return cached }
}
const heavyResult = lazy(() => computeHeavy()) // まだ実行されない
// 後で本当に必要になったときだけ
if (needed) heavyResult() // ここで初めて実行される
```
**③使いどころ**: 「計算コストの高い処理を用意したが、条件によっては結局使わない」というケースが多いとき。使われるかどうか分からない値を先に全部計算しておくのは無駄になりうる。
**④組合せ例**: KNOW-F-0419(参照透過なキャッシュの型)と相性がよく、lazy関数自体が内部でメモ化(1回計算したら2回目以降は使い回す)の性質も持つ。KNOW-F-0392(プラグイン拡張点の型)で登録した工程を、実行時まで評価しないようにする際にも応用できる。
**⑤確認事項(罠と検算・安全注意)**: 遅延評価は「いつ計算が起きるか」を予測しにくくする副作用があり、副作用を含む処理(KNOW-F-0402)を遅延評価と組み合わせると、実行タイミングのバグを生みやすい罠がある(遅延評価は純粋な計算にのみ適用するのが安全)。検算: lazy(fn)を生成した直後の時点でfnがまだ実行されていない(呼び出し回数0)ことを確認し、heavyResult()を呼んだ後に呼び出し回数が1になることを確認する。
---
### KNOW-F-0421 不変条件の型(Invariant)
**①名称**: 不変条件の型(invariant)
**②構造の型(骨子)**:
```
// 不変条件: 「在庫数は常に0以上」という、いつ確認しても成り立つべき性質
function withdrawStock(stock, qty) {
const next = stock - qty
if (next < 0) throw new Error("不変条件違反: 在庫が負になる") // 破られる前に止める
return next
}
```
**③使いどころ**: あるデータ構造について「この条件だけは絶対に崩れてはならない」というルールを、コードのあちこちに散らばせず1箇所で守りたいとき。
**④組合せ例**: KNOW-F-0422(事前条件・事後条件の型)は、不変条件を関数の入口(事前)・出口(事後)という2つのタイミングに分解した、より詳細な型として接続する。KNOW-F-0426(境界値分析の型)は、不変条件が破られる境目(この例では0)を重点的にテストする直接の後段の型になる。
**⑤確認事項(罠と検算・安全注意)**: 不変条件のチェックを一部の入口にだけ置き、別の入口(直接stockを書き換える別の関数など)を素通りさせてしまう罠がある(不変条件を守る責任をどの関数が持つかを明確にする)。検算: withdrawStock(5, 10)を呼び、意図通りエラーが発生し、stock自体が変更されていない(元のstock変数は5のまま)ことを確認する。
---
### KNOW-F-0422 事前条件・事後条件の型(Contract Programming)
**①名称**: 事前条件・事後条件の型(契約プログラミング)
**②構造の型(骨子)**:
```
function divide(a, b) {
// 事前条件: 呼び出し側が守るべき約束
if (b === 0) throw new Error("事前条件違反: bは0であってはならない")
const result = a / b
// 事後条件: 関数自身が守る約束
console.assert(result * b === a || !Number.isInteger(a/b), "事後条件チェック")
return result
}
```
**③使いどころ**: 「呼び出し側が守るべきルール」と「関数自身が守るルール」を分けて明文化したいとき。KNOW-F-0396(モジュール契約の型)のコメントによる契約を、実行時のチェックとして強制力を持たせる版にあたる。
**④組合せ例**: KNOW-F-0396(モジュール契約の型)の実行時版。KNOW-F-0426(境界値分析の型)は、事前条件の境界(この例ではb=0)を重点的に検証するテストの型として直接対応する。
**⑤確認事項(罠と検算・安全注意)**: 事前条件のチェックを本番コードに常時入れておくと処理速度が落ちる場合がある(開発時のみ有効化する、という運用も選択肢になる)。誇大な安全宣言は避け、「チェックした範囲でのみ保証する」という限定を明記する。検算: divide(6,3)が正しく2を返し、事後条件のassertがエラーにならないことを確認する。
---
### KNOW-F-0423 副作用ログ化の型(Side-Effect as Log)
**①名称**: 副作用ログ化の型
**②構造の型(骨子)**:
```
// 副作用を直接実行せず、「何をすべきか」の記述(コマンド)として返す
function decideActions(order) {
const actions = []
if (order.total > 10000) actions.push({ type: "sendMail", to: order.email })
if (order.stock < 5) actions.push({ type: "alertLowStock", itemId: order.itemId })
return actions // 純関数はここまで(副作用の「意図」を返すだけ)
}
// 殻の側で実際に実行する
function executeActions(actions) {
for (const a of actions) { if (a.type === "sendMail") sendMail(a.to) /* ... */ }
}
```
**③使いどころ**: 「どの条件でどの副作用が起きるか」の判断ロジック自体はテストしたいが、実際にメール送信までは行いたくないとき。副作用を「実行する」代わりに「実行すべき内容を表すデータ」として返す。
**④組合せ例**: KNOW-F-0407(純粋コアと不純な殻の型)の応用形であり、コアが返す「アクションの一覧」がまさに殻へ渡す仕様書になる。KNOW-F-0432(モックの型)の代替として使える場合がある(モックで呼び出しを記録する代わりに、そもそも戻り値として記録済みのデータを返す)。
**⑤確認事項(罠と検算・安全注意)**: アクションの一覧を返すだけの関数が、実際には一部の副作用を内部で直接実行してしまう「漏れ」が起きやすい罠がある(コアと殻の境界を機械的に守る)。検算: decideActions(order)を呼び、戻り値のactions配列の中身だけを確認し、実際のsendMail関数が1回も呼ばれていない(呼び出し回数0)ことを確認する。
---
### KNOW-F-0424 純化リファクタリング手順の型(Purification Refactoring Steps)
**①名称**: 純化リファクタリング手順の型
**②構造の型(骨子)**:
```
手順:
1. 対象関数の中の副作用行(I/O・グローバル変更・時刻/乱数参照)に印をつける
2. 印のない行(純粋な計算)だけを別関数として抜き出す
3. 元の関数は、抜き出した純関数を呼び、副作用行だけを残す殻にする
4. 抜き出した純関数に対してKNOW-F-0429(決定性テスト)を書く
5. 元の関数(殻)に対してKNOW-F-0432(モック)で副作用が正しい引数で呼ばれるかを確認する
```
**③使いどころ**: 既存の(副作用と計算が混ざった)大きな関数を安全に整理し直したいとき。一度に全部を書き換えず、段階を踏んで抜き出す。
**④組合せ例**: 第二章全体(KNOW-F-0401〜0423)の適用手順を1つの実行可能なチェックリストにまとめたもので、章の実践版としての位置づけ。KNOW-F-0397(ストラングラー分離の型)と同様、いきなり全置換せず段階的に進める思想を共有する。
**⑤確認事項(罠と検算・安全注意)**: 手順を守らず一度に大量の関数を純化しようとすると、途中で挙動が変わってしまっても気づけない罠がある(KNOW-F-0428の回帰テストを各段階の前後で必ず走らせる)。検算: リファクタリング前後で、元の関数を同じ入力で呼んだ結果(戻り値・副作用の内容)が完全に一致することを確認する。
---
### KNOW-F-0425 トランザクションスクリプト脱却の型(Beyond Transaction Script)
**①名称**: トランザクションスクリプト脱却の型
**②構造の型(骨子)**:
```
// トランザクションスクリプト: 1つの手続きに全ロジックが直線的に並ぶ
function checkout(order) {
// 検証、計算、在庫確認、課金、通知...すべてが1つの関数に直列で並ぶ(数百行になりがち)
}
// 脱却後: 各段階を独立した純関数+殻の呼び出しに分解する(第一・第二章の型の集大成)
function checkout(order) {
const validation = validateOrder(order) // 純関数
if (!validation.ok) return validation
const total = calcTotal(order) // 純関数
const actions = decideActions(order, total) // 純関数(KNOW-F-0423)
return executeActions(actions) // 殻
}
```
**③使いどころ**: 1つの関数が数百行に膨らみ、KNOW-F-0376(単一責務の型)から明らかに逸脱しているコード(俗に言うトランザクションスクリプト)を、章で学んだ型を総動員して整理し直す最終段階。
**④組合せ例**: 第一章(モジュール分割)と第二章(純化)のほぼ全型が動員される、両章の統合例。特にKNOW-F-0376(単一責務)・KNOW-F-0402(副作用分離)・KNOW-F-0407(純粋コアと不純な殻)・KNOW-F-0424(純化リファクタリング手順)の4つが直接の土台になる。
**⑤確認事項(罠と検算・安全注意)**: 分解しすぎて、今度は「checkout関数を読んでも全体の流れが1画面に収まらない」という別の可読性の罠に転じることがある(KNOW-F-0400の粒度調整を都度当てはめる)。検算: 分解後のcheckout関数を上から下まで読んだとき、5〜10行程度で全体の流れ(検証→計算→判断→実行)が把握できるかを確認する。
---
## 第三章 テストの型(KNOW-F-0426〜0450)
第二章で純化された関数は、テストが書きやすい形になっている。第三章は「何をどう確かめれば、書いた関数が正しいと言えるか」という25の型を扱う。境界値・回帰・決定性・フィクスチャという4つの柱を軸に、テストダブル(スタブ・モック・フェイク・スパイ)の使い分けまでを含む。
---
### KNOW-F-0426 境界値分析の型(Boundary Value Analysis)
**①名称**: 境界値分析の型
**②構造の型(骨子)**:
```
対象: function canEnter(age) { return age >= 18 }
境界値のテストケース(18という境目の直近を狙う):
age=17 -> false (境界の内側=不成立側)
age=18 -> true (境界そのもの)
age=19 -> true (境界の外側=成立側)
age=0, age=-1, age=200 なども別途(異常値・極端値)
```
**③使いどころ**: 「だいたい動いているから大丈夫」ではなく、条件式の境目(>=, >, <=, <が切り替わる値)を狙い撃ちしてテストしたいとき。バグの多くは境界の1つずれ(off-by-one)に潜む。
**④組合せ例**: KNOW-F-0421(不変条件の型)・KNOW-F-0422(事前条件の型)で明文化した条件の境目は、そのまま境界値分析の対象になる。KNOW-F-0427(同値分割の型)と対で使い、「代表値+境界値」の組合せでテストケース数を絞り込む。
**⑤確認事項(罠と検算・安全注意)**: 「18以上」という条件を「18より大きい」と読み違えて実装してしまう典型的な罠があるため、境界値そのもの(age=18)のテストを必ず含める(境界の1つ内側・外側だけでは見逃す)。検算: canEnter(18)がtrue、canEnter(17)がfalseであることを両方確認する(実測: 境界の両側で期待通り)。
---
### KNOW-F-0427 同値分割の型(Equivalence Partitioning)
**①名称**: 同値分割の型
**②構造の型(骨子)**:
```
対象: function grade(score) {
if (score >= 90) return "A"; if (score >= 70) return "B"; return "C"
}
同値クラス分割:
クラス1: 90〜100 (代表値95) -> "A"
クラス2: 70〜89 (代表値80) -> "B"
クラス3: 0〜69 (代表値50) -> "C"
各クラスから1件ずつ代表値を選べば、クラス内の他の値も同じ結果になると期待できる
```
**③使いどころ**: すべての入力値を1つずつテストするのは現実的でないとき。「同じ結果になるはずの入力のグループ」に分け、各グループから代表値を1つ選んでテストケース数を絞る。
**④組合せ例**: KNOW-F-0426(境界値分析の型)と対で使うのが定石(同値分割で代表値、境界値分析でクラスの境目、の二段構え)。KNOW-F-0437(パラメータ化テストの型)は、複数の同値クラスの代表値をまとめてテストする実装手段になる。
**⑤確認事項(罠と検算・安全注意)**: 同値分割は「クラス内は本当に同じ結果になる」という仮定に依存するため、分割の粒度を誤る(実は途中に別の条件が隠れている)とテストの見逃しにつながる罠がある。検算: 各クラスの代表値だけでなく、クラスの端(境界)もKNOW-F-0426で別途確認し、クラス分けの仮定自体が正しいかを検証する。
---
### KNOW-F-0428 回帰テストの型(Regression Test)
**①名称**: 回帰テストの型
**②構造の型(骨子)**:
```
// バグを直したら、そのバグを再現するテストを追加してから直す
test("issue-42: 空カートで合計を計算すると0になるべき(以前は例外だった)", () => {
const cart = { items: [] }
expect(calcTotal(cart)).toBe(0) // このテストが将来また壊れたら「再発」を検出できる
})
```
**③使いどころ**: 過去に見つかった不具合を修正した直後。修正するだけでなく、その不具合を再現するテストケースを残しておくことで、将来の変更で同じ不具合が復活していないかを機械的に見張れるようにする。
**④組合せ例**: KNOW-F-0475(ポストモーテムの型)の再発防止策の実装形が本型にあたる。KNOW-F-0441(テストピラミッドの型)における「回帰の網」を実際に張る具体的な行為として直接接続する。
**⑤確認事項(罠と検算・安全注意)**: バグ番号やチケット番号だけをテスト名に書き、内容を書かないと、後でそのテストが何を防いでいるのか分からなくなる罠がある(テスト名に「何が起きていたか」を短く書く)。検算: 修正前のコードに一時的に戻し、追加した回帰テストが実際に失敗する(赤くなる)ことを確認してから、修正後のコードで緑になることを確かめる(テスト自体が機能する検算)。
---
### KNOW-F-0429 決定性テストの型(Deterministic Test)
**①名称**: 決定性テストの型(乱数・時刻の固定)
**②構造の型(骨子)**:
```
// 非決定的: 実行するたびに結果が変わりうる
test("有効期限切れ判定", () => {
expect(isExpired(order)).toBe(true) // Date.now()に依存し、実行タイミングで結果が変わる
})
// 決定的: 時刻を固定して注入する(KNOW-F-0412の応用)
test("有効期限切れ判定", () => {
const fixedNow = new Date("2026-07-10T00:00:00Z").getTime()
expect(isExpired(order, fixedNow)).toBe(true) // 常に同じ結果になる
})
```
**③使いどころ**: 「このテスト、たまに落ちる」という現象(フレークテスト、KNOW-F-0445参照)の原因調査、あるいは予防のため最初からテストを書くとき。
**④組合せ例**: KNOW-F-0412(隠れた入力排除の型)がこの型を可能にする土台。KNOW-F-0446(シード固定の型)は、乱数についての決定性テストを実現する具体的な手段として直接対応する。
**⑤確認事項(罠と検算・安全注意)**: テスト対象の関数自体が内部でDate.now()を直接呼んでいると、テスト側でどれだけ時刻を注入しても意味がない(関数自体の引数化=KNOW-F-0412が前提)。検算: 同じテストを異なる時刻(実行環境のタイムゾーンを変えるなど)で複数回実行し、常に同じ結果になることを確認する。
---
### KNOW-F-0430 フィクスチャ設計の型(Test Fixture Design)
**①名称**: フィクスチャ設計の型
**②構造の型(骨子)**:
```
// フィクスチャ: テストで繰り返し使う「基準となるデータ」を関数化する
function makeOrder(overrides = {}) {
return {
id: "order-1", total: 1000, items: [{ id:"i1", price:500, qty:2 }],
...overrides // 一部だけ差し替えられるようにする
}
}
test("割引適用後の合計", () => {
const order = makeOrder({ total: 2000 }) // 必要な部分だけ変えて使う
expect(applyDiscount(order, 0.1).total).toBe(1800)
})
```
**③使いどころ**: 各テストの冒頭で毎回同じような大きなオブジェクトを手で組み立てており、コピー&ペーストが増えて保守しにくくなっているとき。基準データを1箇所の関数にまとめ、差分だけを各テストで指定する。
**④組合せ例**: KNOW-F-0408(早期I/O分離の型)で洗い出した「関数が必要とするデータの形」がそのままフィクスチャの雛形になる。KNOW-F-0437(パラメータ化テストの型)と組み合わせ、フィクスチャの一部だけを変えた複数ケースを効率よく並べられる。
**⑤確認事項(罠と検算・安全注意)**: フィクスチャを共有しすぎると、あるテストのために基準データを変更したら別のテストが意図せず壊れる「フィクスチャの結合」が起きる罠がある(KNOW-F-0442テスト独立性の型で点検する)。検算: makeOrder()で作った2つのオブジェクトが、それぞれ独立したオブジェクト(一方を変更してももう一方に影響しない)であることを確認する。
---
### KNOW-F-0431 スタブの型(Stub)
**①名称**: スタブの型(テストダブル1)
**②構造の型(骨子)**:
```
// 本物: 実際に外部APIへ通信する
// const rate = await fetchExchangeRate() // 遅い・不安定
// スタブ: 決まった値を返すだけの代役
function stubFetchExchangeRate() { return Promise.resolve(150) } // 常に150を返す
test("円換算", async () => {
const rate = await stubFetchExchangeRate()
expect(convert(100, rate)).toBe(15000)
})
```
**③使いどころ**: テスト対象の関数が依存する外部処理(通信・DB)を、決まった値を返すだけの単純な代役に置き換えたいとき。呼び出されたかどうかや呼び出し方は検証せず、値の提供だけを担う。
**④組合せ例**: KNOW-F-0381(コンストラクタ注入の型)によって差し替え口が用意されていることが、スタブを使える前提条件になる。KNOW-F-0432(モックの型)・KNOW-F-0433(フェイクの型)とあわせて「テストダブル」という総称で括られる4種の型の1つ目。
**⑤確認事項(罠と検算・安全注意)**: スタブは「呼ばれたかどうか」を検証しないため、本来呼ばれるべき箇所で呼ばれていなくてもテストが気づかない罠がある(呼び出しの検証が必要ならKNOW-F-0432モックを使う)。検算: stubFetchExchangeRateが常に150を返すこと、およびconvert(100,150)の計算結果が15000という手計算と一致することを確認する。
---
### KNOW-F-0432 モックの型(Mock)
**①名称**: モックの型(テストダブル2)
**②構造の型(骨子)**:
```
function createMockMailer() {
const calls = []
return {
send(to, body) { calls.push({ to, body }) }, // 呼び出しを記録する
calls
}
}
test("注文完了メールが1回だけ送られる", () => {
const mailer = createMockMailer()
placeOrder(order, mailer)
expect(mailer.calls.length).toBe(1) // 呼び出し回数を検証
expect(mailer.calls[0].to).toBe(order.email) // 呼び出し引数を検証
})
```
**③使いどころ**: 「その処理が正しい値で、正しい回数だけ呼ばれたか」自体を検証したいとき(スタブは値を返すだけだが、モックは呼ばれ方を記録・検証する点が異なる)。
**④組合せ例**: KNOW-F-0423(副作用ログ化の型)が使える場面ではモックの代わりに戻り値の確認だけで済むことがあり、両者は用途が近い(可能ならKNOW-F-0423を優先し、外部ライブラリなど戻り値化できない箇所にモックを使う、という優先順位が実務的)。
**⑤確認事項(罠と検算・安全注意)**: モックで内部の実装詳細(呼び出し回数・順序)まで厳密に検証しすぎると、実装を少し変えただけ(結果は同じ)でテストが壊れる「壊れやすいテスト」になる罠がある(検証は本当に必要な範囲に絞る)。検算: mailer.callsの長さが1であることに加え、placeOrderを2回呼んだ場合はcallsの長さが2になる(呼び出しごとに正しく記録される)ことを確認する。
---
### KNOW-F-0433 フェイクの型(Fake)
**①名称**: フェイクの型(テストダブル3)
**②構造の型(骨子)**:
```
// フェイク: 本物と同じ契約を満たす、簡易だが動作する実装
function createFakeRepository() {
const store = new Map()
return {
save(item) { store.set(item.id, item) }, // 本物のDBの代わりにメモリを使う
findById(id) { return store.get(id) || null } // 実際に動く(単なる記録係ではない)
}
}
```
**③使いどころ**: DBのような重い依存を、実際に動作する軽量な代替実装(メモリ内実装など)に置き換えたいとき。スタブが「決まった値を返すだけ」なのに対し、フェイクは「保存したものを取り出せる」というように、簡易でも本物同様の振る舞いをする。
**④組合せ例**: KNOW-F-0396(モジュール契約の型)で定義した契約をそのまま満たす実装として設計するのが定石。KNOW-F-0447(契約テストの型)で、フェイクが本物と同じ契約を満たしているかを機械的に検証できる。
**⑤確認事項(罠と検算・安全注意)**: フェイクが本物の重要な制約(例: 同じIDのsaveは上書きでなくエラーにすべき、など)を再現していないと、フェイクでは通るのに本物では失敗するテストの見逃しが起きる罠がある。検算: 同じitem.idで2回saveした場合の挙動が、本物のDBの仕様(上書きかエラーか)と一致するように実装されているかを突き合わせる。
---
### KNOW-F-0434 スパイの型(Spy)
**①名称**: スパイの型(テストダブル4)
**②構造の型(骨子)**:
```
// スパイ: 本物の実装を呼びながら、呼び出しの記録も取る
function createSpy(originalFn) {
const calls = []
const spy = (...args) => { calls.push(args); return originalFn(...args) } // 本物も実行
spy.calls = calls
return spy
}
const spiedCalc = createSpy(calcTotal)
spiedCalc(order) // 本物のcalcTotalが実際に動きつつ、呼び出しも記録される
```
**③使いどころ**: 本物の処理は実際に動かしたいが、「何回・どんな引数で呼ばれたか」も併せて確認したいとき。モックが「本物の代わりに動く偽物」なのに対し、スパイは「本物を動かしながら記録もする」点が異なる。
**④組合せ例**: KNOW-F-0432(モックの型)と対比される型で、副作用が軽い(実行しても問題ない)処理にはスパイ、副作用が重い(実行すると危険・遅い)処理にはモックやスタブ、という使い分けが定石。
**⑤確認事項(罠と検算・安全注意)**: 副作用の重い処理(実際の送金など)にスパイを使うと、記録は取れても本物の処理が実行されてしまい危険である(スパイは副作用が軽い/繰り返しても安全な処理にのみ使う)。検算: spiedCalc(order)を呼んだ後、spiedCalc.calls[0][0]がorderと一致し、かつ戻り値が本物のcalcTotal(order)と一致することを確認する。
---
### KNOW-F-0435 AAAパターンの型(Arrange-Act-Assert)
**①名称**: AAAパターンの型(Arrange-Act-Assert)
**②構造の型(骨子)**:
```
test("在庫を減らすと数量が減る", () => {
// Arrange(準備)
const stock = 10
// Act(実行)
const result = withdrawStock(stock, 3)
// Assert(検証)
expect(result).toBe(7)
})
```
**③使いどころ**: テストコードが何を準備し、何を実行し、何を確認しているのかが読み取りにくいとき。3段階に分けてコメントまたは空行で区切るだけで、テストの意図が一目で分かるようになる。
**④組合せ例**: KNOW-F-0430(フィクスチャ設計の型)は主にArrange段階を担当する部品。KNOW-F-0436(Given-When-Thenの型)はAAAパターンの言い換え(業務寄りの言葉づかい)にあたり、どちらを採用してもよい。
**⑤確認事項(罠と検算・安全注意)**: 1つのテストの中にActとAssertを何度も繰り返して書く(複数の検証を1テストに詰め込む)と、どのAssertで失敗したのかが分かりにくくなる罠がある(1テスト1関心事を目安にする)。検算: 各テストのAssert(expect)の呼び出し回数を数え、1つのテストが多数の無関係な事柄を同時に検証していないかを確認する。
---
### KNOW-F-0436 Given-When-Thenの型(Given-When-Then)
**①名称**: Given-When-Thenの型
**②構造の型(骨子)**:
```
test("会員は割引が適用される", () => {
// Given: 会員のユーザーがいる
const user = { membership: "gold" }
// When: 1000円の商品を購入する
const price = calcPrice(1000, user)
// Then: 900円(10%引き)になる
expect(price).toBe(900)
})
```
**③使いどころ**: 業務の関係者(非エンジニアを含む)にもテストの意図が伝わるようにしたいとき。AAAパターン(KNOW-F-0435)と構造は同じだが、「状況→操作→期待される結果」という業務的な語り口を採用する。
**④組合せ例**: KNOW-F-0435(AAAパターンの型)の言い換え版として直接対応する(ArrangeがGiven、ActがWhen、AssertがThen)。振る舞い駆動開発(BDD)と呼ばれる進め方でよく使われる型として、脊椎BOOK-0358f側の実務接続に記載する。
**⑤確認事項(罠と検算・安全注意)**: Given節に無関係な準備を詰め込みすぎると、Whenで何が起きているのか焦点がぼやける罠がある(Givenは「その検証に必要な最小限」に絞る)。検算: Given節に書かれたデータ(この例ではuser.membership)が、Then節の検証(価格が900になる根拠)に実際に使われているかを確認する。
---
### KNOW-F-0437 パラメータ化テストの型(Parameterized Test)
**①名称**: パラメータ化テストの型
**②構造の型(骨子)**:
```
const cases = [
{ score: 95, expected: "A" },
{ score: 80, expected: "B" },
{ score: 50, expected: "C" },
{ score: 90, expected: "A" }, // 境界値も混ぜる(KNOW-F-0426)
]
for (const { score, expected } of cases) {
test(`score=${score} なら ${expected}`, () => {
expect(grade(score)).toBe(expected)
})
}
```
**③使いどころ**: KNOW-F-0427(同値分割の型)・KNOW-F-0426(境界値分析の型)で洗い出した複数のテストケースを、ほぼ同じ形のtest文を何度もコピー&ペーストせずにまとめて書きたいとき。
**④組合せ例**: KNOW-F-0426(境界値分析の型)・KNOW-F-0427(同値分割の型)で選んだケースの一覧を、そのままcases配列に落とし込む直接の実装手段。
**⑤確認事項(罠と検算・安全注意)**: ケースの一覧(cases)を増やしすぎると、1件が失敗したときにテスト名だけでどのケースか分かりにくくなる罠がある(テスト名に入力値を含めるなど、識別しやすい命名を徹底する=KNOW-F-0443参照)。検算: casesの件数と、実際に実行されたテストの件数(テストランナーの出力)が一致するかを確認する。
---
### KNOW-F-0438 プロパティベーステストの型(Property-Based Testing)
**①名称**: プロパティベーステストの型
**②構造の型(骨子)**:
```
// 個別の入出力ペアでなく「常に成り立つべき性質」を検証する
function testProperty(genInput, property, trials = 100) {
for (let i = 0; i < trials; i++) {
const input = genInput() // ランダムな入力を大量に生成
if (!property(input)) throw new Error(`性質が破れた入力: ${JSON.stringify(input)}`)
}
}
// 性質の例: sortした配列は常に長さが元と同じ
testProperty(
() => Array.from({length: 10}, () => Math.floor(Math.random()*100)),
(arr) => sortArray(arr).length === arr.length
)
```
**③使いどころ**: 「この入力とこの出力が一致する」という個別のテスト(例示ベース)では網羅しきれない、大量のランダムな入力に対して常に成り立つべき性質(例: ソート後も要素数は変わらない、ソート結果は昇順に並ぶ)を検証したいとき。
**④組合せ例**: KNOW-F-0421(不変条件の型)で明文化した性質が、そのままプロパティベーステストの検証対象(property関数)になる。KNOW-F-0446(シード固定の型)と組み合わせ、ランダム生成を決定的にすることで失敗時の再現性を確保する。
**⑤確認事項(罠と検算・安全注意)**: ランダムな入力で見つかった失敗を再現できないと、修正の確認ができない重大な罠がある(乱数のシードを必ず記録・固定できるようにする=KNOW-F-0446)。検算: testPropertyが失敗したとき、記録されたシードで再実行すると同じ入力(同じ失敗)が再現されることを確認する。
---
### KNOW-F-0439 ゴールデンマスターテストの型(Golden Master Test)
**①名称**: ゴールデンマスターテストの型
**②構造の型(骨子)**:
```
// 手順:
// 1. 現行の(仕様が明確でない)出力を「正解」として記録する(golden.json)
// 2. 以降の変更で、出力がgolden.jsonと一致するかを機械的に比較する
const golden = require("./golden.json")
test("レポート出力が既存仕様から変わっていない", () => {
const actual = generateReport(fixtureInput)
expect(actual).toEqual(golden) // 仕様書がなくても「今までと同じ」は保証できる
})
```
**③使いどころ**: 仕様書が失われた/曖昧なレガシーコードを、仕様を明文化するテスト(KNOW-F-0436のGiven-When-Then)を書く前に、まず「今の挙動を壊していないか」だけでも機械的に守りたいとき。
**④組合せ例**: KNOW-F-0397(ストラングラー分離の型)で古いモジュールを段階的に置き換える際の安全網として直接組み合わされる。KNOW-F-0440(スナップショットテストの型)は、ゴールデンマスターの記録・比較の仕組みをテストフレームワークが自動化した現代的な実装形にあたる。
**⑤確認事項(罠と検算・安全注意)**: golden.jsonに「たまたま今バグっている出力」まで正解として固定してしまう罠がある(記録前に、その出力が本当に正しいかを一度は人間が確認する)。検算: golden.jsonを意図的に1文字改変し、テストが確実に失敗する(比較が機能している)ことを確認してから元に戻す。
---
### KNOW-F-0440 スナップショットテストの型(Snapshot Test)
**①名称**: スナップショットテストの型
**②構造の型(骨子)**:
```
test("注文カードのレンダリング", () => {
const output = renderOrderCard(order)
expect(output).toMatchSnapshot() // 初回実行時にスナップショットを自動保存
// 2回目以降は保存済みスナップショットと比較し、差分があれば失敗として報告する
})
```
**③使いどころ**: 画面表示やレポートのような「複雑な構造を持つ出力」を、1つ1つの項目を手でexpectするのではなく丸ごと保存し、変化があったときだけ差分を確認したいとき。
**④組合せ例**: KNOW-F-0439(ゴールデンマスターテストの型)の考え方をテストフレームワークが自動化した実装形として直接対応する。KNOW-F-0428(回帰テストの型)の一種であり、UI・出力形式の回帰を効率よく検出する手段になる。
**⑤確認事項(罠と検算・安全注意)**: 差分が出るたびに内容を確認せず機械的に「スナップショット更新」を承認し続けると、意図しない変化(バグ)まで正解として上書きしてしまう重大な罠がある(更新は必ず差分を人間が読んでから承認する)。検算: 意図的な仕様変更を1件加え、スナップショットの差分表示にその変更内容が正しく現れることを確認する。
---
### KNOW-F-0441 テストピラミッドの型(Test Pyramid)
**①名称**: テストピラミッドの型
**②構造の型(骨子)**:
```
/\
/E2E\ 少数: 画面操作を含む結合的なテスト(遅い・壊れやすいが安心感が高い)
/------\
/結合テスト\ 中程度: 複数モジュールをまたぐテスト
/----------\
/ 単体テスト \ 多数: 純関数中心の高速なテスト(第二章の型が対象になる)
/--------------\
```
**③使いどころ**: プロジェクト全体のテストの比率(単体・結合・E2Eがそれぞれ何件あるか)を見直したいとき。単体テストを土台に多く配置し、遅く壊れやすいE2Eテストは要所に絞る、という配分の指針。
**④組合せ例**: 第二章の純化の型(特にKNOW-F-0401純関数)によって作られた関数群が、このピラミッドの土台(単体テスト)を安く大量に書ける対象になる。KNOW-F-0447(契約テストの型)は結合テストの層を効率化する型として中段に位置づけられる。
**⑤確認事項(罠と検算・安全注意)**: E2Eテストばかりが増える「逆ピラミッド」になると、テスト全体の実行時間が伸び、失敗原因の特定(KNOW-F-0451二分探索デバッグ)にも時間がかかる罠がある。検算: プロジェクトのテストを種類別に数え、単体テストの件数が結合・E2Eテストの件数より明らかに多いか(ピラミッド形になっているか)を確認する。
---
### KNOW-F-0442 テスト独立性の型(Test Independence)
**①名称**: テスト独立性の型
**②構造の型(骨子)**:
```
// 依存あり(順序に依存する): テストAがグローバルなカウンタを増やし、テストBがそれに依存する
let sharedCounter = 0
test("A", () => { sharedCounter++; expect(sharedCounter).toBe(1) }) // Bより先に実行される前提
test("B", () => { expect(sharedCounter).toBe(1) }) // Aの後でしか成立しない
// 独立(順序に依存しない): 各テストが自前で状態を用意する
test("A", () => { const counter = 0; expect(counter + 1).toBe(1) })
test("B", () => { const counter = 0; expect(counter + 1).toBe(1) })
```
**③使いどころ**: テストの実行順序を変えたり、1件だけを個別に実行したりすると失敗するテストがあるとき。KNOW-F-0416(段落順序独立の型)をテストコード自体に適用したもの。
**④組合せ例**: KNOW-F-0409(グローバル状態排除の型)がテスト独立性の前提条件になる。KNOW-F-0430(フィクスチャ設計の型)で、各テストが自前のデータを持てるようにすることが独立性の具体的な実現手段。
**⑤確認事項(罠と検算・安全注意)**: テストの実行順序をたまたま固定順にしているテストランナーの設定に依存すると、CI環境で順序が変わった際に突然失敗する罠がある。検算: テスト全体をランダムな順序で実行するオプションがあれば有効化し、順序を変えても全件成功することを確認する。
---
### KNOW-F-0443 テスト命名規約の型(Test Naming Convention)
**①名称**: テスト命名規約の型
**②構造の型(骨子)**:
```
規約の型(骨子): 「対象_条件_期待結果」を含める
悪い例: test("test1", () => {...})
悪い例: test("動作確認", () => {...})
良い例: test("calcTotal_空カート_0を返す", () => {...})
良い例: test("withdrawStock_残高不足_エラーを投げる", () => {...})
```
**③使いどころ**: テストが失敗したとき、テスト名だけを見てどんな状況で何が期待されていたかが分かるようにしたいとき。エラーログの一覧を見ただけで優先度判断ができるようになる。
**④組合せ例**: KNOW-F-0437(パラメータ化テストの型)で複数ケースを回す際、テスト名に入力値を埋め込む書き方(テンプレート文字列の活用)と直接組み合わさる。KNOW-F-0474(バグ票記法の型)とも語彙が近く、両方とも「後で読む人のための最小限の文脈」を名前・記録に残す考え方を共有する。
**⑤確認事項(罠と検算・安全注意)**: 命名規約を細かくしすぎると名前が長大になり、かえって読みにくくなる罠がある(対象・条件・期待結果の3要素を簡潔な語で表す訓練が必要)。検算: テスト結果一覧(失敗したテスト名だけの羅列)を見て、コードを読まずに何が壊れたかの見当がつくかを確認する。
---
### KNOW-F-0444 カバレッジ計測と罠の型(Code Coverage)
**①名称**: カバレッジ計測と罠の型
**②構造の型(骨子)**:
```
カバレッジ計測が示す情報(骨子):
行カバレッジ: 全何行中、何行がテストで実行されたか
分岐カバレッジ: if文の各分岐(true側/false側)がそれぞれ実行されたか
罠: 行カバレッジ100%でも、期待値をexpectで確認していなければ
「実行はしたが何も検証していない」テストになりうる
```
**③使いどころ**: テストがコードのどの部分を通過し、どの部分を1度も通過していないかを機械的に洗い出したいとき。未到達の部分は、少なくともまだテストされていないことが確定する。
**④組合せ例**: KNOW-F-0427(同値分割の型)・KNOW-F-0426(境界値分析の型)でケースを設計した後、カバレッジ計測で「設計時に見落とした分岐」を事後的に発見する補完的な使い方が実務的。KNOW-F-0448(ミューテーションテストの型)は、カバレッジだけでは分からない「検証の中身の弱さ」まで踏み込んで測る、より厳しい型として接続する。
**⑤確認事項(罠と検算・安全注意)**: 「カバレッジ100%を目指す」ことを目的化すると、expectのない空虚なテストで数字だけを稼ぐ本末転倒(グッドハートの法則)が起きる罠がある。カバレッジは目安であり、目標そのものにしないという誠実な扱いが必要。検算: カバレッジレポートで100%と出た行のテストコードを実際に読み、対応するexpect文が存在するかを人手で1件は抜き取り確認する。
---
### KNOW-F-0445 フレークテスト対策の型(Flaky Test Countermeasures)
**①名称**: フレークテスト対策の型
**②構造の型(骨子)**:
```
フレーク(不安定なテスト)の主な原因と対策の対応表:
| 原因 | 対策 |
|-------------------------------|-------------------------------------|
| 時刻・乱数への依存 | KNOW-F-0412で引数化、KNOW-F-0429 |
| テスト間の状態共有 | KNOW-F-0442テスト独立性 |
| 非同期処理の待ち方が不十分 | 固定時間sleepでなく完了通知を待つ |
| 外部ネットワークへの実通信 | KNOW-F-0431/0433で切り離す |
```
**③使いどころ**: 「同じコードなのに、テストが成功したり失敗したりする」現象に遭遇したとき。原因を推測でなく、上表のような既知の原因カテゴリに沿って1つずつ切り分ける。
**④組合せ例**: 本型は第二章(純化の型)と第三章前半(境界値・独立性)のほぼ全ての型が「フレークを予防する」という共通の効能を持つことを整理し直したもの。KNOW-F-0469(競合状態デバッグの型)は、非同期処理起因のフレークを深掘りする際の直接の後続の型になる。
**⑤確認事項(罠と検算・安全注意)**: フレークテストを「たまに落ちるだけだから」と無視し続けると、本当に重要な失敗まで見過ごす「オオカミ少年」状態になる罠がある(フレークは無視せず必ず原因を特定して直すか、直せないなら理由を明記して隔離する)。検算: 疑わしいテストを連続100回実行し、失敗する頻度と、失敗時のログに共通するパターン(特定の時刻帯、特定の実行順序など)がないかを記録する。
---
### KNOW-F-0446 シード固定の型(Seed Fixing)
**①名称**: シード固定の型
**②構造の型(骨子)**:
```
// 固定されていない乱数: 毎回結果が変わる
function shuffle(arr) { return arr.sort(() => Math.random() - 0.5) }
// シード固定: 同じシードなら同じ乱数列を再現する疑似乱数生成器を使う
function seededRandom(seed) {
let s = seed
return () => { s = (s * 9301 + 49297) % 233280; return s / 233280 }
}
function shuffle(arr, seed) {
const rand = seededRandom(seed)
return arr.sort(() => rand() - 0.5)
}
```
**③使いどころ**: KNOW-F-0438(プロパティベーステストの型)やゲーム・シミュレーションのように乱数を使う処理を、テストのたびに再現可能にしたいとき。
**④組合せ例**: KNOW-F-0412(隠れた入力排除の型)の乱数版そのもの(Math.random()を直接呼ばずシードを引数化する)。KNOW-F-0438(プロパティベーステストの型)の失敗再現性を担保する直接の実装手段。
**⑤確認事項(罠と検算・安全注意)**: 「シードを固定した」つもりが、コードの一部でまだMath.random()を直接呼んでいる箇所が残っていると、再現性が部分的に失われる罠がある(grepで全箇所を洗い出す)。検算: 同じseed値でshuffleを2回呼び、結果の配列が完全に一致することを確認する(実測: 同一seedで同一結果)。
---
### KNOW-F-0447 契約テストの型(Consumer-Driven Contract Test)
**①名称**: 契約テストの型(Consumer-Driven Contract)
**②構造の型(骨子)**:
```
// 契約(KNOW-F-0396)を、実装が本当に満たしているかを機械的に検証する
function verifyRepositoryContract(repository) {
test("save後にfindByIdで取得できる", async () => {
await repository.save({ id: "x", name: "test" })
expect(await repository.findById("x")).toEqual({ id: "x", name: "test" })
})
test("存在しないIDはnullを返す(例外にしない)", async () => {
expect(await repository.findById("nope")).toBeNull()
})
}
// 同じ契約テストを、本物にもフェイクにも両方適用する
verifyRepositoryContract(mysqlRepository)
verifyRepositoryContract(fakeRepository)
```
**③使いどころ**: KNOW-F-0433(フェイクの型)で作った代替実装が、本物と同じ契約を本当に満たしているかを保証したいとき。同じテスト関数を本物・偽物の両方に適用することで、乖離を防ぐ。
**④組合せ例**: KNOW-F-0396(モジュール契約の型)で書いた契約の内容を、そのままテストコードに落とし込む直接の実装形。KNOW-F-0433(フェイクの型)の確認事項に書いた「本物の制約を再現できているか」を機械的に保証する後段の型として対になる。
**⑤確認事項(罠と検算・安全注意)**: 契約テストを本物(実DB)に対して実行する際、テストデータが実運用データと混ざらないよう、専用の隔離された環境で行う必要がある(安全上の注意)。検算: verifyRepositoryContractをmysqlRepositoryとfakeRepositoryの両方に適用し、両方とも全項目が成功することを確認する。
---
### KNOW-F-0448 ミューテーションテストの型(Mutation Testing)
**①名称**: ミューテーションテストの型
**②構造の型(骨子)**:
```
手順(骨子):
1. テスト対象のコードに意図的に小さなバグ(ミュータント)を注入する
例: if (score >= 90) を if (score > 90) に変える
2. 既存のテスト一式を実行する
3. テストが失敗すれば「そのバグを検出できるテストがある」と分かる(合格)
4. テストが成功してしまえば「そのバグを見逃すテスト」と分かる(要改善)
```
**③使いどころ**: KNOW-F-0444(カバレッジ計測の型)で100%と出ていても、テストの検証内容(expect)が弱くバグを検出できない可能性が拭えないとき。カバレッジより一段厳しい観点でテストの実効性を測る。
**④組合せ例**: KNOW-F-0444(カバレッジ計測と罠の型)の確認事項で挙げた「カバレッジだけでは検証の弱さが分からない」という限界を、本型が直接補う関係にある。KNOW-F-0426(境界値分析の型)で見つけた境界のミュータント(>=を>に変えるなど)は特に検出されやすい設計にすべき対象である。
**⑤確認事項(罠と検算・安全注意)**: ミューテーションテストは対象コード全体に対して大量の変異と再テストを行うため計算コストが高く、プロジェクト全体に日常的に適用すると実行時間が膨らむ罠がある(重要なモジュールに絞って定期的に実行する運用が現実的)。検算: 既知のミュータント(if条件を1文字変える)を手動で1件仕込み、既存テストがそれを検出して失敗することを確認する。
---
### KNOW-F-0449 TDD赤緑リファクタの型(Red-Green-Refactor)
**①名称**: TDD赤緑リファクタの型
**②構造の型(骨子)**:
```
サイクル(骨子):
赤(Red): まだ存在しない機能のテストを先に書き、失敗させる
緑(Green): そのテストを通す最小限のコードだけを書く(美しさは後回し)
リファクタ: テストが通ったままの状態を保ちながら、コードを整理する
→ 赤に戻って次の機能のテストを書く、を繰り返す
```
**③使いどころ**: 実装してからテストを書くと「テストが通るように書かれた後付けのテスト」になりがちなとき。先にテスト(仕様)を書くことで、実装の前に「何をもって完成とするか」を明確にする。
**④組合せ例**: KNOW-F-0424(純化リファクタリング手順の型)の「リファクタ」段階と直接対応する(緑の状態=テストが通っている状態を保ちながら整理するという条件が共通)。KNOW-F-0428(回帰テストの型)は、TDDサイクルの副産物として自然に蓄積されるテスト資産の後続の使い道にあたる。
**⑤確認事項(罠と検算・安全注意)**: 「赤を確認せずに緑から始める」(先に実装してからテストを書く)と、テスト自体にバグがあって常に成功してしまう(意味のないテスト)場合を検出できない罠がある。検算: テストを書いた直後、実装より先に必ず1回実行し、期待通り失敗する(赤である)ことを目視で確認してから実装に進む。
---
### KNOW-F-0450 最小再現テストケースの型(Minimal Failing Test Case)
**①名称**: 最小再現テストケースの型
**②構造の型(骨子)**:
```
手順(骨子):
1. 失敗する大きなテスト(複雑な入力)を特定する
2. 入力の要素を1つずつ削り、それでも失敗するかを確認する
3. これ以上削ると失敗しなくなる、という最小の入力まで絞り込む
例: 10件のアイテムを含むカートで失敗 → 1件だけのカートでも同じ失敗が起きるか試す
→ 起きるなら、その1件の中身をさらに単純化していく
```
**③使いどころ**: テストが失敗したとき、失敗の原因がどの部分にあるのか大きな入力のままでは特定しにくいとき。第四章のKNOW-F-0452(最小再現の型)をテストケースの設計そのものに適用したもの。
**④組合せ例**: KNOW-F-0452(最小再現の型、デバッグ全般に適用)の直接の先駆けであり、第三章から第四章への橋渡しとなる型。KNOW-F-0426(境界値分析の型)と組み合わせ、絞り込んだ最小ケースがちょうど境界値と一致することが多い。
**⑤確認事項(罠と検算・安全注意)**: 削りすぎて、元の失敗の原因とは別の(偶然似た症状の)問題に行き着いてしまう罠がある(削るたびに必ず「まだ同じ失敗か」を確認しながら進める)。検算: 最小化した入力で失敗を再現させたあと、削った要素を1つ戻すと(意図通りなら)失敗しなくなることを逆方向にも確認する。
---
## 第四章 デバッグの型(KNOW-F-0451〜0475)
どれほど注意深く書いても不具合は起きる。第四章は「動かなくなったとき、原因へ最短距離でたどり着く」ための25の探索の型を扱う。中心にあるのは二分探索(範囲を半分に絞り込み続ける)という考え方であり、ログ・再現・仮説検証はすべてこの考え方の変奏である。
---
### KNOW-F-0451 二分探索デバッグの型(Binary Search Debugging)
**①名称**: 二分探索デバッグの型
**②構造の型(骨子)**:
```
手順(骨子):
1. 「正常に動いていた時点」と「壊れている時点」を両端として特定する
2. その中間の時点(コミット・バージョン・処理段階)を1つ選んで確認する
3. 中間が正常なら「壊れている側」を新しい範囲にする
中間が異常なら「正常な側」を新しい範囲にする
4. 範囲が1つに絞れるまで2〜3を繰り返す(log2(N)回で絞り込める)
```
**③使いどころ**: 「先週は動いていたのに今日は動かない」というように、正常と異常の間に多数の変更(コミット)が挟まっているとき。1件ずつ順番に調べるより、中間を確認して範囲を半分にする方が桁違いに速い。
**④組合せ例**: KNOW-F-0472(バージョン二分探索の型)は本型をバージョン管理システムの変更履歴に対して機械化した具体形。KNOW-F-0465(変数固定切り分けの型)は、同じ二分探索の考え方を「時間軸」でなく「値・条件の軸」に適用したもの。
**⑤確認事項(罠と検算・安全注意)**: 「中間が正常/異常」の判定基準があいまいだと、絞り込みそのものが誤った方向に進む罠がある(判定基準は最初に明確な合否条件として書き出しておく)。検算: 絞り込みが完了した時点(1件の変更)を実際に戻す/戻さないの両方で試し、その1件だけが原因であることを再現テストで確認する。
---
### KNOW-F-0452 最小再現の型(Minimal Reproducible Example)
**①名称**: 最小再現の型(Minimal Reproducible Example)
**②構造の型(骨子)**:
```
条件: 「これ以上単純化すると症状が消える」という最小限の再現手順を作る
良い再現手順の例:
1. calcTotal({items: []}) を呼ぶ
2. 例外 "Cannot read property 'price' of undefined" が発生する
(アプリ全体を起動しなくても、この2行だけで再現できる)
```
**③使いどころ**: バグ報告が「アプリを使っていたら落ちた」のように曖昧なとき、または自分でバグを追う際に無関係な要素(画面操作・大量データ)に埋もれて原因が見えないとき。
**④組合せ例**: KNOW-F-0450(最小再現テストケースの型)のテスト版に対する、デバッグ全般への一般化。KNOW-F-0467(再現手順書の型)は、絞り込んだ最小再現をチームで共有可能な記録として残す後段の型になる。
**⑤確認事項(罠と検算・安全注意)**: 最小化を焦るあまり、環境依存の要因(特定のOS・特定のデータ)まで削ってしまい再現しなくなる罠がある(削った要素は記録しておき、再現しなくなったら1つ戻す)。検算: 最小化した再現手順を、元の複雑な状況を知らない別の人に渡し、その手順書だけで同じ症状を再現できるかを確認する。
---
### KNOW-F-0453 ログレベル設計の型(Log Level Design)
**①名称**: ログレベル設計の型
**②構造の型(骨子)**:
```
レベルの型(骨子・上ほど重大):
ERROR: 処理が続行できない異常(即座に対応が要る)
WARN: 異常ではないが注意すべき状態(想定外だが処理は継続できた)
INFO: 正常な処理の節目(注文が完了した、など)
DEBUG: 開発時にのみ必要な詳細情報(変数の中身など)
log.error("決済失敗", { orderId, reason })
log.info("注文完了", { orderId, total })
```
**③使いどころ**: ログが多すぎて重要な異常が埋もれてしまう、あるいは逆にログが少なすぎて何も分からないとき。レベルを分けることで、普段はINFO以上だけを表示し、調査時だけDEBUGまで表示する、という運用ができる。
**④組合せ例**: KNOW-F-0454(構造化ログの型)は、本型で決めたレベルをさらに機械的に処理しやすい形式にする直接の後続の型。KNOW-F-0475(ポストモーテムの型)の調査では、ERROR/WARNログの一覧が最初に確認する情報源になる。
**⑤確認事項(罠と検算・安全注意)**: すべてをERRORにしてしまうと、本当に重大な問題が大量の誤報に埋もれる「オオカミ少年」化の罠がある(KNOW-F-0445フレークテスト対策と同種の問題)。個人情報をログに含めない安全注意も必須(氏名・カード番号などを平文でログ出力しない)。検算: 実際の運用ログを1日分抜き出し、ERRORレベルのログがすべて「即座の対応が必要」な内容になっているかを人手で確認する。
---
### KNOW-F-0454 構造化ログの型(Structured Logging)
**①名称**: 構造化ログの型
**②構造の型(骨子)**:
```
// 非構造化: 文字列を目で読むしかない
console.log("注文" + orderId + "が完了、合計" + total + "円")
// 構造化: 機械的に検索・集計できる形(キーと値の組)で出力する
log.info("order.completed", { orderId, total, userId })
```
**③使いどころ**: ログの量が増え、目視で「特定の注文IDに関するログだけ」を追うのが困難になったとき。キーと値の組で出力しておけば、後から機械的に絞り込み・集計ができる。
**④組合せ例**: KNOW-F-0453(ログレベル設計の型)と組み合わせて使うのが通常。KNOW-F-0455(相関IDの型)は、構造化ログの中に「同じ処理の流れを結びつけるID」を含める発展形として直接接続する。
**⑤確認事項(罠と検算・安全注意)**: キー名(この例ではorderId, total, userId)がログ出力箇所ごとにばらばら(orderIdとorder_idが混在するなど)だと、機械的な検索・集計が破綻する罠がある(キー名の規約を統一する)。検算: ログ出力箇所を横断してgrepし、同じ意味のキーが常に同じ綴りで使われているかを確認する。
---
### KNOW-F-0455 相関ID(トレースID)の型(Correlation ID)
**①名称**: 相関ID(トレースID)の型
**②構造の型(骨子)**:
```
function handleRequest(req) {
const traceId = generateId() // このリクエスト固有のID
log.info("request.start", { traceId, path: req.path })
const result = processOrder(req.body, traceId) // 下流の関数にも渡す
log.info("request.end", { traceId, status: result.status })
}
function processOrder(body, traceId) {
log.info("order.validate", { traceId }) // 同じtraceIdで紐づく
}
```
**③使いどころ**: 1つのリクエストが複数の関数・複数のモジュール(場合によっては複数のサーバー)を経由する処理で、それらのログを「同じ1件の処理」として結びつけて追いたいとき。
**④組合せ例**: KNOW-F-0454(構造化ログの型)の直接の応用。KNOW-F-0473(環境差分切り分けの型)で複数環境のログを比較する際も、相関IDがあれば同じ処理単位で正確に突き合わせられる。
**⑤確認事項(罠と検算・安全注意)**: 相関IDを一部の関数だけに渡し忘れると、その先のログだけが孤立して追跡できなくなる罠がある(すべての下流呼び出しに一貫して伝播させる規約が必要)。検算: 1件のリクエストについて出力された全ログをtraceIdで検索し、開始(request.start)から終了(request.end)まで途切れなく1本の流れとして追えることを確認する。
---
### KNOW-F-0456 スタックトレース読解の型(Stack Trace Reading)
**①名称**: スタックトレース読解の型
**②構造の型(骨子)**:
```
読解の順序(骨子・上から下ではなく最上段から読む):
TypeError: Cannot read properties of undefined (reading 'price')
at calcTotal (order.js:12:34) <- 直接の発生箇所(まずここを見る)
at checkout (checkout.js:5:10) <- 呼び出し元(次にここ)
at handleRequest (server.js:20:3) <- さらに呼び出し元
```
**③使いどころ**: 例外が発生したとき、エラーメッセージだけでなく、その下に続く「呼び出しの連鎖(スタックトレース)」を読み、どの経路でその関数にたどり着いたかを把握したいとき。
**④組合せ例**: KNOW-F-0452(最小再現の型)の第一歩として、スタックトレースの最上段(直接の発生箇所)が最小再現を作るための出発点になることが多い。KNOW-F-0459(状態ダンプの型)と組み合わせ、発生箇所での変数の中身も併せて確認する。
**⑤確認事項(罠と検算・安全注意)**: 圧縮・変換されたコード(ビルド後のコード)のスタックトレースは、元のソースコードの行番号と一致しない場合がある(ソースマップの整備が必要)。検算: スタックトレースの最上段が指す行(order.js:12)を実際に開き、そこにpriceへのアクセスが本当に存在するかを目視で確認する。
---
### KNOW-F-0457 ラバーダック法の型(Rubber Duck Debugging)
**①名称**: ラバーダック法の型
**②構造の型(骨子)**:
```
手順(骨子):
1. コード(または処理の流れ)を、一行ずつ声に出して(あるいは書いて)説明する
相手はアヒルの人形でも、無関係な同僚でも構わない
2. 「ここでaが5になって、次にbに渡されて…」と言葉にしていく
3. 説明の途中で「あれ、ここおかしいな」と自分で気づくことが多い
```
**③使いどころ**: 長時間コードを見つめても原因が分からないとき。他人に説明する(あるいは説明するふりをする)行為そのものが、無意識の思い込みを言語化して崩す効果を持つ。
**④組合せ例**: KNOW-F-0466(仮説駆動デバッグの型)の準備運動として使われることが多い(説明する過程で仮説が自然に生まれる)。KNOW-F-0463(二分コメントアウトの型)のように手を動かす前に、まず頭の中を言語化する軽量な最初の一手として位置づけられる。
**⑤確認事項(罠と検算・安全注意)**: 説明が「動作の要約」で終わり、「なぜそう書いたか」まで踏み込めないと効果が薄い罠がある(各行について「なぜ」を問う姿勢が重要)。検算: 説明を最後まで終えても原因が見つからなかった場合、説明した範囲そのものが間違っている(本当の原因は別の箇所にある)可能性を疑い、KNOW-F-0451(二分探索デバッグ)へ切り替える。
---
### KNOW-F-0458 差分デバッグの型(Diff Debugging)
**①名称**: 差分デバッグの型(既知の良い状態との比較)
**②構造の型(骨子)**:
```
比較の骨子:
正常なケースの入力: { items: [{price:100, qty:1}] } -> 正しく動く
異常なケースの入力: { items: [{price:100, qty:1, tag:null}] } -> 落ちる
差分: tag:null が追加されている点だけが違う
→ tag:nullを扱う箇所にバグがあると強く推測できる
```
**③使いどころ**: 「同じような処理のはずなのに、片方は動いて片方は動かない」という2つのケースが手元にあるとき。両者の入力・環境・コードを1つずつ突き合わせ、違いだけを洗い出す。
**④組合せ例**: KNOW-F-0451(二分探索デバッグの型)が「時間軸」の差分を扱うのに対し、本型は「同時に存在する2つの状態」の差分を扱う姉妹型。KNOW-F-0473(環境差分切り分けの型)は、本型を「実行環境」という軸に特化させた具体形。
**⑤確認事項(罠と検算・安全注意)**: 差分が複数箇所に渡っていると、どの差分が本当の原因かを見誤る罠がある(差分を1つずつ埋めて(正常な値に戻して)、どの時点で症状が消えるかを確認する=KNOW-F-0465との併用が有効)。検算: 異常なケースのtagだけをnullから正常値に書き換え、それだけで症状が消えることを確認する(実測: tag書き換えのみで再現しなくなる)。
---
### KNOW-F-0459 状態ダンプの型(State Dump)
**①名称**: 状態ダンプの型
**②構造の型(骨子)**:
```
function calcTotal(order) {
console.log("DEBUG calcTotal 入力:", JSON.stringify(order, null, 2)) // 状態を丸ごと出力
const total = order.items.reduce((s,i)=>s+i.price*i.qty, 0)
console.log("DEBUG calcTotal 出力:", total)
return total
}
```
**③使いどころ**: 「途中の変数がどんな値になっているか」を憶測せず、実際に出力して確認したいとき。KNOW-F-0462(プリントデバッグ卒業の型)の入口にあたる最も基本的な調査手段。
**④組合せ例**: KNOW-F-0454(構造化ログの型)の形式(キーと値の組)でダンプすると、後から検索・比較がしやすくなる。KNOW-F-0458(差分デバッグの型)で比較する「正常/異常の状態」を実際に取得する手段として直接使われる。
**⑤確認事項(罠と検算・安全注意)**: 個人情報や機密情報(パスワード・カード番号など)を含むオブジェクトをそのままダンプすると、ログに機密が残る安全上の罠がある(ダンプ前に機密フィールドを除去・マスクする)。検算: ダンプした内容を実際に読み、想定していた構造(フィールド名・型)と一致しているかを1件ずつ照合する。
---
### KNOW-F-0460 ブレークポイント戦略の型(Breakpoint Strategy)
**①名称**: ブレークポイント戦略の型
**②構造の型(骨子)**:
```
配置の指針(骨子):
1. まずスタックトレース最上段(KNOW-F-0456)の行にブレークポイントを置く
2. そこで変数の値を確認し、想定と違う値ならさらに手前(呼び出し元)へ移動する
3. 想定通りの値ならもっと先(呼び出し先)へ移動する
→ これも一種の二分探索(KNOW-F-0451)である
```
**③使いどころ**: プリントデバッグ(KNOW-F-0459)ではコードの書き換え・再実行が必要で手間がかかるとき。実行を任意の行で一時停止し、その場で変数を確認できるツール(デバッガ)を使う。
**④組合せ例**: KNOW-F-0451(二分探索デバッグの型)の考え方を、ブレークポイントの配置場所の選び方に直接応用したもの。KNOW-F-0461(条件付きブレークポイントの型)は、本型をループ内の特定条件でのみ止めたい場合に拡張する型。
**⑤確認事項(罠と検算・安全注意)**: ブレークポイントを置いたまま停止している間に外部からの通信がタイムアウトする、といった副作用が実運用に近い環境では起きうる罠がある(本番環境に近い環境でのブレークポイント停止は影響範囲を事前に確認する)。検算: ブレークポイントで停止した時点の変数の値を、直前にコードを読んで予想していた値と突き合わせ、一致・不一致を記録する。
---
### KNOW-F-0461 条件付きブレークポイントの型(Conditional Breakpoint)
**①名称**: 条件付きブレークポイントの型
**②構造の型(骨子)**:
```
// 1000件のループのうち、id="x99"のときだけ問題が起きる場合
for (const item of items) {
// 条件付きブレークポイントの骨子(擬似):
// item.id === "x99" のときだけ実行を止める
process(item)
}
```
**③使いどころ**: 大量のループ処理の中で、特定の1件だけが問題を起こしているとき。1000回すべてで手動停止するのは現実的でないため、条件を指定して該当箇所だけで止める。
**④組合せ例**: KNOW-F-0460(ブレークポイント戦略の型)の発展形。KNOW-F-0468(エッジケース総当りの型)で「どの入力が問題を起こすか」を先に絞り込んだ後、本型でその1件だけを詳しく調べる、という順序で組み合わせる。
**⑤確認事項(罠と検算・安全注意)**: 条件式自体に誤り(item.id === "x99"のつもりが==や大文字小文字の違いで一致しない)があると、いつまでも止まらず「バグがない」と誤解する罠がある。検算: 条件付きブレークポイントを仕掛ける前に、該当のitem.idが実際に"x99"という文字列で存在することをログで先に確認しておく。
---
### KNOW-F-0462 プリントデバッグ卒業の型(Graduating from Print Debugging)
**①名称**: プリントデバッグの型と卒業条件
**②構造の型(骨子)**:
```
卒業の目安表:
| 状況 | 適した手段 |
|-----------------------------------------|----------------------------------|
| 1〜2箇所の値をさっと確認したい | プリントデバッグ(console.log)で十分 |
| 複数箇所を行き来しながら詳しく調べたい | ブレークポイント(KNOW-F-0460)へ移行 |
| 大量データの中の特定条件だけを見たい | 条件付きブレークポイント(KNOW-F-0461) |
```
**③使いどころ**: プリントデバッグを使い続けるべきか、より高度な道具(デバッガ)に切り替えるべきかを判断したいとき。プリントデバッグ自体は悪ではなく、小さな確認には最速の手段である。
**④組合せ例**: KNOW-F-0459(状態ダンプの型)がプリントデバッグの正式な型。KNOW-F-0460(ブレークポイント戦略の型)への「卒業」先を示す、章の中間的な整理の型にあたる。
**⑤確認事項(罠と検算・安全注意)**: console.logを大量に埋め込んだまま本番コードにコミットしてしまう罠がある(調査用のログは調査後に必ず削除するか、KNOW-F-0453のDEBUGレベルに正式に格下げする)。検算: 直近の変更差分(diff)にconsole.logが残っていないかをコミット前にgrepで確認する。
---
### KNOW-F-0463 二分コメントアウトの型(Bisecting by Comment-Out)
**①名称**: 二分コメントアウトの型
**②構造の型(骨子)**:
```
手順(骨子):
1. 疑わしい処理の後半半分を一時的にコメントアウトする(無効化する)
2. それでも症状が起きるなら、原因は前半にある
3. 症状が消えるなら、原因は後半にある(コメントアウトした部分)
4. 絞り込まれた半分に対して1〜3を再帰的に繰り返す
```
**③使いどころ**: 1つの長い処理(何十行もの手続き)の中のどこかで壊れているが、どこかまでは分からないとき。KNOW-F-0451(二分探索デバッグの型)を「時間軸(コミット)」ではなく「1つの関数の中の行」に適用したもの。
**④組合せ例**: KNOW-F-0451(二分探索デバッグの型)の考え方を最小単位(1関数内)に適用した具体形。KNOW-F-0425(トランザクションスクリプト脱却の型)で分解された小さな関数群があれば、二分コメントアウトの代わりに関数単位で切り離して調べられるため、こちらの型自体が不要になる場合がある。
**⑤確認事項(罠と検算・安全注意)**: コメントアウトした部分に副作用(DB書き込みなど)が含まれていると、後続の処理が本来受け取るはずのデータを受け取れず、別の症状を誤って「原因」と勘違いする罠がある。検算: コメントアウトの範囲を変えるたびに、対象の症状(最初に観測した1つの不具合)だけを見ているか、別の症状にすり替わっていないかを確認する。
---
### KNOW-F-0464 タイムトラベルデバッグの型(Time-Travel Debugging)
**①名称**: タイムトラベルデバッグの型
**②構造の型(骨子)**:
```
考え方の骨子:
通常のデバッガ: 現在の時点の変数しか見られない(先へ進むだけ)
タイムトラベル型: 実行の記録を保存しておき、後から任意の時点へ「巻き戻れる」
用途: 「さっき正常だった変数が、いつの間に異常値になったか」を
巻き戻しながら特定できる
```
**③使いどころ**: 「異常が起きた瞬間」は分かるが、「いつから異常な値が入り込んだか」がその手前まで遡らないと分からないとき。通常の(先に進むだけの)デバッガでは再実行するしかない場面を、記録の巻き戻しで代替する。
**④組合せ例**: KNOW-F-0451(二分探索デバッグの型)を「実行時間の中」で行う特殊形と位置づけられる。KNOW-F-0459(状態ダンプの型)を実行の各時点で自動的に取り続けることと本質的に等価であり、環境によって使える場合と使えない場合がある(利用可否は開発環境のツール事情に依存するため誇大な保証はしない)。
**⑤確認事項(罠と検算・安全注意)**: 実行の全記録を保存するため、通常の実行より大幅に遅く・メモリを多く使う罠がある(本番環境では使わず、再現できる開発環境でのみ使用する)。検算: 巻き戻して確認した「異常値が入った時点」の直後のコードを読み、実際にそこで値を変更する処理が存在するかを突き合わせる。
---
### KNOW-F-0465 変数固定切り分けの型(Variable Pinning)
**①名称**: 疑わしきを固定する型(変数を固定して絞り込む)
**②構造の型(骨子)**:
```
// 複数の変数が絡む処理で、どれが原因か分からないとき
function process(a, b, c) { /* 落ちる */ }
// 1つずつ既知の正常値に固定し、症状が消えるかを確認する
process(KNOWN_GOOD_A, b, c) // aを固定 → まだ落ちるならaは原因でない
process(a, KNOWN_GOOD_B, c) // bを固定 → 落ちなくなったらbが原因の候補
process(a, b, KNOWN_GOOD_C) // cを固定
```
**③使いどころ**: 複数の入力(引数)が絡む処理で、どの入力が異常の原因かを1つずつ切り分けたいとき。KNOW-F-0458(差分デバッグの型)を「複数の変数」という軸で体系的に行う版。
**④組合せ例**: KNOW-F-0458(差分デバッグの型)と対で使う直接の関係。KNOW-F-0426(境界値分析の型)で洗い出した境界の値を「既知の正常値」として使うと、切り分けの精度が上がる。
**⑤確認事項(罠と検算・安全注意)**: 複数の変数が組み合わさって初めて症状が出る(単体では問題ない)ケースでは、1つずつの固定では原因を見つけられない罠がある(その場合はKNOW-F-0438プロパティベーステストのような網羅的な探索に切り替える)。検算: すべての変数を固定した(全て既知の正常値にした)状態で症状が消えることをまず確認してから、1つずつ元に戻していく。
---
### KNOW-F-0466 仮説駆動デバッグの型(Hypothesis-Driven Debugging)
**①名称**: 仮説駆動デバッグの型
**②構造の型(骨子)**:
```
サイクル(骨子):
1. 仮説を立てる: 「たぶんitems配列が空のときにバグる」
2. 仮説から予測を導く: 「なら、items:[]で呼べば再現するはず」
3. 検証する: 実際にitems:[]で呼んでみる
4. 予測通りなら仮説は補強される、外れれば仮説を棄却し次の仮説へ
```
**③使いどころ**: 手当たり次第にログを増やしたりコードを書き換えたりする前に、「何が原因かの予想」を先に言語化してから確認する習慣を持ちたいとき。当てずっぽうの修正の再発防止になる。
**④組合せ例**: KNOW-F-0457(ラバーダック法の型)は仮説を生み出す準備運動として直接接続する。KNOW-F-0452(最小再現の型)は、仮説を検証するための最小の実験環境を作る技術として組み合わされる。
**⑤確認事項(罠と検算・安全注意)**: 1つの仮説に固執しすぎ、反証(予測が外れたという結果)が出ても仮説を疑わず「テストのやり方が悪かった」と結論をねじ曲げる罠がある(予測が外れたら仮説を素直に棄却する)。検算: 立てた仮説と、実際の検証結果(再現した/しなかった)を記録に残し、後から「その仮説は正しかったか」を追跡できるようにする。
---
### KNOW-F-0467 再現手順書の型(Reproduction Steps Document)
**①名称**: 再現手順書の型
**②構造の型(骨子)**:
```
テンプレート(骨子):
前提条件: (バージョン、環境、初期状態)
手順: 1. ... 2. ... 3. ...
期待される結果: ...
実際の結果: ...
再現率: 10回中何回発生したか
```
**③使いどころ**: バグを見つけた本人以外(チームメンバーや将来の自分)が、同じ手順で確実に再現できるようにしたいとき。KNOW-F-0452(最小再現の型)で絞り込んだ手順を、共有可能な文書の形にする。
**④組合せ例**: KNOW-F-0452(最小再現の型)の絞り込み結果を書き留める直接の受け皿。KNOW-F-0474(バグ票記法の型)は、この再現手順書をさらにチケット管理の型式に落とし込んだ発展形にあたる。
**⑤確認事項(罠と検算・安全注意)**: 「再現率」を書かずに済ませると、実は毎回は起きない不具合(フレーク、KNOW-F-0445)なのか、確実に起きる不具合なのかが後から分からなくなる罠がある。検算: 手順書に書いた手順を、書いた本人以外が一度実際になぞり、同じ結果が得られるかを確認する。
---
### KNOW-F-0468 エッジケース総当りの型(Edge Case Enumeration)
**①名称**: エッジケース総当りの型
**②構造の型(骨子)**:
```
総当りの観点表(骨子):
空: 空配列、空文字列、0件
極端: 非常に大きい値、非常に長い文字列
境界: KNOW-F-0426で扱う境界そのもの
型違い: 数値のはずがnull/undefinedが来る
同時実行: 同じ処理が並行して2回走る(KNOW-F-0469参照)
```
**③使いどころ**: 「動いているように見えるが、まだ見つかっていない不具合が潜んでいそうだ」と疑い、能動的に弱点を洗い出したいとき。バグ報告を待つのでなく、事前に観点表に沿って総当りで確認する。
**④組合せ例**: KNOW-F-0426(境界値分析の型)・KNOW-F-0427(同値分割の型)の観点を「デバッグ側」から総合的に見直す実践の型。KNOW-F-0461(条件付きブレークポイントの型)は、洗い出したエッジケースのうち実データで再現する1件を詳しく調べる後段の型。
**⑤確認事項(罠と検算・安全注意)**: 観点表を機械的になぞるだけで、対象のドメイン固有のエッジケース(例: 注文なら「返品済みの注文」など業務特有の状態)を見落とす罠がある(汎用の観点表と業務知識の両方を併用する)。検算: 観点表の各項目について、実際に1件ずつ試して結果(正常/異常)を記録し、空欄を残さない。
---
### KNOW-F-0469 競合状態デバッグの型(Race Condition Debugging)
**①名称**: 競合状態デバッグの型(レース条件)
**②構造の型(骨子)**:
```
典型的な競合状態(骨子):
処理A: 在庫を読む(10) → 1減らす → 書き込む(9)
処理B: 在庫を読む(10) → 1減らす → 書き込む(9) // Aと同時に読んでしまった
結果: 本来8になるべきなのに9のまま(1回分が失われる)
対策の骨子: 読み取りと書き込みを1つの不可分な操作(ロック/トランザクション)にまとめる
```
**③使いどころ**: 「単体で動かすと問題ないのに、同時に複数のリクエストが来ると数字が合わなくなる」という現象に遭遇したとき。KNOW-F-0445(フレークテスト対策の型)の原因表で挙げた「非同期処理」に関する深掘り。
**④組合せ例**: KNOW-F-0404(冪等性の型)・KNOW-F-0417(冪等なリトライの型)が予防策として直接関係する。KNOW-F-0468(エッジケース総当りの型)の「同時実行」の観点を深掘りする専門の型として接続する。
**⑤確認事項(罠と検算・安全注意)**: 競合状態は発生タイミングに依存するため、通常の実行では再現せず、特定の負荷条件でしか出現しない(KNOW-F-0492負荷テストの型と組み合わせて発生させる)罠がある。検算: 意図的に2つの処理を同時に(または極めて近いタイミングで)走らせるテストを書き、対策前は数字が合わなくなり、対策後は常に正しい値になることを確認する。
---
### KNOW-F-0470 メモリリーク追跡の型(Memory Leak Tracking)
**①名称**: メモリリーク追跡の型
**②構造の型(骨子)**:
```
典型的な原因(骨子):
- KNOW-F-0419のメモ化キャッシュが際限なく増え続け、古いキーを削除していない
- イベントリスナーを登録したまま解除し忘れている
- タイマー(setInterval)を止め忘れている
追跡手順: 同じ操作を100回繰り返し、メモリ使用量が右肩上がりに増え続けるかを計測する
(1回ごとに使用量が戻るなら正常、戻らず積み上がるならリーク)
```
**③使いどころ**: 長時間動かし続けたシステムが、徐々に遅くなったり、最終的に落ちたりするとき。KNOW-F-0419(参照透過なキャッシュの型)のようにデータを溜め込む仕組みが、掃除の仕組みを持っているかを疑う。
**④組合せ例**: KNOW-F-0419(参照透過なキャッシュの型)の確認事項で予告した内容の詳細版として直接接続する。第五章KNOW-F-0484(メモリプロファイリングの型)は、本型の「増え続けているか」を実際の数値で裏づける計測手段になる。
**⑤確認事項(罠と検算・安全注意)**: メモリ使用量は一時的に増減するため、1回の計測だけでリークと判断すると誤診断になる罠がある(複数回・長時間の計測で「増え続ける傾向」があるかを確認する)。検算: 同じ操作を100回繰り返した後のメモリ使用量が、1回だけ実行した直後の使用量と比べて大幅に増えたままかどうかを比較する。
---
### KNOW-F-0471 例外握りつぶし検出の型(Swallowed Exception Detection)
**①名称**: 例外握りつぶし検出の型
**②構造の型(骨子)**:
```
// 握りつぶし(危険): エラーが発生したことすら記録されない
try { riskyOperation() } catch (e) { /* 何もしない */ }
// 検出可能な形にする: 最低限ログには残す
try { riskyOperation() } catch (e) { log.error("riskyOperation失敗", { error: e.message }) }
```
**③使いどころ**: 「エラーは出ていないはずなのに、結果だけがおかしい」という現象に遭遇したとき。空のcatchブロック(あるいはログすら出さないcatch)がどこかに潜んでいないかを疑う。
**④組合せ例**: KNOW-F-0453(ログレベル設計の型)のERRORレベルを使って、握りつぶされそうな例外を可視化する直接の対策。KNOW-F-0414(Result型による例外回避の型)を徹底していれば、そもそも「握りつぶされる例外」自体が発生しにくい設計になる。
**⑤確認事項(罠と検算・安全注意)**: 「とりあえずcatchして握りつぶす」コードは一見安全(プログラムが止まらない)に見えるが、実際には不具合を隠しているだけであり、根本原因の発見を遅らせる罠がある。検算: プロジェクト全体のcatchブロックをgrepし、中身が空、またはログ出力すら無いものが何件あるかを数える。
---
### KNOW-F-0472 バージョン二分探索の型(Version Bisect)
**①名称**: バージョン二分探索の型
**②構造の型(骨子)**:
```
手順(骨子・バージョン管理システムの機能を使う場合):
1. 正常だった版(古い)と異常な版(新しい)を指定する
2. 中間の版へ自動的に切り替わる
3. その版で症状を確認し、「良い」か「悪い」かを記録する
4. 記録に基づき、システムが次の中間版へ自動的に切り替える
5. 最終的に「症状が入り込んだ最初の1件の変更」が特定される
```
**③使いどころ**: KNOW-F-0451(二分探索デバッグの型)を、バージョン管理システムが記録している変更履歴に対して機械的に(自動化して)適用したいとき。手作業より高速かつ確実。
**④組合せ例**: KNOW-F-0451(二分探索デバッグの型)の直接の機械化版。KNOW-F-0428(回帰テストの型)があれば、各中間版での「良い/悪い」の判定を自動テストの合否として機械化でき、人手を介さず全自動で原因の変更を特定できる。
**⑤確認事項(罠と検算・安全注意)**: 「良い/悪い」の判定を誤ると(例えば環境要因で偶然失敗した版を「悪い」と誤記録すると)、絞り込みが誤った方向へ進む罠がある(判定は決定性テスト=KNOW-F-0429を使うのが安全)。検算: 特定された1件の変更を単独で適用/未適用にして、症状の有無が完全に切り替わることを再現確認する。
---
### KNOW-F-0473 環境差分切り分けの型(Environment Diff Isolation)
**①名称**: 環境差分切り分けの型
**②構造の型(骨子)**:
```
比較対象の一覧(骨子):
OS/ブラウザのバージョン
依存ライブラリのバージョン
環境変数の値
データの内容(件数・文字コード)
実行タイミング(時刻・タイムゾーン)
「自分の環境では起きないが、相手の環境では起きる」ときは、この一覧を1項目ずつ突き合わせる
```
**③使いどころ**: 「自分のパソコンでは動くのに、相手の環境やサーバーでは動かない」という現象(いわゆる環境差異バグ)に遭遇したとき。KNOW-F-0458(差分デバッグの型)を「実行環境」という軸に特化して適用する。
**④組合せ例**: KNOW-F-0458(差分デバッグの型)の環境版。KNOW-F-0455(相関IDの型)で両方の環境のログを同じ形式で取得できていれば、突き合わせの精度が上がる。
**⑤確認事項(罠と検算・安全注意)**: 「バージョンが同じだから環境は同じはず」という思い込みで、実際には設定ファイルやデータの中身の違いを見落とす罠がある(バージョン番号だけでなく実際の設定値まで突き合わせる)。検算: 一覧表の各項目を両環境で実際に取得し、差分が1項目でも見つかったら、その項目だけを揃えた状態で再現するかを確認する。
---
### KNOW-F-0474 バグ票記法の型(Bug Report Format)
**①名称**: バグ票の型(再現/期待/実際)
**②構造の型(骨子)**:
```
テンプレート(骨子):
タイトル: 短く症状を要約(例: 空カートで合計が例外になる)
再現手順: KNOW-F-0467の再現手順書をそのまま貼る
期待される結果: 合計は0円と表示される
実際の結果: TypeErrorが発生し画面が真っ白になる
影響範囲: 全ユーザーのうち空カート状態で発生(頻度は低いが致命的)
```
**③使いどころ**: 見つけた不具合をチームで共有し、優先順位をつけて対応するとき。「期待」と「実際」を分けて書くだけで、報告者と対応者の認識のずれを防げる。
**④組合せ例**: KNOW-F-0467(再現手順書の型)を土台として、そこに「期待/実際/影響範囲」を加えた発展形。KNOW-F-0428(回帰テストの型)は、このバグ票の「再現手順」がそのままテストコードの元になる直接の後続の型。
**⑤確認事項(罠と検算・安全注意)**: 「期待される結果」を書かずに「実際の結果」だけを書くと、そもそも何が正しい挙動なのかの合意が取れないまま対応が始まる罠がある(期待は必ず具体的な値・状態で書く)。検算: バグ票の「期待される結果」の記述だけを読み、KNOW-F-0428の回帰テストのexpect文をその場で書き起こせるかを確認する(書き起こせないほど曖昧なら記述を見直す)。
---
### KNOW-F-0475 ポストモーテムの型(Postmortem)
**①名称**: ポストモーテムの型(再発防止)
**②構造の型(骨子)**:
```
記録項目(骨子・個人を責めない姿勢=blameless postmortemが前提):
何が起きたか(時系列)
どうやって気づいたか
なぜ起きたか(直接原因と、それを許した構造的な原因の両方)
どう直したか
再発防止策(具体的な型への接続。例: KNOW-F-0428で回帰テストを追加)
```
**③使いどころ**: 重大な不具合が本番環境で発覚し、修正が完了した後。修正して終わりにせず、「なぜ事前に防げなかったか」という構造的な原因まで振り返り、次に同種の問題が起きにくい仕組みを作る。
**④組合せ例**: 第四章の集大成であり、KNOW-F-0451〜0474のどの型を使って原因を突き止めたかを記録に残す。KNOW-F-0428(回帰テストの型)への接続が最も頻出する再発防止策であり、章の締めくくりとして両章(第三・第四章)を結ぶ役割を持つ。
**⑤確認事項(罠と検算・安全注意)**: 個人の不注意を原因として記録して終わらせると、同じ状況に置かれた別の人が同じミスを繰り返す(構造が変わっていないため)罠がある。「なぜそのミスが起きても検知できなかったか」という仕組み側の原因まで掘り下げることが誠実な振り返りである。検算: 再発防止策として挙げた項目(例: 回帰テスト追加)が、実際にリポジトリへコミットされ、KNOW-F-0428の形式で存在することを確認する。
---
## 第五章 計測の型(KNOW-F-0476〜0500)
「速そう」「重そう」は感覚であり、しばしば間違う。第五章は、速さと資源消費を数値で語るための25の型を扱う。中心にあるのは「計測が最適化に先行する」という原則であり、これは本冊全体の締めくくりとして、勘に頼らない設計の最後の砦になる。
---
### KNOW-F-0476 計測先行原則の型(Measure Before Optimize)
**①名称**: 計測が最適化に先行する原則の型
**②構造の型(骨子)**:
```
誤った順序: 「なんとなく遅そう」→ 憶測で書き換える → 速くなった気がする(未確認)
正しい順序: 計測する → ボトルネック(最も時間を食っている箇所)を特定する
→ その箇所だけを直す → 再度計測して効果を数値で確認する
```
**③使いどころ**: 「ここが遅い気がする」という直感だけでコードを書き換えようとした瞬間。書き換える前に、必ず実測してから手を付ける習慣にする。CATALOG設計思想の「解析したうえで、構造を体系化」という姿勢を計測分野で具体化した型。
**④組合せ例**: 第五章のすべての型(KNOW-F-0477〜0500)は、本型の実行手段(どう計測するか)の各論にあたる。KNOW-F-0487(早すぎる最適化を避ける型)は本型の裏返しの警句として対になる。
**⑤確認事項(罠と検算・安全注意)**: 「計測した」つもりが、計測方法自体が不正確(KNOW-F-0481のノイズ除去をしていない等)で、誤った箇所を最適化してしまう罠がある。誇大な高速化の主張(「必ず2倍速くなる」)は避け、実測値のみを断言する。検算: 最適化の前後で同じ条件・同じ計測方法により2回計測し、数値としての改善(または改善なし)を記録する(実測のみを断言する原則、CLAUDE.md「検証ラチェット」と同型)。
---
### KNOW-F-0477 プロファイリングの型(Profiling)
**①名称**: プロファイリングの型
**②構造の型(骨子)**:
```
プロファイラが出す情報の骨子(呼び出しごとの内訳):
| 関数名 | 呼び出し回数 | 自己時間(自身のみ) | 累積時間(子を含む) |
|----------------|---------------|-----------------------|-----------------------|
| calcTotal | 10,000 | 120ms | 120ms |
| validateOrder | 10,000 | 850ms | 850ms | <- ここが重い
| checkout | 10,000 | 30ms | 1,000ms |
```
**③使いどころ**: 「システム全体が遅い」という漠然とした状態から、「具体的にどの関数が時間を食っているか」を数値の一覧として洗い出したいとき。KNOW-F-0476(計測先行原則)を実行する最初の一手。
**④組合せ例**: KNOW-F-0488(ホットパス特定の型)は、プロファイリング結果から「頻繁に通る経路」を読み解く直接の後続作業。KNOW-F-0484(メモリプロファイリングの型)は、時間でなくメモリ消費量を対象にした姉妹型。
**⑤確認事項(罠と検算・安全注意)**: プロファイラを動かすこと自体が処理に負荷を追加し、計測結果がわずかに歪む(プロファイラのオーバーヘッド)ことがある罠がある(相対的な比較には使えるが、絶対時間は本番環境の実測とは異なりうると明記する)。検算: 同じ入力でプロファイリングを2回実行し、上位の重い関数の順位がおおむね安定しているかを確認する。
---
### KNOW-F-0478 アムダールの法則の型(Amdahl's Law)
**①名称**: ボトルネック特定の型(アムダールの法則)
**②構造の型(骨子)**:
```
考え方の骨子:
全体処理時間のうち、改善対象の部分が占める割合をPとする
その部分をS倍速くできたとき、全体の高速化倍率は最大でも
1 / ((1-P) + P/S)
例: 全体の20%を占める処理を無限に速くしても(S→∞)、
全体の高速化倍率は 1/(0.8) = 1.25倍が上限
```
**③使いどころ**: 「この部分をどれだけ頑張って最適化する価値があるか」を、着手前に見積もりたいとき。全体に占める割合が小さい処理をいくら速くしても、全体への効果は限定的であるという判断材料になる。
**④組合せ例**: KNOW-F-0477(プロファイリングの型)で得た「自己時間の割合」がそのままPの実測値になる。KNOW-F-0489(パレート分析の型)は、この法則が示す「割合の大きい部分から着手すべき」という結論を別の角度(80/20)から支持する。
**⑤確認事項(罠と検算・安全注意)**: Pの見積もりが甘い(実際には10%しか占めない処理を50%と誤認する)と、労力配分の判断そのものが誤る罠がある(Pは必ずプロファイリングの実測値を使う)。検算: P=0.2, S=4 のとき計算式に代入し 1/(0.8+0.05)=1/0.85≒1.18倍 となることを手計算し、実際に4倍速い実装に差し替えた後の全体計測値と照合する。
---
### KNOW-F-0479 マイクロベンチマークの型(Microbenchmark)
**①名称**: マイクロベンチマークの型
**②構造の型(骨子)**:
```
function benchmark(fn, iterations = 100000) {
const start = performance.now()
for (let i = 0; i < iterations; i++) fn()
const elapsed = performance.now() - start
return elapsed / iterations // 1回あたりの平均時間
}
console.log(benchmark(() => calcTotal(sampleOrder))) // 例: 0.0021 ms/回
```
**③使いどころ**: 「AとBという2つの実装、どちらが速いか」を、システム全体を動かさずに、対象の関数だけを取り出して比較したいとき。
**④組合せ例**: KNOW-F-0481(計測ノイズ低減の型)と必ず組み合わせる(1回だけの計測はノイズの影響が大きい)。KNOW-F-0480(マクロベンチマークの型)と対比される、対象範囲が最小の計測の型。
**⑤確認事項(罠と検算・安全注意)**: マイクロベンチマークは対象を切り出しすぎるあまり、実際のシステム内での挙動(キャッシュの効き方や他処理との競合)を反映しない場合がある罠がある(「関数単体としては速いが、システム全体では変わらなかった」という結果もありうると明記する)。検算: 同じ関数を10回ベンチマークし、平均値のばらつき(最大値と最小値の差)が平均値の何%以内に収まるかを記録する。
---
### KNOW-F-0480 マクロベンチマークの型(Macrobenchmark)
**①名称**: マクロベンチマークの型
**②構造の型(骨子)**:
```
測定対象の骨子(システム全体、または1機能全体):
シナリオ: ユーザーが商品を検索し、カートに入れ、決済を完了するまで
計測開始: シナリオ開始時刻
計測終了: 決済完了の応答を受け取った時刻
→ シナリオ全体の所要時間を1つの数値として得る
```
**③使いどころ**: KNOW-F-0479(マイクロベンチマークの型)で個々の関数を速くしたつもりでも、利用者が実際に体感する速さが改善したかを最終的に確認したいとき。個々の部品ではなく、組み合わさった全体を測る。
**④組合せ例**: KNOW-F-0478(アムダールの法則の型)の予測が正しかったかどうかを、実際のシナリオで検算する後段の型。KNOW-F-0499(最適化効果検証の型)は、本型による前後比較を計測プロセス全体の締めくくりとして体系化したもの。
**⑤確認事項(罠と検算・安全注意)**: マクロベンチマークだけでは「どこが遅いか」までは分からない(全体の所要時間しか分からない)罠がある。原因の特定にはKNOW-F-0477(プロファイリングの型)を併用する必要がある。検算: 同じシナリオを異なる時間帯に複数回実行し、外部要因(通信混雑など)による変動幅を把握してから、最適化の効果と比較する。
---
### KNOW-F-0481 計測ノイズ低減の型(Reducing Measurement Noise)
**①名称**: 計測ノイズ低減の型(ウォームアップ・複数回実行)
**②構造の型(骨子)**:
```
function reliableBenchmark(fn, warmup = 1000, trials = 10, iterations = 10000) {
for (let i = 0; i < warmup; i++) fn() // ウォームアップ(最適化が効く前の遅い状態を除外)
const results = []
for (let t = 0; t < trials; t++) { // 複数回試行してばらつきを把握
const start = performance.now()
for (let i = 0; i < iterations; i++) fn()
results.push((performance.now() - start) / iterations)
}
results.sort((a,b) => a-b)
return results[Math.floor(results.length / 2)] // 中央値を採用(外れ値に強い)
}
```
**③使いどころ**: KNOW-F-0479(マイクロベンチマークの型)を1回だけ実行して数値がばらつくとき。計測結果自体の信頼性を高めるための前処理・統計処理を行う。
**④組合せ例**: KNOW-F-0479(マイクロベンチマークの型)・KNOW-F-0480(マクロベンチマークの型)の両方に適用すべき前提処理として、章のどの計測型にも横断的に関わる基盤の型。
**⑤確認事項(罠と検算・安全注意)**: 平均値だけを見ると、まれに発生する極端に遅い1回(外れ値)に平均が引きずられる罠がある(中央値やパーセンタイル=KNOW-F-0491の使用を推奨する)。検算: 同一条件でreliableBenchmarkを3セット実行し、それぞれの中央値同士の差が実用上無視できる範囲(例えば5%未満)に収まるかを確認する。
---
### KNOW-F-0482 計算量分析の型(Big-O Analysis)
**①名称**: 計算量分析の型(Big-O)
**②構造の型(骨子)**:
```
入力サイズnに対する処理量の型(骨子):
O(1): 配列の先頭要素を取る(サイズに無関係)
O(log n): 二分探索(KNOW-F-0451と同じ考え方)
O(n): 配列を1周する(reduceなど)
O(n log n): 一般的なソート
O(n^2): 二重ループで全組合せを比較する(ネストしたfor)
```
**③使いどころ**: 実際に計測する前に、コードの構造(ループの入れ子など)を見ただけで、入力が増えたときの遅さの伸び方をおおまかに見積もりたいとき。実測(KNOW-F-0483)と組み合わせて使う理論側の道具。
**④組合せ例**: KNOW-F-0483(実測と理論値の突合の型)は、本型で見積もった理論値を実際の計測で裏づける直接の後続の型。KNOW-F-0478(アムダールの法則の型)とあわせ、「どの部分がなぜ重いか」を理論的に説明する土台になる。
**⑤確認事項(罠と検算・安全注意)**: Big-Oは「入力が非常に大きくなったときの伸び方」を表すものであり、入力が小さい場合はO(n^2)の単純な実装の方がO(n log n)の複雑な実装より実際には速いことがある罠がある(理論値だけで判断せず必ず実測=KNOW-F-0483で確認する)。検算: n=10, 100, 1000で実際の処理時間を計測し、n^2の理論通りなら(10倍のnで100倍の時間)という比率に近いかを確認する。
---
### KNOW-F-0483 実測と理論値の突合の型(Empirical vs Theoretical)
**①名称**: 実測と理論値の突合の型
**②構造の型(骨子)**:
```
突合の骨子:
理論(Big-O): O(n^2)と分析した
実測: n=100で10ms、n=200で40ms(4倍)、n=400で160ms(さらに4倍)
判定: nが2倍で時間が4倍になっており、O(n^2)の予測(2^2=4倍)と一致する
```
**③使いどころ**: KNOW-F-0482(計算量分析の型)で「理論上はこうなるはず」と見積もった後、実際にその通りの伸び方をしているかを確認したいとき。理論と実測が食い違えば、分析の誤りか、実装が理論通りに書かれていないかのどちらかを疑う。
**④組合せ例**: KNOW-F-0482(計算量分析の型)と対で使う直接の検算セット。KNOW-F-0480(マクロベンチマークの型)で得られた複数の入力サイズでの実測値をそのまま突合の材料に使う。
**⑤確認事項(罠と検算・安全注意)**: 入力サイズが小さすぎる範囲だけで計測すると、理論上の傾向がまだ現れず(定数時間の初期化コストなどに埋もれ)誤った結論に至る罠がある(十分に大きい入力サイズの範囲で複数点を計測する)。検算: 上記の例のように、入力サイズを2倍にしたときの時間の伸び率を3点以上で確認し、傾向が安定しているかを見る。
---
### KNOW-F-0484 メモリプロファイリングの型(Memory Profiling)
**①名称**: メモリプロファイリングの型
**②構造の型(骨子)**:
```
計測項目の骨子:
ヒープ使用量(実行中に確保されているメモリの総量)
オブジェクト種別ごとの個数(例: Order型のインスタンスが何個残っているか)
世代別の生存率(短命なオブジェクトがすぐ回収されているか)
手順: 操作前後でヒープのスナップショットを取り、差分を比較する
```
**③使いどころ**: KNOW-F-0470(メモリリーク追跡の型)で「増え続けている」と分かった後、具体的に「何の種類のオブジェクトが増え続けているか」を特定したいとき。
**④組合せ例**: KNOW-F-0470(メモリリーク追跡の型)の直接の後続の型(疑いの確認から原因の特定へ)。KNOW-F-0477(プロファイリングの型)の対象を時間からメモリに変えた姉妹型。
**⑤確認事項(罠と検算・安全注意)**: スナップショットの取得自体が一時的にメモリを消費するため、計測環境そのものの余裕(空きメモリ)を確保しておく必要がある罠がある。検算: 同じ操作を1回だけ行った直後のスナップショットと、100回行った直後のスナップショットを比較し、特定の型のオブジェクト数が操作回数にほぼ比例して増えているかを確認する。
---
### KNOW-F-0485 I/O待ち/CPU待ち切り分けの型(I/O-bound vs CPU-bound)
**①名称**: I/O待ちとCPU待ちの切り分けの型
**②構造の型(骨子)**:
```
切り分けの骨子:
CPU律速(CPU-bound): CPU使用率が高く張り付いている → 計算アルゴリズムを見直す
I/O律速(I/O-bound): CPU使用率は低いが処理が遅い → 通信・ディスクの待ち時間を疑う
判定の目安表:
| CPU使用率 | 処理の遅さ | 判定 |
|-----------|-------------|-------------|
| 高い | 遅い | CPU律速 |
| 低い | 遅い | I/O律速 |
| 高い | 速い | 正常(高負荷を効率よく処理) |
```
**③使いどころ**: 「遅い」という結果だけを見て、闇雲にアルゴリズムを最適化しようとする前に、そもそも待っているのがCPUの計算なのか、ネットワークやディスクの応答なのかを見極めたいとき。原因の種類によって対処がまったく異なる。
**④組合せ例**: KNOW-F-0477(プロファイリングの型)のCPU使用率の情報と、KNOW-F-0490(スループットとレイテンシの型)の待ち時間の情報を組み合わせて判定する。KNOW-F-0417(冪等なリトライの型)はI/O律速の場合に検討すべき対策(通信の再送戦略)と接続する。
**⑤確認事項(罠と検算・安全注意)**: 非同期処理(並行して複数のI/Oを同時に待つ設計)の環境では、CPU使用率が低くても実は複数のI/Oが効率よく重なっているだけで「遅い」とは限らない場合がある(単純にCPU使用率の低さだけでI/O律速と断定しない)。検算: 疑わしい処理区間のCPU使用率とI/O待ち時間を同時に計測し、どちらが処理時間の大半を占めるかを比率で確認する。
---
### KNOW-F-0486 キャッシュ効果測定の型(Cache Effectiveness Measurement)
**①名称**: キャッシュ効果測定の型
**②構造の型(骨子)**:
```
計測項目の骨子:
ヒット率 = キャッシュが使われた回数 / 全アクセス回数
ヒット時の平均応答時間 と ミス時の平均応答時間 の比較
function withCacheStats(memoizedFn, cache) {
let hits = 0, misses = 0
return (...args) => {
const key = JSON.stringify(args)
if (cache.has(key)) hits++; else misses++
return memoizedFn(...args)
}
}
```
**③使いどころ**: KNOW-F-0419(参照透過なキャッシュの型)を導入した後、そのキャッシュが実際にどれだけ効果を上げているか(何%がキャッシュで済んでいるか)を数値で確認したいとき。「入れただけで満足」を避ける。
**④組合せ例**: KNOW-F-0419(参照透過なキャッシュの型)の効果検証として直接接続する。KNOW-F-0499(最適化効果検証の型)の一種であり、キャッシュという特定の最適化手法に特化した計測。
**⑤確認事項(罠と検算・安全注意)**: ヒット率が低いのにキャッシュを維持し続けると、KNOW-F-0470(メモリリーク追跡の型)で扱ったメモリ消費だけが増えて効果が乏しい状態になる罠がある(ヒット率が低ければキャッシュ戦略自体の見直しが必要)。検算: withCacheStatsでヒット率を実測し、50%未満であればキャッシュの対象キーの選び方が適切かを再検討する。
---
### KNOW-F-0487 早すぎる最適化を避ける型(Avoid Premature Optimization)
**①名称**: 早すぎる最適化を避ける型
**②構造の型(骨子)**:
```
判断の骨子:
まだ計測していない箇所を「たぶん遅いだろう」という推測だけで複雑化するのは避ける
複雑化のコスト(可読性の低下・バグの温床)は確実に発生するが、
速度改善の効果は計測するまで不確実である
順序: まず素直に書く → 計測する(KNOW-F-0476) → 実際に遅い箇所だけ最適化する
```
**③使いどころ**: 「将来遅くなるかもしれないから」という理由で、最初から複雑なキャッシュや並行処理を仕込もうとしているとき。まだ存在しない問題のために可読性を犠牲にしていないかを自問する。
**④組合せ例**: KNOW-F-0476(計測先行原則の型)の裏返しとして対になる、章全体の思想を支える型。KNOW-F-0400(モジュール粒度調整の型)の「分割は常に善ではない」という戒めと同じ構造(何事も過剰適用には罠がある)を持つ。
**⑤確認事項(罠と検算・安全注意)**: 「早すぎる最適化を避ける」を口実に、明らかに非効率な設計(O(n^2)で済む場面でO(n^3)を書くなど、最初から避けられる無駄)まで放置してよいわけではない罠がある(「疑わしきは計測する」のであって「何も考えない」のとは違う)。検算: 複雑な最適化を導入する前に、その最適化を入れない素朴な実装で実際に計測し、要求される速度基準を満たさないことを確認してから着手する。
---
### KNOW-F-0488 ホットパス特定の型(Hot Path Identification)
**①名称**: ホットパス特定の型
**②構造の型(骨子)**:
```
ホットパス(頻繁に通る経路)の特定の骨子:
KNOW-F-0477のプロファイリング結果で「呼び出し回数」が突出して多い関数を探す
例: validateOrderが1リクエストにつき50回呼ばれている
(本来1回で済むはずが、ループの中で誤って毎回呼ばれている)
```
**③使いどころ**: プロファイリング結果を見て、単純に「時間がかかっている関数」だけでなく「呼ばれる回数そのものが想定より多い関数」を見つけたいとき。回数の異常は、多くの場合ロジックの誤り(無駄な繰り返し)を示す。
**④組合せ例**: KNOW-F-0477(プロファイリングの型)の呼び出し回数列を読み解く直接の後続作業。KNOW-F-0419(参照透過なキャッシュの型)は、正当な理由で頻繁に呼ばれる(だが同じ引数が多い)ホットパスへの対処として組み合わされる。
**⑤確認事項(罠と検算・安全注意)**: 「呼び出し回数が多い=悪い」とは限らない(本質的に大量のデータを処理する関数は多く呼ばれて当然)罠がある。想定していた回数(設計上何回呼ばれるべきか)と実測の回数を比較することが重要。検算: 1リクエストあたりのvalidateOrderの呼び出し回数を計測し、設計上想定していた回数(この例では1回)と一致するかを確認する。
---
### KNOW-F-0489 パレート分析の型(80/20 Analysis)
**①名称**: 80/20分析の型(パレート)
**②構造の型(骨子)**:
```
経験則の骨子: 全体の処理時間の約80%は、全コードのうち約20%の箇所に集中する
(正確に80/20になるとは限らないが、偏りがあるという経験則)
活用: KNOW-F-0477のプロファイリング結果を時間の降順に並べ、
累積時間が全体の80%に達するまでの関数だけをまず最適化の対象にする
```
**③使いどころ**: プロファイリング結果に数十〜数百の関数が並び、どこから手をつければよいか優先順位を決めたいとき。上位のわずかな件数に集中して取り組む方が、全体を満遍なく触るより効率がよい。
**④組合せ例**: KNOW-F-0478(アムダールの法則の型)を経験則として簡略化した実務版という関係にある。KNOW-F-0477(プロファイリングの型)の結果を並べ替えるだけで適用できる、着手の手軽さが特徴。
**⑤確認事項(罠と検算・安全注意)**: 「80/20」という数字自体を厳密な法則と誤解し、必ずしも当てはまらないケース(処理時間が均等に分散しているシステムなど)にも機械的に当てはめる罠がある(あくまで経験則であり実測で確認する)。検算: 実際のプロファイリング結果を累積時間の降順に並べ、上位何%の関数で全体の何%の時間を占めるかを実測して、80/20に近いか、別の偏り方をしているかを記録する。
---
### KNOW-F-0490 スループットとレイテンシの型(Throughput and Latency)
**①名称**: スループットとレイテンシの型
**②構造の型(骨子)**:
```
用語の骨子:
レイテンシ: 1件の処理が完了するまでにかかる時間(例: 注文1件の処理に120ms)
スループット: 単位時間あたりに処理できる件数(例: 1秒間に800件の注文を処理できる)
両者は必ずしも比例しない: 1件あたりのレイテンシが長くても、
並行処理を増やせばスループットは上がる場合がある(その逆もある)
```
**③使いどころ**: 「速い/遅い」という言葉があいまいに使われているとき。利用者1人にとっての体感速度(レイテンシ)を改善したいのか、システム全体が同時にさばける量(スループット)を改善したいのかを、まず区別する。
**④組合せ例**: KNOW-F-0480(マクロベンチマークの型)はレイテンシ寄りの計測、KNOW-F-0492(負荷テストの型)はスループット寄りの計測として役割分担する。KNOW-F-0491(パーセンタイル計測の型)は、レイテンシを1つの平均値でなく分布として捉える発展形。
**⑤確認事項(罠と検算・安全注意)**: 「平均レイテンシ」だけを見ると、一部の利用者が極端に遅い体験をしていることを見逃す罠がある(KNOW-F-0491のパーセンタイル計測で補う)。検算: 同じ処理を1件だけ実行したときのレイテンシと、100件を並行実行したときの平均レイテンシを比較し、並行度が上がったときに1件あたりの時間がどう変化するかを記録する。
---
### KNOW-F-0491 パーセンタイル計測の型(Percentile Measurement)
**①名称**: パーセンタイル計測の型(p50/p95/p99)
**②構造の型(骨子)**:
```
用語の骨子:
p50(中央値): 全リクエストの半分がこの時間以内に完了する
p95: 95%のリクエストがこの時間以内に完了する(残り5%はもっと遅い)
p99: 99%のリクエストがこの時間以内に完了する(最も遅い1%を除く基準)
function percentile(sortedArr, p) {
const idx = Math.ceil((p / 100) * sortedArr.length) - 1
return sortedArr[Math.max(0, idx)]
}
```
**③使いどころ**: 「平均レイテンシは100msで良好」と報告されていても、実際には一部の利用者が数秒待たされている可能性を見逃さないようにしたいとき。平均だけでなく分布の裾(遅い側)を確認する。
**④組合せ例**: KNOW-F-0490(スループットとレイテンシの型)の測定値を平均でなく分布として扱う直接の発展形。KNOW-F-0481(計測ノイズ低減の型)の中央値採用は、本型のp50をそのまま応用したものである。
**⑤確認事項(罠と検算・安全注意)**: サンプル数が少ない(例えば10件しか計測していない)状態でp99を語ると、統計的な裏づけが薄い罠がある(p99を語るには最低でも100件程度、できれば1000件以上のサンプルが望ましいとされる)。検算: percentile関数に既知のソート済み配列([1,2,...,100])を渡し、p50が50付近、p99が99付近の値を返すことを確認する。
---
### KNOW-F-0492 負荷テストの型(Load Testing)
**①名称**: 負荷テストの型
**②構造の型(骨子)**:
```
段階の骨子:
平常負荷テスト: 想定される通常のアクセス数で問題なく動くかを確認
高負荷テスト: 想定の何倍(例: 10倍)まで耐えられるかを確認
限界点テスト: どこまで負荷をかけると応答が破綻するかを見極める
計測: 負荷を段階的に上げながら、レイテンシ(KNOW-F-0491)とエラー率を記録する
```
**③使いどころ**: 本番投入前に、想定される最大アクセス数に耐えられるかを事前に確認したいとき。KNOW-F-0469(競合状態デバッグの型)で扱った競合状態も、負荷をかけて初めて顕在化することが多い。
**④組合せ例**: KNOW-F-0469(競合状態デバッグの型)の不具合を意図的に誘発する手段として直接接続する。KNOW-F-0491(パーセンタイル計測の型)を負荷テストの結果分析にそのまま適用する。
**⑤確認事項(罠と検算・安全注意)**: 本番環境そのものに対して負荷テストを行うと、実際の利用者に影響を与える危険がある(必ず隔離されたテスト環境で行う、安全上の注意)。検算: 負荷テストの結果、エラー率が0%から上昇し始める負荷の水準(限界点)を記録し、想定される最大アクセス数に対して十分な余裕があるかを確認する。
---
### KNOW-F-0493 継続的計測の型(Continuous Measurement)
**①名称**: 継続的計測の型(回帰の早期検知)
**②構造の型(骨子)**:
```
運用の骨子:
コードの変更のたびに(あるいは毎日定時に)ベンチマーク(KNOW-F-0479)を自動実行する
結果を記録し、前回の結果と比較する
一定以上悪化していたら警告する(性能の回帰=regressionを検知する)
```
**③使いどころ**: KNOW-F-0428(回帰テストの型)が「正しさの回帰」を防ぐように、「速さの回帰」(ある変更で意図せず遅くなった)も同じように機械的に見張りたいとき。
**④組合せ例**: KNOW-F-0428(回帰テストの型)の性能版という直接の対応関係にある。KNOW-F-0481(計測ノイズ低減の型)は、日々の計測結果が計測ノイズによる誤検知(実際には遅くなっていないのに警告が出る)を防ぐための前提技術。
**⑤確認事項(罠と検算・安全注意)**: 計測環境(実行するマシン)が毎回異なると、環境差(KNOW-F-0473)がノイズとして計測結果に混入し、誤った警告や見逃しを招く罠がある(可能な限り同一の計測環境を使い続ける)。検算: 意図的に1件、速度を悪化させる変更を仕込み、継続的計測の仕組みが実際にその悪化を検知して警告を出すかをリハーサルする。
---
### KNOW-F-0494 計測用フラグの型(Measurement Feature Flag)
**①名称**: 計測用フラグの型(フィーチャーフラグ計測)
**②構造の型(骨子)**:
```
function checkout(order, flags) {
if (flags.useNewPricingEngine) {
return newCalcTotal(order) // 新実装
}
return calcTotal(order) // 旧実装
}
// 一部の利用者にだけflags.useNewPricingEngine=trueを配り、
// 新旧それぞれの実測レイテンシを比較する
```
**③使いどころ**: 新しい実装を全利用者に一斉展開する前に、一部にだけ有効化し、実際の利用状況下での性能を比較したいとき。KNOW-F-0397(ストラングラー分離の型)の考え方を計測の目的に応用したもの。
**④組合せ例**: KNOW-F-0397(ストラングラー分離の型)の分岐構造をそのまま流用できる。KNOW-F-0495(A/Bベンチマークの型)は、本型で用意した新旧の分岐を、統計的に比較する後段の型として直接接続する。
**⑤確認事項(罠と検算・安全注意)**: フラグの分岐条件(どの利用者に新実装を配るか)に偏りがある(例えば特定の地域だけに配ってしまう)と、比較そのものが公正でなくなる罠がある(KNOW-F-0496計測再現性確保の型で条件を揃える)。検算: 新実装群と旧実装群それぞれの利用者数・平均的な注文内容が、明らかな偏りなく分布しているかを確認してから比較を行う。
---
### KNOW-F-0495 A/Bベンチマークの型(A/B Benchmark)
**①名称**: A/Bベンチマークの型
**②構造の型(骨子)**:
```
比較の骨子:
群A(旧実装): 平均レイテンシ 120ms, p95 300ms, サンプル数5000件
群B(新実装): 平均レイテンシ 95ms, p95 250ms, サンプル数5000件
差: 群Bが平均で25ms速いが、この差が偶然のばらつきでなく
本当の改善と言えるかを統計的に確認する必要がある
```
**③使いどころ**: KNOW-F-0494(計測用フラグの型)で新旧2つの実装を並行運用した結果を、「たまたま速く見えただけ」ではなく実際に有意な差かを判断したいとき。
**④組合せ例**: KNOW-F-0494(計測用フラグの型)の直接の後続分析。KNOW-F-0491(パーセンタイル計測の型)の指標(p50/p95/p99)を群Aと群Bそれぞれで計測し、平均だけでなく分布全体を比較するのが望ましい。
**⑤確認事項(罠と検算・安全注意)**: サンプル数が少ないと、偶然のばらつきを「改善」と誤認する罠がある(統計的な有意性の判定には専門的な検定手法が必要であり、本冊では手法の存在とその重要性の指摘に留め、詳細は専門書に譲る=誇大な断定を避ける)。検算: 群Aと群Bのサンプル数がおおむね同数であり、比較期間(時間帯・曜日)が偏っていないかを確認する。
---
### KNOW-F-0496 計測再現性確保の型(Measurement Reproducibility)
**①名称**: 計測再現性確保の型
**②構造の型(骨子)**:
```
再現性を確保するための条件の骨子:
同じ入力データを使う(KNOW-F-0430フィクスチャ設計の型を計測にも流用する)
同じ実行環境を使う(KNOW-F-0473環境差分切り分けの型で差分がないことを確認済みにする)
同じ手順・回数で計測する(KNOW-F-0481のウォームアップ・試行回数を固定する)
計測条件を記録として残す(いつ・何を・どうやって測ったか)
```
**③使いどころ**: 「先週測ったときは速かったのに、今日測ったら違う数字が出た」という食い違いに遭遇したとき、あるいは最初から食い違いが起きないように計測手順を設計するとき。
**④組合せ例**: 第五章の計測系の型(KNOW-F-0479〜0495)すべてに横断的に関わる土台の型。KNOW-F-0429(決定性テストの型)の計測版にあたり、「同じ条件なら同じ結果」という原則を計測作業に適用したもの。
**⑤確認事項(罠と検算・安全注意)**: 「環境が同じはず」という思い込みに頼らず、実際にKNOW-F-0473の一覧表(OS・ライブラリバージョン・データ内容など)で毎回照合しないと、再現性が崩れていることに気づけない罠がある。検算: 同一の計測条件を記録した手順書に従って、異なる日に2回計測し、結果の差が許容範囲(KNOW-F-0481で定めた基準)に収まるかを確認する。
---
### KNOW-F-0497 資源コスト計測の型(Resource Cost Measurement)
**①名称**: 資源コスト計測の型(時間以外の資源)
**②構造の型(骨子)**:
```
計測対象の骨子(時間以外の資源):
メモリ使用量(KNOW-F-0484)
ディスクI/O回数・書き込み量
ネットワーク通信量(送受信バイト数)
外部APIの呼び出し回数(従量課金がある場合は金銭コストに直結する)
同時接続数・スレッド数
```
**③使いどころ**: 「速さ」だけでなく、「どれだけの資源を消費して速さを得ているか」を評価したいとき。速い実装が実は大量のメモリや外部API呼び出しを犠牲にしている場合、トレードオフとして計測しておく必要がある。
**④組合せ例**: KNOW-F-0484(メモリプロファイリングの型)は本型のメモリに関する具体形。KNOW-F-0498(トレードオフ可視化の型)は、本型で集めた複数の資源指標を1つの比較表にまとめる直接の後続の型。
**⑤確認事項(罠と検算・安全注意)**: 時間の計測にばかり注目し、外部APIの呼び出し回数(課金対象になりうる)を見落とすと、速度は改善したのに運用コストが増大するという本末転倒が起きる罠がある。検算: 最適化の前後で、外部API呼び出し回数・通信量を実際に記録し、時間の改善幅とあわせて総合的に評価する。
---
### KNOW-F-0498 トレードオフ可視化の型(Trade-off Visualization)
**①名称**: トレードオフ可視化の型
**②構造の型(骨子)**:
```
比較表の骨子:
| 実装案 | レイテンシ | メモリ使用量 | 実装の複雑さ |
|---------|-------------|----------------|----------------|
| 案A(素朴)| 120ms | 少ない | 低い |
| 案B(キャッシュ導入)| 40ms | 多い(KNOW-F-0470要注意) | 中程度 |
| 案C(並行処理化)| 60ms | 中程度 | 高い(KNOW-F-0469要注意) |
```
**③使いどころ**: 複数の最適化案があり、それぞれ得意・不得意が異なるとき。「速さ」という1つの指標だけで即決せず、資源消費や実装の複雑さ(将来の保守コスト)も並べて比較したうえで選びたいとき。
**④組合せ例**: KNOW-F-0497(資源コスト計測の型)の各指標を1つの表にまとめる直接の集約先。KNOW-F-0400(モジュール粒度調整の型)・KNOW-F-0487(早すぎる最適化を避ける型)で扱った「複雑さのコスト」を、数値として比較表に載せる実践形。
**⑤確認事項(罠と検算・安全注意)**: 表に並べる指標を都合よく選び、不利な指標(例えば案Bのメモリ使用量)を意図的に省くと、判断を誤らせる罠がある(実測できる指標はできるだけ揃えて並べる誠実さが必要)。検算: 表に記載した各数値が、実際にKNOW-F-0479〜0497のいずれかの型で実測された値に基づいているか(推測や希望的観測が紛れていないか)を出典つきで確認する。
---
### KNOW-F-0499 最適化効果検証の型(Before/After Verification)
**①名称**: 最適化効果検証の型(前後比較)
**②構造の型(骨子)**:
```
検証の骨子:
1. 最適化前の計測値を記録する(KNOW-F-0496の再現性を確保した条件で)
2. 最適化を実施する
3. 全く同じ条件で最適化後の計測値を記録する
4. 差を計算し、KNOW-F-0476で立てた目標(例: 2倍速く)を達成したかを判定する
5. KNOW-F-0428の回帰テストで、速度以外の正しさが壊れていないことも確認する
```
**③使いどころ**: 最適化作業を「やった気になる」で終わらせず、実際に効果があったことを数値で示したいとき。章の締めくくりとして、KNOW-F-0476(計測先行原則)が説いた「計測してから最適化し、また計測する」というサイクルを完成させる。
**④組合せ例**: KNOW-F-0476(計測先行原則の型)とKNOW-F-0480(マクロベンチマークの型)を組み合わせた実践の型。KNOW-F-0428(回帰テストの型)は、最適化によって速くなった代わりに正しさが壊れていないかを保証する必須の相棒。
**⑤確認事項(罠と検算・安全注意)**: 「速くなった」と主張するときに、比較条件(入力データ・実行環境)が最適化の前後で微妙に違っていないかを確認しないと、誤った結論を報告してしまう罠がある(誇大表現禁止の原則どおり、条件を明記した実測値のみを断言する)。検算: 最適化前後の計測で使った入力データが完全に同一であること、および実行環境(OS・負荷状況)がKNOW-F-0473の意味で揃っていることを確認する。
---
### KNOW-F-0500 計測結果文書化の型(Measurement Documentation)
**①名称**: 計測結果の文書化の型
**②構造の型(骨子)**:
```
記録テンプレートの骨子:
計測日時 / 計測環境(OS・バージョン・負荷状況)
計測対象(関数名・シナリオ名)
計測方法(KNOW-F-0479〜0492のどの型を使ったか)
結果(数値・サンプル数・分布)
結論(何が分かったか、次に何をすべきか)
```
**③使いどころ**: 計測して分かった結果を、実行した本人の頭の中だけに留めず、チーム全体の資産として残したいとき。半年後に「あのとき何を計測してどう判断したか」を再確認できるようにする。
**④組合せ例**: 第五章全体(KNOW-F-0476〜0499)の実行結果を記録する共通の受け皿として、章の最終項目に位置づけられる。KNOW-F-0475(ポストモーテムの型)・KNOW-F-0474(バグ票記法の型)と同じ「後で読む人のための最小限の文脈を残す」という思想を共有しており、本冊全体(第三・第四・第五章)を貫く共通の型でもある。
**⑤確認事項(罠と検算・安全注意)**: 数値だけを記録し、計測方法や環境の記録を省くと、後から見た人がその数値を信頼してよいか判断できなくなる罠がある(数値合わせの切除=結果に都合の悪いデータだけを記録から外す行為は誠実性に反するため厳禁とする)。検算: 記録された文書だけを読んで、計測を実行した本人以外が同じ手順を再現できるだけの情報が揃っているかを確認する。
---
## 終章 合成と堅牢化——五つの型がどう連動するか
ここまでの五章は、独立した5つの技術領域として並べたが、実務では一連の流れとして連動する。終章では、その連動をKNOW-F-0376〜0500全体を俯瞰する形で3つの観点からまとめ、関数ノウハウ500(目録I〜IV・KNOW-F-0001〜0500)の完結にあたっての締めくくりとする。
### 終-1 モジュール分割・純化は「壊れにくさ」の設計、テスト・デバッグ・計測は「壊れていないことの確認」
第一章(モジュール分割)と第二章(純化)は、コードを書く時点で「壊れにくい構造」をあらかじめ作り込む型である。依存の向きを揃え(KNOW-F-0379)、循環を断ち(KNOW-F-0384)、副作用を端に押し出し(KNOW-F-0402)、同じ入力なら同じ出力になるようにする(KNOW-F-0401)。これらは「事前の設計」であり、コードが実際に正しく動くかどうかをまだ確認していない段階の作業である。
第三章(テスト)・第四章(デバッグ)・第五章(計測)は、その設計が実際に機能しているかを「事後に確認する」型である。テストは正しさを、デバッグは不具合の所在を、計測は速さと資源消費を、それぞれ数値と機械的な手順で確認する。CATALOG §0.5の「段落を崩しても通る」という要請は、第一・第二章の型で構造的に実現され、第三・第四・第五章の型で「本当に崩れていないか」が検証される、という二段構えの関係にある。設計だけで「きっと大丈夫」と言い切らず、必ず確認の型を伴わせることが、本冊が「特許請求の範囲になぞらえた新規性」(CATALOG §0)として掲げる誠実さの実務的な意味である。
### 終-2 「段落を崩しても通る」の三要件と五章の対応
CATALOG §0.5で予告した三要件(順序独立性・参照透過性・冪等性)は、第二章のKNOW-F-0416(段落順序独立の型)・KNOW-F-0403(参照透過性の型)・KNOW-F-0404(冪等性の型)としてそれぞれ直接の型を持つ。この三要件は第二章だけに閉じず、他の四章にも次のように波及する。
- 第一章: 依存の向きが揃った(循環のない)モジュール構成は、どのモジュールから読み始めても迷わないという意味で「順序独立」を構造レベルで支える(KNOW-F-0379・KNOW-F-0398)。
- 第三章: テストの独立性(KNOW-F-0442)は、テストケースをどの順に実行しても結果が変わらないという意味で、順序独立性をテストコード自身に適用したものである。
- 第四章: 冪等なリトライ(KNOW-F-0417)・冪等キー(KNOW-F-0418)は、冪等性を実運用の通信の信頼性に応用した形である。
- 第五章: 計測再現性確保の型(KNOW-F-0496)は、同じ条件なら同じ計測結果になるという意味で、参照透過性を計測作業そのものに適用したものである。
このように、三要件は本冊全体を貫く一本の糸として機能する。詳しい解釈と数学的背景は脊椎BOOK-0358fの解釈表を参照されたい。
### 終-3 次への接続——関数ノウハウ500の完結とPC構造ノウハウへの橋
本冊(BOOK-0358n)の完成をもって、KNOW-F-0001〜0500(目録I〜IV、関数ノウハウ500)が完結する。目録I(BOOK-0358k)の命名・引数設計から出発し、目録II(BOOK-0358l)の分岐と反復、目録III(BOOK-0358m)のデータ変換、そして本冊(目録IV)の合成と堅牢化まで、「一つの値を返す小さな関数」から「壊れにくい大きなシステム」までを一本道でたどれる構成になっている。
CATALOG §2の部構成表が示すとおり、関数ノウハウ500の次はPC構造ノウハウ1000(目録V〜X、KNOW-P-0001〜1000)へと接続する。第一章で扱ったモジュール分割の型(単一責務・依存の向き・循環回避)は、そのままPC部品の設計思想(電源ユニットとマザーボードが「どちらに依存すべきか」、拡張カードが「単一責務を持つか」)にも比喩として応用できる。第五章の計測の型(計測が最適化に先行する原則)は、CATALOG §0.4「熱=電源解析請求項」が扱う熱設計の実測(TDP・エアフロー・温度)にもそのまま接続する考え方である。物語脊椎ではBOOK-0358f(関数を正式に組む)からBOOK-0358g(OSを作る実践)への接続にあたり、本冊で身につけた合成と堅牢化の型が、実際にOSという巨大なシステムを組み立てる際の土台になる。
**誇大表現についての確認**: 本冊で示した各型は、「正しく適用すればこの効能が期待できる」という実務上の型であり、CATALOG §3の安全枠にある「必ず〜できる」でなく「〜できる場所まで行ける」という言葉づかいを踏襲する。特に第五章の計測系の型については、実測値のみを断言し、環境や条件が異なれば数値が変わりうることを繰り返し明記した。
---
## 参考: 本冊の構成情報
- **書誌**: BOOK-0358n「PC創造大全 目録IV 合成と堅牢化の型」。「学問の宇宙」PC創造大全(BOOK-0358)の分冊、関数ノウハウ目録の第4巻・完結巻。
- **ID帯**: KNOW-F-0376〜KNOW-F-0500(125項目)。全125件に五点セット(①名称 ②構造の型〈骨子・図または式〉 ③使いどころ ④組合せ例 ⑤確認事項〈罠と検算・安全注意〉)を完備。白カード(未執筆項目)は0件。
- **章構成**: 第一章モジュール分割の型(KNOW-F-0376〜0400・25項目)/第二章純化の型(KNOW-F-0401〜0425・25項目)/第三章テストの型(KNOW-F-0426〜0450・25項目)/第四章デバッグの型(KNOW-F-0451〜0475・25項目)/第五章計測の型(KNOW-F-0476〜0500・25項目)。
- **自己機械検査(欠番・重複)**: 全125見出し(### KNOW-F-XXXX形式)を抽出し、(a)重複0件、(b)0376〜0500の連番に欠番0件、を確認済み(検査コマンドの実行結果: total ids=125, missing=[]、実測)。
- **接続**: →脊椎BOOK-0358f(関数を正式に組む・「段落を崩しても通る」解釈表の出典)/型見本=BOOK-0184(五点セット形式を継承)/前巻BOOK-0358m(データ変換の型・KNOW-F-0251〜0375)/次帯KNOW-P-0001〜1000(目録V〜X・PC構造ノウハウ、CATALOG §2参照)。
- **本冊の完結範囲**: 本冊の完成により、KNOW-F-0001〜0500(関数ノウハウ500・目録I〜IV)が全項目完備で完結する。
# BOOK-0358n PC創造大全 目録IV 合成と堅牢化の型