lead · Stage 5

チームインターフェースで社会技術アーキテクチャを設計する

ownership、dependency、cognitive load、SLOを明示したteam interfaceを作り、組織構造とsoftware境界が互いへ与える影響を観測可能にする。

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

到達目標

  1. team ownership、owned capability、dependency、SLOを一つのinterfaceへ記述できる

    • 所有権、依存、認知負荷、SLOを含むチームインターフェース
    • 曖昧なownershipと過剰なcognitive loadを個人努力で補わない設計判断
  2. assigned cognitive loadをcapacityと比較し、境界変更かenablementかを判断できる

    • 所有権、依存、認知負荷、SLOを含むチームインターフェース
    • Conway、DORA、Team Topologiesを組織図ではなくdelivery境界へ結ぶ5分発表
  3. dependency latencyだけを変え、同じteam snapshotからSLOとenablement判断を再評価できる

    • 所有権、依存、認知負荷、SLOを含むチームインターフェース
    • dependency latencyだけを変えた同一team snapshotのSLO再評価

能力の進行

  1. recognize

    team、capability、dependency、SLO、cognitive loadの境界を識別できる

    証拠: 所有権、依存、認知負荷、SLOを含むチームインターフェース

  2. explain

    communication pathとsoftware構造が相互に制約するConwayの観点を説明できる

    証拠: Conway、DORA、Team Topologiesを組織図ではなくdelivery境界へ結ぶ5分発表

  3. apply

    owner、依存先、期待値、観測値、enablement actionをteam interfaceへ結べる

    証拠: 所有権、依存、認知負荷、SLOを含むチームインターフェース

  4. diagnose

    共有ownership、同期依存、認知負荷超過、SLO不在を個人の遅さと誤診せず反証できる

    証拠: 曖昧なownershipと過剰なcognitive loadを個人努力で補わない設計判断

  5. lead

    別teamが自律的にdeliveryできるdependency契約とenablementを共同運営できる

    証拠: dependency latencyだけを変えた同一team snapshotのSLO再評価

なぜ重要か

software architectureはmoduleだけで決まらない。誰がどのcapabilityを所有し、どのteamへ依存し、どれだけのcognitive loadを引き受け、どのSLOを約束するかが、変更速度とreliabilityを同時に制約する。

Conwayが示したcommunication structureとsystem designの関係は、組織図をそのままcodeへ写す命令ではなく、望むarchitectureに合うinteractionを意図的に設計する問いである。DORAのloosely coupled teamsは、他teamとの細かな調整なしにtest、deploy、変更できる境界を重視する。Team Topologiesはownership、cognitive load、team interactionを具体化する語彙を提供する。

したがってteam interfaceは担当者一覧ではない。owned capability、dependency、期待値、観測値、SLO、enablement actionを別teamがreviewできる社会技術的な契約である。

メンタルモデル

team interfaceを、ownerが提供するcapability、receiving teamが持つdependency、双方が観測するSLO、変更を理解するためのcognitive load、境界を改善するenablementの組として捉える。

dependencyをゼロにすることが目的ではない。同期調整が必要な箇所を小さくし、残る依存を観測可能な契約へ変える。cognitive loadがcapacity内でもSLO breachならdeliveryは止まり得るため、teamとserviceの両方を同時に見る。

team ownershipからdependency SLOとcontributor enablementへ至る社会技術interface

注記

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

  1. この注記は旧図の読み順を保持する補助です。
  2. Ownership: checkout teamがcheckout capabilityのdecision rightを持つ。
  3. Dependency: checkoutはplatform capabilityへ依存する。
  4. Cognitive load: assigned領域をcapacityと比較し、過負荷を個人努力へ隠さない。
  5. SLO: dependency latencyのtargetとobservedを同じ単位で評価する。
  6. Enablement: 各interface snapshotから判断を再計算し、healthyならmonitor、breachedならescalateとしてcheckoutの自律判断を増やす。

