- 제품이 경쟁력을 갖추기 위해서는 시장에 신속하게 출시할 수 있는 능력이 중요하다.
- 유사 서비스와의 경쟁, 제품과 서비스 품질 유지, 새로운 규제에의 적응에 내 조직의 속도
- 처음부터 완벽한 소프트웨어란 존재하지 않는다. 확실한 건 출시 후 빠르게 업데이트해야 한다는 사실 뿐이다.
- 오랜 기간 성공적으로 살아남은 조직들은 아이디어를 빠르게 실천하고 고객의 손에 최대한 빨리 전달하고 피드백에 신속하게 대응하는 능력을 갖췄다.
- 고객에게 제품을 보여주지 않은 채 진행하는 기간이 길수록 위험과 비용이 커진다. 심지어 개발자의 사기마저 떨어뜨릴 수 있다.
- 코드의 가치는 서브밋 시점이 아니라 고객이 그 기능을 이용할 때 실현된다.
- 코드 완성과 사용자 피드백 사이의 시간을 단축할수록 진행 중인 작업의 비용이 줄어든다.
제품 런칭은 절대 끝이 아니다. 내가 지금 어디에 와 있고, 다음에 고쳐야 할 가장 중요한 건 무엇이고, 진행 상황을 측정하고, 그다음 문제를 찾아 수정하는 학습주기가 시작됐을 뿐이다. 절대 끝은 없다. 이 사실을 깨닫는다면 위대한 성취를 이루게 될 것이다. - 에비이드 위클리, 전 구글 제품 매니저
- 지속적 배포(Continuous Delivery, CD) 그리고 애자일 방법론의 핵심 교리는 작은 변경들을 자주 배포할수록 품질이 높아진다는 것이다. '빠를수록 안전하다(faster is safer)'는 말이다.
- CD라는 최종 목표에 다가가는 과정에서 독립적으로 취할 수 있는 가치들
- 민첩성: 자주, 작게 릴리즈한다.
- 자동화: 잦은 릴리스에 수반되는 반복 작업 부담을 줄이거나 없앤다.
- 격리: 변경을 격리하여 문제를 쉽게 해결할 수 있도록 모듈화된 아키텍처를 지향한다.
- 신뢰성: 비정상 종료와 지연시간 같은 주요 상태 지표를 측정하고 꾸준히 개선한다.
- 데이터 중심 의사결정: A/B 실험으로 상태 지표를 비교하여 품질을 높인다.
- 단계적 출시: 변경을 모두에게 동시에 출시하지 않고 소수의 사용자부터 이용해보게 한다.
- 이상적으로는 두 릴리즈 사이의 변경이 적을수록 문제를 해결하기 쉽다.
- 팀이 작을 때는 변경들이 일정한 속도로 코드베이스에 도달한다. 하지만 시간이 흘러 팀이 커지거나 하위 팀으로 쪼개지면서 안티패턴이 고개를 든다.
- 릴리즈 비용이 늘고 위험이 커지면 본능적으로 릴리즈 주기를 늦춰 안정성을 확보할 기간을 늘리려 한다. 하지만 이렇게 해서 얻는 안도감은 짧은 기간만 유지될 뿐, 멀리 보면 팀의 속도가 느려지고 팀과 고객을 모두 좌절시키는 선택이다.
- 답은 비용을 줄이고, 규율을 강화하고, 위험에 점진적으로 대응하는 것이다.
- 당장의 안정을 위한 프로세스 수정에 저항하고 장기적인 아키텍처 개선에 투자해야 하는 게 핵심이다.
- 눈앞의 안정만 추구한다면 낡은 개발 프로세스로 회귀하기 쉽다.
- 가장 효과가 좋았던 투자는 마이크로서비스 아키텍처로의 전환이었다.
- 구글의 역사를 보면 종종 제품을 단순히 마이그레이션하는 수준이 아니라 처음부터 다시 짜는 게 올바른 선택이었다. 유튜브는 원하는 수준의 모듈성을 새로운 아키텍처에 녹였다. 수개월이 걸릴 수 있고 단기적으로는 고통스러운 선택이었지만, 운영 비용과 이해하기 쉬워진 코드베이스, 애플리케이션의 긴 수명을 고려해보면 득이 훨씬 많을 것이다.
- 지속적인 릴리증를 안정적으로 수행하는 핵심은 엔지니어들이 모든 변경에 플래그 가드(flag guard)를 넣도록 하는 것이다.
- 플래그 가드: 플래그를 이용하여 제품의 코드를 기능 단위로 제어(활성화/비활성화)하는 기법
- 제품이 커질수록 서로 다른 개발 단계에 놓인 (성숙된 정도가 다른) 여러 기능이 하나의 바이너리에 공존할 확률이 높아진다. 플래그 가드를 이용해 릴리즈용 빌드, 개발용 빌드를 다르게 설정할 수 있따.
- 바이너리에 이전 기능을 실행하는 코드와 새로운 기능을 실행하는 코드를 모두 넣기도 한다. 새로운 코드가 충분히 준비됐다고 판단되면 다음 릴리즈에서는 이전 기능을 실행하는 코드를 막고 새로운 기능을 데뷔시킨다. 릴리즈 후 문제가 생기면 바이너리 릴리즈 없이 플래그 값만 동적으로 수정할 수 있다.
- 모든 고객의 플래그를 동시에 수정하는 건 좋은 생각이 아니다. 따라서 설정 배포를 안전하게 관리해주는 설정 서비스를 마련해두는 게 좋다.
- 릴리즈 주기를 예측할 수 있도록 하기 위해 선택한 트레이드 오프. 크게 두 가지를 프로세스에 녹여냈다.
- 완벽한 바이너리는 없음을 인정한다.
- 모든 버그를 수정하는 건 불가능할지라도 다음과 같은 질문을 던지는 걸 멈춰서는 안 된다.
- 선이 왼쪽으로 2픽셀만큼 이동하면 광고 노출과 잠재 수익에 영향이 있을까?
- 박스 음영을 살짝 달리해보면 어떨까?
- 시각 장애인이 읽기 어려워지지는 않을까?
- 프로덕션에 새로운 변경을 리리즈할 때마다 무언가를 결정하고 절충해야 한다.
- 핵심 성과 지표(Key Performance Indicator, KPI)와 명확한 문턱값을 활용하면 비록 완벽하지는 않더라도 기능을 출시할 수 있다.
마감일은 확실하지만, 인생은 그렇지 않다.
- 릴리즈 열차 시간에 늦으면 기다리지 않고 출발할 것이다.
- 릴리즈가 다가오면 어느 시점부터 완고한 자세로 개발자와 그들이 들고 온 새로운 기능을 외면해야 한다. 그 시점 이후로는 아무리 애원하고 구걸해도 이번 릴리즈에 끼워주지 않아야 한다.
- 대다수 소프트웨어는 사라지기 전에 팽창(bloat)이라는 불행한 부작용을 경험한다.
- 빠르고 효율적인 릴리즈 열차에도 이따금 지나치게 팽창해서 제품팀과 사용자에게까지 문제를 일으킬 수 있다는 단점이 있다.
- 소프트웨어가 모바일 앱 형태로 전달된다면 고객은 사용하지도 않을 기능들에 모바일 기기의 저장 공간, 내려받는 시간, 데이터 비용을 허비해야 한다. 반대로 개발자들은 느려진 빌드, 복잡한 배포, 드물게 튀어나오는 버그 처리라는 비용을 치러야 한다.
- 모듈화를 통해 동적으로 설정 가능한 배포 전략을 구사할 수 있다.
- 사용자 기기 공간과 자원 활용 효율을 높여주는 전략이다. 이렇게 하지 않으면 고객들은 자신은 한 번도 사용하지 않을 기능이나 다른 기기에 대응한 코드까지 포함한 바이너리를 받아 설치해야 한다.
- 동적 배포를 활용하면 각 고객에게 의미 있는 코드만으로 작게 구성한 바이너리를 배포할 수 있다. 또한 A/B 실험을 실행하여 특정 기능에 드는 비용과 그 기능이 제공할 고객 가치(혹은 비즈니스 가치)를 비교해볼 수도 있다.
- 이러한 프로세스를 갖추려면 비용을 선제적으로 지불해야 한다.
- 고객에게 릴리즈하는 주기보다 내부 릴리즈 주기를 짧게 관리하는 데도 저항이 있을 것이고, 모두를 설득하기가 쉽진 않을 것이다.
- 하지만 위험 관리, 개발자 속도, 빠른 혁신 관점에서 얻는 게 아주 크기 때문에 장기적으로 초기 비용 이상을 충분히 보상 받을 수 있을 것이다.
클라이언트 시장의 다양성은 문제가 아니라 현실이다.
- 릴리즈 승인 모델 변경
- 종합적인 테스트가 현실적으로 불가능하다면 대표적인 테스트만을 목표로 한다.
- 사용자 기반을 조금씩 늘려가며 배포하면 문제를 빠르게 수정할 수 있다.
- 자동화된 A/B 릴리즈를 이용하면 릴리즈 품질 판별에 도움이 되는 통계적으로 중요한 결과를 얻을 수 있다. 피곤하게 대시보드만 쳐다보며 결정할 일이 없어진다.
- A/B 테스트는 사용자 기반이 충분하다면 며칠 혹은 몇 시간이면 통계적으로 유의미한 결과를 얻을 수 있다. 여기에 지표를 얻고 판단하는 과정까지 자동화하면 가드레일 지표에 영향이 없다고 판단할 만큼의 데이터가 쌓이는 즉시 더 많은 사용자에게 신버전을 배포해가며 릴리즈를 최단 기간에 해낼 수 있다.
- 물론 이 방식을 모든 앱에 적용할 수는 없으며, 사용자 수가 충분하지 않을 때는 부담이 클 수 있다. 이런 경우라면 변경 중릭적인 릴리즈를 추천한다.
- 새로운 기능 모두를 플래그로 막아두고 배포하는 것이다. 그러면 배포 과정에서는 유일하게 배포 자체의 안정성만을 검증할 수 있다.
- 늘 배포하라(Always Be Deploying) 정책은 여러 측면에서 개발자 속도를 높여준다. 그리고 이 외에도 규모 문제를 완화해주는 관례들이 있다.
- 규모가 늘면서 커진 복잡성은 보통 릴리즈 주기가 길어지는 형태로 나타난다. 이 지점에서 '늘 배포하라'가 개발 프로젝트에 도움이 된다.
- 릴리즈 열차가 자주 오면 이전의 좋은 상태와의 차이가 적어서 문제가 생겨도 살펴봐야 할 범위가 좁혀진다. 하지만 거대하고 빠르게 커져가는 코드베이스에 내재된 복잡성이 진행 속도를 늦추지 않을 것이라고 팀을 확신시켜야 한다.
- 구글 지도(Google Maps)팀은 각각의 기능을 매우 소중하게 여기지만 릴리즈를 멈춰 세울 정도로 중요한 기능은 거의 없다는 생각으로 일한다.
- 릴리즈가 자주 이루어지면 기능 하나가 릴리즈 주기를 한 번 놓치는 아픔은 모든 기능이 다음 릴리즈까지 지연되는 고통에 비하면 사소하게 느껴진다.
- 특히 아직 충분히 준비되지 못한 기능을 서둘러 열차에 태워 보내서 고객이 겪는 고통에 비하면 아무것도 아니다.
- 릴리즈는 개발자로부터 제품을 보호해야 한다.
- 새로운 기능을 출시하고야 말겠다는 개발자의 열정과 성급함이 고객이 느끼는 경험보다 중요할 수는 없다.
- 강력한 계약에 기반한 인터페이스, 관심사 분리, 엄격한 테스트, 조기의 잦은 소통, 새로운 기능이 수용 조건을 정하는 규약 등을 통해 새로운 기능을 기존 구성 요소들과 격리해야 한다.
- 구글은 오랜 기간 수많은 소프트웨어 제품을 경험하며 (직관과 다르게) 빠를수록 안전하다는 사실을 깨우쳤다.
- 안정적인 제품과 빠른 개발은 서로 대척점에 서 있지 않다. 오히려 더 작게 변경해 자주 배포하는 제품이 품질도 우수하다. 실제 고객을 괴롭히는 버그와 예상치 못한 시장 변화에 더 빠르게 적응한다.
- 빠를수록 비용도 적게 든다. 정기적으로 자주 출발하는 릴리즈 열차가 릴리즈별 비용을 크게 낮춰줘서 릴리즈를 한 번 놓치더라도 별다른 손해를 입지 않게 된다.
- 최종 사용자에게까지 배포하지 않더라도 단순히 지속적 배포를 할 수 있는 구조를 갖추는 것만으로도 엄청난 가치를 창출할 수 있다.
- 바이너리는 프로덕션 환경에서도 설정을 바꿀 수 있게 만들어야 하며, 이 설정 정보는 코드처럼 버전 관리해야 한다.
- 드라이런 검증(dry0run verification), 롤백/롤포워드 메커니즘, 안정적인 패치 등의 안전장치를 갖춘 툴체인도 구비해야 한다.
- 속도는 팀 스포츠다. 거대한 코드를 함께 개발하는 팀이 빠르게 굴러가려면 아키텍처를 모듈화하고 지속적으로 통합해야 한다.
- 변경은 격리해 평가해야 한다. 문제를 조기에 격리할 수 있도록 기능별로 플래그 가드를 세워두자.
- 현실을 직시하자. 기기가 다양하고 사용자 기반이 넓다면 단계적 출시로 대응한다. 프로덕션 환경과 비슷하지 않은 가공의 환경에 맞춰 릴리즈하면 진짜 문제를 한참 후에야 알 수 있다.
- 쓰일 기능만 배포해야 한다. 기능별로 출시 비용과 창출 가치를 모니터링하여, 고객이 여전히 이용하며 충분한 가치를 제공하고 있는지 확인하자.
- 원점으로 회귀하자. 지속적 통합과 지속적 배포를 적용하여 모든 변경에 대한 판단을 더 빠르게 더 많은 데이터에 기초해 내리자.
- 빠를수록 안전하다. 적게 수정하여 빨리 자주 배포하면 각 릴리즈의 위험이 줄고 시장 변화에 적시에 대응할 수 있다.