Rain Lag

아날로그 리팩터링 철도 야드: 경쟁하는 코딩 우선순위를 위한 물리적 전환 시스템 설계하기

철도 화물 정렬장(rail yard) 비유, 칸반 아이디어, 그리고 아날로그 도구를 활용해, 동작을 보존하는 리팩터링과 기능 개발을 분리하면서도 딜리버리를 탈선시키지 않는 의도적인 ‘전환 시스템’을 설계하는 방법을 소개합니다.

아날로그 리팩터링 철도 야드: 경쟁하는 코딩 우선순위를 위한 물리적 전환 시스템 설계하기

소프트웨어 개발은 종종 분주한 철도 화물 정렬장(rail yard)을 운영하는 일처럼 느껴집니다. 새로운 기능은 급한 화물열차처럼 들이닥치고, 버그는 엉뚱한 선로로 빠져든 화차처럼 슬그머니 끼어듭니다. 오래 미뤄진 리팩터링 작업은 한쪽 측선에서 녹슬어 가고 있죠. 모두가 “이 야드를 좀 더 잘 정리해야 한다”고 말하지만, 항상 “이것만은 꼭 먼저 나가야 하는” 열차가 하나 더 있습니다.

이 글에서 제안하는 것은 구체적인 비유와 실천 방법입니다. 코드베이스와 작업 큐를 물리적인 철도 야드처럼 취급하고, 다음을 가능하게 하는 전환(switching) 시스템을 직접 설계해 보자는 것입니다.

  • 동작을 보존하는 리팩터링과 기능/버그 작업을 분리하기
  • 비기능적 이득을 눈에 보이고 추적 가능하게 만들기
  • **작업 중(WIP)**을 제한해 정신적 용량을 보호하기
  • 아날로그 도구(종이, 화이트보드, 포스트잇)를 태스크 라우팅을 위한 수동 스위치처럼 쓰기

왜 고속도로가 아니라 철도 야드인가?

우리는 보통 일을 파이프라인, 백로그, 플로우(flow) 같은 말로 설명합니다. 하지만 실제 개발 현장을 비유하기에는 철도 야드가 더 어울립니다. 왜냐하면:

  • 열차(작업)는 수시로 재구성되고 재조합되기 때문입니다.
  • 화차(하위 작업)는 필요에 따라 분리했다 다시 연결할 수 있습니다.
  • 전환(switching)에는 실질적인 비용이 들고, 계획이 필요합니다.
  • 용량 한계가 분명합니다. 한 선로에 화차가 너무 많으면 아무것도 움직이지 못합니다.

이 비유에서:

  • 선로(Track) = 작업 스트림 (기능 개발, 리팩터링, 버그 수정 등)
  • 화차(Car) = 개별 태스크 또는 코드 변경 단위
  • 스위치(Switch) = 다음에 무엇에 집중할지, 어디로 주의를 보낼지 정하는 의사결정
  • 기관차 용량 = 개발자의 집중력과 에너지

우리는 단지 “더 빠르게” 가려는 것이 아니라, 알림, 채팅, 반쯤 끝난 브랜치에 이리저리 끌려다니는 대신, 의도적으로 전환하는 방법을 배우려는 것입니다.


리팩터링과 기능 개발의 분리: 하나가 아닌 두 개의 본선

어떤 철도 야드든 핵심 설계 선택 중 하나는, 서로 다른 종류의 열차를 위한 **별도의 본선(mainline)**을 갖추는 일입니다. 코드에서도 가장 중요하게 구분해야 할 것은 다음 두 가지입니다.

  1. 동작을 보존하는 리팩터링(behavior-preserving refactor)
  2. 동작을 변경하는 작업 (새 기능, 버그 수정, 실험/시도)

동작을 보존하는 리팩터링이란 다음과 같은 변경을 말합니다.

  • 입력과 출력이 그대로입니다.
  • 기능적 동작은 동일합니다.
  • 대신 구조, 가독성, 도구 친화성이 좋아집니다.

지저분하고 즉흥적으로 짜인 VHDL-AMS를, 도구가 완전히 이해하고 합성(synthesis)까지 할 수 있는 구조화된 서브셋으로 옮겨 적는다고 생각해 봅시다. 동작은 그대로인데, 갑자기 다음과 같은 일이 가능해집니다.

  • 정적 분석 도구가 안정적으로 동작합니다.
  • 합성 도구가 애매한 엣지 케이스를 추측하지 않아도 됩니다.
  • 이후 변경이 더 안전하고 저렴해집니다.

