
AI 에이전트가 코드 리팩터링 (Code Refactoring)에 취약한 이유
요약
AI 에이전트가 새로운 프로젝트 구축에는 능숙하지만, 기존 코드베이스를 수정하는 리팩터링 작업에는 취약한 이유를 분석합니다. 에이전트가 설계-코드의 순방향 패스에는 강하지만, 코드에서 설계를 역추적해야 하는 역방향 작업에는 한계가 있음을 설명합니다.
핵심 포인트
- 에이전트는 요구사항에서 코드로 이어지는 순방향 파이프라인에 최적화되어 있음
- 기존 코드베이스는 설계 이탈, 패턴 모호성, 문서 불일치 등의 문제를 가짐
- 리팩터링은 코드에서 설계와 의도를 역추적해야 하는 고난도 작업임
- 에이전트가 작성한 코드조차 빠르게 기술 부채를 형성할 수 있음
AI 에이전트에게 빈 디렉터리와 한 페이지 분량의 요구사항을 주면 당신을 놀라게 할 것입니다. 하지만 동일한 에이전트에게 기존 코드베이스 (Codebase)와 한 줄짜리 기능 요청을 주면, 요청 사항은 완벽히 충족하더라도 프로젝트를 이전보다 약간 더 나쁜 상태로 만들 가능성이 높습니다.
그리고 여기서 "기존 (existing)"이라는 말이 더 이상 오래되었다는 것을 의미하지는 않습니다. 에이전트 시대에는 설계의 이탈 (drifted design), 모호해진 패턴 (blurred patterns), 쓸모없어진 문서 (obsolete docs)와 같은 상태에 코드베이스가 불과 몇 주, 때로는 며칠 만에 도달할 수 있으며, 종종 에이전트가 최초 작성자인 경우가 많습니다. 이제 고고학적 분석은 여전히 건설 중인 현장에서 이루어져야 합니다.
저는 왜 이런 일이 발생하는지, 그리고 왜 이것이 일시적인 약점이 아닐 것이라고 의심하는지에 대해 살펴보고자 합니다.
두 번의 순방향 패스 (Two forward passes)
에이전트가 처음부터 프로젝트를 구축할 때, 파이프라인 (Pipeline)은 다음과 같습니다:
요구사항 (requirements) → 설계 (design) → 코드 (code)
사용자가 필요한 것을 명시합니다. 에이전트는 요구사항을 소프트웨어 설계로 변환하며, 적합한 패턴을 선택합니다 — 에이전트는 수천 개의 패턴을 보아왔습니다. 그런 다음 설계를 단계별로 코드로 변환합니다.
두 화살표 모두 같은 방향을 가리키며, 둘 다 순방향 패스 (forward passes) 입니다 — 그리고 인류는 이를 철저하게 기록해 왔습니다. 모든 아키텍처 서적, 모든 패턴 카탈로그, 모든 튜토리얼, 모든 오픈 소스 프로젝트는 누군가가 이 방향으로 움직인 기록입니다. 공개된 텍스트로 학습된 모델은 사실상 이 파이프라인이 수백만 번 실행되는 것을 지켜본 셈입니다. 에이전트가 이를 잘하는 것은 놀라운 일이 아닙니다. 오히려 잘하지 못한다면 그것이 놀라운 일일 것입니다.
리팩터링 (Refactoring)은 파이프라인을 역방향으로 실행합니다
이제 반대의 상황을 가정해 봅시다. 에이전트가 기존 프로젝트에 투입됩니다. 에이전트가 가지고 있는 것은 파이프라인의 출력값 (output) 인 코드입니다. 입력값은 정확히 결여되어 있는 것들입니다:
- 설계(design)가 표류했습니다. 코드는 더 이상 이전에 그려졌던 그 어떤 설계와도 일치하지 않습니다.
- 국소적이고 그리 깔끔하지 않은 수정 사항(fixes)과 임시방편(workarounds)들로 인해 패턴이 흐릿해졌습니다.
- 문서와 주석은 더 이상 존재하지 않는 시스템을 설명하고 있습니다.
엔지니어의 과업(인간이든 AI든)은 다음과 같습니다:
- 실제(actual) 현재 요구사항을 복구합니다 (문서가 주장하는 내용으로부터 표류해버린 상태입니다).
- 노이즈가 섞인 코드 내부에서 실제 패턴을 식별합니다.
- 각 이상 징후(anomaly)에 대해, 그것이 왜 존재하는지 파악합니다: 레거시(legacy)인가? 미완성된 계획인가? 과거의 실수인가? 아니면 변경된 요구사항인가?
- 코드 조사 결과에 기반하여 요구사항의 모습을 바로잡습니다.
- 수정된 요구사항에 가장 이상적인 설계가 무엇인지 결정합니다.
- 현재의 코드 상태에서 목표 상태로 가는 경로를 계획합니다.
- 코드를 작성합니다.
이 단계들의 방향을 살펴보십시오. 1~4단계는 역방향으로 진행됩니다: 코드에서 설계로, 설계에서 요구사항으로 말입니다. 5단계와 7단계는 익숙한 순방향 과정(forward passes)입니다. 즉, 쉽고 잘 훈련된 부분입니다. 6단계는 둘 다 아닙니다. 이는 모든 중간 상태에서 계속 작동해야만 하는 라이브 시스템을 관통하는 경로를 그리는 것입니다. 마치 달리는 자동차의 타이어를 교체하는 것과 같습니다.
그리고 역방향에는 순방향에는 없는 문제가 있습니다: 바로 부정치 결정(underdetermined) 상태라는 점입니다. 당신 앞에 놓인 코드를 만들어낼 수 있는 (요구사항, 설계)의 쌍은 매우 다양할 수 있습니다. 순방향 과정은 정보를 손실합니다. 이를 역방향으로 실행한다는 것은 무엇이 손실되었는지 추측해야 함을 의미합니다. 이 추측들을 명확하게 해줄 수 있는 유일한 것은 역사(history)뿐입니다. 그리고 git이 _무엇(what)_이 변했는지는 리포지토리에 완벽하게 보존하지만, _왜(why)_에 대해서는 대부분 보존하지 못합니다. 그것은 리뷰 코멘트, 채팅 스레드, 그리고 이미 떠나간 사람들의 머릿속에 존재했기 때문입니다.
0단계: 냄새 (the smell)
더 나쁜 점은, 리팩터링 (Refactoring)이 필요하다는 것을 사전에 알 수 있는 경우가 거의 없다는 것입니다. 그것은 거의 결코 티켓 (ticket) 형태로 존재하지 않습니다. 새로운 기능을 추가하거나 버그를 수정하기 위해 자리에 앉으면, 당신이 구상하는 모든 구현 방식에서 '냄새'가 납니다. 각각의 방식은 현재의 설계와 충돌하며, 적응시키기 위해 두 가지의 임시방편 (workaround)을 필요로 합니다. 리팩터링을 결정한다는 것은 그 냄새를 인지하는 것, 즉 현재의 설계와 방금 도착한 요구사항 사이의 불일치를 알아차리는 것을 의미합니다.
이것은 하나의 단계가 아니라 백그라운드 프로세스 (background process)입니다. 인간 엔지니어는 자신의 미래에 겪을 고통, 즉 이 엉망진창인 코드를 유지보수하게 될 사람이 바로 자신이라는 동기 부여를 통해 이를 끊임없이 수행합니다. 반면 에이전트 (agent)의 에피소드는 디프 (diff)가 반영되는 순간 종료됩니다. 에이전트의 루프 (loop) 안에는 설계를 의심하기 위해 멈추는 것에 대한 보상이 전혀 없습니다. 오히려 훈련 신호 (training signal)는 반대 방향으로 흐릅니다. "네, 여기 기능이 있습니다"라고 하는 것은 도움이 되는 것으로 해석되지만, "먼저 구조를 재편해야 합니다"라고 하는 것은 회피하는 것으로 해석됩니다. 에이전트는 자신이 지은 집에서 살아야 할 필요가 없습니다.
리팩터링은 압축 작업이다
핵심만 말하자면, 리팩터링은 무엇보다도 '압축 (compression)'입니다. 수만 줄의 코드가 주어졌을 때, 내부 구조를 추출하는 것입니다. 그리고 단순히 현재의 구조뿐만 아니라 역사적인 구조도 추출해야 합니다. 왜냐하면 3단계("이것이 왜 여기에 있는가?")는 코드에 대한 질문이 아니라 시간에 대한 질문이기 때문입니다.
따라서 두 번의 압축이 일어납니다. 첫 번째, 즉 현재 설계가 무엇인지 파악하는 것은 어렵지만, 적어도 입력값이 컨텍스트 윈도우 (context window) 안에 들어옵니다. 두 번째는 원칙적으로 더 어렵습니다. 그 근거 (rationale)가 기록되지 않았거나, 기록되었더라도 문서와 함께 부패해 버렸기 때문입니다. 만약 코드가 에이전트에 의해 작성되었다면, 문제는 사라지는 것이 아니라 반전됩니다. '왜'에 대한 정보가 지칠 정도로 상세하게 기록되어 있지만, 압축되지 않은 채 너무 방대하여 로드할 수 없으며, 그중 무엇이 여전히 유효한지 판단하는 것이 불가능해집니다. 에이전트는 사전 (dictionary)이 버려진 손실 압축 아카이브 (lossy archive)를 압축 해제하라는 요구를 받는 것과 같습니다.
훌륭한 인간 엔지니어가 여기서 제공하는 것은 바로 사전 지식 (prior) 입니다. 그리고 이 사전 지식이 무엇에 대한 것인지 주목해야 합니다. 패턴에 대한 것이 아니라, 바로 퇴화 (decay) 에 대한 것입니다. 수백 개의 코드베이스가 진화하는 과정을 지켜본 엔지니어는 왜곡된 형태를 보고 침식 과정을 재구성할 수 있습니다. '이것은 한때 깔끔한 분리(separation) 상태였으나, 마감 기한이 닥쳤고, 그 후 핫픽스(hotfix)가 발생했구나'라고 말이죠. 그들의 정신적 라이브러리에는 모든 패턴의 퇴화된 버전이 포함되어 있습니다. 왜냐하면 그들은 퇴화가 일어나는 과정을 직접 목격했기 때문입니다.
모델들은 주로 스냅샷(snapshots) — 즉, 최종 상태, 깔끔한 튜토리얼, 큐레이션된 오픈 소스 — 을 통해 학습됩니다. 모델의 라이브러리에는 깨끗한 버전들만 들어 있습니다. 따라서 드리프트(drifted)된 코드에 직면했을 때, 모델은 자신이 학습한 대로 행동합니다. 즉, 엉망이 된 코드를 가장 가까운 깨끗한 형태에 맞추고, 그 코드가 자신이 가장 닮은 교과서적인 버전이라고 자신 있게 설명합니다. 하지만 이 엉망인 코드가 과연 어떤 깨끗한 형태로부터 유래했는가 하는 문제는, 가설로 치부해버릴 것이 아니라 정확히 조사되어야 할 질문입니다. 노이즈를 깨끗한 원형(archetype)에 패턴 매칭(pattern-matching)하는 것은 이야기를 복구하는 것이 아니라, 이야기를 추측하는 것에 불과합니다.
에이전트가 대신 하는 행동
이야기(context)가 결여된 에이전트는 그린필드(greenfield) 스타일을 기본값으로 사용합니다. 즉, 기존 코드를 진리(ground truth)로 받아들이고, 그 안의 모든 것이 의도된 것이라고 가정하며, 그 위에 새로운 것을 구축합니다. 여기에는 새로운 추상화(abstraction)를, 저기에는 새로운 엔티티(entity)를 추가합니다. 그 무엇도 삭제되지 않으며, 각 세션은 하나의 층(stratum)을 추가할 뿐입니다. 그 결과는 지질학적입니다. 층 위에 층이 쌓이며, 각 층은 마치 그 아래의 모든 것이 하중을 견디는 구조물(load-bearing)인 것처럼 작성됩니다.
성숙한 프로젝트에서 에이전트가 작업하는 것을 본 적이 있다면, 이런 모습을 보았을 것입니다. 에이전트는 임시방편(workaround)을 리팩터링하지 않습니다. 오히려 그것을 존중 합니다. 그것을 감싸고(wrap), 일반화(generalize)하며, 그 위에 구축합니다. 결국 임시방편이 아키텍처(architecture)가 되어버립니다.
한 가지 명확히 짚고 넘어갈 점이 있습니다. 많은 혼란을 야기하기 때문인데, 에이전트들은 리팩터링 (refactoring)의 '메커니즘 (mechanics)'에는 상당히 능숙합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
