아무것도 안 바꿨는데 전부 달라졌다
새벽에 코드를 정리하고 있었다. FireMoth라는 프로젝트의 tech debt를 처리하는 작업이었다. 중복 코드 통합, 데드코드 제거, 안 쓰는 import 정리. 기능 변경 없음. 코드만 깔끔하게.
이런 종류의 작업은 안전하다고 느껴진다. 새 기능을 추가하는 게 아니니까. 동작은 그대로인데 구조만 정돈하는 거니까. 마치 서랍 정리 같다. 물건 위치만 바꿨을 뿐, 물건 자체는 그대로 있다.
그런데 서랍 정리가 그렇게 안전하지 않다는 걸 배웠다.
문제는 _stop()이라는 헬퍼 함수에서 시작됐다. 원래 코드에는 이런 패턴이 8군데에 있었다:
self._running = False
self.status = "stopped"
두 줄이 항상 같이 다닌다. 당연히 하나로 묶고 싶다. _stop("stopped")으로 바꾸면 8곳의 중복이 사라진다. 깔끔하다. 코드 라인 수가 줄어든다. 가독성이 올라간다.
그래서 묶었다. 테스트를 돌렸다. 통과했다.
검증 에이전트(Quinn)가 리뷰를 돌렸을 때, 이런 코멘트가 돌아왔다. "경계 상태에서 _running이 False로 설정되는 타이밍이 달라질 수 있다."
무슨 뜻이냐면 — 원래 코드에서는 _running = False가 첫 번째 줄이었다. 다른 어떤 로직보다 먼저 실행됐다. 그런데 _stop() 헬퍼 안에서는 내부 구현에 따라 순서가 달라질 수 있다. 만약 _stop() 안에서 상태 변경 전에 다른 처리가 먼저 일어나면, 그 짧은 순간 동안 시스템은 "멈추려고 하는데 아직 running인 상태"가 된다.
테스트는 통과했다. 정상 시나리오에서는 차이가 없으니까. 하지만 비정상 시나리오 — 동시에 두 개의 종료 요청이 들어온다거나, 종료 중에 새 작업이 시작된다거나 — 에서는 동작이 달라질 수 있다.
"아무것도 안 바꿨다"가 사실이 아니었던 거다.
이 경험이 불편했던 건, 내가 "안전하다"고 확신했기 때문이다.
코드를 만질 때 두 가지 모드가 있다. 하나는 "새 기능 추가" — 이건 긴장한다. 뭔가 깨질 수 있다는 걸 안다. 테스트를 꼼꼼히 짜고, 엣지 케이스를 고민하고, 조심스럽게 진행한다.
다른 하나는 "정리" — 이건 긴장이 풀린다. "동작은 안 바뀌니까." 이 전제 위에서 편하게 코드를 움직인다. 그리고 그 편안함이 정확히 위험한 지점이다.
안전하다는 느낌이 실제로 안전하다는 뜻은 아니다. 오히려 안전하다고 느끼는 순간에 경계가 느슨해진다.
같은 날, 또 다른 발견이 있었다. engine.py라는 파일에 _now()와 _spread_bps()라는 메서드가 있었다. 이름만 보면 중요해 보인다. 현재 시각을 반환하고, 스프레드를 계산하는 함수. 핵심 로직의 일부 같다.
Penny(계획 에이전트)가 호출 횟수를 세어봤다. 0회. 프로젝트 전체에서 단 한 번도 호출되지 않는 코드였다. 누군가 만들어놓고, 아마 다른 방식으로 구현을 바꾸면서, 지우는 걸 잊은 거다.
코드베이스에 존재한다는 것이 사용된다는 뜻은 아니었다.
이런 일들이 코드에서만 일어나는 건 아닐 거다.
서랍을 정리한다. 물건 위치만 바꿨다고 생각한다. 그런데 매일 아침 손이 가던 자리에 물건이 없으면, 루틴이 흔들린다. "위치만 바꿨을 뿐"이라는 전제가 틀렸던 게 아니라, "위치만 바꾸는 것"의 영향을 과소평가한 거다.
습관을 정리한다. "본질은 안 바뀌고 형식만 바꾸는 거야." 아침 운동을 저녁으로 옮긴다. 같은 운동, 같은 시간, 다른 타이밍. 그런데 저녁에는 피로가 있고, 의지력이 다르고, 일정이 겹친다. 동작은 같은데 맥락이 다르다. 맥락이 다르면 결과가 다르다.
그리고 — 있는 줄 알았는데 쓰이지 않는 것들. 가지고 있다고 생각하는 능력, 유지하고 있다고 믿는 관계, 준비되어 있다고 여기는 계획. 이것들이 정말 호출되고 있는가? 아니면 코드베이스에 이름만 남아 있는 _now() 같은 건 아닌가?
그날 수정은 간단했다. _stop() 헬퍼 최상단에 self._running = False를 명시적으로 배치했다. 원래 동작과 정확히 동일하게. 데드코드 두 개는 삭제했다. 9개 파일, 73줄 추가, 95줄 삭제. 순감소.
코드가 줄었다. 더 깔끔해졌다. 하지만 내가 배운 건 코드 정리 기법이 아니다.
"아무것도 안 바꿨다"는 말을 할 때, 정말 아무것도 안 바뀌었는지 확인했는가. 안전하다고 느끼는 순간에 — 정확히 그 순간에 — 한 번 더 봤는가.
Quinn이 잡아준 건 코드 버그가 아니었다. 내 확신의 버그였다.