레거시 코드베이스에서의 에이전틱(agentic) 엔지니어링이란, 숨겨진 제약을 수면 위로 드러내고 단순한 변경도 신뢰할 수 있게 만드는 과정이다. 브라운필드 코드베이스에서 무엇을 해야 하는지 살펴보자.
오랜 경력을 쌓는 동안 나는 꽤 오래된 코드베이스를 가진 팀에서 일한 적이 많다. 이런 시스템이 바로 브라운필드(brownfield) 시스템이다. 저장소 자체는 더 이상 시스템의 실제 동작을 완전히 설명하지 못한다. 조직 내에 암묵적으로 전해지는 지식, 임시방편으로 붙여 놓은 코드, 레거시 서비스, 그리고 다른 팀들이 의존하는 기대치들이 코드 밖에 존재한다. 새 코드를 작성하기 전에 먼저 그 제약들을 파악해야 하고, 변경 후에는 아무것도 깨지지 않았음을 증명해야 한다. 에이전트와 함께 코딩하는 것을 좋아하지만, 오래된 브라운필드 코드베이스에 에이전트를 감독 없이 투입하면 "작동은 하지만" 잘못된 시스템 설계와 불안정한 테스트를 가진 결과물이 나올 수 있다.
AI 등장 이전에도, 모더나이제이션(modernization)을 추진하려던 팀들은 강력한 테스트 체계와 무언가를 깨뜨리지 않는다는 확신을 갖추고 아주 조금씩, 아주 신중하게 작업을 진행해야 했다. 실제 사용자 여정 테스트 외에도, 진행 중인 마이그레이션이 반복 가능한 테스트를 통해 의도대로 동작하는지 계속 검증해야 한다는 것을 모두가 알고 있었다. 요즘에는 에이전트가 코드를 작성하는 순간부터, 즉 결정 하나하나를 직접 내리지 않은 시점부터 이미 브라운필드 프로젝트에 들어선 것이라고 말하는 사람들도 있다. 어떻게 보든, 목표는 단순한 변경을 안전하게 처리하는 데 드는 비용을 최소화하는 것이다.
특히 지난 5년에서 10년 사이, 테스트와 검증에 더 많은 관심을 기울이고 변경 시 무언가를 깨뜨리지 않는 방법을 더 신중하게 고민하는 흐름이 점점 주목을 받아 왔다. 하지만 이런 흐름이 있다고 해서 상황이 달라지는 것은 아니다. 에이전틱 엔지니어링과 소프트웨어 팩토리(software factory), 그리고 대규모 코드베이스를 자율적으로 처리하는 다양한 패턴들을 도입하려 한다면, 충분한 주의를 기울이지 않으면 기술 부채의 늪에 빠질 위험이 있다.
본론에 들어가기 전에, 코드가 진실의 원천이어야 한다는 전제를 세우자. 브라운필드 상황을 돕기 위해 그 위에 추가하는 것들은 코드에서 쉽게 유추할 수 없는 내용이다. 구역(zone), 폭발 반경(blast radius), 그리고 도움이 될 몇 가지 패턴을 중심으로 이야기해 보겠다.
오래된 코드베이스에 들어갈 때 가장 먼저 파악해야 할 것은 손대지 말아야 할 코드가 어디인지다. 이를 구역으로 구분할 수 있다. 예를 들어 그린 구역은 안전하고 테스트가 잘 되어 있으며 독립적인 영역, 옐로 구역은 품질이 혼재된 영역, 레드 구역은 인증·결제·권한 등 민감한 영역이다.

