TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 20 READS

AI 코딩 시대, 병목이 된 CI를 시스템으로 다시 설계한 Linear의 방법

AI 코딩 시대, 병목이 된 CI를 시스템으로 다시 설계한 Linear의 방법
SOURCE IMAGE · HACKER NEWS

AI 에이전트가 코드 작성 속도를 끌어올리면서 개발 워크플로에는 예상치 못한 병목이 생겼다. 코드를 만들어내는 속도는 폭발적으로 빨라졌지만, 그 변경이 올바른지 검증하는 단계는 같은 속도로 따라오지 못했기 때문이다. 모든 PR은 결국 CI를 통과해야 하므로, 개발이 빨라질수록 CI는 인프라 비용을 밀어올리고 개발자와 에이전트가 피드백을 기다리는 시간을 늘리는 지점이 된다. 협업 도구 Linear가 공개한 사례는 바로 이 문제를 정면으로 다룬다. CTO가 "CI 비용이 높다"는 이슈를 할당하면서 시작된 작업은, 개별 명령어를 손보는 수준을 넘어 CI 자체를 하나의 시스템으로 재설계하는 방향으로 확장됐다.

결과부터 보면, 올해 초 이후 테스트 스위트 규모가 거의 네 배로 늘었음에도 PR 대기 시간을 6분 이상에서 5분 남짓으로 줄였고, 테스트당 러너 사용 시간은 대략 절반으로 낮췄다. 주목할 점은 최적화의 상당 부분이 특정 언어에 종속되지 않는다는 것이다. Linear의 코드베이스는 주로 타입스크립트지만, 접근 방식 자체는 다른 언어와 툴체인에도 적용할 수 있는 원칙에 가깝다.

기반부터 갈아끼우기

초기 성과 중 일부는 CI 로직을 거의 건드리지 않고 얻었다. 워크로드를 GitHub Actions에서 더 빠른 CPU와 고성능 스토리지, 개선된 캐시 인프라를 갖춘 서드파티 러너로 옮기자 같은 파이프라인이 평균 34% 빨라졌고, tsc 같은 일부 작업은 52%까지 단축됐다. 여기에 타입스크립트 네이티브 컴파일러인 tsgo로 전환하면서 tsc 검사의 주간 중앙값이 73% 줄어, 타입 검사는 더 이상 병목이 아니게 됐다. 린트도 초기 대상이었다. 일부 커스텀 린트 규칙이 타입 정보에 의존한 탓에 매번 전체 타입 그래프를 빌드해야 했는데, 이를 추상 구문 트리 기반의 정적 분석으로 다시 작성해 ESLint에서 타입스크립트 의존을 걷어냈다. API 린트 시간은 68%, 전체 저장소 린트는 55% 줄었고 메모리 사용량도 크게 감소했다. 규칙이 순수 구문 기반이 되면서 이후 Oxlint로의 이전도 한결 수월해졌다.

크리티컬 패스 위의 작은 작업들

인프라와 개별 검사가 빨라지자 Linear는 시야를 넓혀 CI를 하나의 시스템으로 바라봤다. 모든 실행 앞단에는 어떤 경로가 바뀌었는지, 같은 입력으로 이미 테스트가 통과했는지를 판단하는 작은 게이트 작업들이 있었다. 여덟 개의 API 테스트 샤드는 이 게이트가 끝나기 전에는 시작할 수 없어, 작은 지연이 불균형하게 큰 영향을 미쳤다. 이 게이트들이 필요하지도 않은 전체 작업 트리를 체크아웃하고 있던 점을 발견해 fetch 깊이를 제한하자 가장 느린 게이트가 94초에서 20초로 줄었고, 작업 트리가 아예 필요 없는 작업에서는 체크아웃을 제거해 27초를 7초로 낮췄다.

서드파티 러너가 GitHub 네트워크 밖에 있어 직접 IP 링크로 접속하다 보니, 이 링크의 간헐적 저하로 체크아웃이 멈추는 문제도 있었다. Linear는 actions/checkout을 자체 컴포지트 액션으로 교체해 지수 백오프 재시도를 넣고, 멈춘 연결이 약 30초 뒤 중단되도록 환경 변수를 설정했으며, 영속적인 git 미러를 두는 체크아웃 캐시를 활용했다. 또한 병합 전 최종 검사에서 쓰던 캐시 마커 기록을 크리티컬 패스 밖의 작업으로 옮겨, API PR마다 병합 경로에서 42초를 덜어냈다.

