클로드 코드에게 코드베이스 품질을 200번 개선해 달라고 요청했다.
사람들이 AI에게 똑같은 이미지를 계속해서 다시 입력시키는 실험을 본 적 있어?
아니면 마르케스 브라운리(MKBHD)가 같은 동영상을 유튜브에 1,000번 재업로드해서 화질이 깨지는 과정을 보여준 영상은?
지난 추수감사절 연휴에 시간이 좀 남아서, 클로드(Claude)에게 음식 사진과 설명만으로 영양 성분을 추정해주는 앱을 만들어달라고 시켰어. 제대로 작동하게 만드는 과정에 꽤 흥미로운 설정들이 있었지만, 그 얘기는 지루하니까 패스할게. 아무튼 클로드는 나를 위해 훌륭하고 기능적인 앱을 만들어줬지. 하지만 그 직후, 나는 클로드에게 작고 사악한 실험을 강행했어.
내 코드베이스 전체를 훑으며 아래 명령어를 반복 실행하는 간단한 스크립트를 짰거든.
#!/usr/bin/env bash
set -euo pipefail
PROMPT="Ultrathink. You're a principal engineer. Do not ask me any questions. We need to improve the quality of this codebase. Implement improvements to codebase quality."
MAX_ITERS="200"
for i in $(seq 1 "$MAX_ITERS"); do
claude --dangerously-skip-permissions -p "$PROMPT"
git add -A
if git diff --cached --quiet; then
echo "No changes this round, skipping commit."
else
git commit --no-verify -m "yolo run #$i: $PROMPT"
fi
done
...그 결과는 대재앙이었어. 200번 넘게 브레이크 없는 광란의 질주가 이어졌지. 중간중간 클로드가 한 가지에만 너무 집착하는 게 보여서 프롬프트를 살짝 조정해주긴 했지만, 반복 횟수가 쌓이자 녀석은 엄청난 영역을 건드리기 시작했어. 완벽한 코드 커버리지와 기능 코드보다 더 많은 테스트 코드부터 시작해서, Rust 스타일의 Result 타입 도입, 급기야... 해시 함수의 엔트로피 추정(???)까지 말이야.
이 스크립트는 약 36시간 동안 돌아갔고, 결과물을 파악하는 데도 꽤 시간이 걸렸어. 자, 무슨 일이 벌어졌는지 한번 보자. (참고로 전체 저장소는 공개되어 있고, highest-quality 브랜치를 확인하면 돼.)
앱 소개
이 앱은 4~5개 화면으로 구성된 간단한 앱이야. 사진을 찍고, 설명을 추가하면, AI 응답을 받는다. 이게 끝이지.

