코드 청결도는 코딩 에이전트에 영향을 미치는가? 통제된 최소쌍 연구
요약
코드 청결도가 코딩 에이전트 성능에 미치는 영향을 다룬 연구에 대해 방법론적 한계를 지적하며 비판적 분석을 제공합니다. 실험 설계의 통제 미흡을 지적하는 동시에, 실무적 관점에서 깨끗한 코드베이스와 린터 활용이 에이전트 효율성에 미치는 중요성을 강조합니다.
핵심 포인트
- 연구의 실험 설계가 애플리케이션 테스트 파손 여부를 통제하지 못해 신뢰도가 낮음
- 지저분한 코드베이스는 에이전트의 토큰 소비량을 늘리고 작업 성공률을 낮춤
- 린터(Linter)와 pre-commit 훅을 통한 코드 정리가 에이전트 성능 향상에 효과적임
- 에이전트에게 명확한 리팩터링 지침을 제공하는 것이 실무적인 팁임
연구할 만한 흥미로운 질문이지만, 실험 설계에는 매우 회의적임
실험에서 Opus 4.6으로 “품질 저하” 또는 “정리된” 코드베이스를 합성해 상대 비교에 사용했음
더 나쁘게는 애플리케이션의 테스트를 망가뜨렸는지 통제하지 않았다는 점임
“통과율은 각 작업에 대해 우리가 작성한 숨겨진 테스트 기준으로 에이전트의 최종 상태를 채점한다. 저장소에 이미 있던 관련 없는 테스트를 에이전트가 망가뜨렸는지는 확인하지 않으며, 깨끗한 쪽과 지저분한 쪽의 해법이 둘 다 숨겨진 테스트를 통과하더라도 채점되지 않은 테스트에서는 차이가 날 수 있다”
최종 산출물의 품질을 통제하지 않는다면 토큰 소비량에 관한 결론은 거의 의미가 없어 보임
지저분한 프로젝트와 잘 구조화된 프로젝트를 실패 테스트 수로 비교하면, 애초에 테스트 커버리지와 견고성이 낮을 가능성이 큰 지저분한 코드베이스 쪽으로 성공률이 치우칠 수 있음
공정하게 비교하려면 각 쌍의 두 프로젝트 모두에서 동작하는 단일 테스트 묶음을 작성해야 할 것임
연구가 좋다는 뜻은 아니지만, 테스트 통과가 에이전트의 효과성과 꼭 상관있지는 않으므로 그런 결정을 이해할 수는 있음
결론이 성립하더라도, 기껏해야 품질 저하 코드에 대한 최선의 경우일 뿐임
기능적으로는 괜찮아 보이지만 작업을 끝내는 데 더 많은 토큰 비용이 드는 상황을 보여주는 정도임
애플리케이션의 테스트 파손을 통제하지 않는다면 사실상 가치 없는 연구임
AI 피로감은 오래전에 지루해졌고, 이제는 그냥 고통스러울 정도임
내 경험상 코드베이스에 죽은 코드, 중복 코드, 도달 불가능한 대체 경로, 새는 추상화, 덜 익은 설계 패턴이 널려 있을 때와, 데이터 흐름이 명확하고 캡슐화와 구조가 깨끗할 때의 에이전트 성능 차이는 상당히 큼
나쁜 코드에서는 모든 최전선 모델이 여러 차례 코드 리뷰와 품질 확인, 수정 라운드를 거쳐야 했고, 좋은 코드에서는 1~2번째 시도에 바로 맞는 경우를 봤음
위에서 언급한 죽은 코드 제거, 코드 중복, 도달 불가능 코드 같은 문제는 대부분의 언어 생태계에서 이미 오래전부터 결정적 린터로 해결돼 왔음
LLM에게 이런 항목을 확인하는 스크립트를 실행하게 하고, 같은 스크립트를 pre-commit 훅으로 강제할 수도 있음
내가 작업하는 모든 코드베이스에 이런 설정을 철저히 넣은 것이 에이전트식 코딩에서 가장 큰 효과를 줬음
내가 쓰는 여러 린터를 더 자세히 정리한 글은 여기 있음: https://www.balajeerc.info/Use-Deterministic-Guardrails-for-...
이런 도구들을 꽤 오래 다뤄왔는데, 매번 사람을 대하듯 다루면 성능이 더 좋아지는 것처럼 보였음
에이전트도 거대한 엉망진창 코드베이스보다 깨끗한 코드베이스에서 더 잘할 수밖에 없다고 봄
잘 만들어진 명세와 문서 접근권이 있을 때 더 잘하는 것과 같음
지저분한 코드베이스에서 작업할 때 나도 비슷하게 느낌
어느 순간 끔찍한 패턴들이 나에게도 묻기 시작함
동의하면서 읽었지만 왜 나쁜 댓글처럼 느껴지는지 생각해 보니, 이런 일화적 진술은 과학적 논의의 반대편에 있음
여기에는 질문에 답하려는 논문이 있고, 일화적 증언은 독자에게 편향을 줄 뿐 문제에 대해 객관적으로 결론 내릴 가치를 더하지 못함
모두가 논문을 읽고 방법론이나 결과를 비판하는 쪽이 가장 유용한 논의일 것임
이것도 느낌에 가깝긴 한데, 나는 너무 걱정이 많아서 리팩터링과 코드 정리 패스를 자주 하고 절대 빼먹지 않기 때문에 실제로 성능 차이가 있는지는 확실히 말하기 어려움
다만 LLM이 그다지 좋지 않다고 불평하는 사람들은 대체로 지저분한 코드베이스를 가진 유형처럼 보임
내가 잘 먹힌다고 본 요령은 Python이라면 다음처럼 리팩터링을 지시하는 것임
“Python 코드를 더 Python답게 리팩터링하라. 예를 들어 클래스와 싱글턴을 줄이고, 특히 속도 향상이 있다면 그렇게 하라. Python 코드는 벤치마크 성능 회귀 없이 인기 있는 오픈소스 Python 패키지 코드에서 기대되는 코드 구성 기준을 반드시 따라야 한다”
Rust 코드에는 이런 변형을 써봤음
“/src의 Rust 코드베이스가 여러 개의 1천 줄 초과 파일로 비대해졌다. 벤치마크 성능 회귀 없이 인기 있는 오픈소스 Rust 코드에서 기대되는 코드 구성 기준에 맞게 Rust 코드베이스를 리팩터링하라”
이런 프롬프트는 a) 코드를 논리적으로 재구성하고 b) 파일명이 관련 코드 위치에 대한 의미적 힌트를 주기 때문에 에이전트 성능도 좋아지는 듯함
5천 줄짜리 비대한 파일에서는 에이전트가 관련 코드를 찾으려고 여러 덩어리를 읽어야 해서 비효율적임
벤치마크 성능도 보통 리팩터링 뒤에 좋아지는데, 특히 컴파일되는 Rust에서는 우연일 것 같지만 불평할 일은 아님
맞음. 에이전트식 코딩 도구에 코드베이스 정리, 표적 리팩터링, SOLID 원칙과 좋은 관행 적용을 요청하는 것만으로도 쉬운 개선이 많이 나옴
기본적으로 에이전트식 코딩 도구는 코드를 지우는 데 꺼리는 경향이 있음. 지우라고 해도 그렇고, 예전 코드를 남겨두거나 그 코드가 계속 호출될 수 있게 복잡도를 추가하려고 온갖 노력을 함
단순히 프로토타이핑 중이라면 정말 성가신데, 결국 죽은 코드가 많이 쌓이고 나중에 기능을 더하려 할 때 혼란을 일으킴
이걸 알고 나면 그냥 레거시를 없애라고 요청하면 됨
코드베이스를 깨끗하게 유지하면 AI가 올바른 일을 하도록 자극함. 테스트가 많으면 새 기능을 만들 때 더 추가하고, 문서가 있으면 따로 말하지 않아도 업데이트함
코드 하네스가 개선되면서 이런 것들이 점점 내장될 테고, 프롬프트 경험이 적은 사람도 괜찮은 결과를 얻기 쉬워질 것임
YAGNI 원칙을 적용하라고 요청하는 것도 코드베이스를 줄이는 데 잘 먹히는 듯함
보통 먼저 검토해서 검토 항목 목록을 만들게 한 뒤, 각 항목을 함께 보면서 내가 예/아니오로 결정하거나 추가 수정을 제안함
점진적으로 하는 건 충분히 이해되지만, 운영 중인 어떤 코드베이스든 전체를 한 번에 이렇게 하는 건 극도로 위험해 보임
특히 전체 시스템에 대한 신중한 종단 간 테스트가 없다면 더 그렇다
그냥 “코드베이스를 리팩터링해”라고 해도 꽤 잘 작동함
어차피 코드 스타일 규칙은 이미 CLAUDE.md에 넣어두었음
찾고 있는 단어는 관용적임
“지저분한 저장소를 정리하는 에이전트 파이프라인”이라는 접근은 끔찍해 보이고, 그 자체로 연구 전체를 부정하기에 충분함
이 작업에서 최소 쌍의 절반이 그런 방식으로 만들어진 듯함
AI가 “정리한” 저장소가 실제로 좋은 코드베이스를 대표한다고 가정해야 하는 결론은 전혀 신뢰하지 않겠음
당연히 영향을 줄 수밖에 없다고 봄
어떤 모델도 실제 코드베이스 전체를 문맥에 담을 수 없고, 사람처럼 코드를 훑어야 함
검색하고 파일을 읽는 방식임
파일이 예상되는 위치에 있고, 모델이나 사람이 처음 검색할 법한 이름으로 불린다면 첫 시도에 찾지만, 그렇지 않으면 깊은 검색과 여러 번의 시도가 필요해짐
LLM이 전체 코드베이스를 문맥에 들고 있을 필요는 없음
모든 경로를 보고, 이미 다룬 구역은 무시하면서 다음 경로로 넘어가면 됨
개발자가 하는 방식과 꽤 비슷함
NJIT에서 진행 중인 비슷한 작업에서도 유사한 결과를 봤음. 우리는 이를 맥락적 품질 전염이라고 부름
여기서 흥미로운 부분은 업계에서 흔한 실제 상황들임. 품질이 섞인 코드베이스, 레거시 코드 패턴과 더 새로운 “좋은” 패턴이 섞여 에이전트가 관례를 혼동하는 코드베이스 같은 것들임
최소 쌍 설계는 사실 강점 중 하나임. 저장소끼리 비교하는 것보다 구조, 의존성, 테스트 같은 다른 요인에서 깔끔함을 분리하려 하기 때문임
다만 LLM이 생성한 “엉망화된” 코드를 쓰는 것은, 기계적이거나 사람이 안내한 방식이 아니라는 점에서 다소 의문임
가장 큰 비판은 다른 사람들이 올바르게 짚었듯 전체 테스트 묶음을 확인하지 않았다는 선택임. “행동 동등성” 주장은 테스트와 커버리지만큼만 유효함
이 가설이 설득력 있는 이유는 두 가지임. 1) LLM은 코드베이스에서 보는 것을 모방하므로 쓰레기가 들어가면 쓰레기가 나온다는 점에서 말이 됨 2) 지난 1~2년간 많은 엔지니어가 이 모델들을 쓰며 직관적으로 느낀 것과 맞아떨어짐
그린필드는 거의 항상 바쁜 코드베이스에 합류하는 것보다 쉽고, 엉망은 복잡한 통합과 레거시 목적의 시스템 유지 등에서 생김
에이전트가 자기가 남긴 스텁과 WET 코드를 헤쳐 나가는 법을 배운다 해도, 사람이 실제로 무슨 일이 벌어지는지 따라갈 수 없는 코드베이스를 정말 원하나?
에이전트가 모든 것을 처리하더라도, 영어는 코드가 하는 일을 부정확하게 설명함
그래서 개인적으로는 최소한 코드가 무엇을 하는지 코드로 이야기하고 싶음
오히려 DRY를 너무 밀어붙이는 경우를 봤음
작은 함수 두 개에 공유 헬퍼로 뺄 수 있는 로직이 있더라도, 인간 프로그래머라면 그 추상화가 지저분하고 둘 중 하나를 조금만 바꾸려 해도 깨질 걸 알기 때문에 그렇게 하지 않을 때가 있음
이걸 수치화해서 보는 건 흥미로움 깨끗한 구조는 사람과 에이전트 모두의 인지 부하를 낮추는 듯하고, 그래서 이름 짓기와 모듈화가 생각보다 더 중요하다는 설명이 됨
코드 품질은 결국 코드를 얼마나 쉽게 올바르게 변경할 수 있는지로 정의해야 한다고 봄
정량화하기 어렵지만, 모든 코드 품질 지표가 결국 포착하려는 것도 그것임
그 기준에서 보면 사용하는 코드 품질 지표가 합리적이기만 하다면 꽤 놀랍지 않은 결론임
품질 지표가 코딩 에이전트 맥락에서 좋다면, 기대할 결과가 바로 이것임
많은 토큰은 코드 탐색에 쓰임. 코드를 찾거나 호출 지점을 따라가며, 작업을 수행할 만큼 충분한 문맥을 만드는 과정임
에이전트에게 어떤 형태의 LSP 접근권을 주고, 모노레포라면 AGENTS.md 같은 파일로 계층적 안내를 제공하면 탐색에 드는 토큰 사용량을 크게 줄일 수 있음
하지만 흩어진 코드베이스는 각 작업을 풀기 위해 결국 어떤 형태로든 탐색이 필요함
그리고 이 탐색은 단순한 토큰 사용만이 아님. 각 단계마다 LLM의 대기 시간, 프리필, 디코딩, 출력에서 에이전트의 파싱, 도구 호출, 도구 응답을 거쳐 다시 LLM으로 돌아가는 왕복 지연이 반복됨
일부는 병렬로 가능하지만 실제로는 대부분 순차적이라 작업이 상당히 느려짐
에이전트를 효율적으로 쓰려면 지역성과 구조가 핵심임. 문맥 창은 항상 제한돼 있고, 그 안에서의 주의도 일관적이지 않음
내 경험상 엔지니어에게 영향을 주는 모든 것은 에이전트에도 영향을 줌
좋은 추상화, 적당한 크기의 메서드, 좋은 이름, 원칙 있는 서비스 내부·서비스 간 구조, 단위 테스트 등이 모두 해당함
이런 것들은 역사적으로 엔지니어의 일이었고, 다른 사람이 코드에 기여하기 쉽게 만들기 위한 것이었음
이제는 다른 사람뿐 아니라 다른 에이전트도 코드에 기여하기 쉽게 만들어 줌
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기