ownershipとdependencyの設計は、認知負荷とdelivery SLOへどう現れるか。

delivery interface
team境界とsoftware dependencyを同じsnapshotで評価する社会技術範囲。
  • Ownership
    checkout teamがcheckout capabilityのdecision rightを持つ。
    component
    delivery interface
  • Dependency
    checkoutはplatform capabilityへ依存する。
    component
    delivery interface
  • Cognitive load
    assigned領域をcapacityと比較し、過負荷を個人努力へ隠さない。
    component
    delivery interface
  • SLO
    dependency latencyのtargetとobservedを同じ単位で評価する。
    component
    delivery interface
  • Enablement
    各interface snapshotから判断を再計算し、healthyならmonitor、breachedならescalateとしてcheckoutの自律判断を増やす。
    component
    delivery interface
  • Ownership → Dependency: checkout capabilityのdecision rightとplatform依存を分ける
  • Ownership → Cognitive load: assigned領域をcapacityと比較する
  • Dependency → SLO: dependency latencyのtargetとobservedを比較する
  • Cognitive load → Enablement: capacity内かをenablement判断へ渡す
  • SLO → Enablement: dependency SLO statusをenablement判断へ渡す

checkoutとplatformの依存を、decision right、認知負荷、dependency latency、enablement判断まで結んで説明できる。

動く例で考える

checkoutからplatformへのdependency latencyを再評価する

前提
checkout、platformの2 teamからなるlesson-defined simulated snapshotを使う。実組織のreporting lineやperformance評価ではない。
入力
canonical team order、owned capability、dependency、assigned/capacity、dependency latency 200ms、SLO target 300msを与える。
操作
baselineとtransferで独立したteam snapshotからdependency、cognitive load、SLO、enablementを全て再計算する。transferではdependency latencyだけを450msへ変える。
観測
baselineはhealthyかつmonitor、transferはbreachedかつescalateになる。team ownership、dependency、cognitive load snapshotは同じなので、判断変化をlatencyへ限定できる。
結論
report生成というcommand successと、自律的deliveryを支えるsystem outcomeは別である。各snapshotから再計算したinterfaceと、独立invariantで照合したmonitor/escalate判断をownerとreceiving teamがreviewできて初めてteam interfaceが働く。
python3.13 - <<'PY'
import json

HARNESS = "team_interface_lab_v1"
TEAM_ORDER = ("checkout", "platform")
BASELINE_TEAM_CONTEXT = {
    "dependency_latency_ms": 200,
    "slo_target_ms": 300,
}
TRANSFER_TEAM_CONTEXT = {
    "dependency_latency_ms": 450,
    "slo_target_ms": 300,
}
TEAMS = [
    {
        "team_id": "checkout",
        "owned_capability": "checkout",
        "dependencies": ["platform"],
        "cognitive_load": {
            "assigned": 3,
            "capacity": 4,
        },
        "enablement_action": "consume published latency evidence",
    },
    {
        "team_id": "platform",
        "owned_capability": "runtime-platform",
        "dependencies": [],
        "cognitive_load": {
            "assigned": 2,
            "capacity": 4,
        },
        "enablement_action": "publish latency evidence and escalation path",
    },
]
COMMAND_INPUT = {"exit_code": 0}

def is_nonempty_text(value):
    return type(value) is str and bool(value)

def context_difference(baseline, transferred):
    return [
        field
        for field in sorted(set(baseline) | set(transferred))
        if (
            field not in baseline
            or field not in transferred
            or baseline[field] != transferred[field]
        )
    ]