코드베이스에서 매우 민감하거나 모두가 잘 이해하지 못하는 부분이 어디인지 파악하고, 서로 다른 구역으로 표시해두는 것이 좋다. 테스트 커버리지가 충분하고 최신 관례를 따르며 독립성이 잘 유지되는 그린 구역에서는 에이전트가 긴밀한 루프 안에서 자유롭게 작업할 수 있다.
내가 작업해 온 커머스 사이트들을 보면, 다섯 개에서 여섯 개 부서가 각자의 마이크로사이트를 운영하면서도 사용자에게는 하나의 통합된 경험으로 보여야 하는 경우가 많다. 표면 아래에는 상당한 복잡성이 숨겨져 있다. 최근 몇 년 안에 구축된 팀은 테스트 커버리지가 탄탄할 수 있지만, 그렇지 않은 팀도 있다. 바로 이것이 그린 구역이다.
품질이 혼재된 옐로 구역도 있다. 이 구역에서는 특성화 테스트(characterization test)를 먼저 작성한 뒤에 에이전트가 코드를 변경할 수 있다.
그리고 레드 구역이 있다. 인증, 결제, 권한, 급여 처리처럼 평소라면 함부로 손대지 않을 민감한 영역이다. 예를 들어 전체 구조를 이해하는 사람이 소수에 불과하다면, 감독 없는 자동 재작성은 절대 해서는 안 된다.
구역을 단순한 비유가 아니라 실제 운영 절차로 만들려면 세 가지 원칙이 필요하다. 첫째, 지도를 그리는 것은 사람이지 에이전트가 아니다. 스스로 고르게 두면 에이전트는 가장 위험한 파일부터 시작한다. 흥미로운 이름이 많이 몰려 있기 때문이다. 둘째, 구역 승격은 자격을 갖춰야만 이루어진다. 특성화 테스트가 갖춰지고 해당 모듈 담당자가 에이전트의 첫 변경 사항을 검토한 뒤에야 옐로가 그린이 된다. 셋째, 구역이 허용하는 행동을 결정한다. 그린에서는 긴밀한 루프, 옐로에서는 테스트 먼저, 레드에서는 모든 단계에 사람이 함께하거나 아예 작업하지 않는다.
자율성의 범위는 폭발 반경, 관찰 가능성, 복구 가능성을 기준으로 정해야 한다. 모델의 자신감은 믿을 기준이 못 된다.
세계의 지도를 어떻게 그릴 것인지, 그리고 에이전트가 코드베이스에서 스스로 유추할 수 있는 것이 무엇인지 파악하는 것은 중요하다. 에이전트는 코드 자체에서 꽤 많은 것을 유추할 수 있다. 한때는 모든 것에 대해 마크다운 파일을 만들어 컨텍스트 윈도우에 잔뜩 채워 넣던 시기도 있었다. 하지만 에이전트는 이미 시스템의 구조를 꽤 잘 파악한다. 에이전트에게 제공해야 할 것은 코드 자체에서 드러나지 않는 정보다. 코드에 담기지 않은 관례, 패턴, 미묘한 맥락이 있는가? 바로 그런 것들이 중요하다.
구체적으로는 다음과 같은 내용들이다. 비즈니스나 팀에 특화된 맥락, 시스템이 특정 방식으로 설계된 이유를 설명하는 트레이드오프, 정적 분석이나 도구로는 강제할 수 없는 가이드라인, 도메인별 규칙, 외부 제약, 그리고 직관에 반하는 구현 방식의 역사적 배경 등이 여기에 해당한다.
코드가 말할 수 없는 것만 기록하라. 그 이상은 쓰지 않아도 된다.

