브라운필드 에이전틱 엔지니어링 (번역)
원문: https://addyo.substack.com/p/brownfield-agentic-engineering
브라운필드 에이전틱 엔지니어링
팀보다 나이 많은 코드베이스에서 에이전트를 굴리려면 무엇이 필요한가
오래된 코드베이스에서의 에이전틱 엔지니어링은, 숨겨진 제약을 보이게 만들고 싼 변경을 신뢰할 수 있게 만드는 일이다. 브라운필드 코드베이스에서 할 일을 이야기해 보자.
내 경력 동안 오래된 코드베이스를 가진 팀들에서 일했다. 그런 시스템이 브라운필드다. 저장소가 더 이상 그 시스템이 실제로 어떻게 동작하는지의 완전한 기술이 아니게 된 것이다. 조직의 암묵지, 떡칠된 덕테이프, 레거시 서비스, 다른 팀들이 의존하는 기대치가 트리 바깥에 산다. 새 코드를 쓰기 전에 그 제약들을 배워야 하고, 변경이 그것들을 깨지 않았음을 증명해야 한다. 나는 에이전트와 함께 코딩하는 걸 좋아하지만, 낡은 브라운필드 코드베이스에 에이전트를 무감독으로 던지면, “동작은” 하는데 시스템 설계는 틀리고 테스트는 잘 부서지는 무언가를 얻을 수 있다.
AI 이전에도 모던라이제이션을 하고 싶어 하는 팀들이 있었는데, 그때도 일을 아주 잘게 쪼개고, 튼튼한 테스트를 깔고, 무언가를 깨뜨리지 않는다는 확신의 층을 두는 식으로 진행해야 했다. 실제 사용자 여정 테스트 위에, 진행하는 마이그레이션이 의도대로 동작하게 유지되려면 반복 가능한 테스트의 세례가 필요하다는 걸 대체로 알고 있었다. 요즘은 에이전트가 코드를 내려놓는 순간, 결정 하나하나를 직접 내리지 않았다면 이미 브라운필드 프로젝트라고 말하는 사람들도 있다. 어느 쪽이든 목표는 같다. 싼 변경을 안전하게 하는 것에 최적화해야 한다.

요즘, 특히 최근 5~10년쯤에는 테스트를 더 신경 쓰고, 검증을 더 신경 쓰고, 무언가를 깨뜨리지 않는 방식으로 변경하는 법을 더 신경 쓰자는 생각이 더 주목받은 것 같다. 하지만 그렇다고 달라지는 건 없다. 에이전틱 엔지니어링을 도입하고, 소프트웨어 팩토리와 그런 종류의 패턴들로 큰 코드베이스를 자율적으로 다루려고 일을 하고 있다면, 상당한 추가 주의를 깔아야 한다. 그렇지 않으면 기술 부채의 바다에 발을 들이게 된다.
시작하기 전에 하나 전제하자. 코드가 소스 오브 트루스여야 한다는 것. 브라운필드를 돕기 위해 위에 얹는 것들은, 코드만으로는 쉽게 추론할 수 없는 것들이어야 한다. 구역(zones), 폭발 반경(blast radius), 그 외에 도움이 될 만한 몇 가지 패턴으로 이 이야기를 풀어 보겠다.
구역 (Zones)
오래된 코드베이스에 들어간다면, 아마 가장 먼저 알고 싶은 건 어떤 코드를 만지면 안 되는가일 것이다. 이걸 구역으로 생각할 수 있다. 예를 들어 초록 구역은 안전하고, 테스트가 좋고, 고립되어 있다. 노랑은 품질이 섞여 있고, 빨강은 인증·결제·권한처럼 민감한 곳이다.
코드베이스에서 아주 민감한 부분이 어디고, 모두가 잘 이해하지 못하는 곳은 어디인가? 그런 곳들에 다른 구역을 그려줄 수 있다. 좋은 테스트 커버리지를 갖고, 최신 관습을 따르고, 고립이 잘 되어 있는 초록 구역이 있을 수 있다. 시스템의 그런 부분에서는 에이전트가 빡빡한 루프 안에서 알아서 일할 수 있다.
내가 일했던 사이트들, 특히 커머스 사이트는 다섯, 여섯 개 부서가 각자 자기 마이크로사이트를 갖고 있으면서 최종 사용자에게는 하나의 경험처럼 느껴지는 경우가 있었다. 그리고 표면 아래에는 상당한 본질적 복잡도가 있었다. 어떤 팀은 자기 물건에 아주 좋은 테스트 커버리지가 있었을 것이고, 아마 최근 몇 년 안에 만들어진 것이었을 것이다. 다른 팀은 그렇지 않을 수 있다. 그래서 초록 구역이 생긴다.

