foundations · Stage 1

システム思考とエンジニアリングのトレードオフ

境界、制約、代替案、反証条件を結び、変化に耐える意思決定を記録する。

学習時間
210分
難易度
foundation
更新日
2026-07-30
到達証拠
成果物・説明・判断根拠・転用

到達目標

  1. 利用者、運用者、外部依存を含むシステム境界と三つ以上の制約を図示できる

    • 境界図、制約、代替案比較、反証条件、決定者を含む意思決定記録
    • 平均値による誤診を反証し、判断の更新条件を示す回答
  2. 二つ以上の代替案を遅延、整合性、運用費、可逆性の観測値で比較できる

    • 境界図、制約、代替案比較、反証条件、決定者を含む意思決定記録
    • 選ばなかった案が有利になる条件まで含む5分説明
  3. 選択案を棄却または再評価する反証条件を二つ以上定義し、未知領域で判断を再実施できる

    • 平均値による誤診を反証し、判断の更新条件を示す回答
    • 医療予約システムの新しい制約下で再構成した判断

能力の進行

  1. recognize

    提示された状況からシステム境界、利害関係者、固定制約と仮定を区別できる

    証拠: 境界図、制約、代替案比較、反証条件、決定者を含む意思決定記録

  2. explain

    局所最適が再試行や運用負荷を通じて全体へ波及する因果経路を説明できる

    証拠: 選ばなかった案が有利になる条件まで含む5分説明

  3. apply

    複数案を同じ測定軸で比較し、期限と決定者を持つ意思決定記録を作成できる

    証拠: 境界図、制約、代替案比較、反証条件、決定者を含む意思決定記録

  4. diagnose

    結果の悪化について少なくとも二つの仮説を立て、反証可能な観測で切り分けられる

    証拠: 平均値による誤診を反証し、判断の更新条件を示す回答

  5. lead

    不確実性と対立する価値を明示し、別領域の関係者と再評価可能な合意を形成できる

    証拠: 医療予約システムの新しい制約下で再構成した判断

なぜ重要か

システムとは、目的のために相互作用する要素の集合である。設計判断は一つの部品だけで完結しない。たとえば受付APIの応答を速くするために処理をキューへ移すと、画面は速く見えても、重複実行、滞留、問い合わせ対応という別の費用が生まれる。局所の平均時間だけを最適化すると、利用者が成果を受け取るまでの時間や運用者の復旧時間を悪化させ得る。

シニアエンジニアに必要なのは「正しい方式名」を当てることではない。判断対象の境界、守る制約、比較した代替案、結論を覆す証拠を、後から別の人が再評価できる形にすることである。数値を一元化しても価値の衝突は消えず、分析を行ってもリスクは消滅しない。

メンタルモデル

最初に「制御できる内部」と「契約で依存する外部」を分ける。次に、固定された制約と、まだ反証可能な仮定を分ける。最後に、選択が結果へ届く因果経路と、その途中で観測できる信号を置く。

このループでは、反証条件を決定時に書くことが重要である。障害後に都合のよい基準を選ぶと、失敗した案を正当化し続けられてしまう。各反証条件には閾値、観測期間、決定者を付ける。

判断を更新可能にする因果ループ

注記

図を読む際の補足情報です。

  1. この注記は旧図の読み順を保持する補助です。
  2. 目的: 利用者が二秒以内に受付結果を知る。
  3. 境界: Web、受付API、永続ストア、worker、通知を含め、決済事業者は外部契約とする。
  4. 制約: 重複確定は0件、受付データ損失は0件、運用当番は一名。
  5. 代替案: 同期完了、永続化後に非同期実行、負荷時だけ非同期化を比較する。
  6. 観測: p95受付時間、成果完了時間、重複率、最古滞留時間、復旧時間を測る。
  7. 反証: 観測が閾値を越えたら、境界または選択案を見直す。

目的と制約から、どの観測が判断の再評価を起動するか。

