제323호

우버는 코드 지우는 AI에게 무엇을 맡기지 않았을까

플래그 1만 6천 개를 정리한 AutoClean, 에이전트 곁에 붙여 둔 도구·권한·심판의 분업표

AI·테크우버는 코드 지우는 AI에게 무엇을 맡기지 않았을까

우버가 공개한 ‘코드 지우는 AI’

우버 엔지니어링 블로그에 AutoClean이라는 시스템을 소개하는 글이 올라왔어요. 다 쓰고 남은 피처 플래그1를 찾아 관련 코드를 지우고, 깨진 테스트를 고치고, 검증까지 마친 diff2를 담당 팀에 보내는 시스템이에요. 우버는 지난 몇 년 동안 이 시스템으로 플래그 1만 6천 개 이상, 코드 100만 줄 이상을 정리했다고 밝혔어요.

AI가 코드를 써 준다는 이야기는 이제 익숙해요. 이 글은 방향이 반대예요. 이미 돌아가는 프로덕션 코드를 AI가 지워요. 그런데 글을 끝까지 읽어 보면 에이전트가 무엇을 해냈는지만큼, 우버가 에이전트에게 무엇을 맡기지 않았는지가 자세히 적혀 있어요. 오늘은 그 분업의 모양을 따라가 볼게요.

플래그 하나를 지우는 데 왜 판단이 필요할까요

피처 플래그는 코드 안에 둔 스위치예요. 새 기능을 일부 사용자에게만 켜 보거나, 문제가 생기면 다시 배포하지 않고 끌 수 있게 해 줘요. 우버는 모바일 앱과 백엔드 서비스에 걸쳐 수천 개의 플래그를 관리한다고 해요.

문제는 기능을 모든 사용자에게 켠 뒤예요. 스위치는 늘 켜져 있는데, 스위치를 둘러싼 조건문과 옛 경로, 그 경로를 검사하던 테스트가 그대로 남아요. 팀은 다음 기능으로 넘어가고, 정리는 새 제품 작업과 우선순위를 다투다 뒤로 밀려요. 우버 규모에서는 이렇게 밀린 작은 정리가 금방 큰 적체가 된다고 해요.

우버가 든 예시는 주문 처리 코드예요. 설정에서 new_order_pipeline 플래그 값을 읽어 cfg.UseNewPipeline에 담고, 그 값을 processOrder 함수에 인자로 넘기고, 함수 안에서 새 파이프라인과 옛 파이프라인 중 하나를 고르는 구조예요. 플래그가 완전히 배포돼 값이 늘 true라고 해 볼게요.

플래그를 읽는 자리에 true를 넣는 건 기계적인 일이에요. 판단은 그다음부터 필요해요.

  • processOrder의 useNewPipeline 인자가 정말 늘 true인지 확인해야 해요. 예시에서는 호출하는 곳이 하나뿐이지만, 실제 코드에서는 다른 곳이 다른 값을 넘길 수 있어요. 모든 호출 지점을 본 뒤에야 인자를 지울 수 있어요.
  • 인자를 지우고 값을 함수 안으로 밀어 넣어야 옛 파이프라인 분기가 죽은 코드라는 게 드러나요. 함수만 따로 보면 두 분기 모두 살아 있는 것처럼 보이거든요.
  • 설정 구조체의 UseNewPipeline 필드도 지울 수 있을지 몰라요. 다른 코드가 그 필드를 읽거나 구조체 모양에 기대고 있지 않을 때만요.

플래그 값은 설정 초기화에서 출발해 구조체 필드, 호출 지점, 함수 인자를 거쳐 죽은 코드에 닿아요. 우버는 상수를 끼워 넣는 결정론적 재작성은 정적 도구가 잘하고, AI는 이 전파 경로를 따라가며 어디까지 지울지 정하는 데 가장 쓸모 있다고 정리해요. 테스트도 같은 문제를 안고 있어요. 어떤 테스트는 지워도 되고, 어떤 테스트는 새 기본 동작에 맞춰 다시 써야 해요.

우버는 이 부채가 사람만 괴롭히지 않는다고도 써요. AI 에이전트도 죽은 코드를 읽고 해석하느라 시간을 쓰고, 그만큼 세션이 길고 비싸진다는 거예요. 다만 얼마나 길어지는지에 대한 수치는 글에 없어요.

image불리언 계산은 도구에게 넘겼어요

