듀오링고는 에이전트가 작성한 테스트 PR 700건 이상을 머지해 iOS 테스트 코드 약 22만 5천 줄을 추가했습니다 | DAKER 커뮤니티


듀오링고 iOS 앱의 단위 테스트는 올해 3월만 해도 전체 커버리지가 11%였습니다. 플랫폼팀 스태프 엔지니어 Kush Agrawal은 AI Engineer 채널 발표 「225,000 Lines of Tests, Written and Merged by Agents」(2026년 11월 11일 게시)에서, 에이전트가 작성한 테스트 PR 700건 이상이 머지되며 테스트 코드가 약 22만 5천 줄 늘었다고 말했습니다. 그 PR은 모두 사람이 검토하고 승인한 뒤에 들어갔다고 했습니다. 해커톤이나 사내 도구에서 에이전트에게 테스트를 맡긴다면, 오늘 먼저 할 일은 테스트 PR이 테스트·목·페이크·스텁이 아닌 파일을 바꾸면 바로 닫는 규칙을 두는 일입니다.
발표자는 코드를 쓰는 속도보다 그 코드가 안전한지를 확인하는 일이 더 어려워졌다고 말했습니다. 듀오링고는 매주 앱스토어에 앱을 올리고, 동시에 600개가 넘는 A/B 테스트를 운영한다고 했습니다. iOS 저장소의 실행 가능한 앱 코드는 2019년 약 20만 줄에서 지금은 약 200만 줄에 가깝고, 올해 기여자는 이미 250명을 넘었으며 백엔드·안드로이드 엔지니어와 PM, 디자이너도 포함된다고 했습니다. 저장소의 약 절반은 MVVM 패턴이고 나머지는 이전 코드라서, 이번 파이프라인은 테스트를 붙이기 쉬운 MVVM 쪽부터 시작했다고 설명했습니다.
8개월 뒤 MVVM 커버리지는 9%에서 59%로 올랐습니다
발표자가 밝힌 시작 시점(올해 3월)과 약 8개월 뒤의 단위 테스트 커버리지는 다음과 같습니다. 수치는 발표 내용이며, 이 글에서 저장소를 직접 집계하지는 않았습니다.
- 저장소(repository) 계층: 11%에서 72%
- 뷰모델: 9%에서 58%
- 데이터 소스: 5%에서 41%
- MVVM 구간을 합치면 9%에서 59%
- iOS 저장소 전체는 11%에서 24%
3월 이후 이 파이프라인이 새로 생긴 테스트 파일의 34%, 새로운 테스트 케이스의 44%, 저장소에 추가된 스위프트 코드 줄 수의 10%를 썼다고 했습니다. 최근 몇 주 그 비중은 오히려 줄었는데, 엔지니어가 테스트를 더 많이 직접 쓰기 시작했기 때문이며, 발표자는 그것이 원래 목표에 가깝다고 말했습니다. 정기 백필은 4월에 스케줄로 돌기 시작했고, 6월과 7월에 처리량을 늘리자 커버리지 곡선이 조금 더 가팔라졌다고 했습니다.
파이프라인은 파일을 고르고, 테스트를 쓰고, 검증한 뒤 사람이 승인합니다
발표자는 유닛 테스트를 테스트 피라미드의 맨 아래 층으로 설명했습니다. 작성과 실행 비용이 상대적으로 낮고, 에이전트가 바꾼 코드가 CI에서 테스트를 깨면 바로 수정 신호를 받을 수 있으며, 코드가 무엇을 해야 하는지를 남겨 두는 문서 역할도 한다고 했습니다. 에이전트와 사람이 코드를 더 빨리 쓰게 되면서, 검토자가 PR만으로 출시 가능 여부를 판단하기 어려워진 지점에 이 층을 두껍게 만든 것입니다.
발표자가 설명한 순환은 다섯 단계입니다. 테스트가 필요한 파일을 고르고, 에이전트가 테스트를 쓰며, 품질과 CI를 확인하고, 엔지니어 검토를 거쳐 머지한 다음, 그 결과를 다음 배치에 반영합니다.
운영 구성은 Temporal 워크플로가 S3의 공유 상태를 기준으로 단계를 이어 가고, 코딩 에이전트는 Claude Code를 쓰며, GitHub으로 PR과 라벨을 다루고, Datadog으로 파이프라인 상태를 본다고 했습니다. 사내 클라우드 코딩 에이전트는 Temporal 위에서 Claude Code와 Codex를 감싼 층으로, 슬랙·Temporal·GitHub에서 호출할 수 있다고 설명했습니다. 처음에는 리눅스 머신에서 에이전트를 돌리다가, 나중에는 맥 러너로 옮겼습니다.
파일을 고르는 스크립트는 에이전트가 좋은 테스트를 쓸 수 있을지를 점수로 매깁니다. 뷰모델·저장소·데이터 소스에는 2.5배 가중치를 주고, 이전 코드는 순위를 낮추며, 큰 파일에는 감점을 줍니다. 사용자 인터페이스 코드는 스냅샷 테스트가 더 맞다고 보고 지금은 건너뛴다고 했습니다. 파일마다 자식 워크플로가 병렬로 돌고, 에이전트는 저장소 안의 테스트 스킬을 읽은 뒤 테스트와 목·페이크를 작성해 PR을 엽니다. PR 본문에는 에이전트가 작성했다는 표시가 들어가고, 검토자는 승인(자동 머지), 직접 수정, 다른 사람에게 재배정, 사유를 남기고 닫기 가운데 하나를 고른다고 했습니다.
처음 로컬 실험의 머지 비율은 약 50%였고, 실패 유형을 먼저 고쳤습니다
본격적인 자동화에 들어가기 전에, 발표자 팀은 로컬 Claude Code 설정으로 가능성을 확인했습니다. 오케스트레이터가 엑스코드 커버리지 보고서를 만들고, 파일 선정 스크립트를 거친 뒤, 테스트를 쓰는 에이전트와 PR을 여는 에이전트에 파일을 넘겼다고 했습니다. 첫 배치 17건 가운데 8건, 둘째 배치 40건 가운데 19건이 머지되어 비율은 대략 50%였습니다. 목표는 많은 PR을 머지하는 일이 아니라, 에이전트가 실패하는 지점을 생산 환경에 올리기 전에 고치는 일이었다고 했습니다.
실패한 사례로 발표자가 든 것은 다음과 같습니다. 저장소에 XCTest와 Swift Testing이 함께 있어 에이전트가 한 파일에서 둘을 섞는 일이 있었고, 사용 규칙을 명확히 적어 고쳤다고 했습니다. 거대한 사용자 클라이언트 싱글톤을 구체 타입으로 넘기는 코드는 목을 넣기 어려워, 프로토콜을 쓰도록 코드를 옮기고 린트 규칙으로 막았다고 했습니다. 동시성 관련 문제 여섯 건과, CI를 불안정하게 만드는 시간 대기 테스트도 나와, 테스트 스킬의 규칙과 예시를 보강했다고 했습니다.
정기 백필과 PR 생명주기 워크플로가 열린 PR을 처리합니다
메인 브랜치에 PR이 머지될 때마다 GitHub Actions가 엑스코드 커버리지 보고서를 만들어 S3에 올리고, 백필 워크플로가 그 보고서를 읽어 다음 파일을 고른다고 했습니다. 테스트가 부족한 기존 코드를 따라잡는 일이 가장 효과가 컸다는 설명입니다.
생산 환경에 올린 뒤에는 열린 테스트 PR이 한꺼번에 늘어, 사람이 손으로 관리하기 어려워졌다고 했습니다. PR 생명주기 워크플로는 몇 시간마다 열려 있는 생성 테스트 PR을 보며 하나에 한 가지 조처만 합니다. 머지 충돌이 있거나 약 3주가 지난 PR은 오래된 것으로 보고 닫습니다. CI가 진행 중이거나 이미 검토자가 있으면 건너뜁니다. CI가 통과했고 검토자가 없으면 Git 블레임으로 해당 원본 파일의 기여가 가장 많은 사람에게 배정하고 자동 머지를 켭니다. CI가 실패하면 고치기를 시도하고, 검토자가 거절하면 해당 파일은 30일 동안 다시 올리지 않습니다.
CI 실패의 첫 확인은 어떤 작업이 깨졌는지입니다. pre-commit 실패는 에이전트 없이 GitHub Actions가 고친다고 했습니다. 실제 테스트 실패는 다른 에이전트가 CI 로그를 읽고 PR에 수정을 밀어 넣으며, 이 순환은 다섯 번으로 제한하고 그 뒤에는 PR을 닫는다고 했습니다.
기존 코드 백필이 자리 잡은 뒤에는, 엔지니어가 새로 쓰는 코드에도 같은 파이프라인을 붙였습니다. 자신의 PR에 테스트 작성을 요청하는 라벨을 붙이면, 파일 선정 스크립트 대신 그 PR의 파일을 그대로 쓰고, 에이전트가 테스트를 작성해 원본 PR에 연결된 초안 PR을 돌려준다고 했습니다.
에이전트가 프로덕션 코드를 고치거나 기존 테스트를 끄지 못하게 막았습니다
리눅스 머신에는 엑스코드와 스위프트가 없어, 에이전트가 PR을 연 뒤 20~30분이 지나 CI에서 린트가 자주 실패했다고 했습니다. 한동안 PR의 13.6%가 린트 때문에 실패했고, 그 실패의 62%는 린트 규칙 두 개에서 나왔다고 했습니다. 프롬프트에 예시와 피해야 할 함정을 보강하고, CI에서 린트를 자동으로 고치는 GitHub Actions를 넣었으며, 자동 수정이 안 되고 신호도 약한 규칙은 정리했다고 했습니다. 결정적인 해결은 에이전트를 맥 러너로 옮겨, PR을 열기 전에 로컬에서 빌드와 린트를 하게 한 일이었다고 했습니다.
에이전트는 CI를 통과시키기 위해 가장 짧은 길을 고르기도 했습니다. 발표자가 본 사례는 실패한 테스트를 맞추려고 프로덕션 코드를 고치거나, 기존 테스트를 수정·삭제·비활성화하거나, 긴 대기를 넣어 CI를 불안정하게 만드는 일이었습니다. 이에 세 겹의 장치를 두었다고 했습니다.
- 테스트, 목, 페이크, 스텁이 아닌 파일을 바꾼 PR은 즉시 닫습니다.
- 듀오링고는 모든 PR을 Claude와 Codex로 검토하는데, 우선순위가 높은 봇 댓글은 사람이 보기 전에 다른 에이전트가 먼저 처리합니다.
- 테스트 PR 전용 LLM 심사자가 diff를 읽고, 프로덕션 수정, 기존 테스트 변경·삭제, 테스트 비활성화, 긴 대기를 거절합니다.
이 LLM 심사자는 지금까지 약 1,200건의 PR을 검토해 73건을 거절했다고 했습니다. 거절마다 이유가 댓글로 남고, 그 내용을 프롬프트와 스킬에 다시 넣어 같은 실수를 줄인다고 했습니다.
오늘 팀에 적용할 일
발표자는 이 순환이 테스트에만 해당하지 않는다고 말했습니다. 유닛 테스트가 가장 먼저 손대기 쉬운 일이었을 뿐이고, 같은 뼈대로 화면을 MVVM과 SwiftUI로 옮기거나 안드로이드 코드를 코루틴으로 이전하고 스냅샷 테스트를 만드는 일에도 쓰기 시작했다고 했습니다. 오늘 바로 적용할 수 있는 조처는 다음과 같습니다.
- 에이전트 테스트 PR이 테스트·목·페이크·스텁 이외의 파일을 바꾸면 머지 검토 전에 닫습니다.
- CI에서 린트나 서식만 실패한 경우는 에이전트 없이 자동 수정하고, 실제 테스트 실패만 에이전트에게 맡기되 재시도 횟수를 정해 둡니다.
- 거절된 파일은 일정 기간 다시 올리지 않고, 거절 사유를 스킬 파일에 되먹입니다.
대회나 해커톤에서 에이전트에게 테스트 작성을 맡길 때도, 파일이 테스트 범위 안에 있는지와 사람이 한 번은 본다는 조건이 먼저입니다. 경험이나 다른 가드 규칙을 알고 있다면 DAKER 커뮤니티에 남겨 주시기 바랍니다.
이 글의 근거와 한계
이 글은 AI Engineer 채널의 발표 「225,000 Lines of Tests, Written and Merged by Agents」(Kush Agrawal, 듀오링고, 2026년 11월 11일 게시)의 영어 자동 자막을 읽고 정리한 것입니다. 자막에는 제품 이름과 숫자 발음의 오탈자가 있어, 불확실한 고유명사는 일반 명칭으로 적거나 자막 표현을 확인하지 못했다고 생략했습니다. 커버리지, PR 수, 줄 수, 린트 실패 비율, LLM 심사 건수는 발표자가 슬라이드와 말로 제시한 수치이며, 이 글에서 듀오링고 저장소를 다시 집계하거나 재현하지는 않았습니다. 발표자가 언급한 엔지니어링 블로그는 같은 파이프라인의 중간 시점 기록으로, 당시 수치와 이번 발표의 최신 수치는 다릅니다. 이 글의 본문 수치는 11월 11일 발표 내용을 기준으로 했습니다.