def validate_team_contexts():
    allowed_fields = {"dependency_latency_ms", "slo_target_ms"}
    for context in (
        BASELINE_TEAM_CONTEXT,
        TRANSFER_TEAM_CONTEXT,
    ):
        if (
            type(context) is not dict
            or set(context) != allowed_fields
            or type(context["dependency_latency_ms"]) is not int
            or context["dependency_latency_ms"] < 0
            or type(context["slo_target_ms"]) is not int
            or context["slo_target_ms"] <= 0
        ):
            raise AssertionError(
                "team-interface-transfer-invariant: "
                "invalid dependency context schema"
            )
    if context_difference(
        BASELINE_TEAM_CONTEXT,
        TRANSFER_TEAM_CONTEXT,
    ) != ["dependency_latency_ms"]:
        raise AssertionError(
            "team-interface-transfer-invariant: "
            "transfer changed wrong field"
        )

def validate_team_snapshot(teams):
    team_fields = {
        "team_id",
        "owned_capability",
        "dependencies",
        "cognitive_load",
        "enablement_action",
    }
    basic_shape = (
        type(teams) is list
        and bool(teams)
        and all(
            type(team) is dict
            and set(team) == team_fields
            for team in teams
        )
    )
    team_ids = (
        [team["team_id"] for team in teams]
        if basic_shape
        else []
    )
    known_team_ids = set(team_ids)
    valid = (
        basic_shape
        and all(
            is_nonempty_text(team["team_id"])
            and is_nonempty_text(team["owned_capability"])
            and type(team["dependencies"]) is list
            and len(set(team["dependencies"]))
            == len(team["dependencies"])
            and all(
                is_nonempty_text(dependency)
                and dependency in known_team_ids
                and dependency != team["team_id"]
                for dependency in team["dependencies"]
            )
            and type(team["cognitive_load"]) is dict
            and set(team["cognitive_load"])
            == {"assigned", "capacity"}
            and type(team["cognitive_load"]["assigned"]) is int
            and team["cognitive_load"]["assigned"] >= 0
            and type(team["cognitive_load"]["capacity"]) is int
            and team["cognitive_load"]["capacity"] > 0
            and team["cognitive_load"]["assigned"]
            <= team["cognitive_load"]["capacity"]
            and is_nonempty_text(team["enablement_action"])
            for team in teams
        )
    )
    if (
        not valid
        or tuple(team_ids) != TEAM_ORDER
        or len(set(team_ids)) != len(team_ids)
    ):
        raise AssertionError(
            "team-interface-input-invariant: invalid team snapshot"
        )

def validate_all_inputs():
    if TEAM_ORDER != ("checkout", "platform"):
        raise AssertionError(
            "team-interface-input-invariant: invalid canonical team order"
        )
    validate_team_contexts()
    validate_team_snapshot(TEAMS)
    if (
        type(COMMAND_INPUT) is not dict
        or set(COMMAND_INPUT) != {"exit_code"}
        or type(COMMAND_INPUT["exit_code"]) is not int
    ):
        raise AssertionError(
            "team-interface-input-invariant: invalid command input"
        )

def independent_snapshot(value):
    # baselineとtransferを別のfull snapshotにして、片側の変更が
    # ownershipやcognitive loadへ漏れるfalse comparisonを防ぐ。
    return json.loads(json.dumps(value, ensure_ascii=False))

def evaluate_slo(dependency_latency_ms, slo_target_ms):
    if dependency_latency_ms <= slo_target_ms:
        return "healthy"
    return "breached"

def build_team_interface(teams, context):
    owner = next(
        team
        for team in teams
        if team["team_id"] == "checkout"
    )
    dependency_latency_ms = context["dependency_latency_ms"]
    slo_target_ms = context["slo_target_ms"]
    slo_status = evaluate_slo(dependency_latency_ms, slo_target_ms)
    return {
        "owner": owner["team_id"],
        "owned_capability": owner["owned_capability"],
        "dependencies": list(owner["dependencies"]),
        "cognitive_load": independent_snapshot(
            owner["cognitive_load"]
        ),
        "slo": {
            "target": slo_target_ms,
            "observed": dependency_latency_ms,
            "status": slo_status,
        },
    }