노랑은 품질이 섞인 곳이다. 에이전트가 코드를 바꿀 수 있지만, characterization test(현행 동작 고정 테스트)가 작성된 다음에만 그럴 수 있다.
그리고 빨강 구역이 있다. 인증, 결제, 권한, 급여처럼 민감한 것들. 평소에 함부로 만지지도 않고 성급하게 바꾸지도 않을 것들이다. 예컨대 소수의 사람들만 전체가 어떻게 돌아가는지 이해하는 시스템 같은 곳이다. 그런 시스템에서 무감독 리라이트는 원하지 않는다.
구역을 은유가 아니라 운영 절차로 만드는 규칙이 셋 있다. 첫째, 지도는 사람이 그린다. 에이전트가 아니다. 선택을 맡기면 에이전트는 가장 무서운 파일에서 시작한다. 가장 무서운 파일에 이름이 가장 흥미로운 것이 있기 때문이다. 둘째, 구역은 벌어야만 움직인다. characterization test가 존재하고 모듈의 오너가 에이전트의 첫 변경들을 리뷰하면, 노랑이 초록이 된다. 셋째, 구역이 동사를 정한다. 초록은 빡빡한 루프, 노랑은 테스트 먼저, 빨강은 모든 단계에 사람이 페어링하거나 일 자체가 일어나지 않는다.
코드가 말할 수 없는 것을 적어라
자율성은 폭발 반경, 관측 가능성, 복구 가능성을 따라가야 한다. 모델의 자신감은 나쁜 가이드다.
세상의 지도를 어떻게 생각할지, 그리고 에이전트가 코드베이스에서 스스로 무엇을 추론할 수 있는지 감은 잡아둘 필요가 있다. 에이전트는 코드 자체에서 꽤 많은 걸 추론한다. 모든 것마다 마크다운 파일을 만들어서 컨텍스트 창에 쑤셔 넣던 시절이 있었다. 하지만 에이전트는 시스템의 지도를 이해하는 데 사실 꽤 능하다. 너무 많은 것을 문서로 만들 필요는 없다. 줘야 하는 건 코드 자체에서는 명백하지 않은 것이다. 관습이 있는가? 패턴이 있는가? 거기에 없는 뉘앙스가 있는가? 그게 중요하다.
구체적으로는 이런 것들이다. 비즈니스나 팀 고유의 뉘앙스, 시스템이 왜 그렇게 구조화되어 있는지 설명하는 트레이드오프, 정적 분석이나 도구가 명시적으로 강제하지 않는 가이드라인, 도메인 고유의 규칙, 외부 제약, 직관에 반하는 구현 뒤에 있는 역사적 맥락 같은 것들.
코드가 말할 수 없는 것을 적고, 그것 외에는 적지 마라.
리서치가 세션을 살아남게 하라
에이전트의 탐색이 지속되는 산출물을 남기지 않으면, 다음 에이전트가 같은 고고학을 다시 비용 치른다.
지도에 더하고 싶은 조각은 지속되는 리서치 산출물이다. 노랑과 빨강 작업에서는, 짧은 이해 메모(comprehension memo)를 남기는 별도의 읽기 전용 패스를 선호한다. 진입점, 오너, 호출자, 기존 추상화, 테스트, 프로덕션 신호, 관련 역사, 열린 질문들. 주장은 파일, 이슈, 오너십 기록, 대시보드를 인용해야 한다.
기본 루프에서는 그 리서치가 낭비된다. 에이전트가 인증 흐름이 어떻게 동작하는지 파악하고, 작업을 끝내고, 세션이 끝나면서 그 모델을 잃는다. 채팅 히스토리는 좋은 기록 시스템이 못 된다. 특히 컴팩션을 거친 뒤에는.
리서치 후에는 깨끗한 컨텍스트로 계획을 시작한다. 그럴듯한 접근들이 어떤 파일을 만지는지, 어떤 불변식을 유지하는지, 어떻게 되돌릴지 물어본다. 길을 고르는 건 사람이다. 구현은 지도가 틀렸음을 발견하면 멈춰야 한다. 리뷰는 새로 시작해서 수용 기준에서 거꾸로 작업한다. 깨끗한 리뷰어가 테스트가 구현을 증명하면서 요구사항은 놓치고 있다는 것을 발견할 가능성이 높다.
지시가 하네스가 될 때
같은 교정을 반복할 때마다 하네스에 빠진 조각이 하나 있다.
조각들이 어디에 맞는지 정확히 아는 게 유용하다. 인스트럭션은 저장소에 대한 특이한 사실을 기록한다. 스킬은 재사용 가능한 절차를 꾸러미로 만든다. 폭발 반경 확인이나 스키마 변경 검증 같은 것. 플러그인은 오너십 카탈로그, 장애 아카이브, 대시보드에 대한 통제된 접근을 제공할 수 있다.
하네스는 에이전트 주변의 작업 환경이다. 컨텍스트, 도구, 권한, 테스트, 로그, 복구. 팩토리는 의존할 수 있는 루프를 많이 스케줄링하고, 지속되는 상태를 유지하고, 새로운 사례는 사람에게 돌린다.
실용적인 테스트는 에이전트가 뭔가를 틀렸을 때 무슨 일이 일어나는지다. diff를 조용히 고쳐주면 다음 세션이 같은 실수를 반복할 수 있다. 같은 리뷰 코멘트가 또 나오면, 그걸 lint 규칙, 훅, 타입, 테스트, 스킬로 옮겨라. 산문은 기계적으로 강제할 수 없는 제약을 위해 남겨둔다.
거부 규칙(deny rule), 스코프가 지정된 자격증명, CI 체크는 기억할 필요가 없다. 시간이 지나면 하네스는 팀이 두 번 지불하기로 결정한 실패들의 기록이 된다.
제로 리스크 작업부터 시작하라
무언가가 오늘의 동작을 개선하게 하기 전에, 먼저 오늘의 동작을 고정하라.
에이전트를 기존 코드베이스에 들이는 것은 다른 종류의 모던라이제이션과 비슷하다. 제로 리스크 작업부터 시작할 수 있다. “이 모놀리스를 Rust로 리라이트하자”가 아니라, 먼저 “이것들이 어떻게 동작하는지 설명해봐”일 수 있다.

