Files
document-haness/.agents/skills/rewriting-technical-prose-naturally/references/article-shape.md
T
DongHyeonkaandClaude Opus 5 75bed382c8 docs(keycloak-session-store): remake all 28 diagrams through the techviz pipeline
The originating repository's SVGs were drawn by hand and every one of them
put a title, a subtitle and an explanation band inside the canvas. This
repository forbids both, so they could not be carried over — the whole set
was rebuilt through the skill's pipeline instead.

Each diagram went through prepare, references, prompt, a VizSpec 1.1 citing
document line ranges, lint, and render. All 28 pass lint and produce the
same eight formats the existing keycloak project has. Sentences moved out of
the canvas into <desc> and the paragraph beside each figure; the drawings
carry names only.

Two lint rules did real work rather than formatting work:

  edge-through-node                  caught arrows crossing an unrelated
                                     node and implying an adjacency that
                                     does not exist — four diagrams had to
                                     be restructured, not just relaid out
  evidence-outside-prepared-context  caught a diagram citing another
                                     section; its anchor moved from B-0 to
                                     B-1 so all three sections it draws on
                                     are inside the prepared context

lab-topology also had to change profile: its context offers a different
candidate set, and query-fanout with shard roles is what the section
actually shows — one entry point spreading to two Keycloak nodes.

The document now carries all 28 inline, one per claim that needed one, and
the section recording what was still missing is updated: the diagram gap is
closed, Studio records remain.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 11:03:10 +09:00

7.9 KiB

밀도 — 설명을 어디까지 하는가

순서는 document-skeleton.md가 맡는다. 이 문서는 한 절 안을 무엇으로 채우는가를 맡는다. 순서가 맞는데도 얇게 읽히는 글이 있고, 그것이 이 저장소가 실제로 겪은 문제다.

기준이 되는 글은 ../examples/ 여섯 편이다.

잰 값

scripts/density.mjs로 잰 것이다. 글 한 편과 기록 본문은 다른 잣대로 본다. 기록은 문제·결론· 검증 환경·재현 조건을 Studio 칸이 따로 들고 있고, 본문의 ##는 「본문」 칸 아래라 층이 한 단 낮다. 그래서 본문은 절대값이 아니라 밀도로 잰다 — 절 하나가 몇 낱말인가, 1,000 낱말에 수치가 몇 개인가.

여섯 편 (글 한 편) 기록 본문 기준
낱말 1,277 ~ 2,871
문장 평균 낱말 14.2 ~ 18.3 14.2 ~ 18.3
코드블록 1 ~ 13 1 ~ 13
표 줄 0 ~ 15 0 ~ 15
절당 낱말 160 ~ 718 160 ~ 718
1,000 낱말당 수치 측정 글 10.2 ~ 13.8 · 구조 글 0.9 ~ 1.7 측정 9 ~ 45 · 구조 0.8 ~ 8

고치기 전 AP4 본문은 절 하나가 68 낱말, 수치는 본문 전체에 1개, 표 37줄이었다. 손대지 않은 clean-architecture-backend-template 기록들은 지금도 절 하나가 31 낱말이다. 갈리는 것은 셋이다.

1. 수치 — 크기를 말하지 않으면 독자는 판단할 수 없다

여섯 편은 문단마다 잰 값을 댄다. 구조만 말하고 크기를 말하지 않는 문단이 거의 없다.

라이더에게 할당된 신규 배달이 N개라고 했을 때 … 간선: C(2N, 2)개 n=2, C(4, 2) = 6 … 분당 20만 건의 경로를 계산해야 한다면, 시스템은 분당 120만 건의 실거리 계산을 수행해야 합니다. 이는 초당 약 2만 건으로 … (22396)

세는 과정을 보여 준다. 결과만 적지 않는다. 이 절이 없으면 뒤에 나오는 Redis 자료구조 선택이 과한 짓처럼 읽힌다.

무엇을 세는가. 자료에 있는 것 중에서 고른다 — 몇 개인가, 몇 번 도는가, 몇 바이트인가, 몇 밀리초인가, 몇 곳에서 부르는가, 그중 몇이 프로덕션인가, 전과 후가 각각 얼마인가.

없으면 만들지 않는다. 재지 않았으면 「재지 않았다」로 적고 그 자리를 비운다. 다만 자료에 숫자가 있는데 「여러 곳」·「대부분」·「크게」로 뭉갠 자리는 되돌린다. 그것이 이 저장소가 가장 자주 하는 실수다.

2. 절 — 잘게 자르면 설명이 자리를 못 잡는다

여섯 편의 ## 절은 49개다. 절 하나가 600900 낱말인 것도 있다.

한 절은 주장 하나 + 그 근거다. 근거는 코드·출력·수치·표 중 하나 이상이다. 절이 문단 두 개로 끝나면 자른 자리가 틀린 것이므로 앞뒤와 합친다.

절 제목은 그 절이 무엇을 하는지 말한다. 「현상」·「문제 원인 분석 및 해결」·「개선 결과」처럼 일하는 이름이 낫다. 「같은 이름의 헤더」처럼 명사구만 두면 무엇을 할 절인지 알 수 없다.

3. 표 — 산문을 대신하지 못한다