숫자 놀음
"품질 개선 이전" 버전도 이미 꽤 덩치가 컸어. 타입스크립트(TS) 코드가 약 2만 줄이었는데, 그중 9,700줄 정도가 다양한 __tests__ 디렉토리에 있었지. 이건 약간 의도된 거였어. 'Claude Code'와 작업할 때, 든든한 자체 검증 시스템(harness)이 있으면 결과물 품질이 훨씬 좋아지거든.
cloc . --exclude-dir=node_modules,dist,build,.expo,.husky,.maestro,Pods
132 text files.
127 unique files.
11 files ignored.
github.com/AlDanial/cloc v 2.04 T=0.11 s (1167.4 files/s, 487085.6 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
JSON 4 0 0 23733
TypeScript 99 3019 1541 20160
Markdown 11 1004 0 2700
JavaScript 9 26 51 269
Bourne Shell 2 34 41 213
YAML 2 35 2 162
-------------------------------------------------------------------------------
SUM: 127 4118 1635 47237
-------------------------------------------------------------------------------
하지만 그 후폭풍은... 8만 4천 줄! 코드베이스의 품질을 "개선"한다면서 2만 줄에서 8만 4천 줄로 불어났어.
cloc . --exclude-dir=node_modules,dist,build,.expo,.husky,.maestro,Pods
285 text files.
281 unique files.
10 files ignored.
github.com/AlDanial/cloc v 2.04 T=0.60 s (468.1 files/s, 268654.5 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
TypeScript 247 17587 18749 84185
JSON 5 0 0 24863
Markdown 14 4151 0 10391
JavaScript 9 41 140 598
Bourne Shell 3 41 41 228
YAML 3 50 3 215
-------------------------------------------------------------------------------
SUM: 281 21870 18933 120480
-------------------------------------------------------------------------------
테스트 코드만 1만 줄에서 6만 줄이 됐다고!
cloc . \
--exclude-dir=node_modules,dist,build,.expo,.husky,.maestro,Pods \
--match-d='__tests__'
138 text files.
138 unique files.
1 file ignored.
github.com/AlDanial/cloc v 2.04 T=0.23 s (612.9 files/s, 346313.3 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
TypeScript 138 13919 3685 60366
-------------------------------------------------------------------------------
SUM: 138 13919 3685 60366
-------------------------------------------------------------------------------
이제 훨씬 안전해진 기분이네.
테스트가 700개에서 무려 5,369개로 늘어났어. 원래 프로젝트에는 실제 시뮬레이터를 사용하는 E2E 테스트가 있었는데, 이건 코딩 에이전트가 피드백 루프를 확실히 검증하게 해주는 아주 중요한 요소였거든? 근데 품질을 개선하는 과정에서 걔네들은 잊혀진 모양이야 ¯_(ツ)_/¯.
참고로 주석도 1,500줄에서 1만 8,700줄로 늘어났어.
그래서, 도대체 뭘 한 거야?
Claude Code가 매 실행 후 요약으로 뱉어낸 전체 로그를 가지고 있어.
1. NIH 증후군 (Not-Invented-Here: 여기서 안 만든 건 안 쓴다)
Claude Code는 서드파티 라이브러리 쓰는 걸 정말 싫어하더니, 엄청난 양의 랜덤 유틸리티를 직접 만들어냈어.
의존성 목록(dependency list)이 아주 작게 유지된 건 존중해 줄 만하지만, 그 대가로 유지보수가 불가능한 2만 줄 이상의 유틸리티 코드가 생겨버렸지. 아마 공급망 공격(supply-chain attacks)을 피하고 싶었나 봐.
그중 일부는 정말 불필요했고 그냥 기성 솔루션으로 대체할 수 있는 것들이었어:
간단한
lib/logger.ts같은 거 대신, 성능 추적 기능이 내장된 완전한 계층형 로거를 구현함.리액트 훅(React Hooks). 일부는 우리 유스케이스에 특화된 거였지만, 상당수는 다시 만들 필요가 없거나 (애초에 만들 필요도 없는) 것들이었어.
2. 제정신이 아닌 것들 ('최애' 목록)
Result 타입 구현 (
lib/result.ts): Rust의Result<T, E>와 유사한 타입을 제공하는 모듈이야.나는 Rust의 결과 처리 시스템을 좋아해. 하지만 이미 에러가 표준화된 생태계 전체에 이걸 억지로 가져오려고 하면 잘 작동하지 않는다고 생각해. 전 직장에서 파이썬으로 비슷한 실험을 해봤는데, 사람들에게 잘 받아들여지지 않았고 억지스럽게 느껴졌거든. 나라면 피했을 거야.
AI가 Rust의 패턴을 가져오기 시작했다는 게 너무 웃겼어.
lib/option.ts도 있더라고.
함수형 프로그래밍 유틸리티 (
lib/functional.ts): 타입 안전성이 보장된 합성(composition), 커링(currying), 20개 이상의 파라미터에 대한 오버로딩... 없는 게 없어.서킷 브레이커 (
lib/circuitBreaker.ts)
인프라 (Infra)
어떤 반복 회차에서는 코딩 에이전트가 '보안 엔지니어' 모자를 썼나 봐. 예를 들어, "문자 다양성이 낮은, 명백히 가짜인 키를 감지"하기 위해 hasMinimalEntropy라는 함수를 만들었어. 도대체 왜?
확장성을 확실히 하겠답시고 서킷 브레이킹과 지터(jitter)가 포함된 지수 백오프(exponential backoff)를 구현했어. 우리가 통신하는 API라곤 OpenAI랑 Anthropic 뿐인데 말이야. 참 고오맙습니다.
긍정적인 점: 엄격한 타입 체크를 확실히 하고, 과도한 타입 변환(as any as T 같은 거)을 하지 않도록 많은 공을 들였어. 이 점은 인정해.
성공 기준 - 품질 지표
프롬프트는 모든 버전에서 항상 "코드베이스 품질 개선"에 초점을 맞췄어. 하지만 AI 에이전트가 그 지표를 어떻게 인식하는지 보는 건 실망스러웠지. 주된 원칙은 몇 가지 허영 지표(vanity metrics)를 정해놓고 "많을수록 좋다"며 밀어붙이는 거였어.
메시지 로그를 보면 에이전트는 테스트가 얼마나 많이 추가됐는지, 코드 커버리지(으...)가 임의의 몇 퍼센트를 넘었는지를 자랑하곤 해. 결국 우리는 품질이라는 미명 아래 유지보수가 불가능한 **거대한 괴물(Moloch)**을 떠안게 됐지. 하지만 뭐, 숫자는 올라갔잖아?
요약
결론적으로, 이 프로젝트는 유지보수해야 할 코드는 늘어났고, 그중 대부분은 쓸모없어. 수많은 테스트가 추가됐지만, 정작 가장 중요한 테스트(앱이 여전히 작동하는지 검증하는 Maestro E2E 테스트)는 잊혀졌지. 물론 타입 체크의 품질을 높이는 것 같은 "좋은" 순간들도 있었어.
"이미지를 1,000번 다시 그리기"나 "동영상을 1,000번 재업로드하기" 같은 실험을 진정으로 재현하려면, 루프를 두 단계로 만들어야 할 것 같아:
프로젝트를 읽고 요약한다.
이 요약 설명만을 바탕으로 프로젝트를 처음부터 다시 구현한다.
물론 이건 장난삼아 한 실험이었고, 내가 중요하게 생각하는 방식대로 코드 품질이 개선될 거라 기대하진 않았어. 나는 여기서 의도적으로 Claude Code가 실패하도록 유도했고, 확실히 재미있는 결과가 나왔지.
난 여전히 일상적인 개발 업무에 코딩 에이전트를 사용해. 이번 실험을 통해 느낀 점이 있다면, AI가 짠 코드를 리뷰하는 시간이 결코 시간 낭비가 아니라는 거야.
...아, 그리고 앱은 여전히 작동해. 새로운 기능은 없고, 버그만 몇 개 생겼을 뿐이야.
