Learn everyday

[6월 TIL #18] 코드가 아니라 '의도'가 정본인 개발 시스템

besoluble 장은솔 2026. 6. 29. 23:58

AI와 함께 효율적인 개발을 위해 "문서 먼저 쓰고 작업하자"는 시도를 몇 번 해봤다. 작업 전에 무엇을 만들지, 어떤 규칙을 지킬지 문서로 정리해두는 방식이다. 그런데 문서를 적어둬도 코드가 그대로 따라오지는 않았다. 시간이 지나면 문서와 실제 코드가 어긋났고, 어긋났다는 사실조차 아무도 알려주지 않았다. 결국 문서는 "한때 그렇게 하기로 했던 메모"로 남았다.
 
오늘 이 문제를 정면으로 푼 설계를 봤다. 문서가 코드를 실제로 강제하려면 무엇이 필요한가. 아직 활용 가능할 정도로 흡수하진 못했지만, 적어도 내 "문서 먼저"가 왜 매번 실패했는지는 분명해졌다.
 
 

무엇을 공부했나 — 코드가 아니라 '의도'가 정본인 시스템

이 시스템의 전제는 분명하다. 코드가 정본(source of truth)이 아니라는 것이다. 정본은 사람이 내린 상위 의사결정, 즉 "제품이 사용자에게 무엇을 약속하는가"이고, 코드와 테스트 그리고 화면은 그 약속이 지켜졌다는 증거(evidence)일 뿐이다. 의도가 위에 있고 구현이 아래에 있다.
 
그 의도를 막연한 선언으로 두지 않고, 세 개의 축으로 구조화한다.
 

  • 의도 선언 축 (종단): 큰 경험 영역(Experience) → 약속이 발동되는 순간(Moment) → 그 순간 제품이 내거는 약속(Promise). 의도를 위에서 아래로 점점 구체화한다.
  • 실현 제약 축 (횡단): 보안, 응답 속도, 설명 충분성처럼 여러 약속을 가로지르는 공통 규약은 Aspect로 따로 뺀다. 관점지향 프로그래밍(AOP)의 aspect를 코드가 아니라 제품 약속 레벨로 끌어올린 것이다. 종단 약속과 부모-자식이 아니라 서로 엮인다(weaving).
  • 검증 축: 각 약속을 증거에 닫는다. 결정적으로 확인하는 합격 기준(Acceptance Check)과, 실제 런타임 출력으로 의도가 달성됐는지 판정하는 의미 검토(Intent Check)로 분해된다.

여기까지는 잘 짜인 문서 체계처럼 보인다. 내가 늘 걸리던 의문은 그다음이다. 이렇게 적어둔다고 코드가 정말 그걸 따르나. 이 시스템이 다른 지점은, 그 문서를 강제력으로 바꾸는 장치가 네 겹으로 박혀 있다는 데 있었다. 먼저 한눈에 보면 이렇다.
 

  • ① 셀 수 있는 그래프: 약속을 ID로 엮인 노드로 두고, 다음 단계까지 연결됐는지 기계가 센다.
  • ② 실행 가능한 증거: 모든 약속을 실제 코드를 돌리는 테스트까지 내려보낸다.
  • ③ 증거 없음 = 차단: 검증 안 된(unknown) 약속은 그 자체로 출시를 막는다.
  • ④ 증명을 다시 의심: 통과한 테스트조차 변형(mutation)·교차검토로 한 번 더 깬다.

 

① 약속을 '글'이 아니라 셀 수 있는 그래프로 묶는다

약속은 줄글이 아니라 타입이 있는 노드다. 어떤 Experience의 어떤 Moment에 속하는지, 어떤 Acceptance Check로 쪼개지는지, 어떤 증거가 그걸 닫는지, 어떤 Aspect가 걸리는지, 현재 판정이 무엇인지가 전부 ID로 적혀 있다. 그리고 이 ID는 다른 문서의 실제 ID와 일치해야 한다. 가리키는 대상이 없는 ID는 끊긴 링크로 잡힌다.
 
그 위에 추적성 기수(traceability cardinality) 규칙이 얹힌다. "각 노드는 최소 몇 개의 다음 노드와 연결돼야 하는가"를 정책 파일로 선언하고(약속 하나는 Acceptance Check 하나 이상, Acceptance Check 하나는 증거 행 하나 이상 같은 식이다), 검증기가 실제 그래프를 세서 미달이면 막는다. "문서가 존재한다"보다 강한 "문서가 다음 단계까지 실제로 엮여 있다"를 기계가 세는 것이다.
 
 

② 모든 약속을 실행 가능한 증거로 내려보낸다

약속의 각 합격 기준은 증거 원장(Evidence Ledger)의 한 행으로 내려간다. 그 행에는 실제로 돌릴 수 있는 명령과, 그 명령이 실행하는 테스트가 적혀 있다. 그리고 한 단계 더 본다. 그 테스트가 진짜 제품 코드를 불러다(import) 실행하는지까지 확인한다. 그럴듯한 가짜 입력만 만들어놓고 실제 코드 경로를 건드리지 않는 테스트는, 초록불이어도 약속을 닫지 못한다. "잘 동작한다"는 산문이 아니라 "이 명령을 돌리면 이 코드가 이렇게 동작한다"만 증거로 인정된다.
 
 

③ '증거 없음'을 중립이 아니라 차단으로 둔다

이게 제일 결정적이었다. 판정이 합격·불합격 두 값이 아니라 met · not-met · unknown 세 값이고, unknown(아직 검증 안 됨)도 not-met과 똑같이 출시를 막는다. 증거 없음이 중립이 아니라 차단이다. 대부분의 문서-우선 방식에서는 "아직 확인 안 함"이 초록불로 조용히 통과하지만, 여기서는 증명되기 전 기본값이 빨간불이다.
 
그래서 약속을 새로 선언하면 그 약속은 처음에 unknown 상태라 그 자체로 release를 막는다. 증거로 닫기 전까지 빨간불이 풀리지 않으니, 문서만 써두고 넘어가는 경로가 구조적으로 막혀 있다. 항공기로 치면 출항 전 점검 항목 하나라도 미확인이면 비행기를 못 띄우는 것과 같다. "아마 괜찮을 것"은 통하지 않는다.
 
최종 release 판정도 한 값이 아니라 세 축의 곱이다. 의도가 실제로 달성됐는가(Intent), 약속과 증거를 잇는 추적에 치명적 결함이 0인가, 횡단 제약(Aspect)이 모두 박혔는가. 셋 중 하나만 비어도 빨간불이고, 어디서 깨졌는지 알 수 있도록 세 축을 합쳐 한 줄로 뭉개지 않고 항상 분리해서 보여준다.
 
 

④ 통과한 증명조차 다시 의심한다

테스트가 초록불이어도 그게 진짜 불변식을 지키는지는 별개다. 그래서 증명 자체를 한 겹 더 공격한다. 코드 값을 일부러 틀리게 바꿔도 테스트가 안 깨지면(mutation testing) 그 테스트는 불변식을 지키지 않는 가짜로 본다. 어떤 상수를 검사하는 테스트는 그 상수가 바뀌면 반드시 깨지도록 묶어, 상수와 테스트가 같이 움직여 틀려도 통과하는 false-pass를 막는다. 약속 문장과 합격 기준이 의미상 같은 걸 가리키는지는 또 다른 LLM이 판정하고, 그 판정 모델은 작성 모델과 달라야 한다. 같은 모델이 쓰고 같은 모델이 채점하면 같은 맹점을 공유하기 때문이다.
 
 

알게된 것 — 강제력은 의지가 아니라 구조다

여기까지 보고 내 "문서 먼저"를 돌아봤다. 내 문서는 그래프로 세어지지 않았고, 실행 가능한 증거로 내려가지도 않았고, 안 지켜져도 아무것도 막지 않았고, 통과한 증명을 다시 의심하지도 않았다. 그러니 지켜질 이유가 없었다. 문서가 코드를 강제하는 건 작성자의 의지나 성실함이 아니라, 그걸 AI 지킬 수 있는 구조로 전환해두었는가의 문제였다.
 
횡단 제약이 실제 코드에 어떻게 박혀 검증되는지, 의미 검토용 모델을 어떻게 운영하고 비용을 감당하는지는 더 들여다봐야 한다. 그래도 다음에 또 "문서 먼저"를 시도한다면, 적어도 세 번째, 즉 증명되지 않은 것을 기본 차단으로 두는 것부터는 작게라도 흉내 내보려 한다. 굿!