AutoClean은 우버의 피처 플래그 관리 시스템에서 오래된 플래그를 찾아 세 단계로 처리해요. 코드를 고쳐 쓰고, CI3에서 깨진 곳을 고치고, 결과를 검증해요. 검증을 통과한 diff는 담당 팀에 보내고, 사람이 최종 결정을 내릴 때까지 diff가 낡지 않게 계속 갱신해요.

첫 단계의 에이전트는 코드베이스 전체를 볼 수 있고, 파일 검색·코드 편집·조건식 단순화 도구를 쓸 수 있어요. 눈여겨볼 건 마지막 도구예요. 우버는 불리언 식과 조건문을 정리하는 일을 에이전트가 직접 하게 두지 않았어요. 자체 코드 변환 엔진인 PolyglotPiranha를 MCP4로 연결해, 에이전트가 필요할 때 불러 쓰게 했어요. 에이전트는 플래그 자리에 true를 넣고, PolyglotPiranha로 식을 단순화하고, 값을 processOrder까지 따라가 인자와 죽은 분기를 지우고, 더 정리할 것이 없을 때까지 이 과정을 반복해요.

이유도 적혀 있어요. 조건식을 단순화하다 작은 실수를 하면 프로덕션에서 실행되는 분기가 바뀌어요. 컴파일도 되고 겉보기에 멀쩡한데 동작만 달라지는 종류의 실수예요. 우버는 에이전트 시스템이라고 해서 모든 단계를 에이전트가 맡을 필요는 없고, 틀렸을 때 동작을 조용히 바꿀 수 있는 변환일수록 결정론적으로 처리하고 싶다고 썼어요. 같은 입력에 늘 같은 결과를 내는 방식이요.

Piranha는 새 도구가 아니에요. 우버는 2020년 3월 Piranha를 오픈소스로 공개하면서 그때까지 약 2,000개의 오래된 플래그와 관련 코드를 지웠다고 밝혔어요. 당시에도 매주 플래그 관리 시스템에서 오래된 플래그를 뽑아 diff를 만들고, 플래그를 처음 만든 사람을 기본 리뷰어로 지정하는 파이프라인이 돌고 있었어요. AutoClean은 이 결정론적 도구를 버리지 않고, 판단이 필요한 자리에만 에이전트를 끼워 넣은 구조로 읽혀요.

CI에서 깨졌을 때도 순서가 같아요. 쓰지 않게 된 기본값을 지우거나 린트 오류를 정리하는 것처럼 기계적인 실패는 값싼 결정론적 수정기가 먼저 받아요. 수정기가 알아보지 못한 실패만 다음으로 넘어가고, 테스트를 지울지 다시 쓸지처럼 판단이 필요한 경우에야 에이전트 수정기가 받아요. 고칠 때마다 다시 CI를 돌리고, 초록불이 뜨거나 정해 둔 시도 횟수를 다 쓸 때까지 반복해요.

테스트 고치는 에이전트에게는 테스트 파일만 열어 줬어요

이 단계에서 우버가 적어 둔 문장 하나가 있어요. 수정기마다 손댈 수 있는 범위를 좁게 정했고, 예를 들어 테스트 수정기는 테스트 파일만 고칠 수 있다는 거예요. 그렇게 막지 않으면 에이전트가 실패하는 테스트를 통과시키려고 그 아래 프로덕션 코드를 바꾸는 일이 생긴다고 해요. 우버가 바로 막으려던 결과예요.

이 대목이 무서운 건 그 행동이 에이전트 입장에서는 꽤 합리적이라는 데 있어요. 눈앞의 신호는 “테스트를 통과시켜라”였고, 프로덕션 코드를 바꾸면 테스트는 통과해요. 코드를 지우라는 일이 코드를 바꾸는 일로 번지는 거예요. 우버가 글에 적은 대응은 지시문이 아니라 권한이었어요. 고칠 수 있는 파일 자체를 제한했어요.

초록불 다음에 세운 심판들

CI가 초록불이면 끝일까요. 우버는 그렇지 않다고 봐요. 빌드와 테스트를 통과해도 틀린 정리가 있을 수 있어요. 에이전트가 테스트를 고치는 대신 지워 버렸는데, 그 테스트가 남겨야 할 동작을 검사하고 있었을 수 있어요. 플래그 값을 끼워 넣기만 하고 주변 코드까지 단순화를 퍼뜨리지 못한 채 일찍 멈췄을 수도 있어요. 둘 다 CI가 반드시 잡아내지는 못하는 실수예요.

그래서 검증을 두 겹으로 뒀어요. 먼저 정적 검사가 규칙으로 적을 수 있는 문제를 걸러요. 늘 true만 돌려주게 된 함수, 단순화됐어야 할 조건문, 오래된 플래그 경로로만 닿는 죽은 코드 같은 것들이에요. 이걸 통과하면 CodeCourt라는 에이전트 검증 계층으로 넘어가요.