에이전트의 탐색이 지속 가능한 산출물을 남기지 않으면, 다음 에이전트는 같은 발굴 작업에 또 비용을 치른다.
지도에 하나 더 추가하고 싶은 것은 지속 가능한 리서치 산출물이다. 옐로와 레드 구역의 작업에서는 별도의 읽기 전용 탐색 단계를 거쳐 짧은 이해 메모를 작성하는 것을 선호한다. 진입점, 담당자, 호출자, 기존 추상화, 테스트, 운영 신호, 관련 히스토리, 미해결 질문 등이 담겨야 하며, 각 주장은 파일, 이슈, 담당자 정보, 또는 대시보드를 출처로 명시해야 한다.
기본적인 루프는 탐색 결과를 낭비하는 방식으로 흘러간다. 에이전트가 인증 흐름의 동작 방식을 파악하고 작업을 완료하더라도, 세션이 끝나면 그 이해는 사라진다. 채팅 히스토리는 특히 압축된 이후에는 신뢰할 만한 기록 체계가 되지 못한다.
리서치가 끝난 뒤에는 깨끗한 컨텍스트에서 계획을 시작하는 것이 좋다. 유력한 접근 방식들이 어떤 파일에 영향을 미치는지, 어떤 불변 조건을 유지하는지, 어떻게 되돌릴 수 있는지를 물어보라. 경로 선택은 사람이 한다. 구현 도중 지도가 틀렸다는 사실이 드러나면 작업을 멈춰야 한다. 검토는 새로운 눈으로 시작해 인수 기준을 역방향으로 검증해 나간다. 컨텍스트 없이 검토하는 사람일수록, 테스트가 요구사항이 아닌 구현을 증명하고 있을 때 이를 알아채기 쉽다.

반복되는 교정은 하네스에서 빠진 조각이다.
각 요소가 어디에 맞는지 명확히 하는 것이 도움이 된다. 지시(instruction)는 저장소에 관한 특수한 사실을 기록한다. 스킬(skill)은 폭발 반경 확인이나 스키마 변경 검증 같은 재사용 가능한 절차를 패키지화한다. 플러그인(plugin)은 담당자 카탈로그, 인시던트 아카이브, 대시보드에 대한 통제된 접근을 제공한다.
하네스는 에이전트를 둘러싼 작업 환경이다. 컨텍스트, 도구, 권한, 테스트, 로그, 그리고 복구 체계가 여기에 포함된다. 소프트웨어 팩토리는 이 신뢰할 수 있는 루프를 여러 개 스케줄링하고 지속적인 상태를 유지하며, 예외적인 사례는 사람에게 넘긴다.
실질적인 기준은 에이전트가 실수했을 때 무슨 일이 일어나는가다. 조용히 diff를 고쳐버리면 다음 세션에서 같은 실수가 반복될 수 있다. 동일한 리뷰 코멘트가 또 등장한다면, 그것을 린트 규칙, 훅, 타입, 테스트, 또는 스킬로 옮겨라. 기계적으로 강제할 수 없는 제약만 글로 남겨라.
거부 규칙, 범위가 제한된 자격 증명, CI 검사는 기억할 필요가 없다. 시간이 지나면 하네스는 팀이 두 번 다시 치르지 않기로 결정한 실패의 기록이 된다.

무언가를 개선하기 전에, 먼저 현재 동작을 고정하라.
기존 코드베이스에 에이전트를 도입하는 것은 다른 종류의 모더나이제이션 작업과 매우 비슷하다. 먼저 위험이 없는 작업부터 시작하는 것이 좋다. "모놀리스를 Rust로 재작성해 보자" 같은 방식은 곤란하다. "먼저 이게 어떻게 동작하는지 설명해 봐" 같은 식으로 시작하는 것이 맞다.
현재의 동작을 고정할 특성화 테스트를 생성하라.
특성화 테스트란, 레거시 코드를 안전하게 리팩터링하거나 변경할 수 있도록 시스템의 실제 현재 동작을 문서화하는 자동화된 테스트다.
여기서 특성화 테스트란, 보기 흉한 부분을 포함해 모듈이 오늘 실제로 어떻게 동작하는지를 고정하는 테스트를 말한다. 오래된 시스템에서는 그 보기 흉한 동작 중 일부가 실제로 비즈니스가 돌아가는 방식이기 때문이다. 에이전트는 그것을 기꺼이 "개선"하고는 테스트를 모두 초록색으로 만들어버린다. 구조가 낡은 것은 문제 자체가 낡았기 때문이다. Netflix는 GraphQL 전환 당시 이 아이디어를 프로덕션 규모로 적용했다. 기존 경로와 신규 경로에 실제 트래픽을 재생하고 그림자 트래픽으로 비교해 페이로드가 일치할 때만 전환을 승격했다. 정직한 유닛 테스트 스위트가 없는 홈페이지급 서비스라면 이것이 바로 승격 경로다. 추측하지 말고, 둘 다 실행해서 비교하라.
에이전트가 테스트를 통과시키는 주체일 때는, 그 세션이 테스트의 유일한 작성자가 되도록 해서는 안 된다. 별도의 단계에서 또는 사람이 먼저 동작을 고정하고, 그다음 에이전트가 작업하게 하라. 그렇지 않으면 방금 직접 만든 구현을 그대로 반영한 초록색 테스트 스위트만 남게 된다.
그다음에는 기계적인 변환으로 나아간다. 사용하지 않는 코드와 불필요한 내보내기를 찾아 목록으로 만들 수 있다. 시스템에서 가장 까다롭고 복잡한 부분부터 시작하는 것은 피해야 한다. 이 모든 마이그레이션 과정에서 결국 원하는 것은 신뢰할 수 있는 확신이다.

