TL;DR Muse는 전용 에이전트 오케스트레이션 콘솔 대신, 모든 소프트웨어 팀이 이미 열어두고 있는 네 가지 도구인 Slack, Linear, GitHub, Claude Code를 사용한다. 대시보드도 없고, SPA도 없고, 사람이 직접 작성한 코드도 없다. 모든 PR은 7개의 CI 게이트와 3인 페르소나 AI 리뷰 패널을, 발견 사항 0건 머지 정책 아래에서 통과해야 한다. 40,000표가 수집되었고, 파운데이션 모델 평가 속도는 80% 빨라졌다.
Meshy Universal System for Evaluation의 줄임말인 Muse는 우리의 사내 모델 평가 플랫폼이다. Meshy의 여러 팀은 새로운 모델 체크포인트가 이전 것보다 더 나은지를 확인하기 위해 매일 이 플랫폼을 사용한다.
어느 날, 갓 완성된 따끈따끈한 신규 모델 체크포인트가 나왔다. 다음 학습 전략을 결정하려면 대결(arena match)이 필요했다. 팀은 이를 지원하려면 Muse에 새로운 기능이 필요하다는 것을 알게 되었다.
"@Linear에 이 내용에 대한 Muse 이슈를 만들어줘"
프로덕트 매니저가 Slack에 이렇게 남겼다.
1분 후, Linear 이슈 MES-12345가 생성되었다.
Muse 플랫폼 오너인 나는 이 이슈를 보았다. 평가 작업을 막고 있던 문제였기에 최대한 빨리 출시해야 했다. 그래서 Claude에게 다음과 같은 메시지를 보냈다:
"MES-12345를 조사하고 수정해줘"
Claude는 이슈를 살펴보고, 두 가지 설계 결정 사항을 제시했다. 결정을 내린 뒤, 나는 터미널에서 벗어나 다른 작업을 하러 갔다.
1시간 30분 후, Muse의 새 버전이 배포되었다. 끝.
자율 소프트웨어 엔지니어링은 화려한 에이전트 오케스트레이션 콘솔이나 대시보드일 필요가 없다. 이 글에서는 우리가 이를 어떻게 가능하게 했는지 설명한다.
갤러리, 뷰어, 투표 버튼
Muse가 답하는 질문은 겉보기와 달리 까다롭다: 새 모델 체크포인트가 실제로 더 나은가? 물론 각 학습 실행마다 추적하는 정량적 지표들이 있지만, 이는 3D 생성에서 "좋다"는 것이 어떤 모습인지를 정확히 대변하기에는 한참 부족하다. 그래서 Muse는 단순하지만 효과적인 방법을 택한다. 바로 이중 맹검 테스트다. 두 모델의 결과물을 동일한 입력 이미지로, 좌우를 무작위로 배치해 나란히 보여주고, 동료들이 블라인드 상태로 투표하게 한다.
하나의 평가는 이러한 쌍들의 묶음이다. 누군가 Muse에 생성된 메시 두 세트와 레퍼런스 이미지 폴더를 지정하면, 그 다음부터는 앱이 알아서 처리한다. 모델을 쌍으로 묶고, 배정을 블라인드 처리하고, 한 번에 하나씩 비교 화면을 보여준다. 사람들이 짬이 날 때마다 투표가 쌓인다. 전체 인터페이스는 짧고 간편하게 소비할 수 있도록 만들어졌고, 거의 전적으로 단축키만으로 조작할 수 있다. 마우스에 손을 뻗어야 하는 투표자는 그만큼 투표를 덜 하게 된다.
![]()
Muse 투표 인터페이스: 동일한 입력 이미지로 생성된 두 개의 메시를, 좌우를 무작위로 배치해 보여준다.
이것이 이 제품의 전부다. 갤러리, 뷰어, 투표 버튼. Muse의 기능 자체는 전혀 복잡하지 않다. 바로 그렇기 때문에 이 사례가 깔끔한 케이스 스터디가 된다. 특이한 점은 오직 그것이 만들어지는 방식뿐이다.
에이전트가 탐색할 수 있는 아키텍처
Muse 프로젝트는 2025년 하반기, "바이브 코딩(vibe coding)"이 막 새로운 유행어가 되었을 무렵 시작되었다. Meshy의 학습 및 릴리스 주기에서 점점 커지는 평가 수요를 뒷받침하는 것 외에도, Muse에는 야심 찬 목표가 하나 더 있었다. 사람이 작성한 코드 없이, 회사 전체를 위한 소프트웨어를 만드는 것.
오늘날, 그 성과는 눈부시다:
- Muse 최초 배포 이후 40,000표 이상 수집, 월 평균 30건 이상의 평가 프로젝트 진행
- 4개 이상의 팀이 일상 업무에서 Muse를 적극적으로 사용 중
- 비기술 직군 팀원이 최소 1명 이상 적극적으로 기여한, 사내 최초의 코드베이스
- 완전 자율 개발을 달성한, 사내 최초의 코드베이스
종합적으로 볼 때, Muse는 파운데이션 모델 평가 속도를 최소 80% 향상시켰고, 모델 릴리스마다 최소 10인일(person-day)의 작업을 절감했다고 추산한다. Muse가 없었다면 지오메트리, 텍스처, 스마트 토폴로지 등 Meshy의 생성형 모델군에 대한 현재 수준의 개발 및 릴리스 처리량을 유지하는 것은 거의 불가능했을 것이다.
첫날부터 Muse는 에이전트가 최대한 쉽게 읽고 이해할 수 있도록(agent-legible) 설계되었다. 2025년 당시 우리는 이미, 동일한 유스케이스를 구현할 때 에이전트가 어떤 부분에서는 어려움을 겪으면서도 다른 부분에서는 단 한 번에 성공한다는 것을 발견했다. 이러한 반복 끝에 결국 지금의, 의도적으로 평범한(boring) 기술 스택이 만들어졌다. 백엔드는 Python이다: 기록에는 FastAPI와 Postgres, 메시와 이미지에는 S3를 사용한다. 프론트엔드는 서버 렌더링 방식이다: Jinja2 템플릿, 부분 페이지 업데이트를 위한 HTMX, 스타일링을 위한 Tailwind, 그리고 유일하게 진짜 클라이언트 사이드 컴포넌트인, 메시를 렌더링하는 WebGL 3D 뷰어로 구성된다. SPA도, 별도의 프론트엔드 모듈도 없다. 두 스택 간의 결합 방식은 웹에서 가장 오래된 방식이다: 백엔드가 HTML을 렌더링하고, 브라우저가 이를 표시한다. 페이지를 변경해야 할 때는 HTMX가 프래그먼트를 요청하고, 서버가 이를 렌더링해 응답한다.
다음은 Muse 핵심 평가 앱의 아키텍처다:
![]()
Muse 핵심 평가 앱의 레이어드 아키텍처. 의존성은 오직 아래 방향으로만 향한다.
이 구조에서 에이전트에게 중요한 특성은 두 가지다. 첫째, 프론트엔드 상태가 서버에 존재한다. 사용자가 보는 HTML은 백엔드가 생성한 HTML 그 자체이므로, 테스트(또는 에이전트)는 사용자가 본 화면을 재구성하기 위해 클라이언트 사이드 프레임워크를 구동할 필요 없이 그 HTML을 직접 검증할 수 있다. 둘째, 모든 의존성은 기계적으로 강제되며 오직 아래 방향으로만 향한다. 기능 폴더는 형제 폴더를 임포트할 수 없다. 두 기능이 서로를 필요로 하면, 컴포지션 루트가 한쪽의 기능을 다른 쪽에 주입하므로, 의존성은 정확히 한 파일에서만 확인할 수 있다. 각 레이어는 하나의 역할만 맡으며, 그 역할은 에이전트가 반드시 마주치게 되는 위치에 문서로 남아 있다:
| 레이어 | 역할 | 에이전트가 여기서 안전하게 작업할 수 있는 이유 |
|---|---|---|
main/ | 배선(wiring) 전용. 각 기능 모듈의 URL 라우트를 등록한다. | 새 기능을 추가하는 것은 폴더 하나와 include 한 줄이면 된다. 잘못 지어낼 여지가 거의 없다. |
apps/<feature>/ | 기능별로 폴더 하나: 라우트, 페이지, 서비스, 스토어, 테스트. | 수정의 영향 범위가 해당 폴더로 제한된다. 형제 임포트는 리뷰 코멘트가 아니라 빌드 실패로 이어진다. |
adapters/ | 외부 세계: 웹 렌더링, 데이터베이스, 오브젝트 스토리지. | 모든 I/O는 시스템별로 하나의 접점(seam)을 통해서만 이루어진다. 모든 접점에는 페이크(fake)가 존재한다. |
libs/ | 공유 가능한 설치형 라이브러리. | 서드파티 코드처럼 버전이 관리된다. 변경 시 반드시 눈에 띄게 드러난다. |
의존성은 언제나 이 목록의 아래 방향으로만 향하며, 그 방향은 관례가 아니라 빌드 체크로 강제된다.
코드베이스를 의도적으로 에이전트가 읽기 쉽게 만드는 세 가지 추가 특성이 있다:
- 모든 디렉터리는 자체 문서를 갖추고 있다 (해당 서비스가 하는 일, 레이어링 규칙, 머지를 가로막는 정확한 커맨드 등). 그래서 처음 접하는 에이전트도 질문하거나 스스로 지어내지 않고 방향을 잡을 수 있다.
- 사소하지 않은 모듈에는 설계 문서로 연결되는 백링크가 담긴 독스트링이 있다. 그래서 무엇을 하는지뿐 아니라 왜 그런지도 함께 남는다.
- 어떤 파일도 500줄을 넘을 수 없다, 몇 가지 예외 케이스에는 래칫(ratchet)이 걸려 있다. 이 예외들에서 파일은 줄어들 수만 있고, 다시 늘어날 수는 없다. 컨텍스트 윈도우는 유한하며, 아키텍처는 이 사실을 존중한다.
진행 상황은 작은 방 안에 갇혀서는 안 된다
오늘날 LLM 에이전트는 2025년보다 훨씬 강력해졌고, 우리는 Muse의 개발이 완전히 자율적이기를 원한다. 즉, 사람은 에이전트에게 작업을 가리키기만 하면 된다. 에이전트는 코드베이스와 작업 맥락 속에서 스스로 방향을 잡고, 구현과 CI, 코드 리뷰를 거쳐 마침내 풀 리퀘스트를 스스로 운전해 머지까지 이끈다. 사람은 오직 이슈의 우선순위를 정하고, 핵심 설계 결정을 내릴 뿐이다.
완전 자율 개발이라고 하면 흔히 처음 떠올리는 것은 전용 플랫폼들이다: 오케스트레이션 콘솔, 에이전트 네이티브 워크스페이스, 에이전트 함대를 관리해주겠다고 약속하는 앱들이 끝없이 이어진다. 우리도 살펴보았지만, 채택하지 않았다. 그 제품들이 훌륭하지 않아서가 아니라, 또다시 가독성(legibility) 때문이다.
우리는 코드베이스가 에이전트에게 읽기 쉬워야 하는 것과 마찬가지로, 에이전트의 작업 또한 사람이 읽기 쉽기를 원한다. 우리는 어느 정도 규모가 있는 회사다. 자율 개발을 전용 콘솔 안에 가두면, 그 진행 상황은 그 콘솔 안에서 살다시피 하는 소수의 사람에게만 보이게 된다.
그 결과 여기서도 다시, 협업 및 개발 도구 스택은 의도적으로 평범하다. 이 글에서 다룬 모든 과정은 모든 소프트웨어 회사가 이미 열어두고 있는 네 가지 도구, 즉 Slack, Linear, GitHub, Claude Code 위에서 돌아갔다. 덕분에 에이전트가 만드는 진행 상황은 다른 팀원의 작업과 정확히 똑같은 방식으로 관찰하고 검색할 수 있다. 프로덕트 매니저는 언제나처럼 Linear 이슈를 열어 기능 상태를 확인한다. GitHub 버그 봇이나 보안 리뷰는 언제나처럼 PR 스레드를 읽는다. 버그를 발견한 누구든 언제나처럼 같은 Slack 채널에 스크린샷을 붙여넣고, 수정 사항은 평범한 풀 리퀘스트로 나타난다. 이 "기계"를 감독하기 위해 누구도 새로 배워야 할 것이 없다.
다음은 하나의 자율 이슈가 처음부터 끝까지 진행되는 모습이다:
![]()
하나의 자율 이슈가 처음부터 끝까지 진행되는 과정. 사람은 이 루프에서 정확히 두 지점에서만 개입한다.
사람은 이 루프에서 정확히 두 지점에서만 개입한다. 에이전트에게 Linear 이슈를 가리켜주는 것, 그리고 에이전트가 코드를 작성하기 전에 제시하는 몇 가지 결정 사항들이다. 그 사이의 모든 과정, 즉 구현, 체크, 리뷰 대화, 머지, 배포는 아무도 자리에 앉아 있지 않은 채로 진행된다. 체크가 실패하거나 리뷰어가 이의를 제기하면, 에이전트는 실패 내용을 읽고 수정한 뒤 다시 푸시하고, 루프는 그저 한 바퀴를 더 돈다. 이 루프는 브랜치가 그린 상태가 되고, 모든 발견 사항이 해소되고, 큐가 이를 머지했을 때에만 종료된다.
이를 가능하게 하는 것은 전용으로 만든 통합이 아니다. 세 도구가 이미, 사람이 사용하는 것과 동일한 명령 인터페이스를 에이전트가 직접 조작할 수 있도록 노출하고 있기 때문이다:
- GitHub, CLI를 통해. 에이전트는 풀 리퀘스트를 열고, 각 CI 잡의 상태와 로그를 읽어들이고, 리뷰 스레드에 답변한 뒤 이를 해결됨으로 표시하고, 브랜치를 머지 큐에 등록한다. 이 모든 과정이 평범한
gh커맨드로 이루어진다. - Linear, MCP 서버를 통해. 에이전트는 지정받은 이슈를 읽고, 맥락을 위해 연관 이슈로 이어지는 링크를 따라가고, 상태를 옮기고, 진행 중에 발견한 후속 작업에 대한 이슈를 등록한다. 이 모든 것이 일급(first-class) 툴 콜로 이루어진다.
- Claude Code 자체의 모니터 루프. CI는 몇 분이 걸리고 머지 큐는 그보다 더 오래 걸리는데, 이를 지켜보는 사람은 없다. 대신 에이전트가 지켜보며, 바로 그 지켜보는 방식이 무인 실행을 토큰만 태워버리는 낭비가 아니라 실용적인 것으로 바꿔준다.
에이전트는 풀 리퀘스트를 열어놓고 페이지를 새로고침하며 앉아 있지 않는다. 자신이 무엇을 기다리고 있는지, 즉 CI 실행, 리뷰 스레드, 머지 큐 등록 상태를 기록해두고 턴을 마친다. Claude Code의 Monitor 기능은 그 지켜보고 있는 상태가 변할 때에만, 즉 잡이 빨간불이 되거나, 리뷰 패널이 발견 사항을 게시하거나, 큐가 브랜치를 머지했을 때에만 에이전트를 다시 호출한다. 에이전트는 각 상태 업데이트에 대한 Monitor의 메시지로 깨어나기 때문에, 그에 맞춰 다음 행동을 취한다. 예를 들어 실패한 잡의 로그를 가져와 수정을 푸시하거나, 리뷰 스레드에 답변한 뒤 해결됨으로 표시하거나, 모든 게이트가 그린이 되면 브랜치를 큐에 등록하는 식이다.
이 중 어느 것도 Muse만을 위한 것이 아니다. 이 세 도구로 접근할 수 있는 저장소라면 어디든 같은 방식으로 운전할 수 있으며, 바로 그것이 우리가 이 도구들을 선택한 이유다.
신뢰는 빌드 산출물이다
우리는 LLM 자체를 신뢰하지 않는다. 하니스(harness) 안에 있는 LLM 에이전트를 신뢰하며, 이 하니스는 신뢰성의 수호자로서 엔지니어링될 수 있다. 구체적으로 이 하니스는 우리의 CI/CD 파이프라인과 그 위에 얹힌 리뷰 레이어다. 이것이 모든 풀 리퀘스트가 예외 없이 거쳐야 하는 과정이다:
![]()
모든 풀 리퀘스트는 동일한 하니스를 통과한다: 먼저 자동화된 체크, 그다음 AI 리뷰 패널.
각 잡과, 최근 실제 PR 실행에서 측정된 일반적인 소요 시간은 다음과 같다:
| 잡 | 무엇을 강제하는가 | 일반적인 소요 시간 |
|---|---|---|
| Guardrails | 레이어링(형제 임포트 금지), 폴더 구조, 파일 크기 상한선, 네이밍 | ~60 s |
| Lint | ruff 포맷 및 린트 규칙 | ~30 s |
| Type check | 프로젝트 전체에 대한 pyrefly | ~20 s |
| Unit + integration tests | 스키마로부터 구축된 실제 Postgres에 대한 pytest | ~90 s |
| Visual tests | CI 자체 렌더러에서, 커밋된 골든 이미지와 대조해 비교하는 Playwright 스크린샷 | ~140 s |
| Security | semgrep 정적 분석과 시크릿 스캔 | ~40 s |
| Container boot test | 앱은 반드시 부팅되어야 하며, 모든 라우트가 호출 권한을 선언하지 않으면 부팅을 거부한다 | ~215 s |
가드레일은 우리가 앞서 설명한 아키텍처를 기계가 강제하는 형태로 옮긴 것이다. 여기서 형제 임포트는 리뷰 코멘트가 아니라 빨간 빌드다. 비주얼 골든 이미지는 CI 자체의 렌더링 환경에서 캡처되므로, "내 컴퓨터에서는 되는데요"라는 말은 버그를 main에 머지하는 근거가 될 수 없다.
흥미로워지는 지점은 AI 리뷰 레이어다. 모든 PR은 우리 자체의 3인 코딩 에이전트 리뷰어 패널이 읽는다. 이들은 각기 다른 페르소나를 부여받은 채, 같은 diff를 저장소에 문서화된 설계 원칙에 비추어 읽는다. 여기서는 각 리뷰어에게 주어지는 프롬프트의 개요를 보여준다.
프린시펄 엔지니어(Principal Engineer):
페르소나: 프린시펄 엔지니어, 설계 리뷰, 넓은 스코프
당신은 시스템 스코프에서 리뷰한다: 한 줄이 깔끔한지가 아니라, 이 변경이 있어야 할 자리에 있는지, 아키텍처에 들어맞는지, 이미 존재하는 것을 재사용하는지, 그리고 애디티브(additive)한 상태를 유지하는지를 본다.
당신은 diff와 Guides만 보며, 저장소 전체를 보지는 않는다. 재사용과 패턴 일관성을 판단하되 ...; 수행할 수 없는 저장소 전체 검색을 했다고 주장하지 말라.
당신의 렌즈: 제공된 Guides의 다음 부분에 비추어 diff를 판단하라 ...
리트머스 테스트: "내가 이 코드베이스의 프린시펄 엔지니어라면, 엉뚱한 자리에 있다는 이유로, 기존 기능을 중복 구현한다는 이유로, 또는 이미 해결된 문제에 두 번째 방식을 추가한다는 이유로 이 PR을 돌려보낼 것인가?" 아니라면
[]를 반환하라.
시니어 엔지니어(Senior Engineer):
페르소나: 시니어 엔지니어, 로직 / 구현 / 품질
당신은 변경 내부를 리뷰한다: 로직이 올바른지, 깔끔한지, 테스트 가능한지, 안전한지를 본다. 이것은 가장 꼼꼼한 리뷰이니, 코드 경로를 끝까지 추적하고 대충 훑어보지 말라. diff와 그것이 건드리는 파일들에 집중하라(전체를 읽어라).
당신의 렌즈: 제공된 Guides의 다음 부분에 비추어 diff를 판단하라 ...
리트머스 테스트: "이 로직을 승인할 것인가, 아니면 꼼꼼히 읽었을 때 버그나 테스트 불가능한 이음매(seam), 혹은 보안상의 실수를 발견할 것인가?" 실패하는 입력이나 코드 경로를 구체적으로 지적하라. 로직이 타당하다면
[]를 반환하라.
QA 엔지니어(QA Engineer):
페르소나: QA 엔지니어, 작동하는가, 테스트되었는가, 깨질 것인가
당신은 동작과 위험을 리뷰한다: 이 변경이 주장하는 대로 동작하는가, 테스트로 커버되는가, 라이브 환경이나 프로덕션에서 무언가를 깨뜨릴 수 있는가? PR 제목과 설명을 읽은 뒤, diff가 이를 실제로 이행하는지 확인하라.
당신의 렌즈: 의도 일치 여부 ...; 테스트 커버리지 ...; 리그레션 위험 ...; 프로덕션 함정 ...
리트머스 테스트: "내가 승인 서명을 하는 QA라면, 주장한 대로 동작하지 않는다는 이유로, 테스트되지 않은 동작을 출시한다는 이유로, 혹은 프로덕션을 깨뜨리는 변경이라는 이유로 이 PR을 막을 것인가?" 안전하고 커버리지가 충분하다면
[]를 반환하라.
동일한 코드 diff를 서로 다른 각도에서 살펴보는 것 외에도, 이 3명의 리뷰어는 전체 코드베이스의 개발을 지배하는 공통 규칙 세트, 즉 Guides를 공유한다:
- 함께 변경되는 것들은 함께 산다. 관련 없는 것들은 분리 가능한 상태로 남는다. 하나의 관심사를 변경하거나 삭제하면 오직 한 곳만 건드리게 된다.
- 비즈니스 로직은 외부 세계의 세부사항에 의존하지 않는다. 비즈니스 로직 함수는 실제 I/O 없이 페이크(fake)를 주입받아 테스트에서 실행된다. 백엔드를 교체해도 비즈니스 로직은 전혀 건드릴 필요가 없다. 앱은 결코 자기 자신의 HTTP 엔드포인트를 호출하지 않는다.
- 재발명하지 말고 재사용하라. 각 기능은 구현체가 하나뿐이며, 새로운 소비자는 이를 복사하는 대신 기존 인터페이스를 호출한다.
- 기능은 편집이 아니라 추가로 더한다. 기능을 추가하는 것은 "새 파일 + 등록 한 줄"이며, 이를 삭제해도 다른 기능은 전혀 건드리지 않는다.
- 각각의 일을 처리하는, 확립된 방식은 하나뿐이다. 새로 온 사람은 무언가를 선택하는 대신 기존 패턴을 그대로 따라 하면 되고, 테스트에는 서버나 DB가 필요 없다.
- 코드는 더 이상 쓰이지 않는 순간 삭제한다. 아무도 호출하지 않는 코드는 출시되지 않는다. "혹시 몰라서 남겨둔다"는 이유가 될 수 없다. git이 기억하고 있기 때문이다.
- 작동하는 가장 단순한 것을 선호하라. 복잡성은 어쩔 수 없을 때만 더하라. 모든 추상화는 지금 당장의 구체적인 필요로 그 존재 이유를 증명해야 한다. 변경은 문제를 해결하는 가장 작은 크기여야 한다.
패널은 머지를 가로막는 각 발견 사항을 정확한 라인에 고정된 인라인 코멘트로 게시하며, 그 코멘트들은 해소될 때까지 머지를 막는다.
최근 기능 PR에서 나온 리뷰는 다음과 같은 모습이다:
[medium] 리그레션 위험. 이 함수는 공유되는 create-and-sync 병목 지점(chokepoint)으로, 이제 view 접미사가 붙은 파일들을 하나의 그룹화된 행으로 합쳐버린다. 기존 방식으로 수집된 모든 사전 존재 데이터셋은 다음 동기화 시 조용히 재그룹화되어, 라이브 평가들이 여전히 참조하고 있는 기존 샘플 아이덴티티를 삭제하게 된다. 새로운 그룹화 방식으로 기존 데이터셋을 재동기화하는 경우를 다루는 테스트는 하나도 없다.
그 발견 사항은 정확했다. 코드는 전체 테스트 스위트를 통과했고 첫 번째 CI도 그린이었다. 테스트는 작성된 코드에 대해서는 옳았다. 리뷰어는 작성된 코드가 우리가 실제로 가진 데이터에 대해서는 틀렸다는 것을 알아챘다. Claude는 이를 수정하고, 누락되었던 리그레션 테스트를 추가하고, 스레드에 답변한 뒤 발견 사항을 해결 처리했다. 이 모든 대화는 PR에 그대로 공개되어 있으며, 이는 코드 리뷰를 통해 주니어 엔지니어를 멘토링하는 방식과 정확히 같다.
이것은 하나의 PR에서 있었던 일이다. 시간이 지나며 에이전트를 계속 운영하는 과정에서, 우리는 몇 가지 다른 전형적인 패턴도 발견할 수 있었다:
- 수정이 새로운 문제를 낳은 경우. 우리가 출시한 진단 도구는 다운로드 속도를 측정했다. 리뷰는 계산식이 MB(1,000,000바이트)를 사용했지만 서버는 MiB(1,048,576바이트)를 보내고 있어, 모든 속도 값이 5% 낮게 표시되었을 것이라는 점을 짚어냈다. 몇 번의 푸시가 이어진 뒤, 리뷰는 이 수정이 손으로 동기화해야 하는 상수 두 개를 남겨두었다는 점을 지적했다. 그래서 대신 실제로 받은 바이트 수로부터 속도를 계산하도록 요청했다. 리뷰어는 앞선 요청을 반영하는 과정에서 만들어진 버그를 잡아낸 것이다.
- 막다른 유스케이스. 같은 페이지가 출시되었을 때, 정작 그 페이지 자체는 특정 권한이 필요함에도 링크는 로그인한 모든 사용자에게 노출되었다. 해당 권한이 없는 사람은 누구든 클릭하면 403 에러를 맞닥뜨리게 된다. 링크는 정상적으로 나타났고 권한 게이트도 제대로 작동했기 때문에 테스트는 하나도 실패하지 않았다. QA 리뷰어는 이 둘을 비교해, 실제로 페이지를 열 수 있는 사람에게만 링크가 보이도록 고쳐질 때까지 머지를 막았다.
- 테스트를 통과시키기 위한 편법. 한 수정 사항의 테스트는 자신이 잡아내야 할 예외를 스스로 만들어내고 있었다. 그래서 실제 에러 처리가 망가져 있어도, 테스트가 "올바른" 예외 메시지를 그대로 받았기 때문에 통과해버렸다. 패널이 이를 지적했고, 다시 작성된 테스트는 실제 코드를 통해 실제 에러를 흘려보낸다. 같은 PR에는 개발자의 컴퓨터에서는 통과하지만 CI에서는 실패하는 테스트도 있었는데, 우리 개발용 기기에만 존재하는 자격 증명(credentials) 파일에 은근히 의존하고 있었기 때문이다. 둘 다 테스트 자체에 있던 버그였고, 그 위 레이어에 의해 잡혔다.
우리의 머지 정책은 AI 리뷰 발견 사항 0건이다. 큐가 PR을 받아들이기 전에, 모든 발견 사항은 수정되거나 근거를 들어 반박되어야 한다. 리뷰 패널의 과잉 플래깅 비율이 감당할 만한 수준이기 때문에, 우리는 반박을 유효한 선택지로 만들었다. 발견 사항이 왜 틀렸는지를 글로 적어보는 과정은, 거꾸로 리뷰 패널 자신의 추론에 있는 실수를 이따금 잡아내기도 한다.
체크를 모두 통과한 뒤, 머지 큐는 계속 움직이는 main의 최신 지점(tip)에 대해 모든 브랜치를 재검증하며, 머지가 이루어지면 사람 개입 없이 자동으로 프로덕션에 배포된다.
![]()
모든 게이트를 통과한 뒤, 머지 큐가 브랜치를 재검증하고 머지가 프로덕션에 배포된다.
코드 줄 수는 이야기의 작은 일부일 뿐이다
수많은 자율 주행 PR을 거치며, 우리는 놀랍지만 합리적인 패턴을 발견했다: 체크와 리뷰를 반복하는 데 걸리는 시간이 첫 구현을 작성하는 데 걸리는 시간보다 훨씬 길다는 것, 그리고 그 비율이 작업 유형에 따라 크게 달라진다는 것이다. 여기서는 같은 주에 진행된 두 가지 유형의 개발 작업을 비교해본다.
케이스 A: 죽은 기능 폐기. 작은 레거시 기능이 오프라인 처리되었다. 그 기능의 링크, 확인 대화상자, 번역, 테스트, 스크린샷 베이스라인이 모두 제거되어야 했다. Linear 이슈가 접수된 시점부터 PR이 main에 머지되기까지 단 16분이 걸렸고, 그중 처음 2분은 코드를 작성하는 데 쓰였다. 리뷰는 참고용(advisory-only)에 그쳤다.
케이스 B: 네트워크 진단 기능의 주요 개선. 라인 수 기준으로는 비슷한 규모의 변경이었고 첫 버전은 5분 만에 완성되었지만, 머지까지 걸린 총 시간은 약 3시간이었다. 그 과정에서 에이전트는 5차례의 리뷰 라운드와 머지를 가로막는 7건의 발견 사항을 해결했다.
타임라인 역시 상당히 다르다:
| 케이스 A: 죽은 기능 폐기 | 케이스 B: 진단 도구 개선 | |
|---|---|---|
| 변경 규모 | +10 / -125, 8개 파일 | +439 / -19, 9개 파일 |
| 첫 동작 버전 | 2분 | 5분 |
| 이슈 접수부터 머지까지 | 16분 | 3시간 |
| 첫 버전 대비 머지까지 얼마나 더 걸렸는가 | 8x | 36x |
| 머지를 막은 리뷰 발견 사항 | 없음(참고용) | 7건, 5차례 라운드에 걸쳐 |
| 코드가 잘못되었을 때의 최악의 시나리오 | 아무도 클릭하면 안 되는 링크 | 모든 사용자가 잘못된 숫자를 신뢰한다 |
![]()
라인 수는 비슷하지만, 위험에 비례해 검증 노력이 투입된 두 작업.
두 번째 작업이 열한 배나 더 오래 걸린 이유는 코드를 작성하기가 더 어려워서가 아니라, 위험이 달랐고 그 위험에 비례해 검증 노력이 투입되었기 때문이다. 케이스 A는 삭제 작업이었다: 최악의 결과라 해봐야 애초에 방문해서는 안 되는 깨진 링크였다. 케이스 B는 측정 도구였다: 최악의 결과는 모든 사용자가 잘못된 숫자를 신뢰하게 되는 것이었다. 리뷰 패널은 오류를 찾아낸 뒤, 에러 처리가 타입까지 갖추고 완전하며 철저히 테스트될 때까지 부분적인 수정을 세 차례나 더 거부했다. 라운드를 거듭할수록 구현은 엄격하게 더 견고해졌다. 이 결함들 중 어느 것도 테스트를 실패시키지 않았고, 모두 그대로 출시되었을 것이다.
바로 이 지점이 원샷(one-shot) 코드 생성이 구조적으로 놓치는 부분이다. 원샷 생성은 가장 눈에 잘 띄는 지표, 즉 프롬프트에서 컴파일되고 실행되는 코드까지 걸리는 시간을 최적화한다. 우리의 두 케이스 모두 이 지표를 몇 분 만에 달성했다. 만약 우리가 거기서 멈췄다면, 케이스 B는 시작한 지 몇 분 만에 완료된 것으로 처리되었겠지만, 몇 달 동안 잘못된 채로 남아 있었을 것이다.
삭제 작업을 16분 만에 통과시켜준 것과 같은 파이프라인이, 측정 도구는 3시간 동안 붙잡아두었다. 어떤 작업이 어떤 처리를 받아야 하는지 결정하는 사람은 없었다. AI가 작성한 코드를 프로덕션에서 신뢰할 수 있느냐고 사람들이 물을 때, 답은 이것이다: 코드는 그것을 둘러싼 검증만큼만 신뢰할 수 있다. 그리고 이제 검증의 깊이는 엔지니어의 인일(person-day)이 아니라 에이전트의 시간(agent hours)으로 사들이는 것이 되었다. 잘못된 속도 리포트는, 누군가 그것을 근거로 네트워크 불만을 처리해버리는 순간, 3시간보다 훨씬 큰 대가를 치르게 된다.
우리가 배운 것과 다음 단계
오늘날 자율성은 모델의 속성이라기보다는 하니스의 속성에 가깝다. 모델이 업그레이드될 때마다 우리 에이전트는 더 나아졌다. 그러나 모델의 능력 그 자체가, 에이전트가 자유롭게 풀 리퀘스트를 머지하도록 신뢰해도 된다는 보장을 함께 가져다주지는 않는다. 사람의 관심을 더 많이 투입하거나 가장 비싼 최신(frontier) 모델을 무작정 쓰는 대신, 우리는 에이전트가 올바른 프로덕션 코드를 만드는 데 필요한 모든 것을 갖추도록 "손"과 "눈"을 만드는 쪽을 택했다. 그리고 모델의 능력이 계속 진화함에 따라, 이는 우리가 모델의 한계를 메우기 위해 발판(scaffold)을 다시 짓는 대신 그 물결에 올라탈 수 있게 해준다.
사람의 역할은 사라지는 것이 아니라 응축된다. 남는 것은 더 이상 줄일 수 없는 핵심이다: 무엇을 만들 가치가 있는지 결정하고, 그것을 충분히 명확하게 글로 적고, 기계가 모호하다고 표시하는 순간에 판단을 발휘하는 것. 설계 감각과 스펙(specification)의 품질은 이제 이 팀에서 레버리지가 가장 높은 엔지니어링 역량이다.
그리고 에이전트 팀에 관해 가장 흥미로운 통찰은 팀의 가치가 협업이 아니라 갈등에서 나온다는 것이다. 풀스택 작업을 프론트엔드 에이전트와 백엔드 에이전트로 나누는 것은 말이 될 수 있다. 하지만 작업을 완료하기 위해 프론트엔드 에이전트와 백엔드 에이전트가 서로 대화를 나누게 하는 것은 그다지 말이 되지 않는다. 팀의 이점은 구성원들이 서로 상충하는 목표를 가질 때 나타난다. 우리 Muse 개발 에이전트는 코드를 출시하고 싶어 한다. 우리 AI 리뷰 에이전트는 코드가 어디서 잘못되었는지, 혹은 설계가 부실한지를 찾아내고 싶어 한다. 바로 이들 사이의 긴장이 결과를 신뢰할 수 있게 만들고, 개발 작업에 실제로 얼마만큼의 작업량이 들어가는지를 있는 그대로 드러낸다.
더 중요한 것은, 우리의 탐구가 소프트웨어를 개발하는 새로운 방식, 즉 자율 엔지니어링 조직을 향해 나아가고 있다는 점이다. 우리는 에이전트가 기존 도구만으로 개별 작업을 자율적으로 완수할 수 있음을 보여주었지만, 아직 몇 가지 빠진 조각과 열린 질문이 앞으로의 도전을 기다리고 있다:
- 이런 시스템을 얼마나 더 크게 확장할 수 있을까? 자율 조직이 더 많은 병렬 에이전트와 함께 점점 더 큰 작업과 프로젝트를 다루게 될 때, 어떤 새로운 문제가 나타날까?
- 첫 시도 CI 통과율이나 AI 리뷰 발견 사항 같은, 하니스 자체의 산출물이 어떻게 평가 신호가 될 수 있을까? 조직 안에서 에이전트의 성능을 어떻게 측정하고, 어떻게 스스로 진화하도록 만들 수 있을까?
- 사람의 설계 감각과 제품 취향을 이런 시스템에 어떻게 녹여낼 수 있을까? 만약 엔지니어링이 드문드문한 사람의 판단만으로 자율화될 수 있다면, 우리는 누구를 위해 소프트웨어를 만들어야 하는지, 그리고 그것이 어떤 모습이어야 하는지의 문제를 해결하기 위해 이를 얼마나 더 멀리까지 밀어붙일 수 있을까?
끝까지 읽어주셔서 감사하다. 이 질문들에 대한 답을 찾기 위한 할 일 목록은, 어울리게도 Linear 안에 남아 있다. 그곳에서 우리의 다음 에이전트, 혹은 당신이 그것을 집어 들지도 모른다.







