BabySteps
주니어 개발자들을 위한 패턴 언어 - 시스템의 생명력을 유지하며 복잡한 변경을 수행하는 방법
Contents
The Story 1: The Long Tunnel vs. Stepping Stones (Programming)
두 명의 개발자, 민수와 하나가 똑같이 복잡한 리팩토링 작업을 맡았다. 거대한 User 클래스에서 Billing 로직을 분리하는 작업이다.
민수의 접근 (The Big Jump): 민수는 생각했다. "이건 다 연결되어 있으니까 한 번에 옮겨야 해." 그는 코드를 자르고 붙여넣기 시작했다. 30개의 파일이 수정되었고 컴파일 에러가 150개 떴다. 민수는 "금방 잡을 수 있어"라고 말했지만, 3시간 후에도 에러는 여전했고 무엇이 문제였는지 기억조차 희미해졌다. 시스템은 3시간 동안 '죽어' 있었다.
하나의 접근 (Baby Steps): 하나는 다르게 시작했다.
새로운 Billing 클래스를 빈 상태로 만들었다. (Green)
User의 메서드 하나를 Billing으로 복사했다. (Green)
User가 Billing의 메서드를 호출하도록 위임했다. (Green)
하나는 10분마다 커밋했다. 커피를 마시러 갈 때도, 퇴근할 때도 그녀의 코드는 항상 작동했다. 작업이 끝났을 때 민수는 여전히 디버깅 중이었고, 하나는 이미 다음 기능을 만들고 있었다.
The Story 2: The Piano Lesson (Ordinary Life)
민수와 하나는 취미로 피아노를 배우고 있다. 오늘 숙제는 아주 빠르고 복잡한 쇼팽의 연습곡이다.
민수의 연습 (The Whole Song): 민수는 곡의 처음부터 끝까지 한 번에 완벽하게 치고 싶어 한다. 그는 악보를 펴고 양손으로 힘겹게 연주를 시작했다. 중간에 계속 틀렸지만, "연습하다 보면 되겠지"라며 멈추지 않고 끝까지 쳤다. 한 시간을 연습했지만 민수의 연주는 여전히 뚝뚝 끊겼고, 어디가 틀렸는지조차 알 수 없게 되었다.
하나의 연습 (One Bar at a Time): 하나는 다르게 연습했다.
- 첫 번째 마디의 오른손만 완벽하게 친다.
- 그 마디의 왼손만 완벽하게 친다.
- 양손을 합쳐 한 마디를 완벽하게 친다.
하나는 단 한 마디라도 '완벽하고 리드미컬하게' 들릴 때까지 다음으로 넘어가지 않았다. 그녀의 연습은 매 순간 '음악'이었고, 민수보다 느려 보였지만 결국 곡 전체를 가장 먼저 마스터했다. 기예는 작은 성공을 쌓아 올리는 과정임을 하나는 알고 있었다.
Context
당신은 무엇을 만들어야 할지 알고 있다(TinyExperiment를 통해). 하지만 그 작업은 크고 복잡하다. 리팩토링이거나, 큰 기능 추가일 수 있다.
일상적인 상황:
- 코드를 수정하기 시작해서 1시간이 지났는데, 아직 컴파일이 안 된다.
- "거의 다 됐어, 이것만 연결하면 돼"라고 말하지만, 그 상태가 반나절 지속된다.
- 변경 사항이 너무 많아서 어디서 버그가 발생했는지 알 수 없다.
- 동료가 "그거 지금 실행돼?"라고 물으면 "아니, 지금 작업 중이라 안 돼"라고 답한다.
Problem
한 번에 너무 많은 변화를 시도하면, 시스템의 "생명력(Wholeness)"이 깨진다.
시스템이 작동하지 않는 상태가 길어질수록 복잡성은 폭주하고 심리적 압박은 커진다.
복잡성의 폭주: 여러 변경 사항이 서로 얽혀 문제가 생겼을 때 원인을 찾을 수 없다.
심리적 압박: "돌아갈 수 없다"는 공포가 생기고, 앞이 보이지 않는다.
중단 불가능: 긴급한 다른 이슈가 생겨도 현재 작업을 멈출 수 없다.
Solution
시스템을 항상 작동하는 상태로 유지할 수 있는 가장 작은 단위로 쪼개어 변경하라.
BabySteps는 각 단계가 끝날 때마다 시스템이 온전해야(Structure-Preserving) 한다는 규율이다.
Principle 1: Always Green (항상 녹색 상태 유지)
가장 중요한 규칙이다. 한 단계를 마쳤을 때, 모든 테스트가 통과해야 한다. 만약 테스트가 깨졌다면? 당신은 보폭을 너무 크게 잡은 것이다.
Comment Out and Revert (한 보 후퇴): 새로 작성한 코드에서 오류가 발생했을 때, 즉시 해당 코드를 주석 처리(Comment out) 하십시오. 한 스텝 뒤로 가서 제대로 동작하는 것을 재확인하고, 그 지점에서 다시 아주 작은 증분을 탐색하십시오.
Principle 2: Parallel Change (평행 변경)
기존 것을 지우고 새 것을 넣는 것이 아니라, 새 것을 옆에 만들고 서서히 옮겨라.
확장(Expand): 새 기능을 추가한다.
이주(Migrate): 사용하는 곳을 하나씩 새 기능으로 옮긴다.
수축(Contract): 사용하지 않는 기존 기능을 제거한다.
Principle 3: Make it Easy, Then Make it Right
Kent Beck의 조언: "Make the change easy, then make the easy change."
- 기능을 추가하기 쉽게 구조를 먼저 바꿔라(리팩토링). 그 다음 기능을 추가하라.
Real Examples
Example 1: Function Signature Change
인자를 추가할 때, 기존 호출부를 다 고치는 대신 기본값(Default value)을 주어 먼저 작동하게 만든 뒤, 하나씩 호출부를 수정해 나가는 방식.
Example 2: Renaming a Class
기존 클래스를 상속받는 새 이름의 클래스를 만들고, 사용하는 곳을 하나씩 옮긴 뒤 마지막에 옛 클래스를 삭제하는 방식.
Common Pitfalls
"It's slower!" (느려 보여요!)
토끼와 거북이다. 거북이는 멈추지 않기 때문에 빠르다. 디버깅 지옥에 빠지지 않는 것이 가장 빠른 길이다.
"Just one more line..." (한 줄만 더...)
이 유혹에 넘어가는 순간 3시간의 디버깅이 시작된다. 5분이 지나도 해결이 안 되면 무조건 돌아가라(Revert).
Connection to Other Patterns
TightLoop - 빠른 피드백 루프가 있어야 보폭을 작게 유지할 용기가 생깁니다. 동력
AtomicCommit - 각 Baby Step의 끝이 바로 원자적 커밋의 시점입니다. 기록의 단위
GreenRefuge - 발을 헛디뎠을 때 즉시 돌아갈 안전 지대입니다. 보완
TinyExperiment - 보폭을 떼기 전 어디로 갈지 먼저 길을 찾으십시오. 선행
Signs of Success
- 코딩이 평온한 리듬을 탄다. 스트레스가 없다.
- 디버거를 켜지 않는다. 문제가 생겨도 방금 작성한 3줄 안에 원인이 있다.
- 언제든 퇴근할 수 있다. 10분만 정리하면 작업 끝이다.
- 코드 리뷰가 쉽다. 동료가 작은 커밋들을 보며 의도를 쉽게 파악한다.
The Ultimate Insight
속도는 "빨리 타이핑하는 것"에서 나오지 않는다. "멈추지 않는 것"에서 나온다.
BabySteps는 멈추지 않게 해주는 기술입니다. 시스템을 항상 살아있게 하십시오. 그것이 가장 빠르고 안전하게 목적지에 도달하는 방법입니다.
CategoryPatternLanguage CategoryProgramming CategoryRefactoring CategoryTDD
