기능을 main에 합친다. 검증한 커밋에 태그를 붙인다. 배포할 때는 그 태그를 선택한다.

익숙한 흐름이다. 어떤 제품에서는 이것만으로도 개발과 출시의 시점을 충분히 나눌 수 있다. 개발자는 다음 작업을 통합하고, 운영자는 검증을 마친 버전을 배포한다. 모든 머지를 곧바로 사용자에게 내보낼 필요는 없다.

다른 제품은 출시 후보를 따로 안정화해야 할 수도 있다. 이미 고객에게 전달된 여러 버전을 함께 지원해야 할 수도 있고, 앱 심사나 장비 검증 때문에 개발 속도와 출시 속도가 크게 다를 수도 있다.

이런 차이를 놓고 이야기하다 보면 방법론의 이름이 등장한다. Git Flow, GitHub Flow, Trunk-Based Development(TBD). 이름을 정하면 논의가 끝난 것 같은 느낌이 든다.

나는 그다음 이야기가 더 궁금하다.

왜 이 제품에는 그 방식이 맞는가. 어떤 대기를 줄이려는가. 그 대신 누가 어떤 일을 더 맡는가. 지금은 맞아도 조건이 달라지면 어떻게 바꿀 것인가.

지난 글에서는 플래그가 OFF라는 사실만으로 사용자 영향이 없다고 볼 수 없다는 이야기를 했다. 이번에는 그 질문을 개발 방식의 선택으로 넓혀보려 한다. 안전장치의 이름만으로 안전을 설명할 수 없듯, 방법론의 이름만으로 운영을 설명할 수도 없기 때문이다.

방법론을 만든 사람도 조건을 붙였다

Git Flow를 소개한 Vincent Driessen은 원문에 2020년 회고를 덧붙였다. 지속적 전달을 하는 팀에는 GitHub Flow처럼 단순한 흐름을 권하고, 버전을 명시적으로 구분해 출시하거나 여러 운영 버전을 지원해야 한다면 Git Flow가 여전히 적합할 수 있다고 설명한다. Git Flow 원문과 회고

그 대목이 좋았다. 자신의 방법이 널리 쓰이게 된 뒤에도 적용할 조건을 다시 설명한다. 어떤 문제를 풀려고 만든 것인지 잊지 말라는 이야기로 읽혔다.

이름이 달라도 실천은 겹칠 수 있다. GitHub Flow는 브랜치를 만들고 변경을 검토해 병합하는 가벼운 흐름이다. TBD에서도 짧은 작업 브랜치와 PR을 사용할 수 있으며, 출시 주기에 따라 릴리스 브랜치를 둘 수도 있다. 따라서 PR을 쓴다고 TBD가 아닌 것도, 릴리스 브랜치가 있다고 곧바로 Git Flow인 것도 아니다. GitHub Flow, TBD의 릴리스 브랜치

이 구분을 외우는 것보다 중요한 것은 각 방식이 무엇을 줄이고 무엇을 남기는지 이해하는 일이다.

방식 해결하려는 문제 함께 관리할 비용
Git Flow 다음 버전 개발과 출시 준비, 긴급 수정을 분리 여러 브랜치의 변경 동기화와 병합 비용
GitHub Flow 브랜치와 PR을 통한 단순한 변경 전달 작업 브랜치를 오래 유지하지 않고 병합할 변경을 검증하는 일
TBD 잦은 통합으로 변경 간 불일치와 피드백 지연 축소 작은 변경 단위, 빠른 검사, 배포 가능한 trunk 유지
릴리스 브랜치를 사용하는 TBD 개발 통합과 특정 버전의 출시·지원 병행 수정 전파, 지원 기간, 릴리스 브랜치 종료

이 표는 승자를 고르기 위한 순위표가 아니다. 제품의 제약과 맞춰볼 질문 목록에 가깝다. 여러 버전을 지원한다고 반드시 Git Flow 전체가 필요한 것도 아니다. 필요한 부분만 더 단순하게 구성할 수 있는지 살펴볼 여지가 있다.

태그를 만들어 배포한다는 말에도 빈칸이 있다

태그 기반 배포는 위 방식들과 별도로 생각할 수 있다. 어느 브랜치에서 개발했는지와 어떤 버전을 배포 대상으로 지정하는지는 다른 결정이다.

다음 흐름을 가정해보자.

작은 변경을 main에 통합
  -> 커밋 C를 빌드하고 테스트
  -> 검증한 C에 release-1.8.0 태그 부여
  -> 선택한 버전을 운영에 배포

태그가 있으면 커밋에 사람이 읽기 쉬운 이름을 붙일 수 있다. 그러나 태그만으로 검증한 결과와 배포한 결과가 같아지는 것은 아니다.

