복잡한 기능 앞에서의 본능은 "일단 한번 해보자"다—하지만 아키텍처 결정은 세 번째 모델, 다섯 번째 edge case에 숨어 있다. plan mode의 진짜 가치는 아키텍처급 대화를 코드 전의 문자 단계로 옮기는 것이다. 실전: Pickful의 커뮤니티 배심 시스템—3,032줄, 119 spec 전부 통과, 단일 commit으로 배포, 이후 아키텍처 재설계 0회.
복잡한 기능 앞에서 대부분의 사람이 보이는 본능은 "일단 좀 써보자"다.
문제는 복잡한 시스템에는 특징이 있다는 것이다—아키텍처 결정은 세 번째 모델, 다섯 번째 edge case, 여덟 번째 포인트 규칙 속에 숨어 있다. 코드 속에 머리부터 뛰어들었다가 그런 결정을 마주치고 되돌아가 고치면, 처음부터 문자 단계에서 합의했을 때보다 10배 비싸진다.
나는 Claude Code의 plan mode로 Pickful의 TopicReview 커뮤니티 배심 시스템을 만들면서 이 부등식의 극단적인 버전을 목격했다—단일 commit, 3,032줄, 119개의 spec 전부 녹색, 한 번의 배포. 이후 열몇 개의 commit 중 아키텍처 재작업은 하나도 없었고, 전부 파라미터 튜닝, UI 다듬기, edge case 보수뿐이었다. 시스템은 계속 안정적으로 돌아가고 있고, 지금 커뮤니티 자치의 핵심이다.
이 글은 왜 복잡한 기능이 plan부터 시작해야 하는지, plan 단계에서 실제로 무엇을 하는지, 대화가 어디까지 진행되면 코드를 쓰기 시작해도 되는지에 관한 것이다.
TopicReview는 질 낮은 게시물을 제거할지 말지를 커뮤니티 투표로 결정하는 시스템이다. 한 문장이면 끝나지만—스펙은 겹겹이 펼쳐진다:
복잡성의 핵심은 단일 규칙이 아니라 규칙 간 상호작용에 있다. 규칙 하나를 더할 때마다 다른 어딘가의 롤백을 촉발할 수 있다.
그냥 바로 쓰다 보면 제일 어려운 문제는 눈에 보이는 사실이 아니다—당신이 물어볼 생각조차 못한 질문이다. TopicReview 코드를 거꾸로 짚어보면, plan 없이 가면 반드시 부딪힐 벽이 최소 네 곳 있다:
배심원 자격 규칙. 겉보기는 그저 User.jurors_and_judges.sample(12). 하지만 실제 규칙은: 원작자 제외, 신고자 제외, 이미 초심에서 투표한 사람 제외(같은 사람이 항소에도 투표하는 것을 막기 위해)—세 겹의 제외. 모델을 한 번에 쓰다 보면 한두 개 빠뜨리기 쉽다.
포인트 지연 지급. remove 판결은 보통 배심원 포인트 지급을 유발한다. 하지만 항소가 remove를 뒤집을 수 있다—뒤집히면 배심원의 다수파가 반전되므로 포인트를 새 판결로 재계산해야 한다. 그래서 remove 판결은 24h 항소 창이 닫힌 뒤에야 포인트를 지급해야 한다. 이 규칙을 적어두지 않고 먼저 지급하면, 포인트를 되돌리러 가야 한다—지연 지급보다 10배 더 성가시다.
스케줄 작업 인터페이스 설계. CloseTopicReviewJob은 겉보기엔 "review를 끝낸다"다. 실제로는 세 가지 시나리오를 처리한다:
# 초심 투표 창 만료
CloseTopicReviewJob.set(wait_until: voting_ends_at).perform_later(review.id)
# remove 판결 후 배심원 포인트 지연 지급
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, award_juror_points: true)
# 항소 창 만료
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, appeal_id: appeal.id)
먼저 정리하지 않으면, 단일 시그니처로 먼저 쓰고, 두 번째 시나리오를 구현할 때 전체 인터페이스를 재설계해야 한다.
가장 비싼 벽: provisional removal 중 항소 가능 여부. 표가 다수 remove에 도달하면 게시물은 즉시 감춰진다(provisional), 그러나 전체 review는 여전히 voting 상태—decided가 아니다. 사용자는 이 시점에 항소할 수 있는가?
decided와 appealed 사이의 transition을 확장해야 한다이 결정 하나가 거꾸로 open_appeal!의 파라미터 검증과 상태 머신 논리 전체에 영향을 준다. 중간에 결정한다 = 시스템의 절반을 다시 쓰는 것.
Claude Code의 plan mode는 코드 작성이 허용되지 않는 모드다—Claude는 레포를 읽고, 접근 방법을 고민하고, 당신과 논의할 수 있지만, 어떤 파일 수정도 당신이 plan을 승인할 때까지 hard-block된다.
그 기계적 제약이 바로 plan mode의 핵심 가치다—아키텍처급 대화를 문자 단계에서 일어나도록 강제한다.
plan 단계에서 실제로 하는 몇 가지:
1. 상태 머신과 역할 그리기. 5개 상태, 4개 역할(배심원 / judge / 게시자 / 신고자), 각 역할이 각 상태에서 무엇을 할 수 있고 할 수 없는지—전부 몇 줄의 markdown에 담긴다. 몇 줄 대 수십 파일, 변경 비용은 두 자릿수 차이.
2. 각 역할 흐름 walkthrough:
걷다가 막히는 곳에서 즉시 숨은 질문이 튀어나온다: "배심원은 서로의 vote를 볼 수 있는가?", "게시자는 provisional removal 중 무엇을 할 수 있는가?"
3. Edge case 심문. plan 단계는 "정상 경로"를 설계하는 것이 아니라, 평소 떠올리지 않을 질문들을 일부러 묻는 것이다:
juror_points_awarded 멱등 필드 추가)이 질문들의 95%는 코드를 쓰는 동안 자연스럽게 떠오르지 않는다—plan 단계가 하나씩 짚어내도록 강제한다.
4. 포인트 장부. 포인트 시스템은 논의만으로는 부족할 만큼 복잡해서, 실제로 표를 그려야 한다: 각 point 흐름에 대해 trigger 조건 + 금액 + 롤백 경로를 명기한다. 장부가 맞아떨어지면 모든 edge case(가처분 환급, 항소 환급, bonus)가 계산에 들어맞는다.
단단한 기준:
이 기준에 이르면, 코드 작성은 plan을 Ruby로 번역하는 일이 된다.
이 시스템의 첫 ship:
d162f1e Add community moderation system with jury/judge review and appeals
63 files changed, 3032 insertions(+)
119 specs, 0 failures
63개 파일, 3,032줄, 119 spec 전부 녹색. 한 번에 배포.
이후 열몇 개의 commit이 아키텍처의 견고함을 입증한다:
Apply legacy penalty (1pt) for posts created before 2026-03-26
Fix topic review appeal bugs: window mismatch and verdict not updated
Add topic_removed status for posts removed by community review
Default to keep verdict when review expires with zero votes
Reduce provisional removal penalty to 1pt during trial period
Allow topic creator to withdraw active reviews
Add handled tab to jury dashboard, fix tabs styling
모두 튜닝 / 수정 / 다듬기 / 작은 기능 추가. "가서 상태 머신을 재설계하자" 나 "포인트 모델을 바꾸자" 같은 건 하나도 없다. 골격은 그대로 다 맞았다.
이것이 plan의 진짜 보상이다—실행이 중단되지 않는다. "잠깐, 이 경로는 어떻게 처리하지?"가 없다—plan에 이미 쓰여 있다. "이 edge case를 생각 못 했어"가 없다—plan 단계에서 물었다. "이 인터페이스 다시 설계해야 해"가 없다—plan에서 이미 정리됐다.
몇 시간 연속으로 코드를 쓰면서 하는 일은 오직 하나: 명확한 설계를 키보드에 옮기는 것.
모든 작업이 plan을 필요로 하지는 않는다:
plan mode의 이익은 재작성 비용을 낮추는 것에서 온다. 작업에 재작성 위험이 없다면, plan은 그저 오버헤드다.
"하기 전에 생각하라"는 성격 조언처럼 들린다. plan mode는 그런 것이 아니다.
plan mode는 아키텍처급 대화가 10글자로 바꿀 수 있는 곳에서 일어나게 하는 것이다—30개 파일을 바꿔야 하는 곳이 아니라.
복잡성 하에서, 소프트웨어 업계의 오래된 격언 "words are cheap, code is expensive"를 뒤집어야 한다—"일단 코드부터 쓰고 보자"(이건 비용 차이가 작을 때만 성립)가 아니라, 그 비용 차이를 이용해서 값비싼 대화를 값싼 매체로 앞당기라는 것이다.
3,000줄을 한 번에 배포하는 감각은 아주 좋다. 내가 빨리 써서가 아니다—plan이 "쓰기"를 순수하게 기계적인 행동으로 바꾸었기 때문이다.