코드 삭제가 쉬워야 생존한다!
코드의 영속성에 대한 집착에서 벗어나, 삭제의 용이성에 초점을 맞춰야 함을 강조
삭제하기 쉬운 코드는 단일 위치에 존재하고, 명확한 경계를 가지며, 불필요한 지식을 갖지 않음
추상화(Abstraction)는 중복을 피하기 위한 것이 아니라, 변경 또는 삭제를 용이하게 하기 위해 사용되어야 함
코드 리뷰 시, 기능의 작동 여부뿐 아니라 삭제의 용이성을 함께 고려해야 함
삭제 용이성을 위한 설계 원칙
삭제하기 쉬운 코드는 몇 가지 특징을 공유한다. 첫째, 단일 위치(Single Location)에 존재해야 한다. 중복은 피해야 하지만, 과도한 DRY 원칙 적용은 코드의 얽힘을 유발한다. 둘째, 명확한 경계(Clear Boundary)를 가져야 한다. 격리된 모듈, 깨끗한 인터페이스, 잘 정의된 API 뒤에 있는 서비스는 안전하게 제거, 교체 또는 재작성할 수 있다. 셋째, 불필요한 지식(Ignorance)을 가져야 한다. 시스템의 나머지 부분에 대해 알 필요 없이, 입력받고, 작업을 수행하고, 출력을 반환하는 코드가 삭제하기 쉽다. 이러한 원칙들은 코드의 유연성을 높이고, 변경에 대한 저항성을 줄여준다.
코드 리뷰에서 삭제 용이성 평가
코드 리뷰 시, 기능의 작동 여부뿐 아니라 삭제의 용이성(Deletability)을 함께 고려하는 것이 중요하다. 새로운 기능을 추가하는 PR이 여러 파일을 수정한다면, 미래의 변경에 대한 높은 비용을 예고하는 신호일 수 있다. 반면, 동일한 기능을 잘 정의된 단일 모듈을 통해 추가하는 PR은 더 깨끗한 코드를 남긴다. 이는 아키텍처 결정에도 적용된다. 새로운 의존성을 추가하기 전에, 2년 후 해당 의존성을 제거하는 것이 얼마나 어려울지 자문해야 한다. 이러한 접근 방식은 코드베이스의 장기적인 건강성을 유지하는 데 기여한다.
추상화의 올바른 사용법
추상화는 중복을 피하기 위해 사용되지만, 가장 중요한 목적은 코드를 격리하거나 이름을 부여하여 다른 부분을 건드리지 않고 변경하거나 제거할 수 있도록 하는 것이다. 예를 들어, 로깅(Logging)을 생각해보자. console.log 호출을 여기저기 흩뿌리는 것은 작성하기는 쉽지만 변경하기는 어렵다. 반면, 모든 로깅을 단일 로거 모듈을 통해 라우팅하면, 로깅 라이브러리를 교체하거나, 컨텍스트를 추가하거나, 완전히 비활성화하는 경우 단일 파일만 수정하면 된다. 이처럼 추상화(Abstraction)는 복잡성 때문이 아니라, 변경 또는 삭제 가능성을 위해 사용되어야 한다.
삭제의 중요성: 소프트웨어의 무상함
소프트웨어는 끊임없이 변화하며, 기능은 추가되고 제거된다. 제품은 피벗하고, 요구 사항은 변경된다. 오늘 작성한 코드는 부분적으로 또는 완전히 대체될 것이다. 이는 실패가 아니라, 살아있는 소프트웨어가 작동하는 방식이다. 이러한 무상함(Impermanence)을 받아들이고, 그에 맞춰 설계해야 한다. 목표는 절대 제거할 수 없는 코드가 아니라, 제거 비용이 저렴한 코드를 작성하는 것이다. 30년 이상 운영되는 시스템을 구축한 엔지니어들은 코드를 건드릴 수 없게 만들어서가 아니라, 이해하기 쉽고, 격리하기 쉬우며, 필요할 때 조각별로 쉽게 교체할 수 있도록 설계했기 때문이다.
삭제 가능한 코드 작성을 위한 체크리스트
코드를 커밋하기 전에, 몇 가지 질문을 자문해 볼 필요가 있다. 첫째, 이 기능을 단일 PR로 삭제할 수 있는가? 그렇지 않다면, 그 이유는 무엇인가? 둘째, 이 코드는 몇 개의 파일을 수정하는가? 셋째, 이 모듈은 불필요한 것들을 알고 있는가? 넷째, 이 의존성이 사라진다면 얼마나 큰 문제가 발생할까? 다섯째, 이 추상화는 변경을 더 쉽게 만드는가, 아니면 중복을 피하는 것뿐인가? 이러한 질문들은 코드베이스를 더 건강하게 유지하는 데 기여하며, 삭제 비용에 대한 본능을 개발하는 데 도움이 된다. 결국, 가장 삭제하기 쉬운 코드는 작성하지 않은 코드이다.