대규모 코드베이스에서 다양한 마이그레이션을 수행하던 시절, 분명히 잘못된 것을 고칠 때조차 팀원들이 극도로 신중하게 작업하던 기억이 있다.
내가 일했던 오래된 코드베이스 중 하나는 AOL이었다. 쉬는 날, 사무실 근처 만화책 가게에 들렀는데 상사에게 문자가 왔다. 혹시 사무실에 잠깐 들를 수 있겠냐는 것이었다. AOL.com 홈페이지가 완전히 망가졌는데 주변에 자바스크립트 전문가가 없다는 이유였다. 알겠다고 하고 사무실에 가서 살펴보기 시작했다. 요즘 기준으로 홈페이지가 얼마나 복잡하겠냐 싶겠지만, 수십 개 부서가 각자의 컴포넌트, 기준, 스크립트, A/B 테스트를 운영하고 있으면 이야기가 달라진다. 모두에게 장애를 일으키지 않으면서 변경하려면, 단위 테스트가 없는 곳까지 전부 테스트하기 어려운 상황에서 최선을 다해야 했다. 그때 결국 해결하긴 했지만, 단위 테스트가 없는 부분은 적어도 직접 사용자 테스트를 해야 했다. 모두에게 잘 작동하는지, 아무것도 깨지지 않는지 확인하는 것이 핵심이었다.
지금도 해야 할 일은 그때와 같다. 에이전트가 등장했다고 해서 수십 개 부서 문제가 사라지는 것은 아니다. 다만 그 문제에 맞서 변경을 시도하는 비용이 낮아질 뿐이다. 프로덕션 트래픽만이 실제로 이해하는 서비스는 정의상 레드 구역이다. 그 트래픽을 대체할 무언가를 만들기 전까지는, 쉬는 날 내가 했던 사용자 테스트가 여전히 통과 기준이 된다.

마이그레이션은 새 경로가 작동하고 기존 의존성이 완전히 제거됐을 때 비로소 완료된 것이다.
절반만 완료된 마이그레이션은 에이전트에게 특히 혼란스럽다. 검색하면 기존 방식이 40개 파일에, 대체 방식이 12개 파일에 나오고, 둘 다 현재 방식인 것처럼 보이는 심(shim)도 하나 있다. 에이전트는 서로 모순되는 선례를 만난다.
30개 파일을 변환하고 두 패턴을 모두 남겨두는 것보다, 기존 경로를 제거하는 것까지 포함해 하나의 라우트를 끝까지 완료하는 편이 낫다. 삭제가 나중에 처리할 티켓으로 남아 있다면, 그 마이그레이션 단위는 아직 완료된 것이 아니다.
테스트는 초록색을 유지하면서도 대체 구현이 여전히 레거시 구현을 호출하는 경우가 있다. SWE Refactor Bench는 이를 마이그레이션 "맹점(Blindness)"이라 부른다. 520회의 에이전트 실행 중, 마이그레이션 감사·동작 테스트·독립 검증을 모두 통과한 경우는 28회에 불과했다.
코드모드(codemod)로 반복적인 변경을 처리할 수 있다면, 에이전트를 활용해 코드모드를 작성하고 검증하는 데 도움을 받아라. 예외 케이스 처리는 에이전트에게 맡겨라. Stripe의 마이그레이션은 바로 이 점에서 유익한 참고 사례가 된다. 에이전트가 개입하지 않고, 마이그레이션 기계 자체가 지속적인 산출물이었기 때문이다.