def evaluate_enablement_decision(team_interface):
    if (
        team_interface["slo"]["status"] == "healthy"
        and team_interface["cognitive_load"]["assigned"]
        <= team_interface["cognitive_load"]["capacity"]
    ):
        return "monitor"
    return "escalate"

def build_contributor_enablement(
    teams,
    team_interface,
):
    platform = next(
        team
        for team in teams
        if team["team_id"] == "platform"
    )
    enablement_decision = evaluate_enablement_decision(team_interface)
    return {
        "other_contributor": team_interface["owner"],
        "receiving_team": team_interface["owner"],
        "enabling_team": platform["team_id"],
        "decision": enablement_decision,
        "action": (
            enablement_decision
            + ": "
            + platform["enablement_action"]
        ),
        "review_evidence": {
            "dependency": team_interface["dependencies"][0],
            "observed_ms": team_interface["slo"]["observed"],
            "target_ms": team_interface["slo"]["target"],
            "status": team_interface["slo"]["status"],
        },
    }

def validate_enablement_recomputation(
    team_interface,
    enablement,
):
    expected_decision = (
        "monitor"
        if team_interface["slo"]["status"] == "healthy"
        else "escalate"
    )
    evidence = enablement["review_evidence"]
    if (
        enablement["decision"] != expected_decision
        or evidence["dependency"]
        != team_interface["dependencies"][0]
        or evidence["observed_ms"]
        != team_interface["slo"]["observed"]
        or evidence["target_ms"]
        != team_interface["slo"]["target"]
        or evidence["status"]
        != team_interface["slo"]["status"]
    ):
        raise AssertionError(
            "team-interface-enablement-invariant: "
            "enablement not independently derived"
        )