배포 방식은 크게 두 가지로 나눠볼 수 있다. 태그가 가리키는 소스를 배포 시점에 다시 빌드하거나, 커밋 C로 이미 만들고 검증한 산출물을 보관했다가 그대로 운영에 올리는 것이다.

두 방법은 같은 커밋을 사용할 수 있지만 같은 보장을 하지는 않는다. 다시 빌드할 때 의존성, 빌드 도구, 환경 변수나 외부 설정이 달라지면 결과가 달라질 수 있다. 기존 산출물을 배포한다면 그 차이를 줄일 수 있지만, 이번에는 산출물 보관과 식별, 환경 간 호환성을 관리해야 한다.

그래서 실제로 필요한 연결은 조금 더 길다. 아래의 digest는 산출물의 내용을 식별하는 해시값이다.

태그 -> 커밋 SHA -> 검증 기록 -> 산출물 digest -> 배포 이력

태그를 이동하거나 다시 만들지 못하게 보호하는 정책도 필요하다. 지난주에 검증한 이름이 이번 주에는 다른 커밋을 가리킨다면 이름만으로는 이력을 설명할 수 없다.

이런 세부를 정리하면 태그 기반 배포는 충분히 유용한 선택이 된다. 반대로 “우리는 태그로 배포한다”만 남으면 정작 중요한 질문은 그대로다. 무엇을 검증했고, 운영에는 무엇이 올라갔으며, 문제가 생기면 무엇으로 돌아갈 수 있는가.

통합, 검증, 배포, 노출은 다른 시계로 움직인다

개발 흐름을 한 줄로 통일하려 할 때 가장 쉽게 지워지는 차이가 시간이다.

코드를 합치는 시점, 출시 후보를 검증하는 시점, 서버나 기기에 전달하는 시점, 사용자가 기능을 경험하는 시점은 일치하지 않을 수 있다.

웹 서비스는 자주 통합하고 검증된 산출물을 하루에도 여러 번 배포할 수 있다. 모바일 앱은 자주 통합하더라도 심사와 사용자 업데이트 속도 때문에 여러 버전이 오래 공존할 수 있다. 임베디드 제품은 소프트웨어 검사에 더해 실제 장비와 조합한 검증이 필요할 수 있다.

같은 웹 제품 안에서도 단순 문구 변경과 데이터 마이그레이션은 되돌리기 비용이 다르다. 같은 서버에 올라간다는 이유만으로 출시 판단까지 같아질 이유는 없다.

이때 모든 제품에 같은 절차를 적용하면 어떤 제품은 필요 없는 대기를 떠안고, 다른 제품은 꼭 필요한 검증을 공식 절차 밖에서 보충하게 될 수 있다. 문서에는 통일된 흐름이 있지만 실제 업무는 별도의 메모와 담당자의 기억에 기대게 되는 것이다.

나는 이런 상태보다 차이가 드러나 있는 편이 낫다고 생각한다. 이 제품은 왜 별도의 릴리스 브랜치가 필요한지, 저 제품은 왜 검증한 산출물을 태그로 선택하는 방식이 적합한지 설명할 수 있어야 개선할 지점도 보인다.

먼저 줄일 시간을 찾아야 한다

“속도를 높이자”는 말도 무엇을 재느냐에 따라 다른 뜻이 된다.

PR 생성부터 병합까지 걸린 시간이 짧아졌다고 하자. 그런데 그 전에 작업 브랜치에서 일주일을 보냈거나, 병합한 뒤 검증 담당자를 기다리느라 며칠이 더 걸렸다면 사용자가 결과를 받는 시간은 별로 줄지 않았을 수 있다.

반대로 병합은 조금 늦어졌지만 변경을 작게 나누고 검증을 앞당겨 출시 후 수습이 줄었다면 전체 비용은 낮아질 수도 있다.

그래서 나는 방법을 바꾸기 전에 다음 중 어디에 시간이 쌓이는지 먼저 보고 싶다.

시간이 쌓이는 곳 먼저 살펴볼 것
요구사항 확인 결정할 사람과 범위가 정해졌는가
리뷰 대기 변경이 너무 크거나 특정 사람에게 검토가 몰리는가
통합과 충돌 해결 브랜치 수명이 길거나 공통 파일에 변경이 집중되는가
빌드와 테스트 느린 검사, 불안정한 테스트, 부족한 환경이 있는가
출시 대기 배포 묶음과 승인 일정, 다른 제품과의 의존성이 있는가
출시 후 재작업 빠진 조건과 복구 비용을 누가 부담하고 있는가

