주요 내용
- LSC 프로세스는 되돌릴 수 없다고 여기던 기술적 결정들을 다시 생각하게 해줌
- 전통적인 리팩토링 모델은 코드 규모가 커지면 한계가 나타남
- 코드베이스를 오래 방치할 수록 고치기 힘들어지므로 꾸준히, 조금씩 개선 필요
구글은 일찍이 코드베이스 전반을 전면적으로 건드리는 변경을 원자적으로 수행한다는 아이디어를 포기했습니다. 이번 장에서는 거대한 구글 코드베이스가 기반 인프라의 변경을 유연하게 받아들일 수 있도록 해준 사회적 기법과 기술적 기법을 이야기 합니다.
대규모 변경(Large-scale change, LSC)이란 논리적으로 연관되어 있으나 현실적인 한계 때문에 원자적으로 서브밋할 수 없는 변경들의 집합입니다. 예시는 다음과 같습니다.
구글의 LSC는 거의 항상 자동화 도구를 이용해 생성합니다. LSC로 인해 생성되는 변경들은 대체로 다음과 같이 분류할 수 있습니다.
구글에서 수행하는 LSC의 대다수는 기능은 거의 변경하지 않습니다. 주로 명확성, 최적화, 미래 호환성 개선이 목표입니다.
구글에서는 LSC의 상당 비중을 인프라팀들이 수행합니다. 왜 인프라팀일까요?
첫째, 하부 시스템을 구축하고 관리하는 인프라팀들은 그 시스템을 활용하는 수만 개의 참조를 수정하는 데 필요한 도메인 지식 역시 갖추고 있습니다.
둘째, 변경에 대한 의지가 높은 집단(주로 인프라팀과 같은)이 맡는 것이 효율적입니다. 조직 전체의 관점에서 이득이 있는 대규모 변경을 개별 개발자에게 합당한 보상 없이 부탁한다면 좋아할 사람은 없을 것이므로 조직 차원에서 비용을 부담하는 것이 맞습니다.
셋째, 대규모로 변경해야 할 시스템을 소유한 팀이 주도해야 변경을 완료하는 데 유리합니다.
LSC 프로세스를 설명하기 전에 왜 수많은 변경들이 원자적으로 커밋될 수 없는지를 이해해봅시다.
기술적 한계
대부분의 버전관리 시스템(Version Control System, VCS)에서는 기능을 수행하는 비용이 변경의 크기에 비례해 커집니다. 파일 수천개를 원자적으로 커밋하기엔 메모리나 프로세싱 능력이 부족할 수 있습니다.
병합 충돌
병합 시 충돌이 생길 가능성이 커집니다. 버전 관리 시스템이 제공해주는 기능으로도 커버가 불가능한 경우가 많으며, 변경하는 동안 다른 사람들의 작업을 중단시킵니다.
유령의 묘지
유령의 묘지(haunted graveyard)란 너무 오래되고 둔하고 복잡해서 아무도 손대려 하지 않는 시스템을 뜻합니다. 유령의 묘지를 잘못 수정할 경우 많은 파급효과가 발생합니다. 이런 시스템에 대한 해결 방법은 충실한 테스트가 가장 효과적이며, 꾸준한 테스트는 시스템의 코드 베이스를 지속적으로 개선할 수 있는 동기를 줍니다.
이질성
LCS가 가능하려면 사람이 아닌 컴퓨터가 처리해줘야 합니다. 하지만 컴퓨터는 사람과 달리 모호한 일은 잘 처리하지 못합니다. 때문에 시스템 환경이 일관되어야 하며, 개발 조직의 도구와 스타일 가이드를 단순화해야 합니다. 구글 프리서브밋 검사는 복잡성을 높이지만 일관성 유지를 위해 복잡성을 희생한 좋은 예입니다. 다만 LSC로 인한 테스트 때는 팀 특화 검사를 생략하도록 조언합니다.
테스트
모든 변경에 대해서는 테스트가 수행되어야 하며, 직접 또는 간접 영향을 받는 코드들은 모두 테스트가 수행될 수 있도록 진행해야 합니다. 변경을 작은 단위로 수행하면 테스트의 횟수와 시간은 많이 늘어나지만 큰 변경의 테스트를 한번에 많이 실행할 수 있습니다. 실패를 추적하는 시간보다 소규모 변경을 자주 테스트 하는 것이 더 효율적입니다.
코드리뷰
모든 변경은 테스트와 함께 리뷰도 거쳐야 합니다. LSC도 예외는 아닙니다. 하지만 거대한 커밋의 검토는 지루하고 번거로우며 오류가 스며들 확률이 높습니다. 이러한 변경을 별도의 조각(shard)으로 나누면 좀 더 쉽게 리뷰할 수 있습니다.
[사례 연구: 대규모 변경 테스트하기]
오늘날 구글에는 변경의 10~20%가 LSC 때문인 프로젝트가 흔합니다. 많은 양의 코드가 프로젝트의 전담 인력이 아닌 사람들에 의해 변경된다는 뜻입니다. LSC를 테스트 하는 것은 느리고 힘듭니다. 구글은 속도를 높이기 위해 TAP 열차라는 테스트 자동화 플랫폼(Test Automation Platform) 전략을 활용합니다.
LSC들은 서로 상당히 독립적이며, 영향을 받는 테스트 대다수가 대부분의 LSC에서 문제없이 성공합니다. TAP 열차가 유용한 것은 다음의 두 요인 덕입니다.-LSC 대부분은 순수한 리팩토링이라서 주제 범위가 매우 좁고 코드의 원래 의미가 변하지 않음 -LSC를 구성하는 개별 변경은 단순하고 면밀히 검토해 진행하므로 잘못될 가능성이 크지 않음
TAP 열차는 다음의 다섯 단계로 진행되며, 3시간마다 수행됩니다.
1) 변경에 대해 무작위로 선택한 테스트 1000개 수행 2) 1000개의 테스트를 통과한 변경 중 대표 변경을 하나 선택 (= Train) 3) 변경들에 직접 영향받는 테스트를 모두 실행 일정 이상 큰 혹은 저수준의 LSC인 경우, 구글 레포지터리의 모든 테스트가 실행됨 때문에 시간 소요가 큼 4) 불규칙하지 않으면서 실패한 테스트만 추려 개별 변경에 대해 독립적으로 다시 수행 train에 올라탄 변경 중 어느것이 실패의 원흉인지 파악 5) TAP이 train에 탑승한 각 변경에 대한 보고서를 생성 보고서에는 성공한 개상과 실패한 대상 모두 포함 이는 LSC가 서브밋 여부를 판단하는 근거로 활용
구글은 LSC를 가능케 하기 위해 인프라에 막대한 투자를 했습니다. 이 인프라에는 변경 생성, 변경 관리, 변경 리뷰, 테스트 도구 등이 포함됩니다. 하지만 도구보다 중요한 것은 대규모 변경과 이를 감독하는 프로세스를 둘러싼 문화적 규범의 진화입니다.
정책과 문화
LSC를 만들려는 팀과 개인을 위한 가벼운 승인 프로세스를 고안했습니다. 이는 다양한 언어의 미묘한 특성에 익숙한 숙련된 엔지니어 그룹이 감독하며, 새로 만들려는 LSC 관련 도메인 전문가도 참여합니다. 이 정책과 관련해 코드 소유자가 담당 소프트웨어에 대한 책임감을 갖는 것도 중요하지만, LSC가 구글의 소프트웨어 엔지니어링 관행을 확장하는데 중요한 축임도 이해해야 합니다. 또한 LSC에 거부권을 행사할 수 없다는 사실도 받아들여야 합니다.
코드베이스 인사이트
LSC를 진행하려면 코드베이스 전반을 분석할 수 있어야 합니다. 또한 리포지터리가 커져도 사람이 개입하는 시간은 크게 달라지지 않아야 합니다. 그리고 변경 생성 도구가 코드베이스 전반을 포괄해 다룰 수 있어야 합니다.
변경 관리
마스터 변경을 여러개의 shard로 나눈 후 테스트, 메일링, 리뷰, 커밋 단계로 관리해주는 도구가 가장 중요합니다. 구글은 Rosie라는 도구를 사용합니다.
테스트
LSC 테스트는 마스터 변경이 문제를 일으키지 않음을 확인하는 일 외에, 개별 샤드를 안전하고 독립적으로 서브밋할 수 있는지 까지 검증해야 합니다.
언어 지원
구글은 LSC를 주로 언어별로 진행합니다. 정적 타입 언어가 동적 타입 언어보다 훨씬 유리합니다. 자동 포맷터도 중요한 역할을 담당하고 있습니다.
LSC 프로세스는 크게 다음의 네 단계로 이루어지며, 각 단계의 경계는 매우 모호합니다.
LSC 작성자에게 다음의 내용을 포함한 간단한 제안 문서 작성을 요청합니다.
- 변경을 제안하는 이유
- 코드베이스 전반에 주는 예상 영향
- 리뷰어들이 던질만한 질문과 그에 대한 답변
변경 생성
변경 생성 프로세스는 가능한 자동화해야 합니다. 이전 방식의 코드로 되돌아가거나 변경된 코드에서 병합 충돌이 발생했을 때 상위 변경을 업데이트해야하기 때문입니다.
샤드 관리
Rosie를 실행합니다. Rosie는 거대한 변경을 하나 입력받아서 서브밋할 수 있는 작은 변경(shard)들로 쪼개줍니다. 이때 프로젝트 경계와 소유권 규칙을 참고합니다. 이후 shard는 독립된 테스트-메일-서브밋 파이프라인을 거치게 됩니다.
마무리 청소
가장 중요한 부분은 폐기 대상인 코드를 사용하지 못하도록 하는 장치입니다. 구글은 Tricorder 프레임워크를 사용합니다. Tricorder는 누군가 폐기 대상 객체를 사용하는 코드를 새로 작성하면 코드 리뷰 때 알려줘서 변경을 역행하지 않도록 막아줍니다.