이터레이션 속도 최적화하기
Optimizing for iteration speed
I've written before about the importance of iterating quickly but I didn't necessarily talk about some concrete things you can do. When I've built up the tech team at Better, I've intentionally optimized for fast iteration speed above almost everything els
erikbern.com
저는 빠른 반복의 중요성에 대해 이전에 썼지만, 당신이 할 수있는 구체적인 일에 대해 반드시 이야기하지는 않았습니다. Better에서 기술 팀을 구성했을 때, 저는 의도적으로 빠른 반복 속도 최적화에 우선순위를 두었습니다. 이제 그 방법을 소개하겠습니다.

지속적인 배포
이 모호한 주장은 우리가 세계에서 유일하게 지속적으로 배포하는 금융 서비스일 수 있다는 점입니다. 우리는 매일 50-100 번 정도 프로덕션에 배포합니다. 풀 리퀘스트가 마스터로 병합되면 수천 개의 단위 테스트와 수백 개의 Selenium 테스트로 구성된 상당히 광범위한 테스트 suite를 실행합니다. 저희 팀은 테스트 소요 시간을 최적화하는 데 많은 시간을 투자했으며 최든에 약 15 분 정도 소요합니다. 모든 테스트를 통과하면 프로덕션에 배포합니다.
CI에 Buildkite를 사용하고 Kubernetes를 기반으로 모든 서비스를 실행합니다. Kubernetes는 (다른 수백만 가지 중에서) 블루 / 그린 배포를 지원하므로 배포 중에 서비스 중단 시간이 없습니다.
테스팅
지속적인 배포는 책임있는 자유이며 엄격한 테스트 없이는 불가능합니다. 우리는 약 85 %의 단위 테스트 커버리지를 가지고 있습니다 (스위트 스팟은 약 90 %라고 생각합니다. 100 %는 비현실적입니다). 수동 테스트는 제품 관리자 만 수행합니다. 일반적으로 기능이 이미 프로덕션 환경에 배포된 흐에 사양에 맞는지 확인합니다.
프로덕션에 버그를 릴리스 한 적이 있습니까? 물론입니다. 그러나 평균 복구 시간은 일반적으로 실패가 발생하는 평균 시간보다 더 중요합니다. 수정이 필요한 항목을 배포하면 종종 몇 분 내에 롤백 할 수 있습니다. 그리고 매우 점진적인 변경 사항을 제공하기 때문에 평균 버그의 영향력이 크지 않은 경우가 많습니다. 프로덕션 버그는 종종 지난 며칠 동안 작성된 코드와 관련이 있으므로 새롭고 빠르게 수정할 수 있습니다.
"스프린트"없음
2 주 또는 3 주 스프린트는 미니 워터폴 방식이며, 사용자에게 제품 업데이트 시점에 대한 예측가능성을 제공하기 위해 제품 관리 관련해서 많은 유연성을 희생합니다. 그러나 고객 대면 제품에 대해 작업하는 경우 사용자는 언제든지 제품을 업데이트 할 것이라고 기대하지 않습니다. (사용자와 관련해 우리가 흔히 말하는 예측 가능성이 과대 평가되었다고 생각합니다. 외부 이해관계자에 대한 예측가능성은 단지 영업 사원이 지나치게 많은 제품을 판매하는 것을 피하는 수준일 뿐입니다.)
지속적인 작업 흐름은 v1, v2 및 v3을 모두 같은 날에 시작할 수 있음을 의미합니다. v2에는 v1 및 v3의 사용자로부터 배운 기능이 v2에 대한 사용자 피드백을 기반으로 한 기능이 포함되었습니다.
작은 작업(Small tasks)
랜덤 행렬 이론의 흥미로운 결과는 고차원 공간에서 국소 최소값이 드물다는 것입니다. 소프트웨어 엔지니어링은 대부분 작업을 작은 점진적 조각으로 나누고 각각을 개별적으로 배포하는 것이 빠르게 시장에 가치를 제안하는 수단입니다.
대조적으로, 소프트웨어 엔지니어링에서 가장 무서운 것 중 하나는 프로덕션 배포에 포함되지 않고 빌드되는 코드의 "인벤토리"입니다. 배포 위험을 나타내지만 사용자가 원하지 않는 것을 구축 할 위험도 있습니다. 기능의 일부를 이전에 제공하지 않아 손실되는 사용자 가치는 말할 것도 없습니다 (사용자 가치는 최종 상태의 기능 가치가 아니라 시간에 따라 통합 된 기능 가치로 간주되어야합니다).
기능 플래그 지정은 마지막 옵션이며 아껴 사용합니다. 더 나쁜 것은 기능 분기가 있다는 것입니다. 이는 폐지되어야합니다. Git-flow는 끔찍한 발명품이며 Spotify에서 시도했을 때 사람들은 코드를 리베이스하는 데 50 % 정도의 시간을 소비했습니다.
이러한 이유로 오래 지속되는 풀 요청은 눈살을 찌푸립니다. 풀 요청은 몇 시간 이내에 병합되어야하며 이상적으로는 최대 수백 줄이 되어야 합니다. 검토자를 풀 리퀘스트에 할당하고 Slack 채널에 알리는 자체 시스템을 구축했습니다. 결과는 아래 통계에서 분명합니다. 이것은 PR이 생성 된 시점부터 병합되는 시점까지의 시간을 나타냅니다. 이는 모노 레포에서 가져온 것입니다.


