Skip to main content

Source-control Branching Model

소스 관리의 브랜칭 전략으로 Git-Flow, GitHub-Flow, Trunk-based Development를 알게 된 후 주변 분들께 여쭤보았는데 당시에 온전히 이해되지 않은 부분이 있었다. 그러던 중 마침 최범균님의 소개 - 트렁크 기반 개발을 선호하는 이유 5가지를 보고, 공유해주신 참고 자료를 바탕으로 제대로 정리해보려 한다.


Git-Flow

git-flow

Git-Flow란, GitHub-Flow와 달리 release라는 버전별 배포를 관리하는 경우 사용한다. 소개할 세 가지 모델 중, Vincent Driessen에 의해 가장 먼저(2010년) 소개된 모델이다.

이들의 철학은 다음과 같다. "Gitflow is an alternative Git branching model that involves the use of feature branches and multiple primary branches."

이는 다음과 같은 특징을 가지고 있다.

  • 각 브랜치별 기능 분리 및 정의가 명확하다.
  • 각 개발 단계를 병렬로 수행 가능하다.
  • 매우 구체적인 버전 관리가 가능하다.
  • scale이 용이하다.
  • master 브랜치가 항상 clean한 상태로 유지된다.

즉, master 브랜치와 develop 브랜치가 있으며, 주로 develop 브랜치에서 개발을 하고, 각 release마다 master 브랜치에 merge 하는 방식이다. develop 브랜치에는 development 환경으로 배포, release 브랜치에는 production 환경으로 배포한다.

하지만 merge 전까지 conflict 등의 에러를 확인하기 어렵기 때문에, merge 과정에서 오버헤드가 발생할 수 있다. 그래서 유지보수하기 어렵다는 단점이 있다.


GitHub-Flow

github-flow

GitHub-Flow란, 브랜치 기반의 개발 방식이다. 아래의 Git-Flow와 유사하지만 release 브랜치가 따로 없으며, master와 feature 브랜치를 바탕으로 Pull Request, Code Review, Merge 등의 과정을 빠르게 반복하는 방식이다.

이들의 철학은 다음과 같다. "Once a version is ready to go, it can be deployed."

이는 다음과 같은 특징을 가지고 있다.

  • CD에 특히 강하다 (ready to go)
  • 규칙이 명확하고 간단하다.
  • 브랜칭 전략에 따른 기술부채가 덜하다.
  • 작고 agile하며, release 버전이 많이 필요없을 경우 선호된다.

즉, release를 따로 관리하지 않는 소스 코드를 작은 규모의 팀이 빠르게 개발할 때 용이한 방식이다. 하지만 Git-Flow만큼 구조화가 잘 돼 있지 않아, 전반적인 코드베이스 이해를 하는데에 있어서는 기술부채만큼 기회비용이 생긴다.


Trunk-based Development(TBD)

trunk-based-development

Trunk-based Development(TBD)란, master와 같은 공유된, 소위 Trunk라고 불리는 브랜치에서 모든 커밋을 관리하는 방식을 의미한다.

이들의 철학은 다음과 같다. "Branches create distance between developers and we do not want that."

이는 다음과 같은 특징을 가지고 있다.

  • CI에 특히 강하다. (Trunk 브랜치)
  • 짧은 주기(며칠 이내)의 브랜치에 최적화 돼 있다.
  • 언제든 Trunk 브랜치는 배포할 준비가 돼 있다.
  • 보통 release 브랜치가 따로 없다.
  • merge 할 때의 어려움을 해소할 수 있다.
  • merge 되기 전, 중복된 기능인지 확인하는 어려움을 해소할 수 있다.

즉, Trunk라는 강력한 줄기에 끊임없이 코드를 merge 함으로써, 배포할 준비가 된 최신의 상태를 유지할 수 있으며, 큰 규모의 브랜치가 아닌, 작은 규모의 코드를 merge 하는 방식이기 때문에, merge의 과정에서 어려움이 줄어든다는 장점을 가진다.


큰 규모의 서비스에서 많이 사용하는 Git-Flow가 정답일까?

확실히 팀별로 분업과 scale이 효과적이고, 큰 규모에서도 병렬로 개발이 가능하면서, 이전 버전으로 빠르게 롤백이 가능한 Git-Flow가 무조건 정답일까? 마냥 정답은 아닌 것이, 실제 기업에서도 여러가지 애로사항이 존재하는 경우를 많이 볼 수 있다.

이에 대해 우아한형제들에서는 다음과 같은 고충을 토로하고 있다.

Github-flow일 때보다 늘어난 브랜치들을 관리 해야 하는 부담은 늘었지만 전보다 일관되게 여러 상황들을 대처할 수 있는 것 같습니다. 물론 Git-flow 가이드대로 항상 흘러가지만은 않았습니다. release 브랜치를 시작했는데 기능이 추가돼야 하는 경우도 있고, 때로는 feature들이 많아서 완료된 feature들만 먼저 release에 포함해서 QA를 우선 시작하는 경우도 있었습니다. 현재는 Git-flow를 그대로 따라 하고 있지만 이런 시행착오를 겪으면서 우리에게 맞는 브랜치 전략으로 발전할 거라고 믿고 있습니다.

