Reddit

Broken Windows, Abstractions and the Cost of Always Keeping Things Simple

깨진 유리창, 추상화, 그리고 항상 단순함을 유지하는 것의 비용

YAGNI와 KISS 같은 '단순함 우선' 원칙이 반복 적용되면, 어느 순간 단순했던 설계가 코드베이스의 표준 패턴으로 굳어지며 기술 부채가 된다는 글입니다. 저자는 이를 '깨진 유리창 이론(Broken Window Theory)'에 빗대어, 나쁜 패턴이 나쁜 패턴을 낳는 구조적 원인과 이를 막기 위한 실용적인 대응책을 제시합니다.

본문

이 글은 소프트웨어 개발에서 흔히 인용되는 두 가지 통념, 즉 '깨진 유리창 이론(Broken Window Theory)'과 'YAGNI/KISS' 원칙이 서로 충돌할 때 벌어지는 문제를 다룹니다. 깨진 유리창 이론은 눈에 보이는 무질서가 더 많은 무질서를 부른다는 이론으로, The Pragmatic Programmer에서 코드베이스에 적용된 바 있습니다. 지저분한 패턴이 자리 잡으면 이후 개발자들은 그것을 정리하기보다 그대로 따라 하기 쉽습니다. 한편 우리는 과도한 설계를 피하고, 미리 추상화를 도입하지 말며, 실제 필요가 생길 때만 복잡성을 도입하라고 배웁니다. 두 가지 모두 유용하지만, 문제는 팀이 YAGNI를 반복해서 적용하면서 '어제의 단순한 해법이 이미 한계를 넘어섰는지' 한 번도 멈춰 서서 묻지 않을 때 발생합니다. 처음에는 합리적인 절제였던 것이 점차 기술 부채가 되고, 그 부채가 확립된 패턴이 되는 순간 깨진 유리창 효과가 작동하기 시작합니다.

단순함이 패턴으로 굳어지는 과정

저자는 웹사이트 백엔드를 만드는 시나리오를 예로 듭니다. 초기 요구사항이 단순하기 때문에 뷰, 비즈니스 로직, 데이터베이스 로직을 한 파일에 두고, ORM이나 도메인 계층, 명확한 API 경계 같은 무거운 추상화는 '아직 필요 없다'는 이유로 건너뜁니다. 제품이 진화하면서 코드에 조금씩 추가하지만, 팀은 '추상화가 필요해지는 시점이 오면 그때 변경을 하는 개발자가 알아서 도입할 것'이라고 스스로를 안심시킵니다. 그러나 저자의 경험상 실제로는 그렇게 되지 않습니다. 개발자가 추상화의 필요성을 인식하지 못해서가 아니라, 도입할 시간이나 확신이 없기 때문입니다. 이후 새 기능을 추가하는 개발자는 '모든 것을 같은 모듈에 두는' 기존 패턴을 그대로 따르고, 복잡한 쿼리가 필요한 개발자는 ORM이 유용해 보여도 코드베이스 전체가 raw query를 쓰니 또 하나의 raw query를 추가합니다. 새 엔드포인트를 여는 개발자는 유사한 비즈니스 로직이 API 계층에 강결합되어 재사용이 불가능하다는 것을 알면서도, 풀어내는 대신 로직을 복제해 결합과 중복을 더합니다.

왜 개별 판단이 구조적 문제가 되는가

각 결정은 그 자체로 보면 합리적입니다. 문제는 모든 개발자가 기존 아키텍처를 '다음 기능을 어떻게 만들어야 하는가'에 대한 신호로 읽는다는 점입니다. 이 상태가 오래 지속될수록, 기능 작업을 멈추고 전체를 재구성하는 사람이 되는 것을 정당화하기 어려워집니다. 기여자가 많은 코드베이스에서는 나쁜 패턴이 나쁜 패턴을 낳는다는 것이 깨진 유리창 이론의 작동 방식입니다.

실용적인 대응책

저자는 비난 문화를 만드는 것이 최악이라고 지적합니다. 신뢰와 자신감만 더 떨어지기 때문입니다. 대신 깨진 유리창을 고치기 쉽게 만드는 프로세스와 도구를 권합니다. 첫째, 리팩터링을 기능 작업 틈새에 끼워 넣으라고 기대하지 말고 프로젝트 일정과 견적에 반영해야 합니다. 둘째, 제품이 성장함에 따라 과거의 아키텍처 결정을 주기적으로 재검토해야 합니다. 특히 의도적으로 단순하게 유지했던 패턴이 여전히 적절한지, 아니면 단지 이미 존재하기 때문에 복사되고 있는지를 물어야 합니다. 셋째, 코드베이스가 어떤 추상화를 쓰는지뿐 아니라 어떤 조건에서 도입해야 하는지까지 문서화해 개발자가 판단 시점을 인식할 자신감을 주어야 합니다. 넷째, 확립된 패턴이 원래 목적을 넘어 늘려 쓰이는 사례를 AI 도구로 표시할 수 있으며, 더 나아가 아키텍처 중심 에이전트가 코드베이스 전체를 주기적으로 검토해 반복되는 국소적 결정이 구조적 문제를 만들기 시작한 지점을 제안할 수 있다고 봅니다. 결론적으로 '아직 필요 없다'는 종종 좋은 엔지니어링 판단이지만 영구적인 판단이 되어서는 안 되며, YAGNI에는 피드백 루프가 필요하다는 것이 저자의 주장입니다. 피드백 루프가 없으면 '아직 필요 없다'가 어느새 '6개월 전에 했어야 했다'로 조용히 바뀝니다.

Reddit 반응

  • @u/jhartikainen — 좋은 글이고 합리적인 조언입니다. 다만 애초에 이 문제를 어떻게 예방할 수 있을지가 궁금합니다. 아키텍처를 처음부터 다 만들어두는 것은 비현실적이고 애플리케이션이 어떻게 진화할지 예측할 수 없어 결과도 나쁠 가능성이 높은데, 미래를 추측하지 않고도 코드 수준에서 후속 개발자가 올바른 선택을 하게 도울 방법이 있을까요.
  • @u/hippydipster — '팀이 YAGNI를 멈춰 서서 묻지 않고 반복 적용할 때 문제가 생긴다'는 문장은 결국 팀이 현명한지, 규율이 있는지의 문제로 귀결됩니다. YAGNI 자리에 DRY, TDD, DDD 등 어떤 휴리스틱한 '모범 사례'를 넣어도 성립하는데, 굳이 YAGNI일 이유가 있을까요.
  • @u/reallydontaskme — YAGNI와 KISS가 과도하게 적용된 코드베이스에서 일해 본 적이 없습니다. 항상 반대 문제였습니다. 어떤 개발자들은 추상화 복권에 당첨되면 몹시 행복해하고, 그 추상화가 다른 사례에 안 맞으면 '아무도 예측 못 했다'고 합니다. 그래서 '1, 2, 3 리팩터' 규칙이 있고, 중복 코드도 가치 있는 트레이드오프가 될 수 있습니다.
  • @u/oweiler — 맞는 말이지만 반대 문제(과도한 추상화)가 훨씬 더 자주 발생합니다. 다만 장점도 있는데, 그런 단순한 코드베이스는 탐색과 온보딩이 쉽습니다.
  • @u/aardaappels — 깨진 유리창 이론 자체가 상당 부분 반박된 이론입니다. 나쁜 패턴이 지속되는 이유는 패턴이 존재해서가 아니라, 팀이 공동의 이익을 위해 개입할 공유된 신뢰와 의지가 부족하기 때문입니다.

원문: Substack(Paul Gilmartin) / 번역·요약: Trawling