표는 이미 산문으로 말한 것을 대조할 때 쓴다. 22396의 TTL 표는 앞 세 문단이 설명한 것을 두 열로 정리한 것이고, 20161의 개선 전/후 표도 마찬가지다.

표로 설명을 시작하면 독자가 각 칸을 스스로 풀어야 한다. 표 줄이 산문 문단보다 많으면 그 글은 명세서다.

코드는 이름과 줄 번호로 대신하지 않는다

읽는 사람은 그 파일을 열 수 없다. InboxCleanupJob:56이라고만 적으면 독자에게는 아무 일도 일어나지 않는다.

13569는 「수정 전」과 「수정 후」 메서드를 통째로 싣는다. 7835는 같은 login 메서드를 두 번 싣는다 — 비관심사가 섞인 판과 분리한 판. 20161은 문제의 ES 쿼리 전문을 싣고 1️⃣ 2️⃣로 어디가 문제인지 표시한다.

코드블록 앞이나 뒤에 무엇을 보라는 한 줄을 붙인다. 코드만 던지지 않는다.

인용할 코드가 길면 그 절에 필요한 부분만 자른다. 자른 자리는 // …로 표시한다. 자르는 것과 이름만 대는 것은 다르다.

틀린 시도를 지우지 않는다

22396은 「시도 1. 데이터 압축」을 실패한 채로 남긴다.

하나의 지역에 1000개의 서로 다른 좌표 간 모든 실거리를 저장할 경우, JSON 데이터 원본 크기는 약 24MB였으며, 압축한 크기는 3MB였습니다. 만약 최대 대역폭이 10Gbps라고 했을 때, 3MB 크기로는 초당 3400회의 조회도 버티기 어렵습니다.

실패도 수로 닫는다. 24MB → 3MB는 성공한 압축률인데, 그것으로도 모자란다는 것을 3400회로 보인다.

자료에 실패한 시도가 있으면 살린다. 없는 실패를 지어내지 않는다.

감수한 것을 적고 닫는다

좋아진 것만 적고 닫는 글은 여섯 편에 없다. 23625는 「트레이드오프」를 목록으로 적고, 22396은 TTL 삭제와 명시적 삭제를 장단점 표로 나란히 놓고 왜 단점이 있는 쪽을 골랐는지 적는다. 13569는 빨라져서 생긴 문제와 그 대응 코드까지 싣는다.

독자를 어디서 채워 주나

쓰기 직전에 채운다. 앞에 몰아 두지 않고 뒤로 미루지도 않는다.

여기서 이야기하는 네트워크 대역폭은, ElastiCache 노드가 네트워크를 통해 초당 전송할 수 있는 최대 데이터 용량을 의미합니다. (22396 — 대역폭 이야기를 시작하는 자리)

track_scores : ES 검색 쿼리에서 각 문서의 관련성 점수(_score)를 계산하고 저장할지 여부를 결정하는 설정 (20161 — 그 설정을 바꾼 절의 첫머리)

기준은 하나다. 이 낱말을 모르면 다음 문단을 못 읽는가. 그러면 한 줄로 편다.

용어 절을 따로 두는 것은 그 글의 중심 개념일 때다(17386의 카프카 용어 일곱 개). 한두 개면 쓰는 자리에서 푼다.

검사기를 만족시키려고 문장을 넣지 않는다

이것은 겪은 일이다. check_prose.mjsmonotone-endings가 「물음(~할까요?)·권유(~봅시다)를 섞으라」고 안내했고, 그 글을 고친 사람이 절 제목을 물음으로 바꾸고 문단 가운데 수사 의문을 끼워 넣었다. 수치는 통과했고 글에는 없던 화자가 생겼다.

종결어미 변화는 문장이 하는 일에서 나온다. 한다체 안에도 어미는 여럿이다.

문장이 하는 일 어미
확인한 것 ~였다 · ~했다 · ~됐다
지금 그러한 것 ~한다 · ~된다 · ~넘긴다
아닌 것 ~아니다 · ~없다 · ~않는다
이유 ~때문이다 · ~뿐이다 · ~까지다
값·이름으로 끝나는 문장 명사 종결

물음은 그 절이 실제로 답할 때만 쓴다. 22396의 「왜 실거리가 중요할까?」·「어떤 내비게이션을 사용할까?」는 그 절이 답하는 물음이라 제목으로 맞다. 답이 예·아니오뿐인 물음 (「~인 걸까?」·「~되지 않을까?」)은 답하지 않고 분위기만 만든다. rhetorical-question이 잡는다.

마무리도 같다. 「지금까지 ~를 살펴봤다」로 끝나면 앞 내용을 한 번 더 읽힌 것뿐이다. 17386은 그 문장으로 마무리를 열고 회고와 참고 자료로 닫는다. 되풀이 뒤에 남은 일이 오면 두고, 되풀이가 마무리의 전부면 지운다. closing-recap이 경고로 알린다.

낱말 수는 목표가 아니다

설명을 다 하면 따라오는 값이다. 늘리려고 문단을 넣으면 검사기는 통과하고 글은 나빠진다.

반대로 1,700 낱말짜리 글이 코드도 수치도 없이 표만 서른 줄이라면, 그것은 짧아서가 아니라 설명을 안 해서 얇은 것이다.