다기능 인력과 팀(Cross-functional people and teams)
일부 회사에는 별도의 백엔드 및 프런트 엔드 팀이 있습니다. 또는 극단적으로 “머신 러닝 개발 팀"이 아닌 별도 조직으로 "머신 러닝 연구 팀"이 있는 회사도 있습니다. 이 경우에 이터레이션 속도가 느려지고 업무 협의 위한 오버 헤드가 추가됩니다.
긴밀한 피드백 루프의 최적화를 위해 기능별 팀이 기술별로 분리 된 팀보다 훨씬 더 합리적입니다. (If you want to optimize for a tight feedback loop, cross-functional teams make a lot more sense than teams split up by skills.)
이는 개별 엔지니어에게도 적용됩니다. Better의 모든 엔지니어는 백 로그의 모든 기능을 가져와 배포할 수 있는 풀 스택 엔지니어입니다. 대부분의 경우 복잡성은 실제로 백엔드에 있으므로 대부분의 팀은 백엔드 개발자를 바라보고 있습니다. 그러나 아무도 CSS를 작성하고 필요할 때 픽셀을 푸시하는 데 문제가 없습니다. 일반적인 작업은 백엔드 80 ~ 90 %와 프런트 엔드 10 ~ 20 %입니다. 한 명의 엔지니어가 기능에 대해 작업함으로써 훨씬 더 빨리 배포할 수 있습니다. 팀의 대부분의 엔지니어는 CSS의 Simone Biles는 아니지만 이를 수행하고 작업을 완료 할 수 있으며 일반적인 작업에서 큰 부분을 차지하지 않습니다.
빠르게 변화하는 소비자 대면 스타트 업에서는 전문성을 감당할 수 없습니다. 풀 스택 엔지니어가 더 빠르게 배포 할뿐만 아니라 더 많은 유연성이 내장되어 있습니다.
파격적인 청사진을 그리기 전에 몇 가지 전문 역할을 고용했음을 지적하고 싶습니다. 테스트 자동화 엔지니어, 운영 담당자 및 전담 프런트 엔드 엔지니어가 있습니다. 우리는 몇 가지 특정 영역에 대해 좀 더“전문가”가 필요했습니다. 거기에 도달하는 데 시간이 걸리고 이러한 엔지니어조차도 전체 기술 스택에 적응하는데 시간을 보냅니다.
What else?
확실히 큰 차이를 만드는 사소한 요소들이 있습니다.
범위를 작게 유지하고 학습 프로세스를 중심으로 제품 프로세스를 설계하려면 어떻게해야 할까요? "최소 실행 가능한 제품"인 MVP라는 주제에 많은 관련이 있습니다.
데이터는 분명히 매우 중요합니다. 우리가 제공하는 점진적 기능에서 실제로 어떻게 배울 수 있습니까? 하드 메트릭과 소프트 품질 항목을 논할 수 있습니다
제품 품질과 배포 시간 간의 균형은 어떻습니까?
나는 이것에 대해 하루 종일 쓸 수 있습니다. 대신,주기 시간이 왜 그렇게 중요한지에 대한 메모로 마무리하고 싶었습니다.
반복하거나 죽는다(Iterate or die)
먼저 빠른 반복 속도를 위한 최적화가 처리량과 같지 않다는 점을 지적하겠습니다. “Little의 법칙”에서 처리량은 λ이고 반복 속도는 W의 역입니다. λ와 W 사이의 관계는 복잡하고 슬프게도 좋은 리소스를 찾지 못했습니다. Google 검색에서 다른 것들 중에서도 반도체 칩 제조에 관련 적절한 리소스를 찾을 수 있습니다.

차트를 보면 처리량을 이론적 용량보다 약간 낮게 낮추면 사이클 시간을 훨씬 더 낮출 수 있습니다 (즉, 더 높은 반복 속도). 그러나 칩 제조는 학습 과정이 전혀없는 대규모 제조 과정입니다. 높은 처리량 외에도 빠르게 학습하고 싶다면 이론적 처리량 용량보다 약간 낮은 수준으로 작동하는 것은 아무 문제가 없을 것입니다.
약간의 이론을 가져 와서 미안하여 다시 정리합니다. 한 시간에 1,000 개의 햄버거를 만들어야하는 패스트 푸드 체인점이라고 상상해보십시오. 당신은 어느 시점에서 빵 굽기를 시작하고, 패티를 굽고, 양상추를 자르는 등의 작업을 시작해야합니다. 특정 소프트웨어 프로젝트는 이와 유사 할 수 있습니다. 예를 들어 C ++에서 Java로 큰 애플리케이션을 다시 작성합니다.
그러나 훨씬 더 자주, 소프트웨어 프로젝트는 완전히 새로운 햄버거 레시피를 찾는 것과 같습니다. 이 경우 배치를 작게 유지하고 지속적으로 피드백을 통해 학습하는 것이 중요합니다. 한 시간에 500 개 또는 800 개 버거를 만들고 배치 크기와 사이클 시간을 10 배 더 작게 만들 수 있습니다. 재고를 낮게 유지하도록 강요하는 것은 린 (lean) 제조에 대한 집착이며 대부분 고객의 요구에 훨씬 더 빠르게 대응할 수 있기 때문입니다 (다른 이유는 재고가 1950 년대 일본에서 상당한 비용이 들었 기 때문입니다).
어쨌든 조직 측면에서 보면 한 사람이 상추를 자르고 한 사람이 빵을 만드는 등 버거를 통째로 만드는 일을 담당하는 경우 재고를 훨씬 낮출 수 있습니다. 피드백 루프를 꽉 유지하면 향신료 조합을 계속 변경할 수 있습니다. 피드백으로부터 학습하세요. 레시피는 10 배 또는 100 배 빠르게 진화 할 수 있습니다. 이것이 궁극적으로 다른 모든 사람들을 능가하는 방법입니다.