Meta와 Google은 거의 10년 동안 스태킹을 사용해 왔다. 작은 PR에 매우 효율적인 코딩 워크플로다. 그렇다면 스태킹이란 무엇이며, 왜 업계 전반에 더 널리 퍼지지 않았을까?
구독하기 로그인
2023년 10월 17일
∙ 유료
337
30
16
공유
👋 안녕하세요, Pragmatic Engineer Newsletter의 🔒 구독자 전용 호 🔒와 함께하는 Gergely입니다. 저는 매 호마다 엔지니어링 매니저와 시니어 엔지니어의 관점에서 Big Tech와 스타트업의 과제를 다룹니다.
구독하기
제가 Uber에서 개발자로 일하던 시절, 개인적으로 가장 크게 “와” 하고 느꼈던 순간 중 하나는 스택드 디프를 사용하기 시작했을 때였습니다. 스태킹은 기능 하나에 대한 pull request(PR)를 서로 의존하는 여러 개의 더 작은 PR로 나누는 것을 뜻합니다. 그래서 “stacked”라는 표현을 씁니다. 직관에 어긋나 보일 수도 있지만, 이 워크플로는 PR, 즉 Uber에서 우리가 부르던 표현으로는 “diff”를 더 쉽게 리뷰하고 수정할 수 있게 해 주기 때문에 엄청나게 효율적입니다.
스태킹이라는 개념은 Google에서는 오래전부터 존재했지만, 회사 바깥에서 이 워크플로를 본격적으로 대중화한 첫 회사는 Meta였습니다. Meta는 stacked diffs를 지원하는 자사 내부 개발 도구 Phabricator를 오픈소스로 공개했습니다. 오늘날까지도 Meta 엔지니어들은 stacked diffs를 일상적으로 사용합니다.
오늘 호를 위해 저는 스태킹에 대한 통찰을 듣고자 전직 Meta 소프트웨어 엔지니어 다섯 명에게 연락했습니다. 이들은 모두 지금은 개발자 도구 스타트업 Graphite의 공동창업자이거나 초기 직원입니다. Graphite는 GitHub를 사용하는 어떤 회사에서든 이 스태킹 워크플로를 구현합니다.저는 Graphite에 소액 투자하고 있습니다. Uber에서 스태킹이 생산성에 미치는 큰 영향을 직접 경험했고, 왜 이 도구가 더 널리 제공되지 않는지 궁금했기 때문입니다.
오늘은 다음 내용을 다룹니다:
스택드 디프: 개념
Meta는 stacked diffs를 어떻게 사용하는가
스태킹에 대한 Google의 접근법: chaining
Meta와 Google 밖에서의 스태킹
Meta의 다른 멋진 개발자 도구들 - 여기에는 1년 전 Inside Meta’s Engineering Culture에서 이전에 다루지 않았던 것들도 포함됩니다
Meta의 개발자 도구가 업계에 미친 영향
Meta에서 일하는 것과 Graphite 같은 스타트업에서 일하는 것의 차이
이 워크플로를 지원하는 도구를 찾고 있다면, 다음을 살펴보세요:
스태킹은 실제로 해 보면 훨씬 이해하기 쉬운, 다소 까다로운 개념입니다. 이를 돕기 위해 과정 설명과 시각화를 따로 진행하겠습니다.
Tomas Reimers는 Meta에서 2.5년 동안 일한 뒤, 3년 전 Graphite를 공동창업했습니다. 그는 스태킹을 이렇게 설명합니다:
‘스태킹을 사용하면 리뷰를 기다리지 않고도 작은 PR을 많이 쉽게 만들 수 있습니다. 전통적인 git 워크플로에서는 기능 하나가 PR 하나와 대응됩니다. PR은 각자의 브랜치를 가지며, 여러 커밋으로 구성됩니다. 전통적인, 즉 스태킹이 아닌 워크플로는 다음과 같습니다:
기능 완성
PR 제출
PR 승인 대기
승인되면 main에 머지
‘PR이 머지되면 다음 기능 작업을 시작하고 싶어집니다. 그래서 다시 main에서 브랜치를 따서 작업을 시작합니다. 아무리 매끄럽게 진행돼도 이 과정에는 긴 시간이 걸립니다! 가장 큰 시간 낭비는 PR이 몇 시간 또는 며칠 동안 리뷰를 기다리며 멈춰 있는 것입니다. 우리는 1,500만 건의 pull request에 대한 과거 데이터를 분석했고, 머지되기까지 몇 년을 기다린 PR도 있었습니다!
‘스태킹은 변경의 단위를 여러 커밋으로 이루어진 pull request가 아니라 개별 커밋으로 바꿉니다. 스태킹에서는 더 큰 변경을 훨씬 더 작은 여러 변경으로 나눕니다. 이 변경, 즉 커밋 하나는 개별적으로 테스트하고, 리뷰하고, 반영하고, 되돌릴 수 있습니다.
‘이처럼 더 작은 변경들의 모음, 즉 “stack”은 서로 위에 차곡차곡 쌓아 올리며 계속해서 만들 수 있기 때문에, 엔지니어가 막히지 않고 계속 앞으로 나아갈 수 있습니다. 실용적인 예를 들어 보면, 기능 하나를 만든 뒤 그 완성된 기능을 활용해 다음 기능을 만들고 싶은 경우가 매우 흔합니다. 스태킹을 사용하면 첫 번째 기능이 승인되고 머지되기를 기다릴 필요 없이 계속 작업하면 됩니다!
‘방법론으로서의 스태킹은 개발자가 main 브랜치 의존성으로 인한 지연을 우회하게 해 주며, 지속적인 병렬 개발을 가능하게 합니다.’
스태킹은 시각적으로 설명하면 더 도움이 되므로, 아래는 제가 시도해 본 설명입니다. 전통적인 pull request 흐름은 다음과 같이 작동합니다:
고전적인 PR 흐름. 브랜치를 체크아웃하고, 변경을 만들고, 리뷰를 받고, 다시 main에 머지합니다.
생산적인 엔지니어는 충분히 작고, 원자적이며, 리뷰하기 쉬운 pull request를 만드는 경향이 있습니다. 그렇다면 작은 PR들의 연속을 어떻게 만들 수 있을까요? 선택지는 두 가지입니다:
PR을 만들고, 이 PR이 리뷰되기를 기다린 뒤, 다음 PR을 만든다. 시간이 많이 듭니다!
PR을 만든 뒤, 이 PR의 리뷰를 기다리는 동안 두 번째 새 PR을 담을 새 브랜치를 시작해 계속 작업한다.
생산적인 엔지니어들이 가장 흔히 택하는 방식은 2번입니다. 물론 컨텍스트 스위칭이 조금 더 생기지만, 기다리며 아무것도 하지 않는 것보다는 낫겠죠? 그래서 여러 pull request를 작업할 때의 전형적인 흐름은 다음과 같습니다:
여러 pull request를 작업할 때 더 전형적인 워크플로는, 댓글이 들어오기를 기다리는 동안 여러 PR을 병렬로 작업하는 것입니다. 컨텍스트 스위칭은 더 많지만, 기다리며 아무것도 하지 않는 것보다는 낫습니다!
하지만 이 “전통적인” pull request 흐름은 PR들이 서로 의존할 때는 잘 작동하지 않습니다. PR들이 완전히 독립적이기만 하다면 고전적인 흐름도 충분히 잘 작동하고, 로컬에서 서로 다른 브랜치 사이를 오가면 됩니다.
하지만 PR_1의 변경이 main 브랜치에 들어간 것을 전제로 PR_2에서 변경을 만들고 싶다면 이야기가 달라집니다. 이 경우에는 PR_1 승인을 기다리면서 리뷰어에게 검토를 서둘러 달라고 ping하는 수밖에 없습니다.
실제 현장에서는 개발자들이 보통 PR_1에 커밋을 더 추가해서 이를 거대한 코드 변경으로 만들어 버립니다. 이상적이지는 않지만, PR을 여러 개의 독립적인 부분으로 “분할”할 좋은 방법이 없기 때문에 선택의 여지가 없습니다. 바로 이 지점에서 stacked diffs가 등장합니다.
stacked diffs의 핵심 아이디어는 main 브랜치에서 계속 작업을 이어가고, 리뷰는 나중에 걱정해도 된다는 것입니다. main 브랜치를 체크아웃하고 작업을 시작해 작은 변경을 만들고, 이를 diff로 커밋합니다. 즉 아주 작은 pull request입니다. 그런 다음 계속 작업하면서 두 번째 diff, 세 번째 diff를 만들어 갑니다.
이 diff들을 머지하려면 여전히 리뷰가 필요하지만, 그건 나중에 해도 됩니다. 그러면 워크플로는 다음처럼 바뀝니다:
stacked diffs로 작업할 때의 워크플로. diff를 만든 뒤 다음 코드 변경으로 넘어갑니다.
리뷰를 기다리지 않고 현재 브랜치에서 계속 작업한다는 것은 작은 변화처럼 느껴질 수 있습니다. 하지만 작은 diff와 작은 pull request로 작업할 때는 이것이 엄청난 생산성 향상으로 누적됩니다. 차이를 시각적으로 보면 다음과 같습니다:
pull request를 사용할 때와 stacked diffs로 작업할 때의 워크플로 차이
stacked diffs의 진짜 장점은 하나의 코드 변경을 끝내고 다음으로 넘어갈 때도 “코딩 흐름”이 끊기지 않도록 지켜 준다는 점입니다:
stacked diffs로 작업하면 개발자가 “흐름” 속에 머물며 다음 diff를 만드는 데 집중할 수 있습니다. 리뷰는 나중으로 미룰 수 있습니다.
stacked diffs에서는 모든 커밋(diff)이 하나의 코드 리뷰가 됩니다. 이것이 stacked diffs의 핵심 아이디어입니다. 나머지는 모두 도구의 문제입니다.
대부분의 개발자는 크고 복잡한 작업을 수행하며, 이런 복잡성 때문에 엔지니어에게 pull request를 최대 200줄 변경으로 제한하라고 요구하는 것은 현실적이지 않습니다. 때로는 하나의 완전한 작업 단위가 수백 줄, 심지어 수천 줄에 이를 수도 있습니다.
사실상 커밋인 diff를 사용하면 모든 커밋의 크기를 작게 유지하는 것이 현실적이 됩니다. 1,000줄의 코드를 바꾸는 큰 작업도 다음과 같이 세 개의 별도 diff로 나눌 수 있습니다:
스캐폴딩(800줄): “지루하지만” 장황한 부분입니다. 아마 자동 생성 코드일 수도 있습니다.
핵심 비즈니스 로직(150줄): 코드에 가해진 핵심 변경
엣지 케이스(50줄): 핵심 코드에 속하지 않는 경우 처리
갑자기 이 코드를 리뷰하고 문제를 찾아내기가 훨씬 쉬워집니다. 개발자로서 작업을 더 잘 정리하는 데도 도움이 됩니다!
이론상 stacked diffs는 꽤 쾌적한 워크플로처럼 보입니다. 하지만 main 브랜치나 더 앞선 diff에 변경이 생기면 상황이 조금 더 복잡해집니다. 후자의 경우, 즉 리뷰어가 첫 번째 diff에 큰 변경을 요청하는 상황을 보겠습니다.
이 예시에서는 서로 의존하는 세 개의 stacked diff가 있습니다:
서로 의존하는 세 개의 코드 변경(diff)
마지막 코드 변경(diff)까지 마친 뒤, Diff 1의 코드 리뷰가 끝났습니다. 리뷰어가 몇 가지 문제를 발견했고 Diff1에는 상당한 변경이 필요합니다. 그러면:
Diff 2와 3까지 완료한 뒤 Diff 1에 변경이 필요해졌습니다. 이제 어떻게 할까요?
이제 어떻게 해야 할까요? Diff2와 Diff3는 업데이트된 Diff 1의 변경을 포함하지 않는 한 main 브랜치에 반영될 수 없습니다. 바로 이것이 우리가 Interactive rebasing을 사용하는 이유입니다. 기본적으로 로컬 git 히스토리를 다시 쓰는 것입니다. 우리는 git rebase -i 명령을 실행합니다. Diff1의 변경이 Diff2와 충돌을 일으키면, 업데이트된 Diff2에 포함되길 원하는 변경을 수동으로 선택합니다. 그리고 Diff3에도 같은 작업을 합니다:
Interactive rebase를 사용해 Diff 2와 Diff 3를 모두 리베이스합니다. 이는 Diff 1의 변경에 따라 간단할 수도 있고 복잡할 수도 있습니다!
stacked diffs로 작업한다는 것은 보통 작은 diff를 더 자주 리베이스한다는 뜻입니다. 제가 Uber에서 Phabricator로 stacked diffs를 사용하기 시작했을 때, 리베이스는 거의 매일 하는 습관이 되었습니다. 체인 앞쪽의 earlier diff에 대해 의미 있는 피드백이 자주 와서 큰 코드 변경이 필요했기 때문만은 아니었습니다. main 브랜치 자체도 자주 바뀌었기 때문입니다!
물론 리베이스는 stacked diffs만의 문제는 아닙니다. 많은 엔지니어가 같은 코드베이스의 같은 부분을 작업할 때면 언제든 발생하는 고충입니다. stacked diffs는 오히려 리베이스를 조금 더 쉽게 만들어 줍니다. 병합해야 하는 충돌이 더 작아지는 경향이 있기 때문인데, diff 자체가 더 작기 때문입니다. 가장 위험한 리베이스는 보통 수백 줄 또는 수천 줄의 변경과 수십 개의 충돌이 있는 브랜치를 병합할 때입니다.
리베이스에 능숙해지는 것은 어떤 소프트웨어 엔지니어에게도 도움이 되는 기술입니다. stacked diffs로 작업하면 이 실천을 반드시 익히게 됩니다! 제가 덧붙이고 싶은 점은, 스태킹 워크플로를 사용할 때는 Phabricator (현재는 오픈소스 프로젝트로 유지보수되지 않음), Graphite, Gerrit (이를 patch-based approach라고 부름), Sapling 또는 ReviewStack 같은 전용 스태킹 도구가 기본 git 흐름보다 리베이스를 조금 더 쉽게 만들어 줄 수 있다는 것입니다.
Nick Yan은 Meta에서 4년 동안 일한 뒤, Graphite에 엔지니어로 합류했습니다. 그가 2017년에 당시 Facebook이었던 Meta에 입사했을 때, stacked diffs라는 주제가 온보딩 초기에 다뤄진다는 사실에 놀랐습니다:
© 2026 Gergely Orosz · 개인정보 보호 ∙ 이용 약관 ∙ 수집 고지
Substack는 훌륭한 문화의 본거지입니다