프로그램의 단순함은 흔히 코드의 양이나 도구의 크기로 오해되곤 하지만, 실제로는 서로 다른 기능과 개념이 불필요하게 얽히지 않은, 즉 '결합되지 않은(uncoupled)' 상태를 의미합니다. 유닉스(Unix) 파이프라인은 작은 도구들을 조합하여 사용하지만, 'sort | uniq --count'처럼 집계와 정렬 기능이 강하게 결합된 경우 작은 요구사항 변경에도 전체 시스템이 복잡해지는 문제가 발생할 수 있습니다. 이는 '작은 것이 항상 단순한 것은 아니다'는 중요한 통찰을 제공합니다.
이러한 결합의 문제는 다양한 사례에서 드러납니다. 예를 들어, 유닉스 파이프라인에서 단어 빈도를 계산하는 작업은 여러 임시 파일과 복잡한 정규식, 정렬, 텍스트 파일 조인이 필요해 작은 변경에도 큰 노력이 듭니다. 반면, 클로저(Clojure) 같은 언어에서는 고차 함수와 데이터 구조를 활용해 동일한 작업을 훨씬 유연하고 단순하게 구현할 수 있습니다. 또한, 구글 드라이브 데스크톱 버전처럼 방대한 내부 구현을 가진 프로그램도 사용자에게는 단순한 폴더 동기화 기능으로만 보이게 할 수 있는데, 이는 내부 복잡성과 사용자 경험의 단순성이 별개임을 보여줍니다. 러스트(Rust)의 구조체가 타입 검사와 데이터 표현을 결합하는 반면, 클로저나 타입드 라켓(Typed Racket)은 이 둘을 분리하여 유연성을 확보하는 것도 결합도 문제의 또 다른 예시입니다.
결국 단순함은 프로그램의 유지보수성과 유연성을 결정하는 핵심 요소입니다. 불필요한 결합은 개발자의 유지보수를 어렵게 하고, 사용자가 프로그램을 유연하게 활용하는 것을 방해합니다. 물론 단순한 프로그램을 만드는 데는 많은 비용이 들 수 있습니다. CSS나 SQL처럼 선언적인 방식으로 복잡한 내부 구현을 숨겨 사용자에게 단순한 인터페이스를 제공하는 시스템들은 수많은 개발자의 노력과 시간이 투입된 결과입니다. 하지만 이러한 투자는 장기적으로 시스템의 안정성과 확장성을 높이는 데 기여하며, 궁극적으로는 더 크지만 더 단순한 시스템을 구축하는 길로 이어집니다. 크기와 단순함을 혼동하지 않고, 숨겨진 의존성을 줄이는 데 집중하는 것이 진정으로 견고하고 유연한 소프트웨어를 만드는 데 중요합니다.