CodeCourt는 정리 diff가 지켜야 할 속성을 하나씩 정의하고, 속성마다 그 하나만 확인하는 리뷰 에이전트를 붙여요. 글에 예로 나온 속성은 넷이에요.

속성확인하는 것
추가 없음정리는 코드를 지우거나 단순화할 뿐, 새 코드를 넣지 않아요
동작 보존정리 전후로 프로그램의 관찰 가능한 동작이 같아야 해요
의도 이탈 없음요청받은 플래그만 다뤄요. 관련 있어 보여도 다른 플래그는 지우지 않아요
에이전트 흔적 없음에이전트가 작업하며 남긴 주석이나 부산물이 없어야 해요

리뷰어들은 동시에 돌고, 판정은 단순해요. 모두 통과하면 사람 리뷰로 가고, 하나라도 실패하면 그 diff는 사람이 보기 전에 버려져요.

우버는 이렇게 쪼갠 이유도 적었어요. 정리 에이전트는 긴 지시를 안고 여러 단계를 수행하는데, 지시가 늘수록 일부는 묻히고 일부는 실제로 서로 부딪혀요. 그러면 에이전트는 CI 통과처럼 가장 가까운 신호에 맞춰 움직이다 정작 지켜야 할 정리 조건을 어기기 쉽다는 거예요. 그래서 모든 조건을 한 에이전트가 기억하게 하는 대신, 최종 diff가 만족해야 할 속성으로 떼어냈어요. 초기에는 에이전트 하나가 diff 전체를 처음부터 끝까지 검토하게 해 봤는데, 속성 하나씩 맡은 리뷰어를 여럿 두는 쪽이 결과가 더 좋았다고 해요.

우버의 설명대로라면 사람 리뷰어가 할 일도 달라져요. 정리하다 동작이 바뀌지 않았는지 줄마다 훑는 일은 앞 단계에 맡기고, “이 플래그를 지금 지워도 되나”라는 사업 판단에 집중할 수 있다는 거예요.

1만 6천 개라는 숫자를 읽는 법

글의 마지막 숫자는 크게 보여요. 지난 몇 년 동안 플래그 1만 6천 개 이상, 코드 100만 줄 이상을 정리했다고요. 이 숫자를 읽을 때는 두 가지를 같이 봐야 해요.

먼저 기간이에요. 우버는 “지난 몇 년”이라고만 썼어요. 이 누적이 언제부터인지, 에이전트가 들어오기 전 Piranha 파이프라인 시절의 몫이 포함됐는지는 글만으로 알 수 없어요. 2020년 3월에 이미 약 2,000개를 지운 상태였다는 점을 생각하면, 1만 6천 개 전부를 에이전트의 성과로 읽기는 어려워요.

다음은 빠진 숫자예요. 몇 개의 diff가 CodeCourt에서 버려졌는지, 사람 리뷰어가 몇 개를 승인하고 몇 개를 반려했는지, diff 하나에 시간과 비용이 얼마나 드는지는 글에 없어요. “하나라도 실패하면 버린다”는 설계가 얼마나 자주 작동하는지도 알 수 없어요.

비교해 볼 숫자는 다른 회사에 있어요. 2026년 9월 InfoQ 보도에 따르면 도어대시는 약 623개 저장소에 걸친 6만 개 이상의 플래그를 정리하려고 멀티 에이전트 시스템을 만들었고, 오래된 플래그 50개로 평가했어요. 45개에서 쓸 만한 PR이 나왔고, 정리 한 건에 평균 13.8분과 4.79달러가 들었어요. 사람이 하면 1~2시간이 걸린다는 도어대시 자체 추정과 나란히 놓은 수치예요. 단순한 플래그는 한 번에 100% 정리됐지만 복잡한 플래그는 85%였고, 엔지니어가 다섯 번 개입했어요. 개입이 필요했던 곳은 깊은 호출 체인과 인터페이스를 넘나드는 인자 전달이었어요. 도어대시는 코드를 고치기 전에 엔지니어가 목표 값을 먼저 확인하게 했어요.

uber두 회사의 숫자로 우열을 가릴 수는 없어요. 언어도, 코드 구조도, 측정 기준도 달라요. 다만 도어대시가 막힌 곳은 우버 글이 짚은 곳과 겹쳐요. 값을 끼워 넣는 쪽보다, 그 값이 여러 파일을 건너 어디까지 퍼지는지 따라가는 쪽이었어요.

