← Ascendy EN

infra

Spec을 따랐는데 false negative — 문서와 코드가 갈라질 때

· Ascendy Engineering


TL;DR

무엇이 일어났나

한 PR의 검증 단계에서 거버넌스 문서의 명령을 실행했다. 명령은 실패했고, 첫 분석은 “main에 원래 있던 breakage”였다. 별건으로 분리하고 그 PR 머지는 막지 않았다.

다음 사이클을 열기 직전 다시 조사하니 main은 깨져 있지 않았다. chart 자체는 멀쩡했고, 거버넌스 문서에 적힌 명령 형태가 chart의 runtime 요구사항과 어긋나 있었다. 문서라는 spec을 따른 쪽이 false negative를 만들고, 그 false negative가 “환경 breakage”로 잘못 분류된 사건이다. 후속 PR이 문서의 명령 블록을 chart의 실제 invocation 형태와 일치시켜 고쳤다.

직접 원인은 사소하다

chart의 helpers 파일이 어느 시점에 required 가드를 받았다 — 값이 없으면 render를 실패시키는 Helm 함수다. 도입 PR은 incident 대응이었다(이미지 태그가 빈 채 release가 나가는 regress 차단). 가드 후로는 helm template / helm lint가 image tag를 명시적으로 받아야 통과한다.

CI의 lint job은 이걸 알고 있었다 — 가드 직후 갱신돼 각 component에 placeholder 태그를 전달하도록 바뀌었다. CI는 green. 그러나 거버넌스 문서의 명령 블록은 갱신되지 않았다. 가드 도입 의 명령 형태(helm lint <chart>)를 그대로 두고 있었다. 같은 chart, 두 가지 결과 — CI는 통과, 문서를 따른 쪽은 실패.

문제는 결정 구조다

이건 단순 누락이 아니라 doc-as-spec 워크플로의 구조적 약점이다.

거버넌스 문서는 검증할 때 읽는 단일 진입점이다 — “이 명령을 돌리면 검증이 끝난다”는 약속. CI는 그 약속의 실행일 뿐 정의가 아니다.

이 가정은 chart의 runtime contract가 바뀌는 순간 깨진다. 가드가 도입되거나, helper가 새 required field를 갖거나, image repository가 바뀌면, 문서의 명령은 더 이상 chart의 진짜 요구를 반영하지 않는다. 그리고 CI는 그 gap을 못 잡는다 — CI는 자기 자신의 invocation으로 통과하면 끝이지, 문서의 invocation이 sync됐는지 검증하지 않는다.

결과: 두 source of truth가 같은 chart에 대해 다른 명령을 말하기 시작한다. 다음에 문서를 따라 검증하는 쪽은, 자기가 spec을 정확히 실행했는데도 false negative를 받는다. 그리고 false negative를 받았을 때의 첫 본능은 **“환경에 뭔가 깨졌나”**다 — 문서가 “이 명령은 통과해야 한다”고 말하므로, 통과하지 않으면 환경 탓으로 분류된다. 그렇게 drift가 한 사이클 더 이어진다.

결정 / 트레이드오프

작은 결론은 “문서를 sync하면 끝”이다. 큰 결론은 다르다 — 어떻게 sync가 유지되는지를 메커니즘으로 잡지 않으면, 이 패턴은 chart의 runtime contract가 바뀔 때마다 반복된다. 이건 우리가 반복해서 catch해 온 path-drift 패턴이고, 누락은 의지의 문제가 아니라 메커니즘 부재의 문제다.

실무 메모 둘:

후속


저작·인용: 이 글은 Ascendy Engineering이 작성했으며 출처 표기 시 재인용 가능합니다. 잘못된 정보를 발견하면 GitHub 이슈로 알려주세요.


Tags: helm, documentation, agent-workflow, root-cause-analysis