반복 비용을 없애고 병렬화를 밀어붙이다

다음 표적은 러너 부팅, 패키지 설치, 빌드 의존성 준비처럼 모든 작업에서 반복되는 셋업 비용이었다. 매번 apt로 Postgres 클라이언트를 설치하던 부분은 Node와 클라이언트가 포함된 CI 베이스 이미지로 옮겼고, pnpm 모노레포에서 전체 워크스페이스를 설치하던 것을 API 패키지로 한정해 설치 시간을 44~73초에서 16~18초로 줄였다. node_modules 캐싱은 캐시 히트조차 약 28초가 걸려 필터링된 설치(약 7.5초)보다 느려 오히려 걷어냈다. 이 조합으로 샤드당 셋업 시간이 약 44% 감소했다. 또한 스키마가 바뀌지 않았는데도 전체 마이그레이션 이력을 재생하던 방식을 스냅샷 로딩으로 바꿔 DB 셋업을 12초에서 1~2초로 줄였고, 각각 러너를 띄우던 일곱 개 검사를 두 개 작업으로 통합해 안에서 동시에 실행함으로써 6월 기준 월 약 8만 7천 러너-분, 전체 CI 사용량의 11.8%를 절약했다.

샤드당 고정 비용이 낮아지자 API 스위트를 더 공격적으로 병렬화할 여지가 생겼다. Vitest는 개별 테스트의 소요 시간이 아니라 파일 단위로 작업을 분배하는데, 유독 큰 파일 몇 개가 한 샤드를 장악해 전체 완료를 지연시켰다. Linear는 큰 파일을 구조를 유지한 채 잘게 나눈 뒤 샤드를 네 개에서 여덟 개로 늘려 크리티컬 작업을 약 19% 빠르고 저렴하게 만들었다. 가장 큰 성과는 파일마다 격리하던 Vitest 동작을 안전한 파일에 한해 isolate: false 옵트인 프로젝트로 묶어 워커 내에서 모듈 레지스트리를 공유하게 한 것으로, 월 기준 약 17% 절감 효과를 냈고 가장 느린 샤드가 300~379초에서 약 195초로 떨어졌다.

실무자가 새겨둘 지점

이 마지막 최적화는 성과가 가장 컸지만 정확성 리스크도 가장 높았다는 대목이 중요하다. Linear는 파일마다 명시적 옵트인 주석을 달고 공유 상태를 정리하는 teardown을 추가했으며, 가짜 타이머나 공유 상태를 안전하게 풀 수 없는 파일은 격리된 프로젝트에 그대로 남겼다. 특히 이제 테스트 대부분을 에이전트가 작성하기 때문에, 생성된 테스트가 같은 제약을 기본으로 따르도록 에이전트 스킬 자체를 갱신했다는 점은 AI가 코드를 쓰는 조직이라면 반드시 참고할 만하다. 더 잘게 쪼개는 샤딩이 이득이 되려면 샤드당 고정 비용이 낮아야 한다는 원칙도 명확하다. 셋업이 110~140초일 때 여덟 샤드였다면 셋업에만 15~19분을 썼겠지만, 40초 수준으로 낮춘 덕분에 여덟 샤드가 예전 네 샤드보다 총 셋업 시간을 덜 쓰면서 병렬성은 두 배로 늘렸다. 개선 노력이 없었다면 오늘의 스위트는 약 11분, 지금의 거의 두 배가 걸렸을 것이다. 주당 약 2천 개씩 테스트가 늘어나는 상황에서 CI를 빠르게 유지하는 일은 일회성 프로젝트가 아니라 지속적인 과제라는 결론은, 검증 속도를 방치한 채 코드 생산만 가속하는 팀에게 특히 시사하는 바가 크다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://linear.app/now/ci-bottleneck-reworked
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...