사용자 입장에서는 새로운 기능이 하나도 생기지 않았는데도 가치가 어마어마합니다.

이걸 기능 개발과 같은 “선로”에 섞어 두면 고전적인 탈선이 벌어집니다.

  • 절반만 끝난 리팩터링이 급한 버그 수정을 가로막습니다.
  • 기능 브랜치 안에 상관없는 정리 작업이 우루루 섞여 커집니다.
  • 코드 리뷰에서 “이건 버그 픽스인가요, 이름 바꾸기인가요?”라는 혼란이 생깁니다.

설계 원칙: 리팩터링과 기능 개발은 선로 자체를 분리하고, 규칙도 따로 두십시오.


비기능적 이득: “겉으론 안 바뀌어도” 왜 리팩터링이 중요한가

비기능적 개선은 사용자 지향 로드맵에 잘 보이지 않기 때문에, 우선순위 싸움에서 지기 쉽습니다. 전환 시스템 안에서, 동작을 보존하는 리팩터링이 주는 숨은 이득을 명시적으로 인정해야 합니다.

  • 도구 호환성: 표준 서브셋(예: 잘 정의된 VHDL-AMS 프로파일)을 따르는 코드는 컴파일러, 린터, 포멀 툴이 다루기 훨씬 쉽습니다.
  • 합성과 배포: 깔끔한 인터페이스와 규칙적인 패턴은 예측 가능한 합성을 가능하게 하고, 마법 같은 설정을 줄이며, “프로덕션에서만 깨지는” 악몽을 줄여 줍니다.
  • 유지보수성: 역할이 분명한 작고 명확한 모듈은 온보딩을 빠르게 하고, 변경을 더 안전하게 만듭니다.
  • 검증 가능성: 잘 구조화된 코드는 테스트와 포멀 체크로 덮어 씌우기 훨씬 쉽습니다.

철도 야드 비유로 말하자면, 리팩터링은 휘어진 선로를 곧게 펴고 분기점을 다시 라벨링하는 일에 가깝습니다. 새로운 목적지가 늘어난 것은 아니지만, 화물이 이전과 똑같은 곳으로 가면서도 사고와 지연이 훨씬 줄어듭니다.

이런 비기능적 이득을 “시간 나면 하면 좋겠는 것”이 아니라, 계획 안에 명시적으로 포함된 것으로 다루어야 합니다.


칸반과 철도 야드의 만남: 선로, 화차, 그리고 WIP 제한

칸반이 주는 가장 단순하고도 강력한 아이디어는 작업을 시각화하고, 작업 중(WIP)을 제한하라는 것입니다. 철도 야드 비유는 여기에 물리적 감각을 더해 줍니다.

작업을 선로와 화차로 시각화하기

할 일 목록처럼 생긴 보드 대신, 실제 야드 지도를 닮은 물리적인 보드를 만듭니다.

  • 가로 방향의 **레인(lane)**을 선로처럼 사용합니다.
    • 선로 A: 동작을 변경하는 기능 개발
    • 선로 B: 동작을 보존하는 리팩터링
    • 선로 C: 버그 / 인시던트
    • 선로 D: 실험 / 스파이크(spike)
  • 카드는 하나의 화차입니다.
    • 짧고 구체적인 설명
    • 타입 라벨(Feature, Refactor, Bug 등)
    • 예상 검증 방식(테스트, 시뮬레이션, 합성 실행, 코드 리뷰 체크리스트 등)

카드는 실제 화차처럼 손으로 옮기며 진행 상황을 표현합니다.

  • "Inbound" → "Switched" → "In Progress" → "Verification" → "Outbound/Merged"

선로별 WIP(작업 중) 제한 두기

각 선로에는 엄격한 WIP 제한을 둡니다(예: 1인당 2–3카드까지). 이것이 곧 물리적인 야드 용량입니다. 선로가 가득 찼다면:

  • 더 이상 화차를 추가하지 않습니다.
  • 새로운 일을 올리기 전에, 기존 작업을 끝내거나 명시적으로 폐기해야 합니다.