原因
制約
重複確定は0件、受付データ損失は0件、運用当番は一名。
機構
境界
Web、受付API、永続ストア、worker、通知を含め、決済事業者は外部契約とする。
代替案
同期完了、永続化後に非同期実行、負荷時だけ非同期化を比較する。
結果
目的
利用者が二秒以内に受付結果を知る。
観測
p95受付時間、成果完了時間、重複率、最古滞留時間、復旧時間を測る。
対策
反証
観測が閾値を越えたら、境界または選択案を見直す。
  • 制約 → 境界: 制約を守る評価境界を定める
  • 境界 → 代替案: 境界内で成立する選択肢を比較する
  • 代替案 → 目的: 選択肢を利用者目的に照合する
  • 代替案 → 観測: 選択の結果を測定する
  • 観測 → 反証: 閾値超過で境界または選択案を見直す

境界と制約を起点に、代替案、観測、反証条件までの因果経路を指し示せる。

動く例で考える

同期受付とキュー受付を同じ負荷で比較する

前提
毎秒20件、各処理100ms、外部依存は20回に1回だけ800ms、入力は1000件で固定する。
入力
同じrequest ID列を同期完了、受付後queue、負荷時だけqueueの三案へ投入する。
操作
受付から応答、成果完了、再試行、重複、最古滞留時間を単調時計で記録する。
観測
固定fixtureの五観測からp95応答、重複率、復旧時間、月間運用時間を再計算する。値を採点へ畳み込む前に全観測と測定条件を保存する。
結論
三案の全score、各二つ以上の反証条件、決定者、見直し日、未解決riskを同じdecision recordへ出力し、条件が変われば再計算する。

次の固定fixtureを観測コマンドへ渡せば、教材だけで元指標と点数を再計算できる。fixture名はdecision-observations.csvとし、実案件では同じ列契約の生データへ差し替える。

python3.13 - <<'PY'
import json

HARNESS = "decision_lab_v2"
WEIGHTS = {
    "latency": 0.4,
    "consistency": 0.3,
    "operations": 0.2,
    "reversibility": 0.1,
}
fixture = {
    "sync": {
        "response_ms": [120, 130, 140, 150, 920],
        "duplicates": [0, 0, 0, 0, 0],
        "requests": [1000, 1000, 1000, 1000, 1000],
        "recovery_minutes": [20, 22, 24, 25, 30],
        "monthly_operations_hours": [8, 8, 9, 8, 9],
        "reversibility_score": 3,
        "falsification_conditions": [
            "p95応答が1000msを二窓連続で超える",
            "外部依存timeoutで受付成功率が99.9%未満になる",
        ],
    },
    "queued": {
        "response_ms": [35, 38, 40, 42, 50],
        "duplicates": [0, 0, 0, 0, 2],
        "requests": [1000, 1000, 1000, 1000, 1000],
        "recovery_minutes": [45, 50, 60, 70, 80],
        "monthly_operations_hours": [18, 19, 20, 21, 22],
        "reversibility_score": 4,
        "falsification_conditions": [
            "重複率が0.02%を超える",
            "最古滞留時間が利用者期限の二倍を超える",
        ],
    },
    "adaptive_queue": {
        "response_ms": [80, 90, 110, 500, 700],
        "duplicates": [0, 0, 0, 0, 1],
        "requests": [1000, 1000, 1000, 1000, 1000],
        "recovery_minutes": [30, 35, 40, 45, 55],
        "monthly_operations_hours": [12, 13, 14, 15, 16],
        "reversibility_score": 2,
        "falsification_conditions": [
            "切替境界で同一request IDが二経路へ流れる",
            "月間運用時間が16時間を超える",
        ],
    },
}