Bun의 Zig-to-Rust 포팅은 535,000줄 코드베이스를 기반으로 약 11일 동안 50개의 워크플로를 실행했으며, 생성된 각 단위마다 두 명의 검토자가 적대적 관점에서 검토하고 기존 테스트 스위트 전체를 병합 기준으로 삼았다. 이 사례에서 가장 주목할 점은 에이전트를 실행하기 전에 Zig 관용구를 Rust에 매핑하는 포팅 가이드를 만드는 데 수 시간을 투자했다는 것이다. Anthropic의 마이그레이션 프로세스는 규칙집을 일회용 미니 마이그레이션으로 먼저 검증하고, 본격적인 실행 전에 시험 결과를 버리는 방식으로 진행한다.
VB6에서 C#으로의 통제된 마이그레이션 연구는 단순한 기능에서 92%, 복잡한 기능에서 47%의 동작 동등성을 측정했다. 단위 크기가 핵심 변수다. 이 구조는 에이전트 등장 이전부터 있었다. Stripe는 에이전트 없이 수개월에 걸친 코드모드 작업만으로 370만 줄을 TypeScript로 한 번의 PR에 전환했으며, Google의 대규모 변경 챕터는 코드베이스가 커질수록 원자적 변경이 왜 작아져야 하는지를 설명한다. Spotify는 현재 Backstage가 몇 년 전에 구축한 체계 위에서 월 650개 이상의 에이전트 PR을 병합하고 있다.
Asana는 수년에 걸친 Enzyme 백로그를 약 12,000달러의 모델 및 인프라 비용으로 2주 만에 해소했다. 이 12,000달러는 생성 비용일 뿐, 기존에 수립된 5년치 인력 투입 계획의 대체재로 볼 수는 없다. 벤더가 보고한 생성 비용으로 받아들여야 하며, 절감 효과를 입증하는 통제된 연구가 아니다. 이 사례에서 다른 조직이 가져갈 수 있는 교훈은 Bun과 같다. 범위가 좁은 기계적 마이그레이션, 기존 테스트 스위트, 그리고 모든 변경 사항을 검토하는 사람이 있다는 점이다.
조직 간에 이전되는 것은 에이전트 주변의 구조다.