이렇게 하면 과부하 난맥상이 즉시 드러납니다.

  • 리팩터링 선로가 항상 가득 차 있고 잘 비워지지 않는다면, 이는 개인 생산성 문제가 아니라 전략적 문제입니다.
  • 버그 선로가 계속 넘쳐흐른다면, 품질 측면에서 구조적 개선이 필요하다는 신호입니다.

태스크 분해: 화차를 독립적으로 전환 가능하게 만들기

거대한 리팩터링과 방대한 기능 브랜치는 작은 야드에 300량짜리 대형 열차를 들이밀어 놓는 것과 같습니다. 작업을 독립적으로 전환 가능한 섹션으로 쪼개야 합니다.

“Refactor SDK” 같은 발상을 응용해서, 변경을 어떻게 잘게 나누고 검증할지 규칙을 정의해 둡니다.

코드 영역을 규칙이 있는 섹션으로 나누기

리팩터링의 경우, **섹션(section)**을 정의합니다.

  • 모듈, 도메인 경계, 혹은 기능 슬라이스 같은 단위
  • 각 섹션에는 다음을 부여합니다.
    • 리팩터링 규칙 집합 (허용되는 변경: 이름 변경, 시그니처 변경, 인터페이스 명료화 등)
    • 검증 프로토콜 (어떤 테스트를 돌릴지, 어떤 시뮬레이션/측정을 비교할지 등)

예시 섹션:

  • "아날로그 인터페이스: 모든 신호 이름을 공통 네이밍 스킴으로 표준화"
  • "상태기계: 즉흥 로직을 명시적 상태 열거 방식으로 변환"

각 섹션은 다시 여러 개의 작고, 전환 가능한 화차로 나뉩니다.

  • 화차 1: 모듈 X의 신호 이름 변경 + 회귀 테스트 스위트 Y 실행
  • 화차 2: 컴포넌트 Z의 상태 로직 분리 추출 + 동등성 포멀 체크

동작을 변경하는 작업은 이와 비슷한 분해를 하지만, 시스템 테스트, 통합 테스트, 이해관계자 리뷰 등 더 강력한 검증을 덧붙여야 합니다.


개발자 집중력 보호하기: 야드 용량은 희소 자원이다

철도 야드는 가진 선로 수 이상으로 열차를 처리할 수 없습니다. 마찬가지로, 개발자의 주의력과 정신적 에너지가 가장 희소한 자원입니다.

컨텍스트 스위치를 공짜처럼 다루지 마십시오. 전환이 눈에 보이고, 의도적이며, 두 번 생각하게 만들 만큼 비싸게 설계해야 합니다.

컨텍스트 스위치를 명시적으로 드러내기

보드 위에 작은 물리적 표시를 추가합니다.

  • 진행 중인 각 작업(화차)에는 담당 개발자의 토큰(색깔 자석, 집게 등)을 올려 둡니다.
  • 태스크를 바꿀 때마다 토큰을 실제로 옮겨야 합니다.
  • 하루 동안 토큰이 선로 사이를 옮겨 다닐 때마다, 작은 “스위치 카운터” 카드에 표시 하나를 그립니다.

그러면 이런 것들이 눈에 보입니다.

  • "점심 전까지 컨텍스트 스위치를 15번이나 했다" — 모두가 왜 이렇게 지친지 이유가 드러납니다.
  • 어떤 선로가 특히 혼선을 많이 유발하는지 (예: 버그 선로에서 잦은 인터럽트가 오는지).

아날로그 도구로 상태를 외부화하기

리팩터링 계획이나 다음에 돌려야 할 테스트를 머릿속에만 기억해 두는 것은, 야드 전체 열차 위치를 머리로만 외우려는 것과 비슷합니다.

아날로그 도구를 수동 스위치이자 외부 기억장치로 사용하십시오.

  • 종이 리팩터 맵: 서브시스템의 현재 구조와 목표 구조를 스케치합니다. 각각의 화차(태스크)가 맵의 어느 부분에 대응되는지 표시합니다.
  • 검증 체크리스트: 각 리팩터링 카드마다, 동작 보존을 검증하기 위해 실행할 체크를 구체적으로 적어 둡니다.
  • 파킹 존(parking lot): “중단됐지만 포기된 것은 아닌” 작업을 두는 작은 영역을 보드에 마련합니다. 화차를 잠시 세울 때에는, 왜 멈췄는지, 무엇이 남았는지, 어떻게 재개할지 한 줄 메모를 반드시 남깁니다.

