코딩 스탠다드가 53편이 됐다. 규칙은 빠르게 늘었는데, 글을 다시 읽다가 불편한 점이 하나 보였다. 규칙마다 「이게 낫다」고 적혀 있지만, 실제로 돌려보고 확인한 것이 없다.
예를 들면 이런 문장들이다.
// 표준 글에 적힌 모양
테이블에 인덱스를 걸면 조회가 빠르다.
안 C는 안 A보다 고칠 파일이 적다.
이 탐색 규칙이 에이전트에게 유리하다.
첫 줄은 테이블에 인덱스를 걸면 데이터를 찾는 일이 빨라진다는 말이다. 둘째 줄의 「안」은 선택지라는 뜻이다. 이 블로그는 여러 선택지를 안 A · 안 B · 안 C로 이름 붙여 비교하고, 그중 하나를 고른다. 셋째 줄의 에이전트는 스스로 파일을 찾아 읽고 고치는 AI 프로그램이고, 탐색 규칙은 그 에이전트가 어떤 파일부터 찾아 읽을지 정한 규칙이다.
셋 다 그럴듯하다. 그런데 왜 그런지를 돌려서 보여준 곳이 없다. 이런 문장이 쌓이면 블로그 전체가 「그렇다고 적어둔 곳」이 되고, 6개월 뒤의 나는 규칙을 믿을 근거를 물려받지 못한다.
그래서 정해야 했던 것은 이거였다 — 표준의 규칙을 무엇으로, 어디에, 어떤 모양으로 증명하는가.
먼저: 근거가 없는 게 아니라 「세어본 것」만 있었다
정하기 전에 지금 상태를 쟀다. 표준 글 53편에서 수치를 적은 흔적을 키워드로 셌다. 키워드 검색이라 정밀한 값은 아니다.
| 근거의 종류 | 현황 |
|---|---|
| 관찰 — 실제 프로젝트에서 세어봤다 | 45편에 흔적이 있다 |
| 실험 — 조건을 맞춰 돌려서 비교했다 | 사실상 0. 「벤치마크」 1편, EXPLAIN 2편, 실험 코드를 둔 폴더는 없다 |
| 아무 수치도 없다 | 8편 |
문제는 근거가 없다는 것이 아니었다. 근거의 종류가 한쪽에 몰려 있었다. 「폴더를 세어보니 63곳」처럼 한 프로젝트에서 세어 적은 수치는 그 프로젝트의 사정을 보여줄 뿐, 규칙이 옳다는 증명은 되지 못한다. 다른 프로젝트에서는 숫자가 달라지고, 그때 규칙이 여전히 맞는지는 알 수 없다.
그래서 이 글이 채우는 자리는 「돌려서 증명한다」다.
원칙 1. 근거는 네 종류로 나누고 종류마다 증명 방법을 하나로 정한다
위의 세 예시를 보면 증명하는 방법이 서로 다르다. 인덱스는 돌려서 시간을 재면 되지만, 「고칠 파일이 적다」는 시간을 잴 대상이 아니다. 방법 하나로 묶으면 절반은 증명이 안 된다.
그래서 근거를 종류로 나누고, 종류마다 방법을 하나씩 정했다.
| 종류 | 예 | 증명 방법 | 남는 것 |
|---|---|---|---|
| 1. 기술 사실 | 인덱스를 걸면 빠르다 | 같은 데이터로 있을 때와 없을 때를 돌려 시간·실행계획을 비교 | 실험 코드 + 결과 수치 |
| 2. 안 비교 | 안 C가 안 A보다 낫다 | 같은 기능을 두 안으로 각각 구현해 파일 수·줄 수·「기능 하나 바꿀 때 고치는 파일 수」를 비교 | 두 구현 + 비교표 |
| 3. 에이전트 규칙 | 이 탐색 규칙이 유리하다 | 같은 과제를 규칙이 있을 때와 없을 때 여러 번 돌려 토큰·도구 호출·정답률(과제를 맞게 끝낸 비율)을 비교 | 과제 목록 + 반복 결과 |
| 4. 명세 사실 | HTTP 상태 코드 412는 이런 뜻이다 |
공식 문서를 인용하고 최소 예제로 확인 | 출처 링크 + 재현 결과 |
넷 모두에 조건 하나를 건다. 6개월 뒤에 다시 돌려도 같은 결과가 나오게, 실험 코드와 결과를 저장소에 남긴다. 글에 수치만 적고 코드를 안 남기면, 그 수치 역시 「그렇다고 적어둔 것」으로 돌아간다.
종류 3의 「여러 번」이 몇 번인지는 정하지 못했다. 같은 과제도 돌릴 때마다 결과가 흔들리는데, 몇 번을 돌려야 차이를 믿을 수 있는지는 아직 기준이 없다. 이건 안건 이슈로 남겼다.
원칙 2. 모든 원칙을 증명하지 않는다 — 약속과 주장을 가른다
증명 순서를 정하려고 원칙 헤딩을 훑다가, 원칙에 성격이 두 가지 있다는 것을 알았다.
| 원칙 | 성격 |
|---|---|
| 라우트 원칙 9. 복수 단어 리소스명은 kebab-case로 쓴다 | 약속 — 주소를 /order-items로 쓰든 snake_case인 /order_items로 쓰든, 통일만 되면 똑같이 동작한다 |
| DTO 원칙 3. Jackson이 생성자를 쓰도록 명시한다 | 주장 — 생성자에 @JsonCreator(「JSON을 객체로 바꿀 때 이 생성자를 써라」는 표시)를 붙여 명시하지 않으면, 조건이 바뀔 때 객체를 만드는 방식이 조용히 깨진다는 뜻이다. 지금은 명시 없이도 된다. Spring Boot의 Gradle 플러그인이 컴파일할 때 생성자 파라미터 이름을 클래스 파일에 남기는 옵션을 켜 주기 때문으로 보이는데, 그 설정은 내 코드 밖에 있어서 바뀔 수 있다 |
| 라우트 원칙 3. 수정은 PUT 하나로 받고 PATCH를 열지 않는다 | 주장 — PATCH를 열면(서버가 PATCH 요청도 받게 하면) 같은 수정을 처리하는 코드 경로가 하나 더 생긴다는 뜻이다 |
판별 질문은 하나로 정했다. 「반대로 정해도 통일만 되면 똑같이 동작하는가?」 그렇다면 약속이고, 아니면 주장이다.
안 A — 전부 증명한다
얻는 것은 빠지는 규칙이 없다는 것이다. 분류를 잘못해서 증명을 놓칠 일도 없다.
버린 이유는 약속 원칙에 실험을 붙이면 「kebab-case 주소로 보낸 요청 1만 건 vs snake_case 1만 건, 차이 없음」 같은 결과만 나오기 때문이다. 시간을 쓰고도 규칙에 대해 새로 알게 되는 게 없다. 약속은 원래 어느 쪽이든 동작하니까 약속이다.
골랐다 — 안 B. 주장만 증명하고, 약속은 약속이라고 밝힌다. 약속 원칙은 그 원칙 첫 줄에 > 약속: 반대로 정해도 동작은 같다. 통일이 목적이다.를 단다. 맨 앞의 >는 마크다운에서 그 줄을 인용 상자로 보이게 하는 표시다.
이게 신뢰성 문제의 절반을 푼다. 블로그를 못 믿게 만든 것은 근거가 없는 규칙이 아니라, 근거가 없는 이유가 보이지 않는 규칙이었다. 약속이라고 적혀 있으면 읽는 쪽은 「근거가 빠진 규칙」이 아니라 「근거가 필요 없는 규칙」으로 알아본다.
대신 주장을 약속으로 잘못 분류하면 증명에서 빠진다. 그래서 분류도 원칙 첫 줄에 드러나게 했다. 틀렸으면 읽다가 걸린다.
원칙 3. 실험 코드는 블로그 저장소 안 proofs/ 에 둔다
주장 하나마다 실험 코드가 생긴다. 금방 수십 개가 된다. 정해야 했던 건 이거였다 — 그 코드를 어디에 두는가.
안 A — 이 저장소 안 proofs/ 폴더
얻는 것은 글과 실험이 같은 커밋에 들어가고, 이름으로 짝을 검사할 수 있다는 것이다. 짝 검사는 실험 폴더와 글의 이름이 서로 맞는지를 사람 대신 프로그램이 확인하는 것이다. 무엇을 짝으로 보는지는 원칙 5에서 정한다. 다른 프로젝트에 깔리는 코딩 표준 규칙 파일을 담은 coding-standards/와, JDK · Spring 같은 오픈소스의 소스 코드를 읽으려고 받아 둔 open-source/도 이미 이 저장소 안에 있고, 블로그 사이트를 만들어 배포할 때만 빠진다. 같은 방식이다.
안 B — 실험 전용 저장소 하나
얻는 것은 블로그 저장소가 가볍게 남고, 실험만 따로 CI로 돌리기 쉽다는 것이다.
버린 이유는 글과 실험이 두 저장소로 갈라지면 짝 검사가 닿지 않기 때문이다. 한쪽만 고쳐도 아무도 모른다. 저장소 밖에 걸어뒀다고 적힌 심링크가 실제로는 없었는데 아무도 몰랐던 일, 커밋 대기 표준(에이전트가 고친 뒤 커밋하지 않고 멈추게 하는 표준)의 규칙 파일은 coding-standards/로 배포됐는데 그 이유를 적은 블로그 글은 빠졌던 일이 모두 이 모양이었다.
안 C — 실험마다 저장소 하나
얻는 것은 실험마다 필요한 라이브러리와 도구 버전이 서로 섞이지 않는다는 것이다. 버린 이유는 안 B의 「어긋나도 모른다」가 실험 수만큼 늘어나기 때문이다.
안 D — 코드를 글 본문에만 적는다
얻는 것은 파일이 하나도 늘지 않는다는 것이다. 버린 이유는 다시 돌릴 수 없기 때문이다. DB 설정, 데이터 생성, 실행 명령이 글에 다 들어가지 않는다.
골랐다 — 안 A. 안 B와 안 C는 짝이 어긋나도 모르고, 안 D는 재현이 안 된다. 같은 커밋과 기계 검사(프로그램이 짝을 확인하는 것)를 둘 다 지키는 것은 안 A뿐이다.
대신 저장소가 이미 3.78GB라서 데이터를 커밋하지 않는다. 테스트 데이터는 시드를 고정한 스크립트로 매번 만든다.
원칙 4. 실험 폴더에는 claim.md · run.sh · result.md 셋을 고정한다
실험마다 모양이 다르면, 「반증 조건이 적혀 있나」, 「결과가 스크립트 출력인가」를 검사 프로그램이 확인할 수 없다. 그래서 파일 셋을 고정했다. 폴더 이름은 실험마다 붙이는 영문 이름, 곧 슬러그다.
proofs/<슬러그>/
claim.md 돌리기 전에 쓴다 — 주장 한 문장, 근거 종류, 받치는 표준 글, 반증 조건
run.sh 명령 하나로 처음부터 끝까지 재현한다
result.md run.sh 가 쓴다 — 판정·요약 두 줄, 날짜·환경·반복 횟수·원본 수치
.md로 끝나는 두 파일은 마크다운 문서다. 사람이 읽는 글이다run.sh는 셸 스크립트다. 이 파일 하나를 실행하면 실험이 처음부터 끝까지 돌고, 마지막에result.md를 써 낸다- 판정 자리에는 원칙 8에서 정하는
supported·refuted·inconclusive중 하나가 들어간다
셋 중 가장 중요한 것은 claim.md의 반증 조건이다. 「이 결과가 나오면 주장이 틀린 것이다」를 돌리기 전에 적는다. 결과를 보고 기준을 정하면 어떤 수치가 나와도 「그래도 빠르네」로 읽게 된다. 선을 먼저 그어야 실험이 규칙을 뒤집을 수 있게 되고, 뒤집을 수 있어야 증명이다.
run.sh를 bash로 정한 이유는 이 컴퓨터에 Git Bash가 이미 있고 나중에 리눅스 CI에서도 그대로 돌기 때문이다. Gradle 같은 도구는 Docker 이미지 안에서 돌린다. 내 PC에 무엇이 깔려 있는지에 결과가 좌우되지 않게 하려는 것이다.
result.md를 사람이 요약해 쓰는 안은 버렸다. 요약하다가 불리한 수치가 빠져도 아무도 모른다. 스크립트가 쓰면 빠질 수가 없다.
원칙 5. 실험 하나가 증명 글 하나이고 둘은 슬러그로 짝짓는다
처음에는 폴더를 proofs/<표준 글 슬러그>/<실험 이름>/으로 정했다. 표준 밑에 실험이 매달리는 모양이다. 그런데 증명 글과의 관계를 정하다가 이 모양이 틀렸다는 것을 알았다.
「인덱스 조회 실험」은 Repository 설계 표준과 목록 조회 계약 표준을 둘 다 받친다. 표준 밑에 두면 이 실험을 둘 곳이 없고, 두 곳에 두면 실험이 두 벌이 된다.
그래서 한 단계를 없앴다.
proofs/<증명 슬러그>/
_posts/proof/YYYY-MM-DD-<증명 슬러그>.md
첫 줄은 실험 폴더이고, 둘째 줄은 그 실험을 설명하는 증명 글 파일이다. _posts/는 이 블로그의 글이 들어가는 폴더이고, 글 파일 이름은 이 블로그의 규칙대로 「작성일-슬러그」 모양이다. 날짜를 떼고 나면 두 줄의 슬러그가 같다.
- 짝은 이름이다. 폴더와 증명 글 파일명의 슬러그가 같다. 규칙과 글을 슬러그로 짝짓는 검사와 같은 방식이다
- 받치는 표준은
claim.md에 슬러그로 적는다. 여러 개여도 된다 - 받치는 원칙은 번호로 적지 않는다. 원칙을 쪼개면 번호가 조용히 어긋난다. 아키텍처 소속 표준을 정할 때 이미 겪은 실패다. 원칙 위치는 표준 글 안의 인용 줄(원칙 6)이 맡는다
표준 하나에 증명 글 하나를 두고 실험 여러 개를 묶는 안도 검토했다. 버린 이유는 실험 하나를 다시 돌리면 묶인 글 전체를 다시 봐야 하기 때문이다. 증명 글이 표준 글처럼 길어지는 문제도 그대로 따라온다.
원칙 6. 과정은 증명 글에, 결과 한 줄은 표준 글의 원칙에 둔다
안 A — 표준 글의 원칙 안에 실험 과정을 다 넣는다
얻는 것은 규칙과 증명이 한 자리에 있다는 것이다.
버린 이유는 원칙 섹션이 1,000 토큰을 넘기 때문이다. 표준 글은 이미 평균 5,700 토큰이다. 실험 환경, 데이터 생성, 예상 밖 결과까지 넣으면 「이 규칙이 뭐냐」만 찾던 조회가 실험 과정을 통째로 끌고 온다. 섹션이 조회 단위라는 전제가 깨진다.
안 B — 증명 글만 따로 쓰고 표준 글은 두기
얻는 것은 표준 글이 그대로 유지된다는 것이다.
버린 이유는 규칙을 읽는 사람은 증명이 있다는 것조차 모르기 때문이다. 신뢰성 문제를 풀려고 증명했는데, 규칙을 읽는 자리에서는 여전히 근거가 안 보인다.
골랐다 — 안 C. 과정은 증명 글에, 표준 글의 원칙에는 한 줄만.
> 실측(supported): 100만 행 단건 조회 인덱스 0.4ms vs 없음 380ms — [증명](/post/proof/<슬러그>)
이 한 줄을 앞에서부터 읽으면 이렇다. >는 인용 상자 표시이고, 실측(supported)은 「돌려 봤더니 주장대로였다」는 판정이다. 그 뒤가 결과 요약이다 — 행 100만 개짜리 테이블에서 한 건을 찾을 때 인덱스가 있으면 0.4ms, 없으면 380ms였다는 뜻이다(설명을 위한 예시 수치다). 맨 끝 [증명](주소)는 마크다운의 링크 문법으로, 화면에는 「증명」 글자만 보이고 누르면 그 증명 글로 간다.
안 A는 조회 단위를 깨고, 안 B는 규칙 옆에서 근거를 감춘다. 안 C만 규칙 옆에 근거를 보이면서 원칙 섹션을 짧게 둔다.
대신 수치가 두 곳에 산다. 실험을 다시 돌리면 증명 글과 표준 글이 함께 바뀌어야 한다. 이 대가를 원칙 7이 막는다.
원칙 7. 표준 글이 인용한 요약은 result.md 와 글자 그대로 같아야 한다
안 A — 사람이 기억해서 맞춘다
만들 게 없다. 버린 이유는 result.md만 새로 뽑히고 표준 글은 옛 수치로 남기 때문이다. Claude · Codex · Copilot이 각자 다른 파일을 읽어서, 늘 지켜야 하는 「항상 적용」 규칙을 세 파일에 손으로 복사해 적었다가 14줄 · 13줄 · 12줄로 서로 달라졌던 실패와 같은 모양이다.
안 B — 표준 글에는 판정과 링크만 둔다
낡을 수치가 없으니 문제가 사라진다. 버린 이유는 규칙 옆에서 「얼마나」가 안 보이기 때문이다. 「증명됐다」는 말만 남으면 신뢰성 문제를 반만 푼다.
안 C — 생성기가 표준 글의 인용 줄을 써넣는다
절대 낡지 않는다. 버린 이유는 표준 글 수십 곳에 마커를 박고, 생성기가 글 본문을 고치게 되기 때문이다. 생성기는 result.md를 읽어 글의 정해진 자리를 자동으로 다시 써 주는 프로그램이고, 마커는 그 「정해진 자리」를 생성기가 알아보도록 글에 박아 두는 표시다.
골랐다 — 안 D. run.sh가 요약 한 줄을 만들고, 검사가 「그 줄을 그대로 인용했나」를 본다.
result.md 첫머리 판정: supported
요약: 100만 행 단건 조회 인덱스 0.4ms vs 없음 380ms
표준 글 > 실측(supported): 100만 행 단건 조회 인덱스 0.4ms vs 없음 380ms — [증명](…)
검사는 표준 글의 > 실측(...) 줄마다 링크된 증명의 result.md를 열어 판정과 요약이 글자 그대로 같은지 본다. 안 B와 달리 수치가 보이고, 안 A와 달리 낡으면 걸리고, 안 C와 달리 글에 마커가 없다. 저장소 안 파일끼리 비교하므로 check-standards.rb에 넣어 배포를 막을 수 있다. check-standards.rb는 Ruby로 짠 배포 전 검사 모음이다. 배포 작업이 이것을 먼저 돌리고, 검사 하나라도 걸리면 배포가 멈춘다.
대신 표준 글은 요약 한 줄만 인용할 수 있고, 증명 글 본문의 표 수치는 이 검사 밖이다.
원칙 8. 반증되면 지우지 않고 발행한 뒤 모순 이슈로 규칙을 다시 연다
결과는 claim.md의 반증 조건으로만 판정한다. supported(주장대로), refuted(반증 조건에 걸림), inconclusive(둘 다 아님) 셋이다.
정해야 했던 건 이거였다 — refuted가 나오면 무엇을 하는가. 이게 없으면 불리한 결과가 조용히 묻히고, 증명 체계 전체가 신뢰를 잃는다.
안 A — 실험을 고쳐 다시 돌린다
얻는 것은 데이터가 너무 작았다든지 하는 설계 결함을 바로잡을 수 있다는 것이다. 버린 이유는 맞는 결과가 나올 때까지 조건을 바꾸게 되기 때문이다. 반증 조건을 먼저 적은 의미가 사라진다.
안 B — 표준 글의 규칙을 바로 고친다
얻는 것은 틀린 규칙이 가장 빨리 사라진다는 것이다. 버린 이유는 실험 하나가 틀렸을 수 있기 때문이다. 확인 없이 뒤집으면 잘못된 실험이 멀쩡한 규칙을 지운다.
골랐다 — 안 C.
result.md와 판정을 지우지 않고 커밋한다- 증명 글을 반증된 채로 발행한다. 틀렸다는 것도 증명이다
- 표준 글의 그 원칙에
> 반증: <요약> — [증명](링크)을 단다 coding-standard이슈를kind: 모순라벨을 붙여 열고, 실험의 결함인지 규칙의 결함인지 따진 뒤 규칙을 고친다
실험 설계를 바꿔 다시 돌린다면 새 실험으로 만들고 옛 실험은 남긴다.
안 A는 불리한 결과를 지우고, 안 B는 실험 하나에 규칙을 맡긴다. 안 C만 결과를 남기면서 판단은 따로 거친다. 대신 그 이슈가 닫힐 때까지 그 규칙은 표준 글에 「흔들리는 규칙」으로 드러난 채 남는다.
원칙 9. 시범 1건을 끝까지 돌린 뒤 참조가 많은 표준부터 넓힌다
시범은 DTO 생성자 표준 원칙 3 「Jackson이 생성자를 쓰도록 명시한다」로 정했다.
본문에 돌려볼 수 있는 주장이 둘 있다.
@NoArgsConstructor와@Setter(Lombok이 제공하는 애너테이션들이다 — 클래스에 붙이면 기본 생성자나 setter 메서드를 직접 쓰지 않아도 자동으로 만들어 준다)가 붙어 있으면 Jackson이 에러 없이 setter 경로로 객체를 만든다. setter 경로란 생성자 대신 빈 객체를 먼저 만들고 setter로 필드를 하나씩 채우는 방식이다. 에러가 안 나니 아무도 모르는 사이에 「생성자로만 만든다」는 규칙이 무너진다- 필드를
final로 두면@Setter,@NoArgsConstructor,@Data가 컴파일되지 않는다
2번은 반증될 가능성이 있다. 내가 아는 Lombok 동작으로는 final 필드에 @Setter나 @Data를 붙여도 컴파일 에러 없이 setter만 생기지 않는다. 이 글을 쓸 때는 아직 확인하지 않았다. 맞다면 첫 실험에서 반증 처리 절차(원칙 8)까지 한 번에 거친다. 다음 날 실제로 돌려 보니 판정은 refuted였다 — final 필드에서 컴파일이 막힌 것은 @NoArgsConstructor 하나였고 @Setter와 @Data는 컴파일됐다(증명).
시범에서 보는 것은 수치가 아니라 구조가 굴러가는지다. claim.md → run.sh → result.md → 증명 글 → 표준 글 인용 줄 → 짝 검사와 인용 일치 검사를 한 번씩 모두 거친다. DB 없이 Docker 안의 Java만 있으면 몇 분에 끝난다.
그다음은 다른 글에서 참조가 많은 표준의 주장 원칙 순서다. 패키지 구조(25), Repository 설계(15), 예외 분류(15), 예외 처리(14). 참조가 많은 규칙은 틀렸을 때 그 위에 선 글들이 같이 흔들린다.
바로 참조 순서로 시작하는 안은 버렸다. 패키지 구조는 종류 2(두 안을 구현해 비교)라 무겁고, 구조에 구멍이 있으면 무거운 실험 위에서 발견하게 된다. 싼 것부터 전부 하는 안도 버렸다. 참조가 적은 규칙부터 증명하게 되어 신뢰성이 가장 늦게 오른다.
원칙 10. 증명 후보는 전부 뽑아 proofs/backlog.jsonl 한 파일에 둔다
원칙 9는 무엇부터 할지를 정했지만, 무엇이 증명 대상인지 목록은 없었다. 그래서 코딩 스탠다드 53편을 에이전트 일곱이 나눠 전부 읽고, 원칙마다 약속·주장을 가르고 주장을 한 문장씩 뽑았다. 원칙 345개에서 주장 817개가 나왔다. 정해야 했던 건 이거였다 — 그 목록을 어디에 남기는가. 결론부터 말하면 한 줄에 주장 하나씩을 적는 JSONL 형식 파일 하나였다.
안 A — 주장마다 coding-standard 이슈를 연다
얻는 것은 안건과 같은 통로를 쓴다는 것이다. 열림·닫힘이 이슈 상태로 보이고 커밋 메시지의 Closes #N으로 닫힌다. 버린 이유는 817건이 열리면 진짜 안건이 그 밑에 묻히기 때문이다. 이슈 하나에 목록을 통째로 넣는 것도 안 된다. GitHub 이슈 본문은 한 번에 적을 수 있는 글자 수가 65,536자로 정해져 있는데, 파일이 554KB라 그 한도를 넘는다.
안 B — 남기지 않는다. 실험을 시작할 때마다 다시 뽑는다
얻는 것은 저장소에 검증 안 된 판정이 하나도 안 들어간다는 것이다. 버린 이유는 뽑는 비용이 한 번에 에이전트 일곱이고, 세션이 끊기면 통째로 사라지기 때문이다. 세션은 에이전트와 주고받는 대화 한 판이고, 뽑은 결과를 파일에 적지 않으면 그 대화가 끝날 때 함께 사라진다. 이번 스캔도 도중에 사용량 한도(정해진 시간 안에 AI를 쓸 수 있는 양의 상한)에 걸려 일곱이 전부 멈췄다.
골랐다 — 안 C. 저장소 안 proofs/backlog.jsonl 한 파일. 안 A처럼 안건을 묻지 않고, 안 B처럼 사라지지 않는다. 한 줄이 주장 하나라 grep으로 글 슬러그·근거 종류·비용을 바로 거를 수 있다. 실제 한 줄은 이렇게 생겼다(길어서 칸 몇 개를 줄였다).
{"slug":"aggregate-boundary-standard",
"heading":"원칙 1. 경계는 \"한 트랜잭션에서 함께 바뀌는가\"로 가른다",
"verdict":"주장",
"claim":"외래 키를 따라 경계를 잡으면 애그리거트가 계속 커져 결국 DB 전체가 한 덩어리가 된다",
"type":2, "evidence":"없음", "cost":"저",
"suspect":"FK 그래프가 여러 연결 요소로 나뉜 스키마에서는 전체가 한 덩어리가 되지 않는다 — 확인 필요",
"experiment":null}
이 예시에서 볼 것은 칸의 모양이다. claim에 든 주장의 뜻(외래 키 · 애그리거트 · 트랜잭션)은 그 표준 글이 설명한다.
slug와heading은 이 주장이 나온 표준 글과 원칙 헤딩이다verdict는 약속인지 주장인지,claim은 뽑은 주장 한 문장이다type은 원칙 1의 근거 종류 번호(여기서는 2, 안 비교)이고,evidence는 지금 있는 근거가 무엇인지다cost는 이 주장을 실험으로 증명하는 데 드는 품을 저 · 중 · 고로 적은 것이다suspect는 뽑은 에이전트가 「이 주장은 틀렸을 수 있다」고 본 이유다. 의심이 없으면null이다experiment는 아직 실험으로 옮기지 않았으면null이다
파일에서는 이것이 줄바꿈 없이 한 줄이다. 그래서 grep '"cost":"저"'처럼 한 칸으로 거르면 그 조건에 맞는 주장만 줄째로 나온다.
- 줄 번호를 적지 않는다. 글을 고치면 밀린다. 원칙은 글 슬러그와 헤딩으로 찾는다
- 실험으로 옮기면 그 줄의
experiment에 실험 슬러그를 적는다. 줄을 지우지 않는다 - 사이트에는 싣지 않는다. Jekyll이 사이트를 만들 때 읽는 설정 파일
_config.yml에는 사이트에서 뺄 폴더를 적는exclude목록이 있고, 거기에proofs를 넣었다
대신 검증하지 않은 에이전트 판정이 저장소에 들어간다. 817개 중 303개에는 「틀렸을 수 있다」는 의심이 붙어 있지만 그것도 추측이다. 그래서 첫 줄에 미검증이라고 밝히고, 이 파일의 문장은 표준 글의 > 실측 줄에 인용하지 않는다. 인용할 수 있는 것은 result.md의 요약뿐이다.
판단 기준 정리
| 질문 | 답 | 결론 |
|---|---|---|
| 반대로 정해도 통일만 되면 똑같이 동작하는가 | 그렇다 | 약속. 증명하지 않고 > 약속:을 단다 |
| 근거가 기술 사실·안 비교·에이전트 규칙·명세 중 무엇인가 | 종류마다 | 그 종류의 증명 방법 하나로 증명한다 |
| 실험 코드를 어디에 두나 | 이 저장소 | proofs/<슬러그>/ — 데이터는 커밋하지 않는다 |
| 실험 하나가 표준 여럿을 받치나 | 그럴 수 있다 | 폴더를 표준 밑에 두지 않는다. 증명 글과 슬러그로 짝짓는다 |
| 반증 조건은 언제 적나 | 돌리기 전 | claim.md에 먼저 적는다 |
| 결과는 누가 쓰나 | 스크립트 | run.sh가 result.md를 쓴다. 사람이 요약하지 않는다 |
| 표준 글에 무엇을 적나 | 한 줄 | > 실측(판정): <요약 그대로> — [증명] |
| 다시 돌려 요약이 바뀌었나 | 그렇다 | 인용 일치 검사가 배포를 막는다 |
refuted가 나왔나 |
그렇다 | 지우지 않고 발행하고, 모순 이슈를 연다 |
| 증명 후보 목록은 어디에 두나 | 저장소 파일 하나 | proofs/backlog.jsonl — 미검증이라 인용하지 않는다 |
이 표준을 정하기까지
시작은 신뢰성이었다. 코딩 스탠다드 규칙은 계속 정해지는데 그걸 뒷받침하는 실험이 없어서 글을 믿기 어렵다는 문제 제기였다. 인덱스가 빠른 이유를 돌려보거나, 한 규칙이 다른 규칙보다 나은 이유를 파일 수로 비교하거나, 에이전트 탐색 규칙이 유리한 이유를 재보는 글이 없었다.
재보니 근거가 없는 게 아니라 한쪽으로 몰려 있었다. 45편에 「세어봤다」가 있었고, 「돌려봤다」는 사실상 0이었다. 문제의 이름이 「근거 부족」에서 「실험 부족」으로 좁혀졌다.
세 예시가 서로 다른 방법을 요구한다는 데서 원칙 1이 나왔다. 인덱스는 시간을 재면 되지만 「고칠 파일이 적다」는 잴 시간이 없다. 명세 사실까지 더해 넷으로 나눴다.
그러자 코드가 엄청 많아질 거라는 우려가 나왔다. 관리 방법부터 정하고 가자는 요청이었다. 전용 저장소가 가장 깔끔해 보였지만, 짝이 어긋나도 모르는 구조라는 점에서 이 저장소가 이미 두 번 당한 모양과 같았다. 그래서 proofs/를 저장소 안에 두기로 했다.
파일 셋을 고정하면서 반증 조건이 중심에 섰다. 결과를 보고 기준을 정하면 실험은 규칙을 뒤집을 수 없다. 뒤집을 수 없는 실험은 증명이 아니라 확인 절차일 뿐이다.
증명 글과의 관계를 정하다가 앞의 합의가 틀렸다는 것을 발견했다. 폴더를 표준 밑에 두기로 했었는데, 실험 하나가 표준 둘을 받치는 경우를 떠올리자 둘 곳이 없었다. 폴더 한 단계를 없애고, 원칙 위치는 번호 대신 표준 글의 인용 줄이 맡게 했다. 이 글에서 방향이 꺾인 유일한 지점이다.
반증 절차는 불리한 결과가 묻히는 것을 막으려고 정했다. 다시 돌리는 안과 바로 고치는 안이 각각 결과를 지우거나 실험 하나에 규칙을 맡긴다는 것을 확인하고, 기록과 판단을 분리했다.
수치가 두 곳에 사는 대가는 검사로 막았다. 생성기가 글을 고치는 안은 글 곳곳에 마커를 퍼뜨린다. 요약 한 줄을 글자 그대로 인용하게 하면 검사는 문자열 비교로 끝난다.
증명 순서를 정하려고 원칙을 훑다가, 증명할 수 없는 원칙을 발견했다. kebab-case냐 snake_case냐는 돌려서 나올 결과가 없다. 이 발견이 「약속」이라는 분류를 만들었고, 신뢰성을 떨어뜨린 것이 근거가 없는 규칙이 아니라 근거가 없는 이유가 안 보이는 규칙이었다는 것도 이때 드러났다.
마지막으로 시범을 하나 고르면서 첫 반증 후보가 나왔다. DTO 원칙 3의 「final이면 @Setter가 컴파일되지 않는다」는 내가 아는 Lombok 동작과 어긋난다. 구조를 검증하는 시범이 규칙까지 흔들 수 있다는 것이 이 선택의 이유다.
다음 날 증명 대상을 전부 뽑아보니 「약속」은 거의 없었다. 원칙 345개 중 약속은 2개였고 62개는 혼합(원칙 하나에 약속인 부분과 주장인 부분이 섞인 것)이었다. 원칙 하나가 「무엇을 고르나」와 「그게 왜 낫나」를 같이 담고 있어서, 이름처럼 반대로 정해도 되는 부분도 대개 「그렇게 지으면 grep이 좁혀진다」 같은 주장을 달고 있었다. 이름을 어떤 모양으로 짓든 동작은 같지만(약속), 「이 모양으로 지어야 그 이름으로 grep했을 때 걸리는 파일이 줄어든다」는 부분은 돌려서 확인할 수 있는 주장이다. 에이전트 판정이라 확정은 아니지만, 약속이라는 분류는 원칙 단위보다 원칙 안의 부분 단위로 걸린다는 것이 드러났다. 817개를 어디 남길지는 이슈로 열면 안건이 묻히고 안 남기면 세션이 끊길 때 사라져서, 파일 하나로 정했다(원칙 10).
정리
- 세어본 것은 증명이 아니다. 그 프로젝트의 사정일 뿐, 규칙이 옳다는 증거는 조건을 맞춰 돌려본 실험에서 나온다
- 약속은 증명하지 않고 약속이라고 밝힌다. 신뢰를 깎는 것은 근거가 없는 규칙이 아니라 근거가 없는 이유가 안 보이는 규칙이다
- 근거 종류마다 증명 방법이 하나다. 기술 사실은 돌리고, 안 비교는 구현해서 세고, 에이전트 규칙은 반복하고, 명세는 인용해 확인한다
- 실험은
proofs/<슬러그>/에 두고 증명 글과 이름으로 짝짓는다. 저장소가 갈라지면 어긋나도 모른다 - 반증 조건은 돌리기 전에 적고, 결과는 스크립트가 쓴다. 둘 중 하나라도 사람 손에 맡기면 실험이 규칙을 뒤집을 수 없다
- 표준 글에는 요약 한 줄을 글자 그대로 인용한다. 다시 돌려 바뀌면 검사가 배포를 막는다
- 반증은 지우지 않고 발행한다. 규칙은 모순 이슈에서 따로 따진 뒤에 고친다
- 증명 후보는
proofs/backlog.jsonl한 파일에 둔다. 에이전트 판정이라 미검증이고, 표준 글에 인용하지 않는다
자신만의 철학을 만들어가는 중입니다.
댓글남기기