def nearest_rank_p95(values):
    ordered = sorted(values)
    return ordered[(95 * len(ordered) + 99) // 100 - 1]

def score(option):
    raw = fixture[option]
    requests = sum(raw["requests"])
    duplicate_rate = sum(raw["duplicates"]) / requests
    metrics = {
        "p95_response_ms": nearest_rank_p95(raw["response_ms"]),
        "duplicate_rate": duplicate_rate,
        "recovery_minutes": max(raw["recovery_minutes"]),
        "monthly_operations_hours": max(
            raw["monthly_operations_hours"]
        ),
    }
    latency = (
        5 if metrics["p95_response_ms"] <= 100
        else 4 if metrics["p95_response_ms"] <= 800
        else 3
    )
    consistency = (
        5 if duplicate_rate == 0
        else 4 if duplicate_rate <= 0.0002
        else 3
    )
    operations = (
        4
        if metrics["recovery_minutes"] <= 30
        and metrics["monthly_operations_hours"] <= 10
        else 3
        if metrics["recovery_minutes"] <= 60
        and metrics["monthly_operations_hours"] <= 16
        else 2
    )
    scores = {
        "latency": latency,
        "consistency": consistency,
        "operations": operations,
        "reversibility": raw["reversibility_score"],
    }
    scores["weighted_total"] = round(
        sum(scores[name] * WEIGHTS[name] for name in WEIGHTS),
        2,
    )
    return {
        "raw": {
            name: value
            for name, value in raw.items()
            if name not in {
                "reversibility_score",
                "falsification_conditions",
            }
        },
        "metrics": metrics,
        "scores": scores,
        "falsification_conditions": raw["falsification_conditions"],
    }

options = {name: score(name) for name in fixture}
report = {
    "harness": HARNESS,
    "measurement_conditions": {
        "fixture": "decision-observations.csv",
        "sample_count": 5,
        "arrival_rate_per_second": 20,
        "request_ids_reused_across_options": True,
        "clock": "monotonic",
        "score_scale": "1..5; thresholds fixed before comparison",
        "weights": WEIGHTS,
    },
    "options": options,
    "decision_record": {
        "purpose": "利用者へ二秒以内に受付結果を返す",
        "boundary": [
            "利用者",
            "受付API",
            "worker",
            "課金基盤",
            "通知基盤",
        ],
        "selected": max(
            options,
            key=lambda name: options[name]["scores"]["weighted_total"],
        ),
        "decision_maker": "service owner",
        "review_date": "2026-08-31",
        "unresolved_risks": [
            "同期案の外部依存tail latency",
            "切替案の二経路整合性",
        ],
    },
}
print(json.dumps(report, ensure_ascii=False, indent=2))
PY

重みは遅延40%、整合性30%、運用20%、可逆性10%で、閾値も比較前に固定する。同期案は3×0.4 + 5×0.3 + 4×0.2 + 3×0.1 = 3.8、受付後queue案は5×0.4 + 3×0.3 + 2×0.2 + 4×0.1 = 3.7であり、全scoreでは同期案は3.8、キュー案は3.7、負荷時queue案は3.6となる。同期案と受付後queue案の差は0.1だが、これは真理ではない。出力には元データ、測定条件、全score、各案の反証条件、decision recordが残るため、重みや観測が変われば同じ手順で再判断できる。

ラボ成果物: 「制約、代替案、反証条件を含む意思決定記録」には、上の生データ、境界図、採否、見直し日を含める。

トレードオフと失敗モード

受付方式を選ぶdecision table
成立条件 優先する案 得るもの 反証・回復
処理が短く外部依存も安定 同期完了 単純な状態と即時の成否 p95が期限の70%を超えたら非同期案を再評価
受付期限が短く処理時間が不規則 永続化後にキュー 入口の負荷吸収 滞留時間と重複率を監視し、流量制御または停止
通常は短いが突発負荷がある 負荷時だけ切替 平常時の単純さと縮退 切替境界の二重処理を冪等keyで防ぐ

もっともらしい誤診と反証

  1. 誤診: 「平均応答が改善したので利用者体験も改善した」。反証: 同じrequest IDで受付時間と成果完了時間を結び、p95、失敗、重複、問い合わせ率を比較する。受付だけ速いなら全体改善ではない。
  2. 誤診: 「キュー滞留はworker台数不足」。反証: 到着率、処理率、外部依存待ち、毒メッセージの再試行を分解する。処理率が十分でも同じ項目が再試行していれば、増員は障害を増幅する。

回復設計では、停止、排出、再実行、補償の順序を決める。可逆な設定変更と不可逆なデータ変換を同じ承認手順にしない。未観測の区間があれば「問題なし」ではなく「判断不能」と記録する。

知識チェック

  1. 受付p95が50ms、成果p95が3分のとき、どちらをSLOにすべきか。正答は用途ごとに両方を契約すること。50msだけを選ぶ誤答は、画面の速さが最も見えやすいためもっともらしい。
  2. 全指標を一つの点数へ集約すれば合意できるか。正答は「重みの根拠と元指標を残すなら比較補助になる」。点数だけでよいという誤答は客観的に見えるが、価値判断を隠す。
  3. 反証条件「遅くなったら見直す」の不足は何か。閾値、期間、対象指標、決定者がなく実行不能である。

5分teach-back

境界図だけを見せ、目的、二つの代替案、採用案の因果経路、選ばなかった案が有利になる条件、見直しを起動する一つの数値を説明する。聞き手は「境界を通知まで広げたら結論は変わるか」を質問する。

未知へのtransfer

医療予約システムで同じ判断を再実施する。二重予約0件、緊急枠、個人情報、受付期限という新しい制約を置き、同じ評価軸をそのまま流用せず、利害関係者と反証条件を作り直す。

出典と次の学習

NASA Systems Engineering Handbookは、ライフサイクル全体で目的、要求、技術リスクを反復的に結ぶ考え方の確認に使う。NIST SP 800-160 Vol.1 Rev.1は、信頼性やセキュリティを後付け機能ではなくシステム特性として扱う根拠になる。CMU SEIのATAM資料は、品質特性間のトレードオフをシナリオとリスクで議論する方法を補う。一次資料のURLは教材metadataのsourcesに集約している。

次はアルゴリズム測定で、判断記録の「証拠」を再現可能な実験へ具体化する。1日後は反証条件、7日後は別の境界、30日後は実案件の記録、90日後は結論が変わった事例を再レビューする。

実践ラボ

同期受付と非同期キューを比較する意思決定レビュー

提出成果物: 制約、代替案、反証条件を含む意思決定記録

  1. 利用者、運用者、課金基盤、通知基盤を境界図に置き、成功指標と制約を分離する
  2. 同期完了、受付後キュー処理、負荷時のみキュー処理の三案を同じ入力で比較する
  3. p95応答時間、重複率、復旧時間、月間運用時間を測定し、値と測定条件を記録する
  4. 各案が有利になる条件と、選択案を棄却する反証条件を二つ以上書く
  5. 決定者、見直し日、未解決リスクを付けてレビュー可能な一ページへまとめる

説明して理解を確かめる

5分で境界図を示し、採用案の因果経路、選ばなかった案が勝つ条件、次に判断を見直す観測値を説明する。

アセスメント

  1. 問い: 平均応答が400msから250msへ改善した一方、問い合わせが倍増した。『性能は改善した』という診断をどう反証するか。

    期待する証拠: p95またはp99、受付後の完了時間、失敗率、再試行、重複、利用者別分布を境界図と結び付けた切り分け

  2. 問い: 意思決定記録に必ず残すべき反証条件を、数値閾値と観測期間を含めて二つ示す。

    期待する証拠: 案の前提に直接対応し、誰がいつ再評価するかまで実行可能な条件

別問題へ転用する

医療予約システムで同じ判断を再実施

復習スケジュール

  1. 1日後

    決定を支える最も壊れやすい仮定と、その反証観測は何か

  2. 7日後

    境界を一つ広げると、選択案の費用とリスクはどう変わるか

  3. 30日後

    選ばなかった案が有利になる具体的な負荷または規制条件は何か

  4. 90日後

    決定を支える最も壊れやすい仮定と、その反証観測は何か

評価ルーブリック

4段階の評価基準
観点未達発展途上熟達卓越
technical-correctness同期処理とキューの境界を混同し、完了保証や重複の説明に誤りがある主要な処理経路は正しいが、再試行または外部依存の状態遷移が欠ける受付、永続化、実行、通知の境界と失敗時の状態を一貫して説明する部分失敗、補償、観測不能区間まで含め、反例でも境界図の整合性を保つ
judgment一案を唯一解として提示し、制約、代替案、再評価条件がない二案を比較するが、利害関係者の衝突または可逆性を扱っていない制約に優先順位を付け、代替案と反証条件から期限付きの判断を導く二次効果と不確実性を定量化し、段階導入で判断の可逆性を高める
evidence印象または平均値だけで結論を出し、測定条件を示さない複数指標を示すが、入力条件と観測期間が揃っていない同じ負荷条件の分布、失敗率、運用記録で案と反証を支える測定誤差、欠測、標本の偏りを明示し、結論が変わる感度を検討する
communication結論だけで、対象範囲、決定者、実行担当が分からない背景と結論は読めるが、選ばなかった案と未解決リスクが見えない一ページで境界、比較、決定、反証条件、見直し日を追跡できる利用者、運用、事業の異なる関心へ同じ判断根拠を誤解なく翻訳する

出典

以下の外部資料は利用者が選択したときだけ開きます。