오스왈드의 시선

제가 늘 하는 말이 있어요. 모든 사람, 모든 일에 초지능이 필요한 건 아니고, 일과 환경에 맞는 모델이 따로 있다는 거예요. 저도 그렇게 나눠 써요. 2026년 9월 기준으로 전체 계획 수립과 오케스트레이션, 초기 블루프린트는 여전히 Claude나 GPT-6에 맡기고, 오래 걸리고 반복이 많은 작업은 DeepSeek이나 GLM에 맡겨요.

우버 글에서 그 말과 가장 가까운 대목은 불리언 계산을 PolyglotPiranha에 넘긴 부분이에요. 에이전트가 할 수 있는 일이어도, 틀리면 조용히 동작이 바뀌는 계산은 같은 입력에 같은 답을 내는 도구에 맡겼어요. 일에 맞는 모델을 고르는 데서 한 걸음 더 나가, 일의 일부를 아예 모델 밖으로 뺀 셈이에요.

테스트 수정기에게 테스트 파일만 열어 준 결정도 같은 줄에 있다고 봐요. 무엇을 시킬지와 함께 무엇을 못 하게 할지를 정한 거예요. 에이전트를 업무에 들일 때 “어떤 일을 맡길까”라는 질문은 쉽게 나와요. 우버 글은 “그 일을 하다 어디까지 번질 수 있나”를 같이 묻게 해요.

하나라도 실패하면 고치지 않고 버리는 설계는 사람 리뷰어의 시간을 아끼려고 기계의 시간을 더 쓰겠다는 선택으로 읽혀요. 그 선택이 수지가 맞는지는 몇 개를 버렸는지 알아야 따질 수 있는데, 그 숫자가 빠져 있는 건 아쉬워요.

마치며

우버 글에는 에이전트의 능력을 보여 주는 문장만큼, 에이전트 둘레에 쳐 둔 장치를 설명하는 문장이 많아요. 버려진 diff의 수나 비용이 공개되지 않아 효과를 숫자로 따지기는 어려워요. 그래도 어떤 일을 도구에, 어떤 일을 에이전트에, 어떤 일을 사람에게 맡겼는지는 꽤 자세히 적혀 있어요.


💬 구독자님 팀 코드에는 다 켜 둔 채 남아 있는 피처 플래그가 대략 몇 개쯤 될 것 같나요? 어림짐작으로 댓글에 남겨 주세요.

📨 오래된 코드 정리를 미뤄 둔 개발팀 동료가 있다면 이 글을 전달해 주세요.


여러분의 생각이 다음 호를 만듭니다

이번 호에서 가장 공감했거나, 다른 경험을 한 지점은 무엇인가요?

댓글은 무료 회원이면 누구나 남길 수 있어요.

커피 한 잔 보내기

이 관점이 좋았다면, 다음 글에 커피 한 잔

오스왈드에게 커피와 함께 짧은 쪽지를 보내주세요. 응원은 다음 취재와 집필에 보탭니다.

오스왈드에게

커피와 쪽지 보내기

“우버는 코드 지우는 AI에게 무엇을 맡기지 않았을까”을 읽고 떠오른 말을 남겨주세요. 메뉴 이름만큼의 응원금과 함께 전달됩니다.

참고자료 & 더 읽기

안광섭(오스왈드) 프로필 일러스트

필자 안광섭 (오스왈드)은 세종대학교 겸임교수, INLEVEL9 전략 컨설턴트입니다. 경력, 연구, 저서와 최신 활동은 저자 소개에서 계속 갱신합니다. 최근 소식 · 2026년 8월: 2026 세종도서 교양부문 우수도서 선정.

📝 용어 설명

각주

  1. 피처 플래그(feature flag): 코드 안에 넣어 두는 켜고 끄는 스위치예요. 새 기능을 일부 사용자에게만 보여 주거나, 문제가 생겼을 때 다시 배포하지 않고 기능을 끌 수 있게 해 줘요. ↩

  2. diff: 코드가 어떻게 바뀌었는지 보여 주는 변경분이에요. 리뷰어는 이 변경분을 보고 반영할지 결정해요. ↩

  3. CI(지속적 통합, Continuous Integration): 코드가 바뀔 때마다 자동으로 빌드하고 테스트를 돌려 문제가 없는지 확인하는 체계예요. ↩

  4. MCP(Model Context Protocol): AI 모델이 외부 도구나 데이터에 연결해 쓸 수 있게 해 주는 표준 연결 방식이에요. ↩