현재 동작을 고정할 수 있는 characterization test를 생성하라.
Characterization test는 시스템의 실제 현재 동작을 문서화해서 레거시 코드를 안전하게 리팩터하거나 변경할 수 있게 해주는 자동화된 테스트다.
characterization test는 모듈이 오늘 하는 일을, 못생긴 부분까지 포함해서 못 박는 테스트다. 오래된 시스템에서는 그 못생긴 동작 일부가 비즈니스가 실제로 돌아가는 방식이고, 에이전트는 초록 스위트 뒤에서 그걸 기꺼이 “고쳐” 버린다. 그 기계가 오래된 이유는 문제가 오래됐기 때문이다. Netflix는 GraphQL 전환에서 같은 아이디어를 프로덕션 규모로 썼다. 새 경로와 옛 경로에 리플레이와 섀도 트래픽을 보내고, 페이로드를 diff하고, 일치할 때만 승격했다. 홈페이지급 표면에 정직한 단위 테스트 스위트가 없을 때의 승격 경로다. 추측하지 말고 둘 다 돌려서 비교하라.
에이전트가 테스트를 통과하게 만드는 주인공이 될 때, 그 같은 세션이 테스트의 유일한 저자가 되게 하지 마라. 동작을 먼저 고정한다. 별도 패스에서, 아니면 사람이. 그다음 에이전트에게 일을 준다. 그렇게 하지 않으면 방금 발명한 구현을 그대로 인코딩하는 초록 스위트를 얻게 된다.
그다음 기계적 변환의 길로 들어선다. 죽은 코드와 사용되지 않는 export 목록 작업 같은 것. 시스템의 가장 까다롭거나 가장 털이 많은 부분부터 시작하고 싶지는 않을 것이다. 그리고 궁극적으로 이런 마이그레이션 어디서든 그 확신을 갖고 싶을 것이다.
내가 큰 코드베이스에서 여러 종류의 마이그레이션을 하던 시절을 기억한다. 고장 난 것을 고칠 때조차 사람들이 상당한 주의를 기울였다.
내가 일했던 오래된 코드베이스 중 하나는 AOL이었다. 어느 날 쉬는 날이었는데, 회사 근처 만화책 가게에 들렀다. 그때 상사한테 문자가 왔다. 스윙바이 할 수 있겠느냐고. AOL.com 홈페이지가 완전히 깨졌는데, 파고들 자바스크립트 전문가가 주변에 충분히 없다는 것이었다. 들어가 봤다. 요즘 사람들은 생각하겠지. 홈페이지가 얼마나 복잡하겠어. 그런데 수십 개 부서의 사람들이 수많은 컴포넌트, 수많은 기준, 수많은 스크립트, A/B 테스트를 소유하고 있으면, 다른 모두를 위한 세계를 깨뜨리지 않는 게 중요하다. 테스트 커버리지가 원하는 만큼 모든 곳에 깔려 있지 않을 테니까. 그때는 고칠 수 있었다. 하지만 자체 단위 테스트가 없는 것들은 최소한 사용자 테스트를 해야 했다. 모두에게 깨뜨리지 않으면서 얼마나 잘 동작하는지. 그게 중요한 일이었다.
그게 여전히 그 일이다. 에이전트가 수십 개 부서 문제를 없애주는 게 아니다. 그 문제에 대한 변경을 시도하는 비용을 싸게 만들어줄 뿐이다. 오직 프로덕션 트래픽만 진짜로 이해하는 표면은 정의상 빨강 구역이고, 그 트래픽을 대신해줄 무언가를 만들기 전까지는, 내 쉬는 날에 했던 그 사용자 테스트가 여전히 관문이다.
완결 단위로 마이그레이션하라
마이그레이션은 새 경로가 동작하고 옛 의존성이 확실히 사라졌을 때 완결된다.
반쯤 끝난 마이그레이션은 특히 에이전트를 혼란스럽게 한다. 검색하면 옛 방식이 마흔 개 파일에서 나오고, 대체재가 열두 개 파일에서 나오고, 둘 다 현재인 것처럼 보이게 하는 셈(shim)이 있다. 에이전트에게는 모순된 선례로 보인다.
나는 서른 개 파일을 바꾸고 두 패턴을 모두 살려두는 것보다, 옛 경로 삭제까지 포함해 한 경로를 끝에서 끝까지 완결하는 걸 선호한다. 삭제가 나중의 정리 티켓으로 남아 있다면, 마이그레이션 단위는 완결되지 않은 것이다.
대체재가 여전히 레거시 구현을 호출하는 동안 테스트는 초록으로 남아 있을 수 있다. SWE Refactor Bench는 이걸 마이그레이션 “Blindness”라고 부른다. 520번의 에이전트 실행 중 그 마이그레이션 감사, 동작 테스트, 독립 검증을 모두 통과한 것은 28개뿐이었다.
코다모드(codemod)가 루틴한 변경을 해줄 수 있다면, 그걸 작성하고 검증하는 데 에이전트를 써라. 예외 큐를 에이전트에게 줘라. Stripe의 마이그레이션이 여기서 유용한 것은 정확히 에이전트가 전혀 관여하지 않았기 때문이다. 지속된 산출물은 마이그레이션 기계였다.
더 큰 마이그레이션들의 교훈
Bun의 Zig-to-Rust 포팅은 535,000줄 코드베이스에서 11일간 약 50개 워크플로우를 돌렸다. 생성된 단위마다 적대적 리뷰어 두 명, 기존 테스트 스위트 전체를 병합 게이트로. 가져올 만한 부분은, 에이전트가 하나라도 돌기 전에 Zig 관용구를 Rust에 매핑하는 포팅 가이드에 몇 시간을 쏟았다는 것이다. Anthropic의 마이그레이션 프로세스는 본 대규모 실행 전에 일회용 미니 마이그레이션으로 규칙집을 스트레스 테스트하고, 시행착오 산출물을 버린다.
통제된 VB6-to-C# 연구는 단순 기능에서 92%, 복잡한 기능에서 47%의 동작 등가성을 측정했다. 단위 크기가 지렛대다. 이 모양은 에이전트와 전혀 무관하게 원래 있었다. Stripe는 한 PR로 370만 줄을 TypeScript로 옮겼다. 몇 달간의 코다모드 작업이었고, 에이전트는 관여하지 않았다. Google의 large-scale-changes 챕터는 코드베이스가 커질수록 원자적 변경이 작아지는 이유를 설명한다. Spotify는 이제 Backstage가 수년 전에 만든 레일 위에서 매달 650개 이상의 에이전트 PR이 머지된다고 보고한다.
Asana는 수년 묵은 Enzyme 백로그를 2주 만에, 모델과 인프라 비용 약 12,000달러로 정리했다. 그 12,000달러는 토큰 청구서일 뿐, 그들이 장부에 들고 있던 5년 인건비 추정치를 대체하지 않는다. 생성 비용에 대해 벤더가 보고한 수치로 취급하라. 통제된 절감 연구가 아니다. Bun과 옮길 수 있는 부분은 같다. 좁고 기계적인 마이그레이션, 미리 존재하던 스위트, 모든 변경을 여전히 리뷰하는 사람들.
회사들 사이에서 옮겨지는 것은 에이전트 주변의 구조다.
실제로 바뀐 것
에이전트는 그럴듯한 여러 구현을 시도해보는 가격을 바꿨다. 하나를 고르는 데 필요한 증거를 바꾸진 않았다.
올해는 유력 회사들이 에이전트로 큰 리라이트를 하는 사례를 점점 더 많이 읽게 될 것이다. CTO들과 이야기하면, 팀들이 에이전트로 여러 언어나 프레임워크에서 리라이트를 시도하게 하는 경우가 있었다. 이제 더 싸게 해볼 수 있고, 트레이드오프를 평가할 수 있기 때문이다.
Shopify는 Shop 컨슈머 앱을 React Native에서 네이티브 Swift와 Kotlin으로 12주 만에, 작은 팀과 화면 단위의 에이전트 게이트 체크포인트로 재구축했다. 훨씬 큰 상인(merchant) 앱은 여전히 브라운필드 문제다. 수백 개 화면, 깊은 플랫폼 통합, 같은 관문들, 더 긴 시계.
Rust로의 리라이트 사례들도 봤다. 프레임워크 수준의 마이그레이션들도 봤다. 여러 종류의 마이그레이션이 진행됐다. 그리고 대부분은 사람들이 훨씬 더 긴 시간 프레임에서 했을 마이그레이션들이었다. 요즘은 토큰이 충분하면, 에이전트에게 여러 스택이나 언어에 걸친 마이그레이션 완수를 시도하게 할 수 있다.
에이전트에게 경쟁하는 여러 옵션으로 무언가를 실제로 구현하게 할 수 있다. 한 팀이 단일 옵션을 골라 올인하는 대신, 전부 구현하게 하는 것이다. 전부 단위 테스트로 검증할 수 있다. 전부 퍼포먼스 프로파일링할 수 있다. 그다음 결정하면 된다. 어떤 경우에는 그게 훨씬, 훨씬 싸다. 요즘 팀들에게는 완전히 다른 게임이라고 생각한다.
마지막에 병렬화하라
더 많은 생성 코드는 더 적은 인간 소유권이 아니라, 더 선별적인 인간 리뷰로 이어져야 한다. 루프/목표/병렬화를 생각하는 길로 가기 전에, 브라운필드 프로젝트를 성공으로 이끌 게 뭔지 진지하게 고민하라.
소프트웨어 팩토리는 많은 변경을 한꺼번에 돌릴 수 있다. 나는 그 부분을, 하나의 단위가 의존할 수 있는 판정자와 복구 경로, 그리고 사람들이 흡수할 수 있는 리뷰 형식을 갖춘 뒤에야 복사하겠다.
병렬화는 이미 갖고 있는 병목을 곱한다. 자동화된 검증은 체크된 변경 다섯 개를 감당할 수 있다. 모든 줄을 읽는 시니어 한 명은 큐와 흩어진 주의력을 얻고, 결국 의례적인 승인에 도달한다.
나는 자동화된 리뷰가 의도, 바뀐 불변식, 테스트 결과, 패리티 불일치, 롤백 경로를 앞세우기를 선호한다. 완전한 diff는 언제나 접근 가능하게. 인간의 주의는 먼저 가장 큰 폭발 반경과 가장 약한 오라클로 간다.
워크트리는 변경을 고립하지만 동작을 고립하지 않는다. Git 메타데이터, 자격증명, 로컬 서비스, 네트워크 접근을 공유할 수 있다. 신뢰하는 작업은 그 트레이드오프를 받아들일 수 있다. 신뢰할 수 없는 콘텐츠를 소비하는 무인 에이전트는 더 강한 샌드박스와 스코프된 자격증명이 필요하다.
에이전트는 모호함에 가격을 매긴다
생성된 줄 수는 코드베이스가 나아졌는지 말해주지 않는다. 리드 타임, 리뷰 시간, 인간 개입, 탈출 결함, 롤백, 오라클 불일치, 남겨진 서프레션을 추적하겠다.
마이그레이션에서는 남아 있는 옛 import, 새 경로가 서빙하는 트래픽, 패리티 불일치, 제거된 레거시 의존성을 추적하라. 모든 트래픽이 여전히 옛 경로로 흐르는 초록 스위트는 busywork다.
에이전트는 모호함에 보이는 가격을 매긴다. 부족의 관습이 되풀이되는 리뷰 코멘트가 된다.
그 비용은 원래도 있었다. 온보딩, 리뷰, 장애 복구 중에 지불되던 비용이었다. 에이전트는 그중 더 많은 부분을 셀 수 있게 만든다. 팀들이 이미 가치가 있다고 알고 있던 유지보수 작업에 더 강한 논거를 준다.
다음에 에이전트가 홈페이지에 해당하는 작업을 할 때, 나는 그것이 수리보다 더 많은 걸 남기기를 바란다. 합성 사용자 여정, 오너십 기록, 회귀 테스트.
다음 엔지니어와 에이전트가 물려받는 것도 중요하다.
원문 말미에서 Addy는 이번 달 새 직장에 간다고 밝히며, 뉴스레터의 스폰서십과 구성에 변화가 있을 것이라고 예고했다. 에이전틱 엔지니어링과 소프트웨어 글은 계속 올린다고 한다.