그리고 TBD처럼 feature/*** 등의 브랜치는 어떻게 처리하는지에 대한 QnA가 있었는데, 아래와 같이 답변을 해주셨다.

woowahanbros-branching-model

추가로 release 브랜치도 배포되고 나면 더이상 사용하지 않기 때문에 삭제한다고 말한다. 롤백을 해야하는 경우 revert를 사용해서 해당 커밋을 되돌렸다고도 추가로 설명해주었다.


사실 가장 단순하면서 직관적이고 최신인 Trunk-based Development는 어떨까?

Trisha Gee의 아티클

최범균님의 소개 - 트렁크 기반 개발을 선호하는 이유 5가지에서 JetBrains Lead Java Developer인 Trisha Gee의 아티클을 소개했다. 해당 아티클에서는 다음과 같은 장점들을 소개한다.

  • Speed and Efficiency
  • Greater Code Stability
  • Enhanced Team Collaboration
  • Improved Continuous Integration and Delivery (CI/CD) Practices
  • Reduced Technical Debt

아티클에서 몇 가지 재미있는 구절들

While these days we tend to mean "run your build and tests on a team server every time you commit" when we say CI, what CI really meant was actually integrate your code regularly. Code living on separate branches is, by definition, not integrated.

(...)

Integrating small changes regularly into your code is usually less painful than a big merge at the end of a longer period of time.

그렇다. 진정한 CI라고 하는 것은, 브랜치에 잠들어 있는, 또는 혼자만 수정하고 개발하고 있는 브랜치가 아니라, **"지속적으로 병합하는 것"**을 정확히 지적하고 있다. 또한 이러한 방식이 이후 merge를 할 떄의 기술부채를 줄이는 데 효과적이라는 점도 언급하고 있다.


Trunk-based development encourages frequent commits, which leads to smaller and more manageable changes. How and why? For the same reason that we don't want big merges from long-lived branches - the longer we leave it to commit our changes, the higher the chances our commit will clash with someone else's changes.

이 또한 너무나도 공감되는 구절이다. 가령 10개 이상의 파일을 건드리고, +13,950 -4,123과 같은 Pull Request를 받는다면, 대체 어떻게 코드 리뷰를 할 지 막막하다. (게다가 테스트 코드조차 존재하지 않는다면?) 이에 대해서는 Pull Request, 어떻게 사용할까?에도 상술해두었다.


If you're not pairing, then at least you want to be working on the same code, right? If you're all working on your own branches, you are not collaborating.

이건 Pair Programming에 대한 내용인데, 실제로 우아한형제들에서도 프론트/백 개발자가 함께 진행한다는 내용을 들은 적 있다. 제대로 해보진 않았지만 JetBrains, 우아한형제들과 같은 규모가 있는 기업에서 사용한다니 나도 꼭 해보고싶다는 생각이 든다.


Trunk-based Development의 주의사항

하지만 Continuous Delivery라는 책을 저술한 Dave Farley는 또한 다음과 같은 주의사항을 함께 제시하고 있다.

  • Code Reviews
  • Don’t wait to commit
  • Automated Testing

여기서 Dave는 Code ReviewPair Programming 중, Pair Programming의 손을 들어주었다.


Code Review vs Pair Programming에 대한 개인적인 생각

사실 우아한형제들의 대표 김범준님께서도 언급은 안 하셨지만 바로 이 Pair Programming을 경험하셨지 않을까 조심스레 추측해본다. (특히 팀간의 벽을 실제로 허물고 함께 개발하고, 무엇보다 이때의 경험이 아주 재미있었다는 점을 고려해본다면)

개인적인 생각으로 이 둘의 가장 큰 차이점은 동기성(Synchronicity)이라고 생각한다. Code Review의 경우, 구현이 완료된 코드를 리뷰하는 방식(Asynchronous, 비동기) 인데 반해, Pair Programming의 경우 실시간으로 서로의 코드를 피드백**(Synchronous, 동기)** 하는 방식이다.

즉, Code Review는 리뷰를 해줄 때까지 기다려야 하는 단점이 있고 이는 꽤나 빈번하게 발생했던 경험이 있다. 반면 Pari Programming의 경우, 실시간으로 빠르게 정보 공유가 가능하다는 장점이 있어, 특히 주니어 레벨의 개발자일수록 더욱 효과적이라고 생각한다.

정말 Pair Programming! 꼭 한번 경험해보고싶다.


Trunk-based Development의 또 다른 케이스, 맘시터

개발 회사 중 기술 블로그의 퀄리티가 우수한 회사 중 하나로 맘시터가 있는데, 맘시터에서는 Git Flow에서 트렁크 기반 개발으로 나아가기라는 아티클로 TBD에 대한 적응기를 소개했다.


그래서 결론

여러가지 아티클과 자료를 참고했을 때, 관통되는 내용을 찾을 수 있었다.

기술부채를 줄이기 위해 작은 단위로 빠르게 병합하는 것

이는 결국 개발의 효율을 극대화 하고, 개발자가 갖게 되는 부담을 줄이기위한 목적이기 때문에 어디까지나 정답은 없을 것이다. 하지만 Source-control Branching Model은 결코 쉽게 결정될 것이 아니라, 팀의 상황에 맞게 적용하기 위해 정말 많은 고민의 과정을 거쳐야 할 것이다.

Related Links