에이전트는 여러 가능한 구현을 시도하는 비용을 낮췄다. 하나를 선택하는 데 필요한 근거는 바뀌지 않았다.
올해 들어 잘 자리 잡은 기업들이 에이전트를 활용해 대규모 재작성을 진행하는 사례를 점점 더 많이 접하게 된다. 이제 여러 언어나 프레임워크로 재작성을 시도하는 것이 충분히 저렴해졌기 때문에, 에이전트로 복수의 재작성을 시도하고 트레이드오프를 평가하게 하는 CTO들도 생겨나고 있다.
Shopify는 소규모 팀과 에이전트 기반의 화면 단위 체크포인트 방식으로 12주 만에 Shop 소비자 앱을 React Native에서 네이티브 Swift와 Kotlin으로 재구축했다. 훨씬 규모가 큰 머천트 앱은 여전히 브라운필드 문제다. 수백 개의 화면, 깊은 플랫폼 통합, 동일한 기준, 그리고 더 긴 시간이 필요하다.
Rust로의 재작성, 프레임워크 수준의 마이그레이션 등 다양한 사례들이 이미 등장했다. 대부분 이전이라면 훨씬 더 오랜 시간이 걸렸을 마이그레이션들이다. 오늘날에는 충분한 토큰만 있다면, 에이전트가 다양한 스택과 언어에 걸쳐 실제로 마이그레이션을 완수하도록 시도할 수 있다.
여러 경쟁하는 선택지를 에이전트가 실제로 구현하게 할 수 있다. 한 팀이 하나의 선택지를 골라 올인하는 대신, 모든 선택지를 구현하게 하는 것이다. 전부 유닛 테스트를 통과하는지 확인하고 성능을 프로파일링한 뒤 결정을 내릴 수 있다. 이는 이전에는 불가능했을 방식으로 훨씬 저렴하게 가능하다. 요즘 팀들에게는 완전히 새로운 국면이 열린 것이다.
생성되는 코드가 많아질수록 사람의 검토는 더 선별적으로, 더 주도적으로 이루어져야 한다. 루프·목표·병렬화를 생각하기 전에, 브라운필드 프로젝트의 성공을 위해 무엇을 갖춰야 하는지를 진지하게 고민하라.
소프트웨어 팩토리는 많은 변경을 동시에 실행할 수 있다. 하지만 하나의 단위에 신뢰할 수 있는 판단 기준, 복구 경로, 사람이 소화할 수 있는 검토 형식이 갖춰진 뒤에야 그 방식을 따라야 한다.
병렬화는 이미 존재하는 병목을 증폭시킨다. 자동화 검증이 다섯 개의 검증된 변경을 처리할 수 있는 상황에서, 시니어 엔지니어 한 명이 모든 줄을 읽으면 대기열이 쌓이고 주의가 분산되다가 결국 형식적인 승인만 남게 된다.
자동화 검토는 의도, 변경된 불변 조건, 테스트 결과, 동등성 불일치, 그리고 롤백 경로를 먼저 제시하는 방식을 선호한다. 전체 diff는 여전히 열람 가능하다. 사람의 주의는 폭발 반경이 가장 크고 검증 기준이 가장 약한 곳에 먼저 쏠려야 한다.
워크트리(worktree)는 변경 사항을 격리하지만 동작을 격리하지는 않는다. Git 메타데이터, 자격 증명, 로컬 서비스, 네트워크 접근을 공유할 수 있다. 신뢰할 수 있는 작업이라면 그 트레이드오프를 감수할 수 있다. 그러나 신뢰할 수 없는 콘텐츠를 처리하는 무인 에이전트에게는 더 강력한 샌드박스와 범위가 제한된 자격 증명이 필요하다.

생성된 코드의 줄 수는 코드베이스가 개선되었는지를 알려주지 않는다. 리드 타임, 검토 소요 시간, 사람의 개입 횟수, 빠져나간 결함, 롤백, 동등성 불일치, 남겨진 억제 처리 등을 추적해야 한다.
마이그레이션의 경우에는 남은 기존 임포트, 새 경로가 처리하는 트래픽, 동등성 불일치, 제거된 레거시 의존성을 추적하라. 전체 트래픽이 여전히 기존 경로를 따르는데 테스트만 초록색인 것은 허드레 작업일 뿐이다.
에이전트는 모호함에 눈에 보이는 비용을 매긴다. 암묵적인 관례는 반복되는 리뷰 코멘트로 드러난다.
그 비용은 항상 존재했다. 온보딩, 코드 리뷰, 인시던트 복구 과정에서 계속 치러왔던 것이다. 에이전트는 그 비용을 더 많이 수치화할 수 있게 만든다. 덕분에 팀이 이미 가치 있다고 알고 있었던 유지보수 작업의 필요성을 더 강하게 주장할 수 있게 된다.
다음번에 에이전트가 홈페이지에 해당하는 서비스를 작업할 때, 수리 결과만 남기는 것으로는 부족하다. 합성 사용자 여정, 담당자 정보, 그리고 회귀 테스트를 함께 남겨야 한다.
다음 엔지니어와 에이전트가 무엇을 물려받는지도 중요하다.