TBD가 통합 지연과 충돌 비용을 줄이는 데 도움이 될 수는 있다. 그렇다고 요구사항의 결정권자가 없는 문제까지 해결해주지는 않는다. DORA도 TBD를 짧은 브랜치와 잦은 통합, 빌드가 깨졌을 때 빠르게 복구하는 실천과 함께 설명한다. DORA의 TBD 설명

속도를 말하려면 구간별 대기와 함께 변경이 사용자에게 전달되는 시간, 실패와 재작업도 봐야 한다. 특히 한 팀에서 줄어든 일이 다른 팀의 수습으로 넘어간 것은 아닌지 확인해야 한다.

앞단의 숫자가 좋아졌다는 이유로 뒤에서 늘어난 일을 지워서는 안 된다.

통일에도 비용이 있고, 다양성에도 비용이 있다

물론 통일의 이점은 분명하다. 공통 도구를 쓸 수 있고, 새로 합류한 사람이 익힐 내용도 줄어든다. 보안 패치나 배포 체계 개선을 여러 제품에 적용하기 쉬워질 수도 있다.

모든 팀이 각자 파이프라인을 만들고 담당자가 떠난 뒤 아무도 고치지 못한다면 그것을 자율성의 성과라고 말하기 어렵다. 같은 문제를 여러 번 풀고 있는지도 살펴야 한다.

다만 통일로 줄어드는 운영 비용만 셀 수는 없다. 통일된 절차에 맞추기 위해 생긴 대기, 불필요한 빌드, 맞지 않는 플래그, 반복되는 예외 승인도 비용이다.

반대로 제품별 방식을 허용한다면 차이를 설명하고 유지하는 비용을 감당해야 한다. 누가 설정을 알고 있는지, 수정은 어디에서 하는지, 장애 때 다른 사람이 이어받을 수 있는지까지 포함해서다.

비교해야 할 것은 그 전체다. 관리자가 보기 쉬운 대시보드가 됐다는 사실만으로 제품을 운영하기도 쉬워졌다고 결론 내릴 수는 없다.

공통 파이프라인을 권장 경로로 제공하고, 제품의 제약 때문에 다른 방식이 필요하면 이유와 유지 비용을 검토해 허용할 수도 있다. 여러 제품에서 같은 요구가 반복되면 공통 기능으로 흡수하면 된다. 반대로 특정 제품에만 필요한 절차까지 모든 제품에 추가할 필요는 없다.

합칠 가치가 있는 부분을 찾는 일과, 남겨야 할 차이를 판단하는 일이 함께 있어야 한다.

다르게 운영한다고 모호한 것은 아니다

가령 한 제품은 main의 검증된 산출물을 바로 배포하고, 다른 제품은 태그로 출시 대상을 고르며, 또 다른 제품은 지원 버전마다 릴리스 브랜치를 유지한다고 하자.

이 사실만으로 관리가 안 된다고 볼 수는 없다. 각 방식의 이유, 검증 대상, 운영 담당자와 변경 절차가 명확하다면 충분히 관리 가능한 구조다.

오히려 모든 저장소가 같은 브랜치 이름을 쓰는데 실제 배포 대상이나 긴급 수정의 반영 경로를 아무도 설명하지 못한다면, 겉모습만 같을 뿐이다.

다음은 제품 하나의 선택을 짧게 기록하는 예시다. 실제 조직의 규칙을 옮긴 것은 아니다.

선택: main에 자주 통합하고, 검증된 산출물을 태그로 지정해 배포한다.

이유: 다음 개발을 막지 않으면서 출시 후보를 고정해야 한다.
검증: 선택한 커밋의 검사 결과를 산출물 digest에 연결한다.
배포: 태그가 지정한 기존 산출물을 사용한다.
복구: 현재 데이터와 호환되는 이전 산출물로 전환하고, 데이터 복구가 필요하면 별도 절차를 따른다.
책임: 제품 운영 담당자가 출시를 결정하고 배포 담당자가 이력을 남긴다.
재검토: 후보 검증 대기가 길어지거나 여러 버전 지원이 필요해질 때.

이 기록이 있으면 “왜 우리만 다르냐”는 질문에 답할 수 있다. 조건이 달라졌을 때 무엇을 다시 논의해야 하는지도 보인다.

예외를 무조건 임시로 볼 필요도 없다. 장비 검증이나 여러 버전 지원처럼 오래 유지되는 제약에는 지속적인 운영 방식이 필요하다. 없앨 날짜를 억지로 정하기보다, 선택을 다시 검토할 조건을 정하는 편이 정직하다.

차이를 관리하는 일이 관리자의 일이다

“제품마다 다르면 관리하기 어렵다”는 말에는 동의한다. 실제로 더 어렵다. 제품의 제약을 이해해야 하고, 공통화할 부분과 남길 부분을 나눠야 하며, 필요한 인력과 도구에도 투자해야 한다.