def main():
    validate_all_inputs()
    baseline_context = independent_snapshot(BASELINE_TEAM_CONTEXT)
    transferred_context = independent_snapshot(
        TRANSFER_TEAM_CONTEXT
    )
    baseline_teams = independent_snapshot(TEAMS)
    transferred_teams = independent_snapshot(TEAMS)
    validate_team_snapshot(baseline_teams)
    validate_team_snapshot(transferred_teams)
    if (
        baseline_teams is transferred_teams
        or baseline_teams[0] is transferred_teams[0]
        or baseline_teams != transferred_teams
    ):
        raise AssertionError(
            "team-interface-transfer-invariant: team snapshots drifted"
        )
    baseline_interface = build_team_interface(
        baseline_teams,
        baseline_context,
    )
    transferred_interface = build_team_interface(
        transferred_teams,
        transferred_context,
    )
    if (
        baseline_interface["slo"]["status"] != "healthy"
        or transferred_interface["slo"]["status"] != "breached"
        or baseline_interface["slo"]["status"]
        == transferred_interface["slo"]["status"]
    ):
        raise AssertionError(
            "team-interface-slo-invariant: SLO not input-derived"
        )
    changed_fields = context_difference(
        baseline_context,
        transferred_context,
    )
    baseline_enablement = build_contributor_enablement(
        baseline_teams,
        baseline_interface,
    )
    transferred_enablement = build_contributor_enablement(
        transferred_teams,
        transferred_interface,
    )
    validate_enablement_recomputation(
        baseline_interface,
        baseline_enablement,
    )
    validate_enablement_recomputation(
        transferred_interface,
        transferred_enablement,
    )
    if (
        baseline_enablement["decision"] != "monitor"
        or transferred_enablement["decision"] != "escalate"
    ):
        raise AssertionError(
            "team-interface-enablement-invariant: "
            "decision did not change with SLO"
        )
    report = {
        "harness": HARNESS,
        "team_interface": baseline_interface,
        "contributor_enablement": baseline_enablement,
        "dependency_latency_transfer": {
            "changed_fields": changed_fields,
            "same_team_snapshot": baseline_teams
            == transferred_teams,
            "baseline_context": baseline_context,
            "transferred_context": transferred_context,
            "baseline_slo_status": baseline_interface["slo"][
                "status"
            ],
            "transferred_slo_status": transferred_interface["slo"][
                "status"
            ],
            "baseline_interface": baseline_interface,
            "transferred_interface": transferred_interface,
            "baseline_enablement": baseline_enablement,
            "transferred_enablement": transferred_enablement,
        },
        "input_derived_evidence": {
            "ownership": baseline_interface["owner"],
            "dependency": baseline_interface["dependencies"],
            "cognitive_load": baseline_interface[
                "cognitive_load"
            ],
            "slo": baseline_interface["slo"],
        },
        "fixture_metadata": {
            "kind": "simulated",
            "provenance": "lesson-defined checkout/platform fixture",
            "limitations": (
                "実組織のdelivery、support、incidentを観測しない"
            ),
            "synthetic_or_observed_explicit": True,
        },
        "runtime_bound": {
            "records": len(TEAMS) * 2,
            "subprocesses": 0,
            "maximum_iterations": len(TEAMS),
        },
        "external_network_used": False,
        "command_success_distinction": {
            "command_completed": COMMAND_INPUT["exit_code"] == 0,
            "system_outcome_checked": (
                baseline_interface["slo"]["status"] == "healthy"
            ),
            "command_success_equals_system_outcome": False,
            "outcome_evidence": baseline_interface["slo"],
        },
        "mastery_evidence": {
            "lab_steps": [
                {"step": 1, "evidence": baseline_teams},
                {"step": 2, "evidence": baseline_interface},
                {
                    "step": 3,
                    "evidence": baseline_interface["slo"],
                },
                {"step": 4, "evidence": baseline_enablement},
                {
                    "step": 5,
                    "evidence": {
                        "interface": transferred_interface,
                        "enablement": transferred_enablement,
                    },
                },
            ],
            "assessments": [
                {
                    "assessment": 1,
                    "evidence": baseline_enablement,
                },
                {
                    "assessment": 2,
                    "evidence": baseline_interface,
                },
            ],
            "rubric_dimensions": [
                "technical-correctness",
                "judgment",
                "evidence",
                "communication",
            ],
            "transfer": {
                "task": (
                    "依存サービスのlatencyだけを変え、同じteam "
                    "ownershipとcognitive load境界でSLOと"
                    "enablement判断を再評価する"
                ),
                "changed_assumption": "dependency-latency",
                "evidence": {
                    "changed_fields": changed_fields,
                    "baseline": baseline_interface["slo"]["status"],
                    "transferred": transferred_interface["slo"][
                        "status"
                    ],
                },
            },
            "contributor_enablement": baseline_enablement,
        },
    }
    print(json.dumps(report, ensure_ascii=False, sort_keys=True))

if __name__ == "__main__":
    try:
        main()
    except AssertionError as error:
        raise SystemExit(str(error)) from None
PY

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

dependency SLOとcognitive loadに限定したteam interface decision table
観測 判断 interaction 残す証拠
SLO healthy、loadはcapacity内 monitor serviceとして利用し観測を継続 target、observed、owner
SLO breached、ownership明確 escalate 個人評価にせず期限付きcollaboration dependency latency、impact、escalation
loadがcapacity超過 境界または責務を減らす facilitatingで移管 assigned領域、interrupt、学習時間
ownerが複数でdecision right不明 interfaceを再設計する 単一のcapability ownerを合意 決定者、support、release境界
  • 誤診: checkoutのdeliveryが遅いのでcheckout memberの生産性が低い。反証: dependency latency、SLO breach、ownership、handoff、cognitive loadを同じteam interfaceで確認する。
  • 誤診: service ownershipを全員へ割り当てればbottleneckは消える。反証: capabilityごとのdecision right、support boundary、SLO、escalationを一意にし、receiving teamが自律判断できるか観測する。

