BabySteps
주니어 개발자들을 위한 패턴 언어 - 시스템의 생명력을 유지하며 복잡한 변경을 수행하는 방법
Contents
The Story: The Long Tunnel vs. Stepping Stones
두 명의 개발자, Jin과 Kai가 똑같이 복잡한 리팩토링 작업을 맡았다. 레거시 코드의 거대한 User 클래스에서 Billing 로직을 분리하는 작업이다.
Jin의 접근 (The Big Jump): Jin은 생각했다. "이건 다 연결되어 있으니까 한 번에 옮겨야 해." 그는 코드를 자르고 붙여넣기 시작했다. 30개의 파일이 수정되었다. 컴파일 에러가 150개 떴다. "금방 잡을 수 있어." 3시간 후, 에러는 잡았지만 테스트가 실패했다. 2일 후, Jin은 여전히 깨진 시스템과 씨름하고 있었다. 그는 지쳤고, 무엇이 문제였는지 기억조차 희미해졌다. 시스템은 2일 동안 '죽어' 있었다.
Kai의 접근 (Baby Steps): Kai는 다르게 시작했다. 1. 새로운 Billing 클래스를 빈 상태로 만들었다. (테스트 통과) 2. User의 메서드 하나를 Billing으로 복사했다. (아직 사용 안 함, 테스트 통과) 3. User가 Billing의 메서드를 호출하도록 위임했다. (테스트 통과) 4. User의 기존 코드를 지웠다. (테스트 통과)
Kai는 10분마다 커밋했다. 커피를 마시러 갈 때도, 퇴근할 때도 그의 코드는 항상 작동했다. "느려 보이지? 하지만 난 디버깅하느라 멈춘 적이 없어."
작업이 끝났을 때, Jin은 아직도 디버깅 중이었고, Kai는 이미 다음 기능을 만들고 있었다.
Context
당신은 무엇을 만들어야 할지 알고 있다(TinyExperiment를 통해). 하지만 그 작업은 크고 복잡하다. 리팩토링이거나, 큰 기능 추가이거나, 라이브러리 교체일 수 있다.
일상적인 상황:
- 코드를 수정하기 시작해서 1시간이 지났는데, 아직 컴파일이 안 된다.
- "거의 다 됐어, 이것만 연결하면 돼"라고 말하지만, 그 상태가 반나절 지속된다.
- 변경 사항이 너무 많아서 어디서 버그가 발생했는지 알 수 없다.
- 동료가 "그거 지금 실행돼?"라고 물으면 "아니, 지금 작업 중이라 안 돼"라고 답한다.
당신은 불확실한 결과(TinyExperiment의 영역)가 아니라, 복잡한 과정(BabySteps의 영역)과 싸우고 있다.
Problem
한 번에 너무 많은 변화를 시도하면, 시스템의 "생명력(Wholeness)"이 깨진다.
시스템이 컴파일되지 않거나 테스트를 통과하지 못하는 상태는 죽은 상태다. 죽은 상태가 길어질수록:
복잡성의 폭주: 여러 변경 사항이 서로 얽혀 문제가 생겼을 때 원인을 찾을 수 없다.
심리적 압박: "돌아갈 수 없다"는 공포가 생긴다. 앞으로 가는 수밖에 없는데, 앞이 보이지 않는다.
중단 불가능: 긴급한 다른 이슈가 생겨도, 현재 작업이 마무리될 때까지 컨텍스트 스위칭을 할 수 없다.
Christopher Alexander는 말했다. "자연의 모든 생명체는 전체성을 유지하면서 성장한다(Structure-Preserving Transformations)." 애벌레가 나비가 될 때도, 그 중간 단계에서 생명체는 죽지 않는다. 하지만 프로그래머들은 자주 수술 중인 환자를 죽여놓고, 수술이 끝나면 다시 살아날 거라 기대한다.
Solution
시스템을 항상 작동하는 상태로 유지할 수 있는 가장 작은 단위로 쪼개어 변경하라.
BabySteps는 단순히 "작게 하는 것"이 아니다. 각 단계가 끝날 때마다 시스템이 온전해야(Structure-Preserving) 한다는 규율이다.
Principle 1: Always Green (항상 녹색 상태 유지)
가장 중요한 규칙이다. 한 단계를 마쳤을 때, 모든 테스트가 통과해야 한다. 만약 테스트가 깨졌다면? 당신은 보폭을 너무 크게 잡은 것이다. * 빨간 막대(실패)를 5분 이상 보지 마라. * 해결되지 않으면 즉시 돌아가라(GreenRefuge).
Principle 2: Parallel Change (평행 변경)
기존 것을 지우고 새 것을 넣는 것이 아니라, 새 것을 옆에 만들고 서서히 옮겨라. 1. 확장(Expand): 새 기능을 추가한다. (기존 기능은 그대로 둠) 2. 이주(Migrate): 사용하는 곳을 하나씩 새 기능으로 옮긴다. 3. 수축(Contract): 사용하지 않는 기존 기능을 제거한다.
이 과정의 모든 순간에 시스템은 작동한다.
Principle 3: Make it Easy, Then Make it Right
Kent Beck의 조언: "Make the change easy (warning: this may be hard), then make the easy change." * 구조를 바꾸면서 기능을 추가하지 마라. * 기능을 추가하기 쉽게 구조를 먼저 바꿔라(리팩토링). * 그 다음 기능을 추가하라.
Principle 4: The Mikado Method
목표가 너무 멀어 보일 때: 1. 목표를 시도한다. (실패: A가 필요함) 2. 돌아간다(Revert). 3. A를 시도한다. (실패: B가 필요함) 4. 돌아간다(Revert). 5. B를 구현한다. (성공!) 6. A를 구현한다. (성공!) 7. 목표를 구현한다. (성공!)
의존성의 그래프를 그리며 가장 끝단(Leaf)부터 하나씩 해결해 나간다.
Real Examples
Example 1: Function Signature Change
함수 인자를 process(a, b)에서 process(a, b, c)로 바꾸고 싶다.
Bad Way (Big Bang): 1. 함수 정의를 바꾼다. 2. 컴파일러가 에러를 낸 50군데 호출 코드를 전부 찾아다니며 수정한다. 3. 30분 동안 시스템은 컴파일되지 않는다.
Baby Steps Way: 1. process(a, b, c=None)으로 변경한다. (기본값 제공, 기존 코드 수정 불필요, Green) 2. 함수 내부에서 c가 있으면 사용하도록 로직 추가. (Green) 3. 호출하는 곳을 하나씩 process(a, b, new_value)로 수정. (한 번에 하나씩, Green) 4. 모든 호출이 수정되면 c의 기본값을 제거. (Green)
Example 2: Renaming a Class
Manager를 Service로 바꾸고 싶다.
1. Manager를 상속받는 Service 클래스를 만든다. (Green)
class Service extends Manager {}
2. Manager를 사용하는 코드를 하나씩 Service로 타입을 바꾼다. (Green) 3. 다 바꿨으면 Manager의 내용을 Service로 옮기고 상속을 끊는다. (Green) 4. Manager를 삭제한다. (Green)
Common Pitfalls
"It's slower!"
직관적으로는 느려 보인다. 1번 할 일을 4번에 나눠서 하니까. 하지만 통합 비용과 디버깅 시간을 고려하면 훨씬 빠르다. 토끼와 거북이다. 거북이는 멈추지 않기 때문에 빠르다.
"Just one more line..."
"이것만 고치면 될 것 같은데..."라는 유혹. 이 유혹에 넘어가는 순간 3시간의 디버깅 지옥이 시작된다. 5분이 지나도 해결이 안 되면 무조건 돌아가라(Revert).
"Dirty Code during transition"
중간 단계에는 코드가 지저분해 보일 수 있다(구버전과 신버전의 공존). 이것은 건축 중인 건물의 비계(Scaffolding)다. 완성을 위해 필요한 과정이지, 더러운 것이 아니다. 작업이 끝나면 제거된다.
Connection to Other Patterns
TinyExperiment - BabySteps를 하기 전에, 무엇을 해야 할지 모른다면 TinyExperiment로 먼저 길을 찾아라. 선행 관계
GreenRefuge - 발을 헛디뎠을 때(Test Fail), 즉시 안전한 곳(Green State)으로 돌아가라. BabySteps의 안전망이다. 보완 관계
AtomicCommit - 각 Baby Step은 하나의 커밋이 된다. 결과물
TightLoop - 단계가 작을수록 피드백 루프가 짧아진다. 효과
TwoWorlds - 문제 해결(TwoWorlds)이 끝나면, 구현은 BabySteps로 진행하라. 적용
Signs of Success
* 스트레스가 없다. 코딩이 평온한 리듬을 탄다. * 디버거를 켜지 않는다. 문제가 생겨도 방금 작성한 3줄 안에 원인이 있다. * 언제든 퇴근할 수 있다. 10분만 정리하면 작업 끝이다. * 코드 리뷰가 쉽다. 동료가 작은 커밋들을 보며 의도를 쉽게 파악한다.
The Ultimate Insight
속도는 "빨리 타이핑하는 것"에서 나오지 않는다. "멈추지 않는 것"에서 나온다.
BabySteps는 멈추지 않게 해주는 기술이다. 시스템을 항상 살아있게 하라. 그것이 가장 빠르고 안전하게 목적지에 도달하는 방법이다.
CategoryPatternLanguage CategoryProgramming CategoryRefactoring CategoryTDD