그 어려움을 줄이기 위해 표준을 만드는 것은 자연스럽다. 그러나 표준에 맞지 않는 현실을 모두 현장의 예외 처리로 남기면, 판단의 부담은 줄어들지 않는다. 담당자에게 옮겨갈 뿐이다.

나는 기술 관리에 바로 그 판단이 포함돼야 한다고 생각한다. 어떤 제품의 방식이 달라야 하는지, 그 차이를 유지하는 비용이 타당한지, 공통 도구가 부족해서 각 팀이 같은 일을 반복하고 있지는 않은지 살펴야 한다.

관리자가 모든 배포 설정을 직접 작성할 필요는 없다. 기술 리더와 제품 담당자가 선택할 수 있게 권한을 나누고, 함께 검토할 시간을 확보하고, 결정이 충돌할 때 우선순위를 정할 수 있어야 한다.

“알아서 하라”고 말하면서 시간과 권한을 주지 않으면 선택권은 형식에 그친다. “전부 통일하라”고 말하면서 제품별 제약을 해결하지 않으면 문서 밖의 일이 늘어난다.

두 경우 모두 누군가는 빈칸을 채운다. 그리고 그 사람의 수고가 공식적인 계획과 비용에서 빠지기 쉽다.

이전에 책임이 둥둥 뜨면 버그가 된다고 썼다. 개발 방식도 마찬가지라고 생각한다. 규칙이 답하지 못한 상황에서 누가 판단해야 하는지 모르면, 일정이 가까워질수록 가장 손이 빠른 사람이 떠맡게 된다.

규칙을 정한 뒤 남는 판단을 모두 현장에 넘기고 있다면, 관리가 끝난 것이 아니라 관리할 일이 보이지 않게 된 것일 수 있다.

선택한 방식도 고칠 수 있어야 한다

방법론을 도입하면 그 선택을 지키는 일이 목표가 되기 쉽다. 예외가 나오면 먼저 규칙 위반인지 확인하고, 방식이 잘 맞지 않는다는 의견을 구성원이 충분히 적응하지 못한 탓으로 돌리기도 한다.

하지만 실제 운영에서 드러난 불편은 선택을 수정할 데이터일 수 있다.

자주 통합하려는데 변경을 독립적으로 나누기 어렵다면 구조와 계약을 살펴야 한다. 릴리스 브랜치마다 같은 수정이 반복해서 누락된다면 지원 범위와 반영 절차를 바꿔야 한다. 태그 배포를 도입했는데 후보 검증 대기만 늘었다면 후보를 고르는 시점과 검증 방식을 다시 봐야 한다.

작은 범위에서 새 방식을 적용하고, 이전 방식과 비교할 수 있는 기록을 남기면 좋겠다. 변경 크기와 위험도가 달라졌는지까지 함께 봐야 단순한 전후 숫자를 과신하지 않을 수 있다.

빠르다고 판단한 방식이 실제로도 빨랐는지, 줄었다고 생각한 일이 다른 사람에게 넘어간 것은 아닌지 확인하는 것이다. 기대한 효과가 없다면 선택을 수정할 수 있어야 한다. 방법을 바꾸는 비용도 있겠지만, 맞지 않는 방식을 계속 유지하는 비용 역시 있다.

나는 이런 관리를 기대한다

나는 제품마다 무조건 다른 방식을 쓰고 싶다는 이야기를 하는 것이 아니다. 같은 방식이 잘 맞는다면 함께 쓰는 편이 좋다. 다만 같아야 한다는 결론부터 내려놓고 제품을 거기에 맞추는 순서는 경계하고 싶다.

통합 지연을 줄여야 하는 곳에서는 작업 브랜치의 수명을 줄이고 자주 통합한다. 출시 대상을 지정할 때는 태그를, 별도의 안정화와 버전 유지보수가 필요할 때는 릴리스 브랜치를 사용할 수 있다. 공통으로 해결할 수 있는 일은 도구로 줄이고, 다른 판단이 필요한 곳에는 그 판단을 맡을 사람과 시간을 둔다.

그렇게 선택한 결과를 확인하고 다시 고치는 일까지 포함해서 운영이라고 생각한다.

“우리는 TBD다”라는 설명을 듣고 나면 나는 한 문장을 더 듣고 싶다.

이 제품에서 무엇을 줄이려고 골랐고, 어떤 비용은 감수하기로 했으며, 그 결과를 누가 확인하고 있는지.

모든 제품을 같은 방식으로 배포하게 만드는 것보다, 서로 다른 방식이 왜 필요한지 설명하고 그 결과를 책임지는 일이 더 어렵다. 내가 기대하는 기술 관리에는 그 어려운 판단이 들어 있다.