知識チェック

  1. Conwayの観点を「現在の組織図を固定する法則」と誤解すると何が起きるか。
  2. DORAのloosely coupledをdependencyゼロではなく自律的deliveryとして測るには何を見るか。
  3. cognitive loadのassignedとcapacityを、個人の能力評価からどう分離するか。
  4. SLO breach後にteam interfaceを更新するowner、evidence、終了条件を挙げよ。

出典と次の学習

DORA Capability: Loosely coupled teams、ConwayのHow Do Committees Invent?、Team Topologies: Key concepts、ISO/IEC/IEEE 42010:2022を、delivery autonomy、communication structure、team interaction、architecture descriptionの根拠として用いた。

次は自分のdelivery flowからteam間dependencyを一つ選び、owner、owned capability、cognitive load、SLO targetとobserved、enablement actionを同じteam interfaceへ書く。別teamとreviewし、latencyだけを変えたとき判断が更新されるか検証する。

実践ラボ

checkoutとplatformのteam interfaceをSLOで再評価する

提出成果物: 所有権、依存、認知負荷、SLOを含むチームインターフェース

  1. simulated team snapshot、canonical team order、dependency contextのschemaと限界を固定する
  2. checkoutのowner、owned capability、platform dependency、cognitive loadを可視化する
  3. dependency latencyとSLO targetからbaselineのstatusを導く
  4. receiving teamを助けるplatformのenablement actionとreview evidenceを記録する
  5. dependency latencyだけを変えて同じteam snapshotのSLO statusとenablement判断を再評価する

説明して理解を確かめる

5分で、DORAのloosely coupled teams、Conwayのcommunication structure、Team Topologiesのteam interactionとcognitive loadを、変更可能なteam interfaceへどう結ぶか説明する。

アセスメント

  1. 問い: checkoutのdeliveryがplatform待ちで遅い。checkout memberの速度だけを改善すべきか。

    期待する証拠: ownership、dependency latency、SLO、interaction mode、cognitive load、enablement action

  2. 問い: 全員が共有serviceを所有しているのでhandoffはないという主張を評価せよ。

    期待する証拠: 明示的owner、decision rights、support boundary、SLO、escalation、system outcome

別問題へ転用する

依存サービスのlatencyだけを変え、同じteam ownershipとcognitive load境界でSLOとenablement判断を再評価する

復習スケジュール

  1. 1日後

    ownershipを担当者名ではなくcapability境界で示す理由は何か

  2. 7日後

    cognitive loadがcapacity以下でもdependencyがdeliveryを止める例は何か

  3. 30日後

    SLO statusが変わったときteam interfaceのどこを再評価するか

  4. 90日後

    ownershipを担当者名ではなくcapability境界で示す理由は何か

評価ルーブリック

4段階の評価基準
観点未達発展途上熟達卓越
technical-correctnessteam名とservice名だけを並べるownershipとdependencyはあるがcognitive load、SLO、観測値が結び付かないteam snapshotとcontextからowner、dependency、load、SLO statusを一貫して導くschema、順序、重複、snapshot drift、causal bypassをfail closedで検出できる
judgment遅延を個人の生産性問題と断定する組織課題を挙げるが境界変更かenablementかを選ばないdependency、load、SLO、変更頻度、可逆性からinteractionを選ぶ一時的collaborationから長期API境界へ移る条件と終了条件を設計する
evidence組織図だけをarchitecture evidenceにするSLO targetはあるがobserved latencyとteam snapshotの出自がない固定入力、ownership、load、target、observed、status、transferを監査可能に残すdeliveryとreliabilityの実績でteam interfaceを継続校正できる
communication担当者へ個別に聞くことをinterfaceとする窓口はあるが期待値、期限、escalationが曖昧であるowner、capability、dependency、SLO、enablement actionを一つの契約として伝える別teamが同期meetingなしで安全に変更とreviewを進められる

出典

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