목차5개 항목
출처와 참고
tef의 「Write code that is easy to delete, not easy to extend」(2016)와 그 번역 「확장하기 쉬운 코드가 아니라 삭제하기 쉬운 코드를 작성하자」를 바탕으로 재구성한 노트다.
삭제하기 쉬운 코드란
코드 한 줄은 자산이 아니라 유지 비용이다. 많이 쓸수록 이해하고, 테스트하고, 바꿔야 할 것이 늘어난다. 그런데 미래를 예측해 재사용·확장을 위한 추상화를 쌓으면 오히려 코드가 서로 엉켜 지우기 어려워진다.
삭제하기 쉬운 코드는 이 방향을 뒤집는다. 좋은 설계의 기준을 「얼마나 확장하기 쉬운가」가 아니라 「틀렸을 때 얼마나 쉽게 버리고 바꿀 수 있는가」로 둔다.
단계
| 단계 | 원칙 | 이유 |
|---|---|---|
| 0 | 쓰지 않는다 | 가장 쉽게 지울 수 있는 코드는 처음부터 없는 코드다 |
| 1 | 일단 복사해서 쓴다 | 사용처가 두세 개 생기기 전에 공통화하면 잘못된 추상화가 공용 API로 굳는다. 사람들은 문서가 아니라 관찰된 동작에 의존하므로 공용 API는 바꾸기 어렵다 |
| 2 | 반복은 함수로 뽑되 util 덤프를 피한다 | 상태가 없고 맥락에 덜 묶인 코드(로깅, 파일 처리, 컬렉션)는 재사용하기 쉽고 지워질 일도 적다. 하지만 아무거나 한 파일에 모으면 떼어 내기 어렵다 |
| 3 | 보일러플레이트를 허용한다 | 유연한 라이브러리는 쓰는 쪽 코드를 장황하게 만든다. 대신 자주 바뀌는 비즈니스 로직과 안정적인 프로토콜 코드를 섞지 않는다 |
| 4 | 저수준 위에 쓰기 쉬운 래퍼를 얹는다 | urllib3 위의 requests처럼. 래핑의 목적은 세부를 숨기는 것이 아니라 관심사를 나누는 것이다. 서드파티 종속도 래퍼 한 곳에 가둔다 |
| 5 | 때로는 큰 덩어리로 쓴다 | 처음부터 올바른 구조를 알 수 없다. 거친 프로토타입을 만들고 배운 뒤 통째로 버리는 편이 작은 실수 여럿을 고치는 것보다 쉽다 |
| 6 | 바뀔 가능성을 기준으로 쪼갠다 | 모듈 경계는 재사용 여부가 아니라 무엇이 함께 바뀌는가로 정한다(Parnas의 정보 은닉). 오류 처리는 시스템 가장 바깥에서 몰아서 한다 |
| 7 | 계속 바꿀 수 있게 만든다 | 피처 플래그로 배포와 출시를 분리하고, 큰 변경을 잘게 나눠 자주 병합한다 |
느슨한 결합의 예
경계를 잘 그은 시스템은 한쪽을 통째로 갈아 끼울 수 있다.
| 예 | 무엇을 분리했나 |
|---|---|
| 운영체제의 공개 API와 내부 API | 응용 프로그램이 내부 구현에 묶이지 않는다 |
| HTTP 캐시와 CDN | 원 서버를 몰라도 캐시 계층을 바꿀 수 있다 |
| 파이프라인·미들웨어 | 단계를 끼우고 빼기 쉽다 |
| 파일 시스템, SQL 같은 공용 인터페이스 | 저장소 구현을 바꿔도 쓰는 쪽은 그대로다 |
| Erlang 슈퍼비전 트리 | 실패한 부분만 재시작한다 |
한계
- 「복사부터」는 사용처가 늘어난 뒤 반드시 정리한다는 전제가 있다. 정리하지 않으면 중복이 부채가 된다.
- 공개 라이브러리처럼 처음부터 안정적인 API를 약속해야 하는 코드에는 그대로 적용하기 어렵다.
정리
- 코드는 비용이다. 덜 쓰는 것이 가장 싼 설계다.
- 추상화는 실제 사용 사례를 본 뒤에 만든다.
- 경계는 바뀔 가능성으로 긋는다.
- 목표는 완벽한 설계가 아니라 시간이 지나도 버리고 바꿀 수 있는 시스템이다.
관련 노트: Simple Design, 리팩터링 원칙, 변경하기 쉬운 코드의 네 가지 기준, 기술 부채, 소프트웨어 비대화