목표는 간단합니다. 이틀 뒤에 중단된 리팩터링으로 돌아왔을 때 정신을 다시 부팅하는 비용을 최소화하는 것입니다.


내일부터 바로 쓸 수 있는 단순 전환 시스템 설계하기

이걸 쓰려고 전체 프로세스를 갈아엎을 필요는 없습니다. 작게 시작해도 충분히 효과를 볼 수 있습니다.

1단계: 나만의 야드 그리기

화이트보드나 큰 종이에 다음을 그립니다.

  • 가로로 3–4개 선로 (Features, Refactors, Bugs, Experiments)
  • 세로로 Inbound, Switched, In Progress, Verification, Outbound 컬럼

2단계: 기존 작업 분류하기

현재 사용하는 디지털 백로그를 꺼내서:

  • 각 아이템을 포스트잇에 옮겨 씁니다.
  • Feature / Refactor / Bug / Experiment 중 하나로 분류합니다.
  • 해당 선로의 "Inbound" 컬럼에 붙입니다.

3단계: WIP 제한과 토큰 정하기

  • 선로·인원별 WIP를 정합니다(예: 1인당 1–2개).
  • 각 개발자에게 이름이 적힌 물리적인 토큰을 줍니다.
  • 토큰은 WIP 한도만큼의 화차에만 올려둘 수 있습니다.

4단계: 리팩터링 하나를 분해해 보기

동작을 보존하는 리팩터링 하나를 골라서:

  • 현재 구조와 목표 구조를 종이에 맵으로 그립니다.
  • 이를 독립적으로 검증 가능한 여러 화차로 쪼갭니다.
  • 각 화차마다, 구체적인 검증 절차를 적습니다.

5단계: 일주일 동안 컨텍스트 스위치 측정하기

7일 동안, 토큰을 선로 사이에 옮길 때마다 스위치 카운터에 표시를 하나씩 더합니다.

일주일 뒤에 다음을 리뷰합니다.

  • 1인당 하루 평균 컨텍스트 스위치는 몇 번인가?
  • 어떤 선로가 중단을 가장 많이 유발했는가?
  • 리팩터링 화차가 특정 컬럼에서 계속 막히고 있는가? 그렇다면 어디에서, 왜 그런가?

이 인사이트를 바탕으로 WIP 제한을 조정하거나, 규칙을 더 명확히 하거나, 리팩터링을 위한 집중 시간대를 따로 확보할 수 있습니다.


결론: 야드를 운영하라, 야드에 휘둘리지 말라

코딩은 단순히 코드를 쓰는 행위가 아니라, 제한된 환경 속에서 주의, 리스크, 노력을 어떻게 라우팅할지 결정하는 일입니다. 작업을 철도 야드처럼 보고, 단순한 아날로그 전환 시스템을 설계해 두면 다음을 이룰 수 있습니다.

  • 동작을 보존하는 리팩터링이 기능 개발을 탈선시키지 않게 관리
  • 비기능적 가치를 눈에 보이고 의도적으로 다루기
  • 숨은 컨텍스트 스위치 비용과 정신적 피로를 줄이기
  • “언젠가 정리하자”는 막연한 다짐을, 구체적이고 검증 가능한 태스크로 바꾸기

필요한 도구는 놀랍도록 저기술입니다. 종이, 펜, 포스트잇, 화이트보드면 충분합니다. 대신 그 대가로 얻는 것은 고기술입니다. 더 깔끔한 아키텍처, 더 신뢰할 수 있는 합성과 도구 체인, 그리고 자신의 용량과 트레이드오프를 이해하는 팀입니다.

당신의 야드에는 이미 하루 종일 열차가 드나들고 있습니다. 이제 질문은 이것입니다. 머릿속에서 즉흥적으로 스위치를 계속 바꿔 가며 버틸 것인가, 아니면 열차가 안전하고 예측 가능하게 움직일 수 있도록 야드 자체를 설계할 것인가?

아날로그 리팩터링 철도 야드: 경쟁하는 코딩 우선순위를 위한 물리적 전환 시스템 설계하기 | Rain Lag