덜어낼수록 강해진다 — 5개월의 contraction 기록
5월에 이 프로젝트를 처음 시작했을 때, 인프라 목록은 이랬다: Hermes-Agent(에이전트 런타임), n8n(워크플로우 엔진), Modal(서버리스 GPU), WordPress(Docker), gbrain(PGLite), launchd 서비스 5개, cron 작업들. 지금은 opencode 하나에 launchd 4개. 나머지는 다 사라졌다.
지우는 게 추가하는 것보다 훨씬 어렵다. 무언가를 지운다는 건 “이 기능이 없어도 문제없다”는 확신이 필요하다는 뜻이다. 반면 추가는 “언젠가 도움이 될 수도”라는 희망으로 정당화된다. 스타트업이 기능을 계속 쌓다가 무너지는 이유와 같다.
n8n을 지운 결정이 가장 아팠다. 30개가 넘는 워크플로우가 연결되어 있었고, 모든 자동화가 거기서 돌고 있었다. 한 번에 걷어내는 건 불가능했고, 하나씩 launchd cron으로 이전하면서 “이게 정말 필요한가”를 매번 물었다. 실제로 필요했던 건 절반도 안 됐다. 나머지는 “만들어놨으니 돌아가는” 워크플로우였다 — niklas luhmann이 지적했듯, 일단 만든 구조는 그 자체로 존속 이유를 만들어낸다.
이 경험에서 배운 걸 코드로 정리한 게 ponytail 원칙이다. 무언가를 추가하기 전에 다섯 개의 질문을 거친다: 이게 정말 필요한가(YAGNI), 표준 라이브러리에 이미 있나, 네이티브 플랫폼 기능으로 되나, 이미 설치된 디펜던시가 해결하나, 한 줄로 가능한가. 다 통과해야 비로소 최소한만 구현한다. 이 원칙을 적용한 후로 새 dep을 추가한 적이 거의 없다.
재미있는 건, 이걸 시스템에 적용하니까 자연스럽게 아키텍처가 투명해졌다. 의존성이 적을수록 각 컴포넌트의 책임이 명확해지고, 고장 났을 때 원인 추적이 쉬워진다. launchd 하나로 모든 서비스를 관리하니까 docker-compose도 필요 없고, 별도의 모니터링도 필요 없다 — launchd가 죽으면 10초 안에 재시작하고, 그마저도 안 되면 drewgent watchdog이 감지한다.
사실 90%의 기능은 결국 안 쓴다. 지난 5개월 동안 추가한 기능 중 진짜로 지금까지 남아 있는 건 10%도 안 된다. 유지보수 비용은 남은 기능뿐 아니라 삭제하지 않은 모든 기능에서 발생한다. “안 쓰지만 문제는 없는” 기능이 가장 위험하다 — 존재조차 잊어버리기 때문이다.
이게 바로 taste다. 기술적으로 할 수 있는 것과 해야 하는 것을 구분하는 능력. “이걸 만들 수 있어”와 “이걸 유지할 의향이 있어”는 전혀 다른 문제다. 나는 5월에 전자를 기준으로 결정했고, 지금은 후자를 기준으로 결정한다. 그 차이가 14개에서 6개로의 압축을 만들었다.
앞으로도 덜어낼 게 있는지 계속 살펴볼 생각이다. 아마도 CLI 인수 하나, 미사용 스킬 하나, 방치된 크론 작업 하나씩. 완전히 투명해질 때까지.