에이전트가 쓴 코드를 아무도 깊이 모를 때, 근본 원인은 코드를 읽어서가 아니라 운영에서 모은 사실로 가설을 걸러서 찾는다.
Cindy Sridharan(@copyconstruct) · X 스레드 · 2026-10-11 02:08 KST(2026-10-10 17:08 UTC) · 원문 포스트
읽기 전에
코드를 읽지 않거나 읽어도 깊이 알지 못하는 사람이 에이전트와 함께 버그의 근본 원인(root cause)을 어떻게 찾나? 저자는 디버깅을 제약 충족 문제(constraint satisfaction problem)로 다루라고 답한다. 사람이 코드를 알아서 에이전트를 바로잡는 대신, 바깥에서 모은 사실이 에이전트의 가설을 걸러 낸다.
아래층은 사실 층(factual tier)이다. 에이전트는 이 층에 데이터를 더 요청할 수는 있어도 고칠 수는 없다. 위층은 가설 층(derivative tier)이다. 위층의 가설은 아래층 사실을 하나도 어기지 않을 때만 살아남는다. 코드는 일어난 일이 아니라 일어날 수 있는 일을 말하므로 맨 나중에 읽는다.
먼저 알아 둘 낱말
변수(variable)와 대조군(control)
변수는 버그가 나타나는 경우다. 대조군은 의심하는 원인 한 가지만 빼고 변수와 똑같은 경우다.
신호(signal)
버그를 가리키거나 설명할 수 있는 데이터 한 점이다. 로그 한 줄, 지연 측정값, 호출 스택, 레지스터 값이 모두 신호일 수 있다.
고충실도 신호(high-fidelity signal)와 잡음(noise)
변수에는 있고 대조군에는 없는 신호가 고충실도 신호다. 양쪽에 다 있는 잡음은 볼 곳을 알려 주지 못하는 대신 가설을 지우는 제약으로 남는다.
강한 신호(strong signal)와 약한 신호(weak signal)
강한 신호는 들어맞는 가설이 몇 안 되는, 이상적으로는 하나뿐인 신호다. 거의 모든 가설과 들어맞는 신호는 고충실도라도 약한 신호다.
P99 지연(P99 latency)
응답 시간을 줄 세웠을 때 요청의 99%가 그 아래에 드는 값이다. 인용된 포스트 이미지 2의 그래프는 단위가 마이크로초(latency_us)여서 눈금 350k는 0.35초다.
샤딩된 클라이언트(sharded client)와 샤딩 안 된 클라이언트(non-sharded client)
지연 버그(latency bug)의 저장 서비스에서 샤딩된 클라이언트는 요청을 데이터가 있는 샤드(shard)로 곧장 보낸다. 샤딩 안 된 클라이언트를 쓰면 요청이 아무 파드(pod)에나 가서, 받은 파드가 맞는 샤드에서 데이터를 가져오거나 맞는 샤드로 요청을 넘겨야 한다.
반박 검토자(adversarial reviewer)
주 에이전트의 판단을 공격하는 별도 에이전트다. 저자는 이 검토자를 가설을 확인하는 도구가 아니라 지우는 도구로 쓴다.
Mach 포트와 응답 권한(reply right)
Mach 포트는 XPC 메시지가 오가는 커널 채널이다. 요청이 오면 커널은 서버에 응답과 함께 사라지는 응답 권한을 주므로, 멈춤(hang) 버그에서 lsmp 출력에 남아 있던 권한은 보내지 않은 응답의 증거다.
준안정 장애(metastable failure)
Bronson 외의 2021년 HotOS 논문은 촉발 요인이 사라진 뒤에도 시스템이 나쁜 상태에 머무는 장애를 이렇게 부른다. 저자의 예에서 용량 감소는 장애의 시작만, 무제한 재시도는 용량 회복 뒤의 지속만 설명하므로 둘을 합친 다원인 가설(multi-cause hypothesis)이 필요하다.
읽는 순서
원본 (완역) 블록에서 본 포스트 이미지 3의 흐름도부터 본다. STEER 칸에서 쫓을 가설 하나를 고르는 일을 저자는 조향(steering)이라 부른다. STEP 2 칸의 열과 행은 가설-사실 행렬(hypotheses-by-fact matrix)을 가리킨다. 열은 신호마다 들어맞는 가설 수를, 행은 가설마다 만족하는 사실 수를 센다. 아래쪽 화살표는 루프가 어느 조건에서 어디로 되돌아가는지 보여 준다. 왼쪽 아래 화살표의 점진적 신호 발견(Progressive Signal Discovery)은 같은 시스템을 한 층 아래에서 다시 관찰해 사실을 보태는 일이다.
Executive Summary에서 사실 층·가설 층과 세 단계를 다룬 부분을 읽는다. 흐름도의 칸 이름을 세 버그의 장면과 이어 읽는다.
본 포스트 이미지 1과 2로 돌아간다. 이미지 1은 Medium 원문을 이루는 네 부분의 안내다. 이미지 2는 이 프레임워크가 사람의 코드 이해를 전제로 삼지 않는 이유를 저자의 말로 적는다.
인용된 포스트의 이미지 1과 2는 지연 버그를 다룬 초안이다. 이미지 2에서 저자가 Claude에게 준 지시 6개에는 기능 플래그를 끈 대조군과 비교하라는 지시가 없다. 흐름도의 STEP 1과 견주어 읽는다.
팩트체크에서 주장마다 근거와 판정을 본다. 프레임워크가 기대는 사례는 저자가 직장에서 고친 버그 세 건이다.
검증을 통과한 가설이 여럿 남으면 무엇을 늘리나? 사실이 모자라 가설을 가려내지 못한 상태이므로 가설이 아니라 사실을 더 모은다. → 본 포스트 이미지 3의 맨 아래 화살표
에이전트가 쓴 코드를 한 줄도 빼지 않고 읽으면 틀린 결론에 맞설 수 있나? 아니다. 저자는 모든 줄을 읽어도 에이전트를 확신 있게 반박할 만큼 깊이 알지 못한다고 쓴다. → 본 포스트 이미지 2, 인용된 포스트 이미지 1
코드 이해가 줄어든 자리를 사람은 무엇으로 채우나? 검증 설계로 채운다. 저자는 '검증 설계 > 코드 이해'를 에이전트 네이티브 선봉에 필요한 사고 전환 일곱 가지 가운데 첫 항목으로 둔다. → 본 포스트 이미지 4
Executive Summary
신뢰도 B. 주장 30건 가운데 거짓은 0건이다. 사실 16건, 부분 사실 7건, 밖에서 가릴 수 없는 자기 보고와 예측 7건이다. 판정과 근거는 팩트체크에 있다.
Cindy Sridharan은 에이전트가 쓴 코드를 아무도 깊이 이해하지 못할 때 디버깅을 제약 충족 문제로 풀어야 한다고 주장한다. 실제 신호에서 모은 사실을 아래층의 제약으로 깔고 그 위에 에이전트의 가설을 올리면, 모든 사실을 만족하는 가설만 원인으로 남는다. 코드 읽기는 그렇게 범위를 좁힌 뒤의 마지막 단계다. 이 틀에서 사람의 가치는 코드 지식에서 검증 설계로 옮겨 가므로, 소프트웨어 팩토리는 사실을 넉넉히 공급하고 버그당 사람 개입 횟수를 세는 체계로 바뀌어야 한다.
근거 문서는 그가 2026-10-11 02:08 KST(2026-10-10 17:08 UTC)에 올린 X 스레드의 답글이 링크한 Medium 글 「How to effectively debug when you no longer understand the code」다. 스레드 본문은 첨부 스크린샷 4장을 이 글의 요약(TL;DR)이라 부른다. 글은 그가 업무에서 에이전트와 함께 고친 운영 버그 세 건인 지연 버그, 메모리 누수, 멈춤(hang) 버그에서 일반 기법과 팩토리 설계 원칙을 끌어낸다. 그는 자기 믿음이 10년 가까이 된 사내 코드베이스와 서비스에서 일한 경험에 뿌리를 둔다고 밝힌다.
약 9,000줄지연 버그가 난 새 기능의 크기. 에이전트가 저장소 2곳에 걸쳐 하루 남짓 만에 썼다. 병합은 위험도 순 패치 10개로 나눴다.
약 5,000대클라이언트 쪽 첫 운영 배포 범위. 데이터센터 1곳에 있는 가장 작은 클라이언트 묶음이다.
약 10분클라이언트 기능 플래그를 켠 뒤 P99 지연 그래프가 뛰기까지 걸린 시간.
약 5 대 약 400DNS 조회가 돌려준 주소 수. 앞이 기능 플래그를 끈 대조군(control), 뒤가 켠 변수(variable) 클라이언트다.
2×1 대 5×80샤드 수와 샤드당 복제본 수. 앞이 사전 운영, 뒤가 운영 클러스터다. 사전 운영에서 클라이언트가 조회한 주소는 2개였다.
1줄지연 버그의 수정. DatabaseClient를 ShardedDatabaseClient로 바꿨다.
약 50 GB메모리 누수에서 메모리 부족(OOM)으로 종료되기 전까지 heaptrack에 기록된 힙 할당량.
137K회Claude의 첫 오진이 내세운 재시도 횟수. 그 근거로 든 상태에 상태 기계는 도달할 수 없었다.
60초멈춤 버그에서 가상 파일 시스템(VFS) 층이 원격 요청의 응답을 기다리다 타임아웃을 내는 한도.
67시간 · 30 MBlsmp(Mach 포트 목록) 출력 30 MB 가운데 증거가 된 한 줄. 데몬의 요청에 대한 1회용 응답 권한이 67시간째 쓰이지 않은 채 남아 있었다.
약 3분의 1멈춤 버그에서 근거 인용을 요구한 주장 가운데 인용을 대지 못해 버린 비율.
세 버그 모두 에이전트의 결론을 사람이 붙잡아야 했다
에이전트 코딩 담론에 실제 운영 사례가 드물다는 것이 그가 세 버그로 글을 여는 이유다. 세 버그는 그가 코드를 아는 정도, 새 코드인지 회귀인지, 원인 컴포넌트의 소스 접근 여부, 재현 여부, 원인 규명에 걸린 시간, 수정 검증 방법이 모두 다르다. 이 차이가 맞는 디버깅 접근을 바꾼다.
항목
지연 버그
메모리 누수
멈춤 버그
상황
저장소 서비스의 새 기능. 상세한 구현 지시, 모든 패치의 모든 줄을 직접 읽는 검토, 서브에이전트 검토, 동료 승인, 사전 운영 배포를 거쳤다. 그는 ‘바이브 코딩’이 아니었다고 거듭 강조한다. 운영 클라이언트에 플래그를 켜자 클라이언트와 서버의 P99 지연이 함께 뛰었다.
Docker 안의 도구가 원격 서비스 인증 정보를 잘못 넣으면 인증 오류 대신 OOM으로 죽는다는 사용자 리포트. 처음 문제가 난 버전, 마지막 정상 버전, 재현 절차, heaptrack 출력이 다 들어 있었다. 원인 자리의 재귀형 인증 상태 기계는 그가 2025년에 손으로 썼다.
그의 팀 WebSocket 서비스의 데이터를 가상 파일 시스템으로 보여 주는 데스크톱 데몬의 간헐적 타임아웃. 데몬은 출시 전 실험판 OS에서 돌았다. 그는 데스크톱 경험이 없다. 데몬은 출시 주기가 긴 다른 팀 소유라 로그를 더하면 몇 주가 걸린다. 크래시도 오류 로그도 없었다. 사용자가 재시작해 증거가 지워져 sysdiagnose 출력만 남았다.
빗나간 답
로컬 재현, 운영 설정, 과거 포스트모템, 텔레메트리, git 이력, 독립 서브에이전트 검증까지 지시받은 Claude가 약 5분 만에 ‘서버가 TLS 인증서를 RPC마다 다시 읽는다’고 진단했다. 서브에이전트도 반박하지 못했다.
Claude는 재현하지 않고 코드를 읽었다. 첫 분석은 토큰 거부로 쌓인 재시도가 인메모리 로그를 불렸다며 재시도 상한을 냈다. 둘째 분석은 최근의 amortization 작업(일부 API 호출의 비용을 나누는 변경)을 지목했다. 그 기능은 기본으로 꺼진 opt-in인데 사용자는 켜지 않았다. 셋째로 Claude는 주장을 거둔 뒤 새 가설 4개와 커밋 24개 이분 탐색을 제안했다.
서버가 느리다는 그의 첫 짐작은 서버 텔레메트리가 기각했다. 인증 팀은 로그가 없다는 것이 요청이 오지 않았다는 증거라며 에스컬레이션을 돌려보냈다. 시계 어긋남을 흉내 낸 동료의 재현은 증상만 같았다. 패킷 순서가 중간부터 갈라졌다. IPC 표에는 미응답 응답 권한이 없었다.
사람이 보탠 것
대조군 지정, 선행 지표인 TCP 핸드셰이크 시간 측정, ‘호스트의 다른 도구로 두 쪽의 차이를 더 찾으라’는 주문.
반박 두 번, 그리고 ‘인증 정보가 없는 두 경로를 끝까지 따라가 heaptrack 출력과 맞는 무한 루프를 찾으라’는 방향.
패킷 캡처부터 보자는 방향과 근거 인용 요구. 그와 에이전트가 낸 가설을 산더미 같은 데이터에 거듭 대 보는 검사.
원인을 짚은 신호
운영 호스트에서 본 DNS 조회 주소 수. 핸드셰이크 시간은 두 쪽이 같았다. fs_usage로 보니 인증서는 첫 TLS 세션 수립 때 한 번만 읽혔다.
heaptrack이 가리킨 일반 재귀 프레임, 그리고 리포트에 이미 있던 버전과 기본 설정. 리포트의 재현 절차도 같은 OOM과 같은 힙 프레임을 냈다.
TCP 핸드셰이크와 WebSocket 업그레이드 뒤 ping/pong만 오가는 패킷 캡처, 그리고 lsmp 출력의 한 줄. 둘 다 여러 사용자 리포트에서 되풀이됐다.
근본 원인
새 mTLS 경로가 샤딩된 클라이언트를 쓰지 않아 요청이 데이터를 가진 샤드 대신 아무 파드로 갔다. 파드가 맞는 샤드에서 데이터를 가져오거나 요청을 넘기느라 서버의 꼬리 지연이 늘었다.
같은 작업에 딸려 온 프로세스 전역 캐시(기본으로 켜진 opt-out)가 1회용 상태 기계를 붙잡아 재시도마다 다시 들어가게 했다. 재진입마다 재귀 호출의 비동기 프레임이 남아 쌓였다. 그가 인용한 Claude의 분석은 그 이유를 Swift async에 꼬리 호출 최적화가 없다는 데서 찾는다.
인증 데몬이 XPC 요청을 받고도 완료 핸들러를 부르지 않았다. 토큰을 기다린 데몬은 영원히 멈췄다. 그는 증거를 붙여 인증 팀에 새 리포트를 냈다.
앞 단계가 놓친 이유
두 클라이언트 모두 유효한 구현이다(sqlite·인메모리 백엔드에는 샤딩이 필요 없다). 컴파일과 단위 테스트를 통과했다. 통합 테스트는 샤딩된 클라이언트 사용을 보지 않았다. 작은 사전 운영 클러스터에서는 지연이 드러나지 않았다.
상태 기계 테스트는 많았지만 캐시 안 재진입을 막는 테스트는 없었다. 그는 그런 쓰임을 예상하지 못했다.
간헐적이라 재현되지 않았다.
세 사례에서 에이전트를 원인으로 이끈 방법은 달랐다. 지연 버그에서는 대조군 하나가 그럴듯한 오진을 걷어 냈다. 메모리 누수에서는 리포트에 이미 있던 사실로 가설을 묶고서야 원인에 닿았다. 그가 10년 넘는 경력에서 가장 난해했다고 꼽는 멈춤 버그는 Claude가 수없는 대조 검사를 싸게 돌려 준 덕분에 커널 수준의 증거까지 내려가 풀렸다.
사람의 코드 이해에 기대는 디버깅은 더는 확장되지 않는다
2026년 여름 전까지 그는 코드를 생성한 개발자가 직접 쓰지 않은 코드일수록 더 완전히 이해해야 한다고 믿었다. 범위는 코드베이스 전체가 아니라 자기 패치가 무엇을, 어떻게, 왜 고치는지였다. 에이전트 코딩은 이 관행을 뒤집었다. 그는 동작하지 않는 패치보다 생산성의 겉모습을 좇아 쏟아지는 불필요한 복잡성을 더 무겁게 본다.
그는 앞으로 코드를 거의 전적으로 에이전트가 쓰고 읽고 디버깅한다고 본다. 파일 이름과 메서드 시그니처까지 하나하나 정하는 손 코딩의 ‘느린 연소(slow burn)’가 멘탈 모델을 새겼다. 에이전트가 몇 분 만에 수천 줄을 쓰면 그 과정이 없어, 모든 줄을 읽어도 설계의 윤곽만 남는다. 그 이해로는 자주 틀리는 에이전트를 권위 있게 반박하지 못한다.
메모리 누수에서는 상태 기계를 직접 쓴 덕분에 반박할 수 있었다. 지연 버그에서는 모든 줄을 읽고도 첫 오진을 그럴듯하다고 여겼다. 사람의 반박에 기대는 디버깅은 확장되지 않으므로, 어떤 스킬·절차·도구·팩토리도 이 사실을 전제로 삼아야 한다. 그가 세우는 목표는 사람의 입력이 거의 없이 에이전트가 어떤 문제든 디버깅하는 절차다. 이상에 머물 수도 있지만 에이전트 플랫폼을 만드는 팀의 북극성이어야 한다는 것이 그의 입장이다.
디버깅은 사실이 가설을 걸러 내는 제약 충족 문제다
그는 엔지니어가 마음먹으면 에이전트로 몇 자릿수 더 생산적이 될 수 있다고 본다. 낯선 코드를 읽지 않고 다루는 능력은 코딩이나 전통적 디버깅과 다른, 연마할 수 있는 기술이다. 멈춤 버그에서 Claude의 분석을 다 이해하지 못하고도 원인에 닿은 것은 사실에 묶인 절차 덕분이라는 것이 그의 주장이다.
에이전트의 강점에 맞추고, 코드는 맨 마지막에 읽는다
그는 지연 버그에서 Claude에게 로컬 재현과 과거 장애와의 패턴 대조처럼 사람의 방식을 시킨 것을 가장 뻔한 실수로 꼽는다. 좋은 워크플로는 에이전트의 강점에 기대고 약점에 가드레일을 친다.
구분
그가 꼽는 내용
강점 다섯
막대한 데이터를 빠르게 소화한다. 커널, 파일 시스템, 패킷, 프로세스 상태, 스레드 스택처럼 층이 다른 출처를 교차 대조한다. 거의 모든 도구와 데이터 형식을 안다. 지치지 않아 손으로는 버거운 가설도 몇 번이고 시험한다. 일회용 스크립트와 벤치마크를 즉석에서 만든다.
예측 가능한 약점
빈약하거나 없는 증거로 서사를 짓는다. 난해한 엣지 케이스에 매달린다. 자기가 앞서 찾은 최선의 발견을 지나친다.
코드부터 읽히는 것은 안티패턴이다. 그는 코드를 ‘믿을 수 없는 화자(unreliable narrator)’라 부른다. 코드는 무엇이 일어날 수 있는지 말할 뿐 무엇이 일어났는지 말하지 않는다. 읽을 코드를 에이전트가 고르는데, 잘못 고르면 추론이 틀어진다. 넓은 상태 공간을 버그의 작은 핵까지 줄이는 데에도 코드 읽기보다 나은 방법이 있다.
사실은 제약이고, 가설은 모든 제약을 만족해야 남는다
제약 없는 열린 문제는 에이전트에게 스스로 목을 맬 밧줄을 쥐여 준다. 코드 한 조각을 골라 읽고 그럴듯한 가설 하나를 사실처럼 내놓을 수 있기 때문이다. 아래층인 사실 층(factual tier)은 로그, 트레이스, 프로파일, 텔레메트리, 커널·DNS 데이터, 사용자 리포트, 설정, 버전 이력처럼 외부 출처와 자동 도구가 모은 데이터다. 에이전트는 사실을 분석하고 더 요청할 뿐 고치거나 반박하지 못한다. 수집 방식이 의심스러우면 사용자에게 알린다.
위층인 가설 층(derivative tier)은 가설, 결론, 추측, 서사처럼 에이전트가 사실에서 끌어낸 모든 것이다. 에이전트가 가장 그럴듯하게 들리면서 가장 자주 틀리는 층이라 의심을 가장 많이 받아야 한다. 그는 사실 하나하나를 제약으로 다룬다. 모든 제약을 만족하지 못한 가설은 버린다.
원인은 대개 하나다. 지연 버그의 운영 규모는 원인이 드러나는 조건이지 두 번째 원인이 아니다. 메모리 누수는 프로세스 전역 캐시와 1회용 상태 기계가 겹쳐야 났다.
트리거가 사라진 뒤에도 나쁜 상태가 이어지는 준안정 장애(metastable failure, Bronson 외, HotOS 2021)도 그가 드는 결합 사례다. 짧은 용량 감소는 실패의 시작을, 재시도 상한 없는 클라이언트는 용량이 돌아온 뒤의 지속을 설명한다. 결합 가설은 전체로서 모든 사실을 만족하되, 원인 하나를 빼면 설명되지 않는 사실이 남아야 한다. 불편한 사실을 더 복잡한 가설로 덮어서는 안 된다.
세 단계: 신호를 가리고, 가설을 고르고, 모든 사실로 검증한다
시간과 토큰을 아끼려면 모든 가설을 검증하지 말고 모든 사실을 만족하는 가설을 일찍 찾아야 한다. 쫓을 가설을 고르는 일을 그는 조향(steering)이라 부른다. 1·2단계가 조향, 3단계가 검증이다. 그의 용어로 변수는 버그가 나는 쪽, 대조군은 의심 요인 하나만 다른 비교 대상, 신호는 버그를 짚거나 설명할 수 있는 모든 데이터 점이다.
단계
하는 일
지연 버그에서
반박 검토자가 지울 것
1. 사실 층에서 고충실도 신호를 가린다
변수에만 있는 데이터 점이 고충실도 신호다. 양쪽에 다 있으면 잡음이다. 잡음은 어디를 볼지 알려 주지 않지만, 양쪽 로그에 다 찍힌 오류에 기댄 가설을 지운다. 쉬운 버그에서는 모든 것이 정상인 ‘행복 경로’, 까다로운 버그에서는 다르게 더 시끄럽게 실패하는 ‘슬픈 경로(sad path)’가 좋은 대조군이다. 대조군의 값어치는 변수와 같은 방식으로 망가지지 않았다는 확신만큼이다.
DNS 주소 수는 변수에만 있었다. 핸드셰이크 시간은 양쪽이 같아 잡음이었다.
대조군에도 있는 신호. 변수와 같은 방식으로 망가져 모든 신호를 잡음으로 만드는 대조군. 엉뚱한 호스트·시간대·도구 버전으로 모은 사실.
2. 가설 층에서 가장 유망한 가설을 고른다
만족하는 가설이 아주 적은 신호, 이상적으로는 하나뿐인 신호가 강한 신호다. 가설과 신호를 교차시킨 가설-사실 행렬을 열로 읽으면 신호마다 맞는 가설 수가, 행으로 읽으면 모든 사실을 만족하는 가설이 보인다. SLO와 SLI는 대개 증상일 뿐인 약한 신호다.
서버 P99 상승은 서버 회귀, 인증서 재적재, 클라이언트 라우팅 회귀, 용량 부족, 이벤트 루프 멈춤, 스로틀링, 느린 업스트림의 일곱 가설에 모두 맞았다. Claude의 인증서 가설이 그럴듯했던 이유다. DNS 조회는 클라이언트 라우팅 하나에만 맞았다.
거의 모든 가설이 맞는 약한 신호. 가설을 쫓지 않고 신호마다 맞는 가설 수만 어림한다. 기존 가설을 쪼개거나 합치거나 결합해 보지 않고 세운 새 가설. 신호가 모두 약하다는 확인 없이 더 깊이 내려간 데이터 수집.
3. 모든 사실로 검증한다
유망한 가설도 후보일 뿐이다. 잡음까지 포함한 모든 사실에 대 본다. 하나라도 어긋나면 버린다. 사실이 너무 적으면 여러 가설이 살아남아 가를 수 없다.
클라이언트 라우팅 가설은 DNS 조회, 서버 P99, 같았던 핸드셰이크 시간, 나머지 데이터와 모두 맞았다.
가설이 만족하지 못하는 사실 하나. 인용 요구가 실용적인 방법이다. 결합 가설이면 반례가 어느 원인을 무너뜨리는지 본다. 원인을 하나씩 빼서 모두 꼭 필요한지도 시험한다.
모든 가설이 모자랄 때도 있다. 메모리 누수 리포트의 사실은 셋이었다. 누수는 최근 버전에서 시작됐다. 사용자는 amortization을 켜지 않았다. 힙 프로파일은 amortization 진입점(dedupedRunStateMachine, 683행)이 아니라 일반 재귀 호출(runStateMachine, 562행)을 가리켰다. 오래된 상태 기계 버그 가설은 첫째 사실과, amortization 가설은 둘째 사실과 어긋났다.
그는 이것을 가설 집합이 잘못됐다는 신호로 읽는다. 완전히 새 가설 전에 더 싼 전술부터 차례로 쓰는데, 전술마다 행렬을 다시 만든다.
전술
하는 일
예
1. 사실 수집 경위를 다시 확인한다
엉뚱한 호스트·시간대·도구 버전에서 모은 사실은 옳은 가설을 틀려 보이게 한다. 에이전트는 사실을 고칠 수 없으므로 모은 사람에게 재확인을 요청한다.
메모리 누수에서는 세 사실이 모두 확인돼 효과가 없었다.
2. 거친 가설을 쪼갠다
무엇이 원인인지 특정하지 못하는 가설을 따로 검증할 수 있는 가설로 나눈다. 한쪽을 반박하는 사실이 다른 쪽까지 지우지 않는다.
배포 뒤 P99가 회귀했다면 ‘배포가 원인’을 ‘연결 풀 설정 변경’과 ‘로그 상세화’로 나눈다. 메모리 누수의 ‘amortization 작업’ 가설에는 기본으로 켜진 캐시와 기본으로 꺼진 amortization이 섞여 있었다. Claude는 주장을 거두면서 이 구분을 스스로 짚었다.
3. 잘게 쪼갠 가설을 합친다
증상 하나씩만 설명하는 가설들을 모든 증상을 설명하는 더 넓은 가설 하나로 바꾼다.
가용 영역 전체의 네트워크가 나빠졌을 때 ‘17번 호스트 디스크가 느리다’, ‘42번 GC 멈춤’, ‘88번 시끄러운 이웃’을 ‘그 영역 호스트들이 공유하는 무언가가 나빠졌다’로 합친다.
4. 상보적 가설을 결합한다
각자 일부 사실만 설명하되 어느 사실과도 어긋나지 않는 가설들을 둘 다 남긴 채 묶는다. 어느 하나를 빼도 설명되지 않는 사실이 남아야 한다.
배포 직후 베타 OS 호스트에서만 요청이 실패하면 ‘배포가 버그를 넣었다’와 ‘베타 OS에 버그가 있다’를 결합한다. 메모리 누수에서는 캐시 가설이 최근 시작과 amortization 꺼짐을, 1회용 상태 기계 가설이 힙 프레임을 설명한다.
5. 그제야 새 가설을 세운다
1~4가 실패한 뒤에만, 기존 사실에 근거해 세운다. 더 세울 가설이 없으면 점진적 신호 발견으로 사실을 더 모은다.
메모리 누수에서 Claude는 2번을 건너뛰고 곧장 새 가설로 갔다.
메모리 누수는 2번과 4번을 거쳤다면 모든 사실을 만족하는 결합 가설을 얻을 수 있었다. 인증 정보를 처음부터 넣지 않았을 때만 난다는 리포트의 조건까지 대면 남는 가설은 그것 하나다. 그는 이 행렬을 에이전트에게 만들게 하는 것이 틀을 적용하는 싸고 반복 가능한 방법이라고 본다.
고충실도 신호가 모두 약하거나 다섯 전술 뒤에도 모든 가설이 모자라면, 가설을 더 만들지 않고 1단계로 돌아가 다른 층에서 사실을 모은다. 그는 이것을 점진적 신호 발견(progressive signal discovery)이라 부른다. 리포트에 있거나 로그·텔레메트리·설정처럼 싼 데이터에서 시작해 한 층씩 내려간다. 각 층은 다음 층이 설명할 범위를 좁힌다. 찾는 것은 가장 깊은 신호가 아니라 가장 강한 신호라서, 강한 신호가 나오면 멈춘다.
지연 버그는 두 층이 걸렸다. 1층인 텔레메트리·설정·코드·포스트모템에서는 약한 P99 신호만, 2층인 운영 호스트에서는 강한 DNS 신호가 나왔다. 멈춤 버그는 로그, 패킷 캡처, 프로세스 상태, 커널 IPC 표의 네 층이 걸렸다. 프로세스 상태는 인증 경로가 루프가 아니라 대기 중임을 보여 줬다. 사람은 대개 한두 층에서 멈추지만 에이전트는 층마다 다른 도구를 들고 지치지 않고 내려간다.
사실은 두껍게, 가설은 얇게
사실 층은 두꺼울수록 좋다. 여러 출처의 다른 데이터와, 같은 버그의 여러 사례에서 모은 같은 종류 데이터(여러 사용자의 패킷 캡처, 두 운영 호스트의 CPU 프로파일)가 다 필요하다. 데이터가 많으면 1단계가 패턴 인식이 된다. 여러 사례에 되풀이되면서 대조군에 없는 패턴은 진짜 신호일 가능성이 높다. 바이트 수, 연 파일 수, DNS 조회 수, RPC 요청 수, 첫 바이트까지 걸린 시간을 세는 산술이 신호를 가르기도 한다. 지연 버그의 DNS 주소 수 비교가 그 산술이다.
가설 층은 반대로 얇을수록 좋다. 두꺼운 가설 층은 사실 층의 결함을 드러낸다. 하나는 제약이 너무 적은 경우로, 메모리 누수에서 사실은 Claude의 가설 여섯 개 가운데 둘만 지웠다. 다른 하나는 지연 버그의 P99처럼 너무 많은 가설이 통과하는 낮은 질의 제약이다.
재현도 가설이고, 반박 검토는 제거 도구다
에이전트가 만든 재현은 가설 층에 속한다. 합성 하네스나 비운영 환경의 재현이면 더 의심한다. 에이전트는 드문 엣지 케이스를 재현해 고친 뒤 끝났다고 선언할 수 있는데, 사용자가 겪은 버그는 그대로 남는다. 재현은 버그가 있다는 것만 증명하므로, 사용자에게 일어난 일을 기록한 사실 층이 재현보다 앞선다.
재현을 시도할 때는 사실 층의 가장 강한 신호를 먼저 고르고 재현이 같은 신호를 내는지 확인한다. 같은 신호를 내지 못하는 재현은 실재하더라도 다른 버그다. 반증된 재현은 잡음이다. 멈춤 버그의 동료 재현은 이 확인에서 걸러졌다. 메모리 누수의 재현은 통과했다.
반박 검토(adversarial review)는 만병통치약처럼 회자되지만 그는 예측 가능하게 실패하는 약한 안전장치로 본다. 검토자는 가설이 이미 있는 제약을 만족하는지만 볼 뿐 빠진 제약은 보지 못한다. 같은 모델이 같은 컨텍스트와 데이터를 보면 두 에이전트의 동의는 한 의견을 두 번 센 것이다. 확인은 몇 번을 쌓아도 가설을 증명하지 못하지만 반례는 하나로 충분하므로, 검토는 검증보다 제거의 도구다. 지연 버그의 서브에이전트가 인증서 가설을 반박하지 못한 것도 인증서를 한 번만 읽는다는 추적이 아직 사실 층에 없었기 때문이다.
Sridharan의 종단 간 에이전트 디버깅 루프. 가운데 줄이 사실 수집에서 근본 원인까지의 정상 경로다. 위의 상자 셋은 단계마다 붙는 반박 검토자의 질문이다. 아래 화살표는 되돌아가는 길이다. 사실 하나가 가설과 어긋나면 다음 가설로, 살아남는 가설이 없으면 다섯 전술로, 여럿이 살아남으면 사실 수집으로 간다. 강한 신호가 없거나 5번 전술이 실패하면 한 층 아래에서 사실을 모은다.
소프트웨어 팩토리는 사실을 기본으로 모으고 사람 개입을 결함으로 세야 한다
그가 팩토리에 거는 원칙은 사람 없이 모든 버그를 진단하고 검증하는 에이전트 네이티브 디버깅 절차다. 사람이 끼어든 자리는 절차의 빈틈이다. 세 버그에서 사람이 보탠 대조군, 제약, 새 검사는 에이전트가 스스로 마련했어야 했다. 그는 버그당 사람 개입 횟수를 팩토리가 얼마나 에이전트 네이티브인지 보여 주는 가장 정직한 척도로 본다. 목표는 0에 가까운 값이다.
기존 디버깅 도구는 사람이 적은 데이터를 보도록 만들어졌다. trace 로그를 기본으로 끄는 관행, 대시보드 집계, 그래프가 그렇다. 에이전트에게는 원시 데이터가 더 필요하다. 사람에게 대시보드를 쓸모 있게 만드는 집계가 에이전트에게는 신호를 망가뜨린다. 지연 버그의 서버 P99는 사람에게는 완벽한 지표였지만 에이전트의 제약으로는 거의 쓸모가 없었다. 두 목표가 부딪히면 그는 대상을 나눠 맞춘다.
대상
맞추는 쪽
그의 주장
사실 층
에이전트
팩토리가 텔레메트리, 운영 환경, 소스 관리, 합리적 범위의 사용자 기기까지 닿아야 한다. 보안과 개인정보 때문에 그 접근은 읽기 전용 기본값, 조사 범위 한정, 시간 제한, 부하·속도 제한 인지, 개인정보 제거, 감사 가능성을 갖춘 단계적 접근이어야 한다.
결정 지점
사람
모든 결정을 사람이 진다는 뜻은 아니다. 느리지만 동작하는 기능의 롤백, 다른 팀으로의 에스컬레이션, 기능 지원 중단처럼 정치적 위험과 책임이 따르는 결정이 대상이다. 그 결정은 나중에 에이전트가 적용할 정책 플레이북이 된다.
정책으로 못 푸는 결정
사람이 고르고 에이전트는 선택지를 낸다
메모리 누수에서 캐시를 끌지, 상태 기계를 캐시 안에서도 안전하게 만들지는 득실이 달라 사람이 골랐다. 에이전트의 일은 선택지를 근거와 함께 모호함 없이 내놓는 것이다.
그는 에이전트 네이티브가 되려면 사람만 디버깅하던 시대의 습관을 뒤집는 사고 전환 다섯 가지가 필요하다고 본다.
전환
그의 근거와 사례
검증 설계 > 코드 이해
세 버그에서 그의 가장 값진 기여는 코드 지식이 아니라 대조군과 제약을 고르고 검증되지 않은 가설을 기각한 판단이었다. 좋은 검증 루프는 코드보다 넓은 시스템 이해를 요구한다. 샤딩이 깨지지 않았음을 보이려면 샤딩의 구현, 회귀의 모습, 그것을 재고 비교할 신호를 알아야 한다. 이 결정은 코딩 에이전트가 첫 줄을 쓰기 전 계획 단계에 들어간다. 가장 강한 신호는 기본으로 모이는 맨 위층에 있어야 한다. 애플리케이션 층이 DNS 조회 수를 기록했다면 지연 버그의 강한 신호를 1층에서 바로 얻을 수 있었다.
데이터 수집 > 코드 독해
인덱싱, 검색, 더 긴 컨텍스트 같은 도구 개선은 대부분 코드 이해를 겨눈다. 지연 버그와 메모리 누수에서 에이전트의 첫 오답은 코드 읽기에서 나왔다. 맞는 원인은 운영 호스트의 DNS 조회, 힙 프로파일, 커널 IPC 표처럼 에이전트가 평소 보지 않는 사실에서 나왔다. 팩토리의 일은 코드를 읽기 쉽게 만드는 것이 아니라 맞는 층에서 맞는 때에 더 나은 사실을 모으게 하는 것이다.
기본 수집 > 필요할 때 수집
필요할 때마다 모으면 디버깅 루프가 길어진다. 운영 호스트 일부에서 고급 데이터 수집을 상시 켜 두거나, 운영 환경 일부를 에이전트 실험용으로 떼어 두는 방법을 든다.
정책을 정하는 사람 > 고기 프록시인 사람
지난 1년 사이 많은 개발자가 에이전트의 텍스트를 복사해 옮기거나 에이전트 대신 명령을 치는 ‘고기 프록시(meat proxy)’가 됐다. 사람은 많은 텍스트를 처리하는 데 약하다. 에이전트는 열쇠 구멍으로 일하면 헤맨다. 사람은 에이전트의 최종 결정에 모호함이 있을 때만 들어온다. 그때마다 에이전트가 기댈 정책이 자란다.
‘스킬 이슈’를 ‘인프라 이슈’로
‘코드는 더는 읽거나 이해할 필요가 없다’, ‘루프가 모든 것을 푼다’, ‘컨텍스트 엔지니어링만 더 하면 된다’, ‘반박 검토를 더 넣으면 된다’ 같은 공리에 회의하면 ‘스킬 이슈(skill issue)’라는 일축이 돌아온다. 그 일축은 빠진 실력이 무엇인지 말하는 대신 마크다운 파일 몇 문단에 담긴 ‘Claude에게 …하라고 시키면 된다’를 되풀이한다. 세 버그에서 에이전트는 능력이 있었지만, 시키기 전에는 대조군도, 재현도, 리포트의 사실로 가설을 묶는 일도, 근거 인용도 하지 않았다. 에이전트를 막은 것은 능력이 아니라 얼마나 멀리 가려 하느냐였다. 디버깅이 운전자의 실력에 달리면 교훈은 한 사람에게 갇힌다. 다음 도약은 프롬프트를 잘 쓰는 개인이 아니라 에이전트가 끝까지 가게 만드는 팩토리에서 나온다.
그가 본 팩토리 구현은 대부분 검증을 강조하면서 운영 전 검증의 구체적 방법은 건너뛴다. 그가 꼽는 빈자리는 넷이다.
빈자리
그의 주장
통과한 테스트 너머의 신호
속성 기반 테스트와 형식 검증 이야기는 많아도, 통과한 테스트보다 엄격하게 변경을 검증하는 신호 이야기는 없다. 그는 2018년에 낸 책에서 개발 수명 주기의 단계마다 쓸 수 있는 테스트를 네 폭짜리 그림(quadriptych)으로 정리했다. 에이전트는 그 테스트들을 돌려 결과를 대조하는 일을 사람보다 잘한다.
운영 환경에 대고 개발하기
개발이 클라우드 에이전트로 옮겨 가면 에이전트는 변경을 만든 즉시 검증해야 한다. 운영 환경의 격리된 한 조각에 대고, 모든 신호를 읽기 전용으로 본 채 개발한다.
‘상자 속 풀스택’은 여전히 안티패턴
운영 스택 전체(데이터베이스, 캐시, 큐)를 로컬에 띄우고 에이전트가 그 일부를 디버깅하는 방식이 다시 돌아왔다. 그가 2017-12-31 글 「Testing Microservices, the sane way」에서 마이크로서비스 테스트에 반대한 방식이다. 에이전트에게는 사실 층을 운영과 똑같이 재현할 수 없어 더 나쁘다. 에이전트에게 운영 접근을 안전하게 주는 보안 구조는 운영 중 통합 테스트와 개발 에이전트의 이른 회귀 탐지에도 다시 쓸 수 있다.
디버깅 틀을 테스트와 배포 게이트에
대조군 선택, 고충실도 신호 분리, 단계마다의 반박 검토는 통합 테스트, 종단 간 테스트, 배포 전 게이트, 자동 카나리 분석에도 맞는다. 테스트의 목적은 성공과 실패라는 이진 결과가 아니라 모든 고충실도 신호의 일치가 된다. 과거 장애의 신호는 배포 전 검사가 된다. 사전 운영 환경이 클라이언트의 DNS 조회를 직전 성공 실행과 비교했다면 지연 버그는 작은 규모에서도 운영 전에 잡을 수 있었다.
마지막 사례에는 조건이 붙는다. 사전 운영에서는 샤딩된 클라이언트와 샤딩 안 된 클라이언트가 모두 주소 2개를 받을 수 있다. DNS 조회 개수만 비교하는 게이트는 이 차이를 놓친다. 조회한 이름이나 응답 주소 집합까지 비교해야 잡힌다(팩트체크).
에이전트는 개인과 조직의 약점을 키운다
공개 논의는 이 도구의 궁극적 잠재력에 쏠려 운영 소프트웨어의 현실을 잘 다루지 않는다. 그가 보는 현실에서는 모델이 좋아져도 엔지니어가 크게 나아지지 않는다. 복잡한 문제의 디버깅에 약했던 엔지니어는 Claude Code를 써도 약하다. 예전 같으면 동료에게 물었을 사람이 이제 Claude의 오진으로 만든 PR을 연다. 이런 ‘헛수정(non-fix)’은 잘해야 문제를 못 고친다. 나쁘면 새 버그를 넣거나 넓은 범위의 암묵적 기능을 깨뜨린다.
헛수정이 쌓인 코드베이스는 앞뒤가 맞지 않게 된다. 그는 ‘가독성’ 불만의 상당수가 ‘빈 칼로리’ PR로 비대해진 코드베이스에서 나온다고 본다. 꼼꼼하지 않던 엔지니어가 갑자기 구조, 성능, 보안, 엣지 케이스, 텔레메트리를 챙기지도 않는다. ‘스킬’로 적어 둔 요구도 에이전트는 겉으로만 충족할 수 있다.
에이전트 방식은 이해를 속도와 맞바꾼다. 그는 신뢰성을 지키면서도 빠르게 좋은 소프트웨어를 낼 수 있다고 보지만, 조건은 에이전트를 신탁처럼 대하지 않는 것이다. 코드에 대한 깊은 이해를 시스템에 대한 더 높은 수준의 이해와 엄격한 검증 절차로 바꾸는 것이 그가 내놓는 길이다. 이 절차를 꾸준히 적용하면 사람 없이도 에이전트의 디버깅을 믿을 근거가 쌓인다. 엄두가 나지 않던 스택의 층까지 디버깅 비용도 준다.
그는 이 분야를 숙달했다고 주장하지 않는다. 그가 끝으로 요구하는 것은 엔지니어의 인식 규율을 바꾸는 일이다.
스레드의 스크린샷은 공개본의 발췌이거나 앞선 초안이다
본 게시물의 스크린샷 4장 가운데 첫 장은 글의 서두로, 공개본에는 오른쪽 탐색 패널이 목차를 보여 준다는 괄호 문장이 더 붙었다. 둘째 장은 「Do we need to still understand the code?」 절의 한 대목, 셋째 장은 「Putting It All Together」 절의 그림인 위 루프다. 넷째 장의 7항목 가운데 공개본은 다섯만 사고 전환 제목 아래 두었다. ‘Develop In Production’은 ‘Develop Against Production’이 되어 ‘Repurpose Debugging Framework’ 항목과 함께 「What Software Factories Are Missing」 아래로 갔다. 공개본에서 둘 사이에는 「“Full Stack In A Box” Is Still An Anti-Pattern」 절이 있다. 인용된 2026-09-14 게시물의 스크린샷 2장은 「The Latency Bug」 절의 초안으로, ‘reviewed by a human coworker’가 공개본에서 ‘reviewed and approved by a human coworker’로 바뀌었다.
그의 틀은 다음 버그에서 시험된다. 사람이 끼어든 횟수가 줄지 않는다면, 빠진 것은 더 나은 프롬프트가 아니라 팩토리가 미리 마련하지 못한 대조군과 사실이다.
팩트체크
스레드(2026-10-10 17:08 UTC)와 링크된 Medium 글에서 거짓으로 판정한 주장은 없다. 기술 사실과 출처 표기는 대체로 1차 문서와 맞는다 — 남는 것은 인용 문구와 그림 출처의 작은 어긋남, 밖에서 확인할 수 없는 자기 보고 수치, 출처 없이 쓴 선행 기법이다.
판정 어휘
사실공개된 1차 문서나 원문이 주장을 그대로 뒷받침한다.
부분 사실핵심은 맞지만 문구·범위·세부 가운데 하나 이상이 1차 문서와 다르다.
불확실공개된 자료로는 참과 거짓을 가릴 수 없다. 작성자의 자기 보고와 예측이 여기에 든다.
거짓1차 문서가 주장을 반박한다.
등급: A = 핵심 주장이 검증되거나 서로 맞고 거짓이 없다. B = 핵심은 맞고 자기 보고나 작은 부분 불일치가 섞였다. C = 핵심 주장 여럿을 확인할 수 없거나 눈에 띄는 부분 오류가 있다. D = 중대한 오류가 있다. F = 대부분 거짓이다.
B
정합성 한줄평: 기술 사실과 출처 표기는 대체로 맞다. 작은 어긋남 몇 곳과 밖에서 확인할 수 없는 자기 보고 수치가 남는다.
스레드와 스크린샷
부분 사실
주장: 이 글은 사람의 코드 지식에 기대지 않고 에이전트가 올바른 근본 원인에 이르게 하는 디버깅 프레임워크를 제안한다. (본 포스트)
글은 그런 틀을 실제로 내놓는다. 코드를 "믿을 수 없는 화자"로 두고 코드 읽기를 마지막 단계로 미룬다. 수집한 사실을 제약으로 삼아 가설을 거른다. 지연 버그에서 저자가 보탠 것은 대조군과 제약이었고 코드 지식이 아니었다. 메모리 누수 버그는 다르다. 에이전트가 내세운 틀린 증거를 잡은 것은 저자가 2025년에 직접 쓴 상태 기계에 대한 지식이었다. 틀이 같은 답에 이르는 경로(전술 2와 4)는 "would've"로 쓴 가정이다. 버그 세 건 모두 사람의 개입이 필요했다고 글 스스로 적는다. 코드 지식 없이 근본 원인에 이른다는 성질은 제안이고, 사례로 보인 것은 일부다. 스레드 제목은 Medium 제목에 "with agents"와 "read or"를 더한 형태다.
스크린샷 1은 글 서두의 네 부분 소개와 같다. 공개본은 첫 문장에 오른쪽 탐색 패널이 목차를 보여 준다는 괄호 하나를 더했다. 스크린샷 2는 「Do we need to still understand the code?」 절의 다섯 문단과 글자 그대로 같다. 에이전트가 쓴 코드는 직접 쓴 코드만큼 깊이 알 수 없다는 문단에서 시작해 "lodestar" 문장으로 끝난다.
스크린샷 3은 글의 그림 34 「The End-To-End Agentic Debugging Loop」과 같은 그림이다. 상자와 문구가 일치한다. 사실 수집, 1단계 신호 분리, 2단계 강한 신호 찾기, 조향, 3단계 검증, 근본 원인이 한 줄로 이어진다. 단계마다 반박 검토자가 붙고, 아래에 다섯 전술과 한 층 아래로 내려가는 수집 고리가 있다. 본문과 다른 곳이 한 군데 있다. 그림과 전술 5의 설명은 "전술 1~4"를 거친 뒤에 새 가설을 허용한다. 반박 검토자 절의 본문은 "전술 2~4"를 거쳤는지 보라고 쓴다.
주장: 스크린샷 4의 일곱 항목이 「The Paradigm Shift In Thinking Required In The Agent-Native Vanguard」의 내용이다. (본 포스트)
공개본에서 그 제목 아래 있는 소절은 다섯 개다. "Designing Verification > Understanding Code"부터 "Turn 'Skill Issue' Into An 'Infrastructure Issue'"까지다. 나머지 두 항목은 별도 절 「What Software Factories Are Missing」에 있다. 스크린샷의 "Develop In Production"은 공개본에서 "Develop Against Production"이다. 본문은 운영 환경의 격리된 일부에 대고 모든 신호를 읽기 전용으로 받으며 개발하라고 쓴다. 운영 환경 안에서 개발하라는 뜻이 아니다. 공개본의 소절 「"Full Stack In A Box" Is Still An Anti-Pattern」은 스크린샷에 없다.
주장: 인용된 2026-09-14 포스트의 스크린샷 2장은 쓰고 있던 글의 지연 버그 대목이다. 처음 쓴 전술로는 근본 원인을 찾지 못했다. (인용된 포스트)
공개본 「The Latency Bug」 절에 같은 대목이 있다. Claude에게 준 지시 여섯 개도 같다. 다른 곳은 다섯 군데다. 초고의 긴 구현 지시 목록이 공개본에서 "APIs, layering, telemetry, edge cases, testing, error handling"으로 줄었다. "reviewed by a human coworker"가 "reviewed and approved by a human coworker"로 바뀌었다. 초고 끝의 "To reiterate…" 문장이 빠졌다. "as well as individual traces"가 빠졌다. 초고의 P99 그래프 한 장(세로축 0~350k)은 축 범위와 시간대가 공개본의 서버 쪽 그래프(그림 3)와 같다. 공개본은 그 앞에 클라이언트 쪽 그래프(그림 2)를 더했다. 전술 이야기도 공개본과 맞는다. Claude의 첫 진단인 인증서 재로딩은 틀렸고, 답은 대조군 비교에서 나왔다.
길이는 수치로 맞다. Medium 표기 63분, 약 1만 6,300단어, 그림 35장이다. 집필 기간은 자기 보고다. 공개 흔적으로는 2026-09-14 03:15 UTC 포스트가 이미 "Writing a post…"라며 다듬어진 초고 대목을 보였다. Medium 페이지의 구조화 데이터는 게시 시각을 2026-10-10 11:57 UTC로, 마지막 수정을 2026-10-11 01:45 UTC로 적는다. 초고 공개에서 게시까지는 26일이다. 초고는 그 전부터 있었으므로 한 달 넘게라는 말과 어긋나지 않는다. 밖에서 확정할 자료도 없다.
주장: lsmp는 "list Mach ports"의 줄임말이다. 파일에 대한 lsof처럼 프로세스마다 Mach 포트와 그 권한을 보여 준다. (글이 인용한 Claude의 요약 포함)
macOS 매뉴얼의 이름 줄은 "Display mach port information for processes on the system"이다. 설명은 태스크의 포트 공간에 있는 활성 권한을 모두 출력한다고 쓴다. 권한 열의 값은 recv, send, send-once, port-set이다. send-once 권한 수를 세는 sonce 열도 있다. 약어 풀이 자체는 매뉴얼에 적혀 있지 않다. 기능 설명은 글과 맞는다.
주장: XPC 요청을 보내면 서버는 한 번 쓰면 사라지는 "응답 티켓"을 받는다. 그 티켓이 남아 있다면 응답은 보내지지 않은 것이다. (글이 인용한 Claude의 요약)
XNU의 mach_msg 매뉴얼이 이 의미를 적는다. MACH_MSG_TYPE_MOVE_SEND_ONCE로 보내면 "The caller loses the supplied send-once right." send-once 권한 하나는 메시지 한 통의 수신을 보장한다. 그 메시지는 이 권한으로 보낸 응답이거나, 권한이 파기되었을 때 커널이 보내는 send-once 알림이다. 서버의 포트 공간에 권한이 남아 있다면 응답도 파기도 일어나지 않았다. 반대 방향은 성립하지 않는다. 권한이 사라졌다고 해서 응답이 갔다는 뜻은 아니다. 글은 남은 쪽만 주장한다. 글이 "IPC request"에 링크한 Apple 커널 가이드 보관 문서는 send-once를 권한 종류 목록에만 적는다. 소비 의미는 XNU mach_msg 매뉴얼에 있다.
주장: 인증 데몬이 XPC 요청을 받고 완료 핸들러를 끝내 부르지 않으면, 요청을 보낸 클라이언트는 토큰을 기다리며 영원히 멈춘다.
Apple XPC 문서는 클라이언트가 메시지를 보내고 서비스의 응답을 받는 구조를 설명한다. 보관 문서 「Creating XPC Services」는 응답을 요구하는 메시지에 서비스가 xpc_dictionary_create_reply로 답해야 한다고 쓴다. xpc_connection 매뉴얼에 따르면 동기 API는 응답이 올 때까지 호출자를 막는다. 오류는 연결이 끊기거나 무효가 되어 원격이 답을 영영 보내지 않을 것이 확실할 때만 돌아온다. 매뉴얼은 시간 제한을 말하지 않는다. 연결이 살아 있는 채로 서비스가 답을 미루면 클라이언트는 계속 기다린다. 글에 나오는 60초 제한은 그 위 가상 파일 시스템 계층의 것이다.
주장: Swift 비동기 코드에는 꼬리 호출 최적화가 없다. 재귀 호출마다 프레임이 살아남아 컨테이너의 메모리가 바닥날 때까지 쌓였다. (글이 인용한 Claude의 분석)
메커니즘은 맞다. "없다"는 단정은 부정확하다. Swift는 사용자 코드의 꼬리 호출 최적화를 보장하지 않는다. Swift Forums의 2019-06-12 답변은 "support…but it doesn't make any guarantees"라고 쓴다. 비동기 함수의 구현은 오히려 필수 꼬리 호출을 쓴다. LLVM 패치 D95443(2021-05-17 커밋)이 이를 위해 swifttailcc 호출 규약을 더했다. 같은 해 LLVM 개발자 회의 발표(John McCall·Arnold Schwaighofer)는 호출자가 피호출자의 비동기 문맥을 swift_task_alloc으로 잡고, 피호출자가 돌아온 뒤에야 swift_task_dealloc으로 푸는 코드를 보인다. 재귀 await 한 단계마다 문맥 하나가 힙에 남는다는 결론은 이 구조와 맞는다. heaptrack이 힙에서 이 할당을 잡은 것과도 맞는다. 원인은 "꼬리 호출 최적화가 없다"보다 "호출 문맥이 반환 전까지 풀리지 않는다"가 정확하다.
주장: heaptrack은 리눅스용 힙 메모리 프로파일러다. 사용자가 붙인 출력은 OOM 직전까지 약 50GB가 힙에 할당된 위치를 보여 주었다.
개발자 Milian Wolff가 2014-12-02 블로그 글에서 heaptrack을 "a heap memory profiler for Linux"로 발표했다. malloc 계열 호출을 하나하나 역추적과 함께 기록한다. 정점 사용량과 해제되지 않은 할당도 보고한다. 리눅스 도구라는 점은 Docker 컨테이너에서 난 버그라는 글의 정황과 맞는다. 50GB라는 값은 사용자 보고 속 수치다.
주장: System Diagnostics(sysdiagnose)는 시스템 성능 문제 조사에 쓸 시스템 전역 진단 정보를 모은다.
글의 이 문장은 sysdiagnose 매뉴얼의 이름 줄 "gathers system-wide diagnostic information helpful in investigating system performance issues"와 같다. 글이 링크한 Apple 지원 문서는 활성 상태 보기의 System Diagnostics가 Mac의 여러 로그로 보고서를 만든다고만 쓴다. 문구를 실은 쪽은 매뉴얼 페이지다. 두 문서 모두 글의 설명과 어긋나지 않는다.
주장: 연결 업그레이드 뒤 패킷에 보인 것은 주기적인 WebSocket 핑과 퐁뿐이었다 — 핑·퐁은 연결 유지용 하트비트다.
RFC 6455 §5.5는 Ping(0x9)과 Pong(0xA)을 제어 프레임으로 정의한다. Ping을 받은 쪽은 Pong으로 답해야 한다. "A Ping frame may serve either as a keepalive or as a means to verify that the remote endpoint is still responsive." 주기적 송신은 표준의 의무가 아니라 구현 관행이다. websocket.org 안내서는 30~45초 간격을 권한다. 인증 단계에서 멈춘 연결에서도 핑·퐁은 계속 오간다. 글이 묘사한 패킷 모양은 이 구조와 맞는다.
주장: tcpdump로 클라이언트의 TCP 핸드셰이크 시간을 잴 수 있다. fs_usage 출력으로 인증서를 언제 읽는지 볼 수 있다.
tcpdump 매뉴얼은 패킷마다 타임스탬프를 찍는다고 쓴다. -ttt 옵션은 앞 줄과의 시간 차를 마이크로초 단위로 출력한다. 출력에는 SYN과 ACK 플래그가 나온다. SYN에서 마지막 ACK까지의 시간이 핸드셰이크 시간이다. macOS 매뉴얼은 fs_usage를 "report system calls and page faults related to filesystem activity in real-time"으로 소개한다. 줄마다 프로세스 이름이 붙는다. 두 도구의 용도는 글의 설명과 맞는다.
주장: 준안정 장애(metastable failure)란 "system enters a bad state that persists even after the trigger is removed"인 상태다(Bronson et al).
저자와 학회는 맞다. 논문은 Nathan Bronson, Abutalib Aghayev, Aleksey Charapko, Timothy Zhu의 「Metastable Failures in Distributed Systems」이고 HotOS '21(2021-05-31~06-02)에 실렸다. 따옴표 안 문구는 원문 그대로가 아니다. 원문은 "a trigger causes the system to enter a bad state that persists even when the trigger is removed"다. 뜻은 같다. 글의 예인 용량 감소와 무제한 재시도의 결합은 논문 §2.1의 재시도 사례와 같은 계열이다. 논문 사례에서는 요청당 재시도 한 번으로도 장애가 유지된다. "무제한" 재시도가 필요 조건은 아니다. 글이 용어에 건 작성자의 X 포스트는 2021-06-01 16:35 UTC에 이 논문을 소개한 글이다.
주장: 2018년에 낸 책에서 소프트웨어 개발 단계별 테스트를 네 칸으로 나눈 그림(quadriptych)을 제안했다.
책은 O'Reilly의 「Distributed Systems Observability」다. O'Reilly 페이지는 출간을 2018-07, 분량을 34쪽으로 적는다. 3장 제목은 「Coding and Testing for Observability」다. 글의 그림 35는 사전 운영, 배포, 출시, 출시 후의 네 칸에 테스트 종류를 나열한 조판 그림이다. 같은 네 칸 구성은 같은 해 2018-03-25 블로그 글 「Testing in Production, the safe way」에 손그림으로 먼저 나왔다. 손그림에는 그림 35에 있는 Stress tests와 Soak tests가 없다. 네 칸 그림이 2018년에 나왔다는 점은 두 자료로 맞는다.
그림 2와 3은 "Edit chart" 버튼이 보이는 텔레메트리 대시보드 화면이다. 그림 35는 저자가 2018년 책의 그림이라고 소개하는 테스트 네 칸 그림이다. 같은 구성의 손그림이 2018-03-25 블로그 글에 있다. 개념도와 흐름도는 고지대로 Claude가 그렸을 수 있다. 밖에서 확인할 방법은 없다. "모든"이라는 말은 적어도 그림 2·3·35에는 맞지 않는다.
주장: 약 10년 전 글에서 "full stack in a box" 방식이 마이크로서비스 테스트에 나쁜 생각인 이유를 설명했다.
링크된 글은 「Testing Microservices, the sane way」이고 2017-12-31에 나왔다. 「Full Stack in a Box — A Cautionary Tale」 절에서 전체 서비스 구성을 개발자 노트북에 재현하려던 시도를 "fallacy"라고 부른다. 저자가 예전 회사에서 Vagrant 상자로 전체 스택을 띄우려 했던 경험담이다. 게시일에서 2026-10-11까지는 8년 9개월이다. "약 10년"은 넉넉하게 올려 잡은 표현이다.
주장: 코드가 어떻게 동작하는지에 대한 강한 멘탈 모델이 좋은 소프트웨어를 만들고 디버깅하는 데 꼭 필요하다는 믿음을 오래 지켜 왔다.
링크된 글 「Effective Mental Models for Code and Systems」는 2018-12-30에 나왔다. 글은 코드의 품질을 처음 쓴 사람이 아니라 미래의 독자와 디버깅하는 사람이 판단한다고 쓴다. 코드를 쓸 때의 멘탈 모델을 다시 세우는 짐이 독자에게 있다는 이유다. Ken Thompson이 디버거 대신 머릿속 모델로 버그를 짚어 내는 일화도 인용한다. 2026-10 글이 이 믿음을 다시 생각한다고 쓴 것과 이어진다. 두 글 사이는 7년 9개월이다.
주장: 약 9천 줄(100% AI 생성)의 코드에서 실제 버그를 고쳤다. (인용된 2026-09-14 포스트)
포스트와 글의 서술은 서로 맞는다. 글에 따르면 두 저장소에 걸친 약 9000줄을 에이전트가 하루 남짓에 처음부터 끝까지 썼다. 저자는 열 개 패치를 한 줄도 빼지 않고 읽었다고 쓴다. 동료 한 명이 검토하고 승인했다고 쓴다. 외부에서 확인할 자료는 없다. "100%"는 시제품 단계의 말이다. 글은 저자가 패치에 "a lot of additional changes"를 더했다고 쓴다. 그 변경을 손으로 했는지 에이전트로 했는지는 밝히지 않는다.
주장: 클라이언트 약 5,000대에 기능을 먼저 켰다. DNS 응답은 대조군이 약 5개, 변수가 약 400개였다. 사전 운영은 2샤드에 샤드당 복제 1개, 운영은 5샤드에 샤드당 복제 80개였다.
자기 보고다. 수치끼리는 맞물린다. 5×80 = 400은 변수의 DNS 응답 약 400개와 같다. 2×1 = 2는 사전 운영에서 주소 2개를 조회했다는 서술과 같다. 대조군의 약 5개는 운영 샤드 수 5와 같은 값이다. 한 대목에는 조건이 붙는다. 글은 DNS 조회를 이전 정상 실행과 비교하는 배포 게이트가 사전 운영의 작은 규모에서도 이 버그를 잡았으리라고 쓴다. 샤딩된 클라이언트가 샤드마다 주소 하나를 받는 구조라면 사전 운영에서는 두 클라이언트 모두 주소 2개를 받는다. 이 경우 개수만 비교하는 게이트는 차이를 보지 못한다. 조회 이름이나 응답 주소 집합을 비교해야 잡힌다.
주장: 올바른 인증 정보를 설정하지 않은 사용자의 컨테이너가 OOM으로 죽었다. 원인은 기본값으로 켜진 프로세스 전역 캐시가 일회용 상태 기계를 끝없이 다시 들어가게 만든 것이었다.
자기 보고다. 글 안의 셈은 맞는다. 사실 세 개는 최근 버전에서 시작, amortization 기능은 기본값 꺼짐, 힙 프로필의 runStateMachine 562행이다. 이 사실이 Claude의 가설 여섯 개 가운데 둘만 걸렀다는 서술은 첫 두 가설에 추가 가설 네 개를 더한 수와 맞는다. "137K번 재시도"는 저자가 기각한 Claude의 첫 가설 속 수치다. 글은 이 수치를 측정 사실로 내세우지 않는다.
주장: 인증 데몬은 요청의 응답 권한을 67시간 동안 쥐고 있었다. 저자가 근거를 요구한 주장 가운데 약 3분의 1은 근거를 대지 못했다. 저자는 그 주장을 바로 버렸다.
자기 보고다. 글 안에서는 맞물린다. 사용자가 본 60초 시간 초과는 가상 파일 시스템 계층의 제한이다. XPC 동기 호출 자체에는 시간 제한이 없으므로 그 아래 요청이 67시간 남아 있었다는 서술과 모순되지 않는다. 동료가 시계 어긋남으로 만든 재현을 저자는 받아들이지 않았다. 패킷 순서와 IPC 표 두 신호가 실제 사례와 달랐기 때문이다. 이 판단은 재현도 가설로 다루라는 글의 규칙과 일관된다.
예측이라 참과 거짓을 아직 가릴 수 없다. 공개 조사는 절반 가까이 온 수준을 보여 준다. JetBrains 개발자 생태계 조사(2026-05~07, 전문 개발자 1만 5천여 명)에서 응답자는 지난달 업무 코드의 평균 약 47%를 에이전트가 전부 썼다고 답했다. 다섯 명 가운데 한 명은 AI 없이 쓴 코드가 0이라고 답했다. 둘 다 자기 보고 수치다. 2026-10-06에 나온 Stack Overflow 2026 조사(3만여 명 응답)에서 AI를 디버깅에 쓰는 비율은 61%다. 운영 시스템의 배포·운영·장애 대응에 쓰는 비율은 20%다. "거의 전적으로"는 이 수치보다 앞서 나간 주장이다.
믿음이자 예측이라 판정할 수 없다. 제품은 이미 있다. Anthropic은 2025-10-20 Claude Code on the web을 연구 미리보기로 냈다. 작업은 Anthropic이 관리하는 클라우드의 격리된 샌드박스에서 돈다. 2026-09-23 "cloud sessions"라는 이름으로 정식 출시했다. 사용 비중을 가르는 공개 수치는 없다. JetBrains 2026 조사의 "전문 개발자 90%가 주 1회 이상 에이전트 사용"은 로컬 에이전트와 원격 클라우드 에이전트를 나누지 않는다.
주장: 지금까지 본 거의 모든 "소프트웨어 팩토리" 구현은 검증의 중요성을 강조하면서 사전 운영 검증을 어떻게 할지는 얼버무린다.
공개된 팩토리 사례 하나가 이 말과 다르다. StrongDM의 팩토리 글(Justin McCarthy, 2026-02-06)은 사전 운영 검증을 구체적으로 적는다. 코드베이스 밖에 둔 시나리오를 홀드아웃 세트처럼 쓴다. 통과 여부 대신 시나리오를 만족한 실행의 비율("satisfaction")을 잰다. Okta, Jira, Slack, Google Docs, Google Drive, Google Sheets의 행동 복제본인 Digital Twin Universe에 대고 운영 한도를 넘는 양으로 검증한다. 저자의 좁은 요점은 이 사례에도 맞는다. 검증 신호는 운영 환경의 실제 신호가 아니라 복제본에서 나온다. 저자가 반대 패턴으로 꼽은 "full stack in a box"에 가까운 방식이다. "얼버무린다"는 이 사례에 맞지 않는다. "운영 신호를 쓰지 않는다"는 맞는다.
주장: 디버깅은 관측한 사실에서 출발해 가설을 세우고 검증해야 한다. 코드를 읽어 추측하는 데서 시작하면 안 된다.
확립된 원칙이다. Andreas Zeller의 온라인 교재 「The Debugging Book」은 디버깅을 과학적 방법으로 정리한다. 가설을 세우고, 가설을 지지하거나 반박할 예측을 내고, 실험으로 시험한다. 가설과 관찰의 불일치가 사라질 때까지 되풀이한다. 가설과 실험을 표로 적어 두라고도 권한다. David Agans의 「Debugging: The 9 Indispensable Rules」(AMACOM, 2002)는 "Make It Fail", "Quit Thinking and Look", "Change One Thing at a Time"을 규칙으로 든다. 글의 "코드는 믿을 수 없는 화자", 재현, 대조군 원칙은 이 규칙들과 겹친다. 글은 두 자료를 인용하지 않는다.
주장: 버그가 나는 경우(변수)를 한 가지만 다른 경우(대조군)와 견주면, 변수에만 있는 데이터가 고충실도 신호가 된다.
통과하는 경우와 실패하는 경우의 차이를 좁혀 원인을 찾는 방법은 델타 디버깅(Zeller와 공저자, 2002)이 정식화했다. 「The Debugging Book」의 해당 장은 통과 입력을 키우고 실패 입력을 줄여 둘이 만나는 지점을 찾는다. 그때 남는 차이를 "what makes the difference between passing and failing"이라고 쓴다. 글의 대조군 비교는 이 생각을 입력이 아닌 운영 관측 데이터에 옮긴 것이다. 글이 더한 것은 대조군 고르기의 요령이다. 까다로운 버그에는 정상 경로보다 "다르게, 더 시끄럽게 실패하는 경우"가 좋은 대조군이라고 쓴다.
주장: 가설과 사실을 행렬로 놓고, 만족하는 가설이 적은 신호를 강한 신호로 삼는다. 반박 검토는 가설을 확증하지 말고 제거하는 데 써야 한다.
같은 틀이 Richards J. Heuer Jr.의 「Psychology of Intelligence Analysis」(CIA 정보연구센터, 1999) 8장 「Analysis of Competing Hypotheses」에 있다. 3단계는 "hypotheses across the top and evidence down the side"인 행렬을 만들고 증거의 진단력(diagnosticity)을 따지라고 한다. "If an item of evidence seems consistent with all the hypotheses, it may have no diagnostic value." 책이 드는 고열 환자의 예는 글의 P99 지연 신호와 같은 구조다. 고열은 환자가 아프다는 것은 알려 주지만 어느 병인지는 가리지 못한다. Heuer는 가설을 확증하지 말고 기각하라고 쓴다. 가장 그럴듯한 가설은 반대 증거가 가장 적은 가설이다. 4단계는 증거로 가를 수 없는 두 가설을 하나로 합칠지 묻는다. 글의 전술 3인 합치기와 같은 질문이다. 글은 Heuer를 인용하지 않는다.
틀 전체는 저자의 것이다. 부품은 새것이 아니다. 가설 검증 루프, 대조군 비교, 진단력 행렬, 반증 우선 원칙은 1999~2002년 문헌에 이미 있다. 글은 그 출처를 밝히지 않는다. 새로운 것은 에이전트에 맞춘 운용 규칙이다. 에이전트는 사실 층을 고치지 못하고 다시 모아 달라고 요청만 한다. 재현도 가설이다. 재현은 사용자 사례의 가장 강한 신호를 똑같이 만들어 낼 때만 받아들인다. 반박 검토는 근거 인용을 요구하는 제거 도구로만 쓴다. 강한 신호가 나올 때까지 관측 층을 한 단계씩 내려간다. 버그 하나당 사람이 개입한 횟수를 팩토리의 지표로 삼는다. 이 규칙들을 실무 버그 세 건에 대 보인 점이 글의 기여다.
“세 버그 이야기”(The Tale of Three Bugs)는 내가 직장에서 에이전트의 도움을 받아 고쳐야 했던 버그 세 개의 이야기를 소개한다.
“우리는 아직도 코드를 이해해야 하는가?”(Do we still need to understand the code?)는 제목 그대로다.
“낯선 코드를 디버깅하기 위한 에이전트 기반 패러다임”(Agentic Paradigms for Debugging Unfamiliar Code)은 아마 이 글 전체의 핵심일 것이다. 이 부분은 내가 써 온 일련의 일반적인 기법을 소개한다. 이 기법들 덕분에 나는 에이전트와 함께 효과적이고 체계적으로 일하며 낯선 코드를 디버깅할 수 있다.
“자율성과 책임성을 위한 에이전트 기반 생산 현장 재정비”(Retooling the Agentic Factory Floor For Autonomy And Accountability)는 에이전트를 위한 최적화와 사람을 위한 최적화라는 두 목표가 서로 어긋날 수 있을 때 둘 사이의 트레이드오프를 평가하는 프레임워크를 제시한다. 또 완전히 자율적인 디버깅을 가능하게 해 줄, 기존 “소프트웨어 팩토리(software factory)” 구현에 빠져 있는 조각들을 짚어 내고, “에이전트 네이티브(agent-native)” 선봉에 선 사람들에게 요구되는 사고의 패러다임 전환을 자세히 설명한다.
이미지 2/4 — 에이전트가 생성한 코드를 사람이 깊이 이해하지 못한다는 대목 · X에서 원본 보기
나는 에이전트가 생성한 코드에 내가 직접 쓴 코드만큼 익숙하지 않다. 물론 설계의 대략적인 윤곽과 상위 수준의 결정은 알지만, 세세한 부분 하나하나까지 알지는 못한다. 수고를 들여 에이전트가 생성한 코드를 한 줄도 빠짐없이 읽을 때조차, 내 이해는 “서서히 타오르는(slow burn)” 방식으로 일하던 때만큼 깊지도 포괄적이지도 않다.
그리고 장담하건대, 에이전트가 완전히 틀렸을 때 권위 있게 반박할 수 있을 만큼 내 이해가 깊거나 명료하지 않다는 건 불 보듯 뻔하다. 실제로 에이전트는 그렇게 틀릴 때가 아주 많다.
어떤 “스킬(skill)”이든, 프로세스든, 도구든, 소프트웨어 팩토리든 이 근본적인 사실을 감안해야 한다.
이 사실은 새로운 패러다임을 요구한다. 엔지니어가 코드를 기껏해야 대강만 이해하고, 최악의 경우 전혀 이해하지 못하는 상황에서도 엔지니어와 에이전트가 대량의 코드를 생산적으로 출시하고 디버깅할 수 있게 해 주는 패러다임이다.
바라는 최종 목표는 사람의 입력이 최소한이거나 아예 없어도 에이전트가 어떤 종류의 문제든 디버깅할 수 있게 해 주는 프로세스를 갖추는 것이다. 이 목표는 야심 차고 이상적인 목표로 남을지도 모른다. 그래도 에이전트 기반 플랫폼과 도구를 만드는 팀에게는 북극성이 되어야 한다.
“코드를 더 이상 완전히 이해하지 못할 때 효과적으로 디버깅하는 법”에 관한 글을 쓰는 중이다.
이건 약 9천 줄(LOC, 100% AI 생성)짜리 코드에서 내가 실제로 고쳐야 했던 버그다.
여기서 처음 시도한 전술(tactic) 중 어느 것도 이 문제의 “근본 원인”을 찾는 데 도움이 되지 않았다 ㅋㅋ. 문제를 분리하려면 다른 접근법을 택해야 했다.
이미지 1/2 — 쓰고 있던 글의 「The Latency Bug」 절, 보라색 형광 표시 3곳 · X에서 원본 보기
지연 시간 버그(The Latency Bug)
스토리지 서비스에 추가한 새 기능에서 난 버그였다. 이 기능은 리포지토리 두 곳에 걸쳐 약 9000줄의 새 코드를 더했고, 처음부터 끝까지 전부 에이전트가 하루 남짓 만에 작성했다. 둘째 날이 끝날 무렵에는 완전히 작동하는 프로토타입이 준비되어 있었다.
이 기능은 결코, 조금도 “바이브 코딩(vibe coded)”으로 만든 기능이 아니었다. 오히려 정반대로, 이 기능은 아주 꼼꼼하게 개발되었다. 나는 먼저 Claude에게 새 기능에 대한 자세한 설명과 함께, 그 기능을 정확히 어떻게 구현하기를 원하는지에 대한 포괄적인 지시 사항을 주었다. 여기에는 새 API가 어떤 모습이기를 원하는지, 계층 구조가 어떻게 작동해야 하는지, 어떤 텔레메트리를 보고하게 하고 싶은지, 어떤 엣지 케이스를 처리하게 하고 싶은지, 구현을 서버 쪽 변경과 클라이언트 쪽 변경으로 어떻게 나눠야 하는지, 각 변경을 어떻게 테스트해야 하는지, 오류를 어떻게 처리하고 전파해야 하는지 등등이 들어 있었다.
이것이 완전히 작동하게 되자, 나는 약 9000줄짜리 이 프로토타입을 위험도가 낮은 것부터 높은 순서로 정렬한 별도의 패치 10개로 쪼개기 시작했다. 약 9000줄을 한 번에 몰아서 반영하지 않고, 분리된 증분을 하나씩 메인 브랜치에 병합하려는 것이었다.
나는 모든 패치의 모든 코드 줄을 하나하나 직접 읽었다. 위험도가 가장 낮은 패치에서 시작해 마지막이자 위험도가 가장 높은 패치까지 읽었다. 모든 패치에 서브에이전트 한 묶음의 검토를 여러 차례 거치게 했다. 패치를 다듬고 더 명확하게 만들려고 추가 변경도 많이 했다. 이 패치들은 모두 사람 동료의 검토도 받았다. 다시 말하지만, 이 기능은 결코, 조금도 “바이브 코딩”으로 만든 기능이 아니었다.
이미지 2/2 — P99 지연 시간 그래프와 Claude에게 준 지시 6개 · X에서 원본 보기
제목이 P99(latency_us)이고 세로축이 0~350k, 가로축이 12:00~19:00인 선 그래프로, 선은 13:45 무렵까지 약 25k~125k에 머물다가 약 150k~200k로 계단처럼 올라선 뒤 남은 구간 내내 그 수준에 머물며 300k 안팎까지 치솟는 스파이크를 보인다.
집계 지연 시간 그래프로 보나 개별 트레이스로 보나, 이 새 기능을 켰을 때 서버가 요청에 응답하는 데 훨씬 오래 걸리는 것이 분명해 보였다. 이제 과제는 무엇이 서버 성능을 이렇게 심하게 회귀(regress)시켰는지 알아내는 것이었다.
나는 요즘 모두가 하는 일을 했다. Claude가 지연 시간 텔레메트리를 들여다보게 하고, 지연 시간 회귀를 디버깅해 달라고 했다. 막연한 요청도 아니었다. 나는 이 회귀에 대해 Claude에게 구체적인 지시를 주었다:
Claude에게 지연 시간 회귀가 단위 테스트에서든 로컬 클라이언트와 서버로든 로컬에서 재현되는지 확인해 달라고 요청했다
Claude에게 클라이언트와 서버의 운영 설정을 조정 가능한 여러 속성 측면에서 꼼꼼히 살펴봐 달라고 요청했다
Claude에게 이 서버에 관한 이전의 모든 성능 관련 조사 자료와 장애 포스트모템 문서에 접근할 권한을 주었다
Claude에게 텔레메트리 시스템의 읽기 전용 접근 권한을 주어, 데이터를 더 모으려고 추가 쿼리를 실행할 수 있게 했다
Claude에게 프로젝트 전체의 git 히스토리를 특히 살펴보고, 예전에 고친 성능 안티패턴이 약 9000줄 가운데 어디에서든 실수로 다시 적용되지 않았는지 확인해 달라고 요청했다
Claude에게 자기가 내놓을지 모르는 어떤 이론이든 독립된 서브에이전트가 검증하게 하라고 요청했다
원본 (완역) 블록은 X 스레드 전부를 옮긴다. 본 포스트, 인용된 포스트, 작성자 답글, 첨부 스크린샷 6장 안의 글이 모두 들어 있다. 스레드가 링크한 Medium 글은 이 페이지에서 완역하지 않는다. 그 글의 주장은 Executive Summary와 팩트체크가 다룬다.
수록
본문 전량 수록. 조회수·좋아요 같은 X 화면 요소와 다른 사용자의 답글은 원문의 일부가 아니라서 싣지 않는다.
시각
게시 시각은 한국 표준시(KST)로 적고 협정 세계시(UTC)를 함께 적는다.
용어
사실 층(factual tier), 가설 층(derivative tier), 대조군(control), 변수(variable), 고충실도 신호(high-fidelity signal), 강한 신호(strong signal), 조향(steering), 반박 검토자(adversarial reviewer), 점진적 신호 발견(progressive signal discovery), 소프트웨어 팩토리(software factory). 모두 저자의 용어를 옮긴 말이다.
고지
원문의 저작권은 Cindy Sridharan에게 있다. 한국어 번역과 해설은 저자와 무관한 비공식 판이다.