~12 min · architecture, guardrails, process, design
Level 0젖은 흙
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
규칙이 살 수 있는 자리는 다섯 군데야
"구조로 옮겨"라고 하면 동작 하나처럼 들려. 사실은 칸이 다섯인 사다리고, 지금 몇 칸에 있는지 알면 아직 뭐에 노출돼 있는지가 정확히 나와.
1칸 — 권고. 규칙이 누가 한 번 읽은 문서에 살아. 결정하는 시점엔 아무것도 다스리지 않아.
2칸 — 상기. 규칙이 작업 맥락 안에, 일이 벌어지는 바로 그 자리에 있어. 낫긴 한데 여전히 앞 강의의 주의력 다툼에서 지는 쪽이야.
3칸 — 탐지기. 사후에 검사가 돌아서 위반을 보고해. 희망 대신 증거를 내놓는 첫 칸이야. 구조적인 사각지대가 하나 있는데, 그게 하도 커서 세 번째 트랙 전체가 그 얘기야.
4칸 — 거부. 도구가 그 행위를 거절해. 경고도 아니고 확인 대화상자도 아냐. 호출이 실패하고 이유를 말해.
5칸 — 부재. 그 여지가 아예 없어. 금지된 것으로 가는 경로가 없어. 아무도 넘겨준 적이 없으니까.
1칸이랑 2칸은 규율이야. 3칸은 증거고. 4칸이랑 5칸이 구조야. 절차 실패는 대부분 4칸짜리 문제를 더 잘 쓴 1칸으로 풀려는 사람들 얘기고.
올라가는 건 공짜도 아니고 항상 옳지도 않아
칸마다 아래 칸보다 비싸고, 5칸이 자동으로 목표인 것도 아냐. 부재는 그 입력이 진짜로 필요 없을 때만 쓸 수 있어. 파일을 써야 하는 단계라면 경로를 알아야지. 작업에 필요한 걸 안 주는 건 불가능해. 그래서 실제 시스템은 규칙마다 다른 칸에 의도적으로 내려앉고, 어느 칸인지를 적어놔.
다른 비용은 과잉 제약이야. 안 걸려야 할 때 걸리는 거부는 앞으로 돌 모든 실행에 붙는 세금이고, 그 세금 내는 사람이 결국 그걸 꺼. 그러면 절차만 늘어난 채로 1칸으로 돌아가는 거지. 설계 쪽 답은 가드가 눈에 보이고 고칠 수 있는 방향으로 실패하게 만드는 거야. 너무 많이 거부하면 시끄럽게 티가 나서 고쳐지고, 너무 적게 거부하면 조용해서 한 달 뒤 감사에서 발견되거든.
이걸 싸게 만드는 수
애초에 감당 가능한 이유는 그 배치 자체가 데이터가 되기 때문이야. 파이프라인마다 자기 단계 중 어느 것이 어떤 입력을 닫는지 선언하면, 새 파이프라인에 가드를 다는 건 엔진을 갈라내는 게 아니라 레지스트리에 한 줄 넣는 일이 돼. 규칙이 템플릿 다섯 개에 흩어진 산문이길 그만두고, 하네스가 읽는 필드 하나가 되는 거야.
신경 쓰는 규칙마다 몇 칸인지 소리 내서 말해. "그건 규칙 있어"는 상태가 아냐. 1칸도 4칸도 "그건 규칙 있어"고, 둘은 그것 말고 공통점이 없어. 칸을 적어두는 게 팀이 문장 하나로 보호받고 있다고 믿는 걸 막아줘.
Code
배치는 코드가 아니라 데이터야·python
# A pipeline is a registry entry, not a subclass. Adding a guard to a
# new pipeline is a field here - the engine is never forked.
PIPELINES = {
"create": {
"template": "templates/<pipeline>.md",
"revisions_kind": "create",
"required_stages": [
"sweep", "plan-gate", "compose", "register", "ko-cold",
],
# The engine INVERTS the existence check for this one: an
# already-existing target is an error that redirects to a
# different pipeline.
"new_quest": True,
# Rung 4, scoped to one stage. `compose` has to author the
# source language, so the guard cannot cover the whole run -
# the sealed window is a SUFFIX here, and a PREFIX in the
# pipeline whose last stage reconciles both languages.
"ko_only_stages": ["ko-cold"],
},
}
def sealed_window(pipeline: dict) -> tuple[int, int] | None:
"""First closing stage through last - inclusive, because context is
cumulative. A seal that switches off mid-run protects nothing."""
stages = pipeline["required_stages"]
closing = [stages.index(s)
for s in pipeline.get("ko_only_stages", [])]
return (min(closing), max(closing)) if closing else None
팀이 실제로 신경 쓰는 규칙 셋을 골라서 각각 1 부터 5 까지 칸을 매겨. 그렇게 매긴 근거를 한 문장씩 붙이고. 그다음 제일 낮은 칸 규칙을 골라서, 4 칸 버전이 뭘 거부할지랑 그 거부가 뱉을 오류 메시지를 적어봐. 오류 메시지를 못 쓰겠으면 그 규칙이 뭘 금지하는지 강제할 만큼 정확히 모르는 거야.
Hint
오류 메시지가 진짜 시험이야. 가드는 자기가 볼 수 있는 입력만 갖고 기계적으로 판단해야 해. 초안에 "잘못된 이유로 이걸 하고 있다면" 같은 문구가 들어가면 그 규칙은 판단의 영역이고, 사람이 보고서를 읽는 3 칸이 제 자리야.
Progress
Progress is local-only — sign in to sync across devices.