결합을 제거해야 할 악이 아니라 전략적으로 관리해야 할 속성으로 보고, 통합 강도·거리·변동성이라는 세 축을 통해 모듈성과 복잡도를 평가·균형화하는 접근을 소개한다. 소프트웨어의 프랙탈적 특성, 로컬/글로벌 복잡도, 모듈의 역할과 효과적인 결합의 조건, 그리고 ‘균형’의 정의와 휴리스틱을 간결히 정리한다.
같은 저자의 Domain-Driven Design 책을 읽고, 모듈식 설계를 다루는 데 적용한 역사적 관점이 흥미로워 이 주제에 관심이 생겼다. 이번 신간은 그 논의를 확장할 것처럼 들렸고, 기대를 저버리지 않았다. Parnas, Myers, Brooks, Conway, Ousterhout의 아이디어를 토대로 새롭고 유용한 내용을 촘촘히 연구해 담아냈다. Vlad Khononov는 내가 소프트웨어 설계 논의에서 근본적이라고 보는 것—즉, 감에 의존하지 않고 의사결정을 내릴 수 있도록 원칙에 기반한 정의를 제공하는 일—을 해낸다. 다만 통찰은 깊지만 꽤 전문적인 책이기도 하다. 나는 소프트웨어 설계 덕후들에게는 추천하지만 모든 프로그래머에게까지 권하진 않는다(예컨대 _A Philosophy of Software Design_은 널리 추천한다). 다만 앞으로 이 책의 휴리스틱을 자주 써서 성과를 거둔다면 이 평가는 바뀔 수 있다.
이 책의 논지: 평판과 달리 결합은 근본적으로 제거해야 할 나쁜 것이 아니라 전략적으로 관리해야 할 속성이다. 본질적 복잡성과 마찬가지로, 유용한 소프트웨어 시스템은 구성 요소들 간의 어떤 형태의 결합 없이는 존재할 수 없다. 이 생각을 내면화하는 쉬운 방법은, 때때로 응집(cohesion)을 ‘좋은 결합’으로 정의한다는 사실을 떠올리는 것이다.
소프트웨어는 프랙탈적이다. 시스템, 서비스, 네임스페이스, 클래스, 함수 수준에서 모듈성을 논할 수 있다. 각 수준마다 인터페이스와 구현, 모듈의 깊이와 복잡도가 있다. 복잡성은 이원적이다: 로컬과 글로벌. 그리고 소프트웨어가 프랙탈이기 때문에, 한 수준의 글로벌 복잡성은 더 높은 수준에서 로컬 복잡성으로 바뀐다. 단순히 모듈을 더 작은 단위로 쪼개는 일은 로컬 복잡성을 위로 밀어 올려 전체 시스템을 더 복잡하게 만들 뿐이다. 우리의 일은 로컬 최소값을 쫓는 것이 아니라 균형을 찾는 것—총 유지보수 비용을 줄이는 충분히 괜찮은 절충안을 찾는 것이다.
책의 3분의 2는 정의에 할애한다. 익숙한 개념들(복잡성, 모듈성, 결합), 역사적으로 중요한 개념들(구조적 설계의 모듈 결합, 코나선스(connasence)), 그리고 저자가 새로 소개하는 몇 가지 개념(통합 강도, 거리, 변동성)이 있다. 이들을 조합해 시스템 내 구성 요소의 모듈성과 ‘균형’을 평가하고, 문제를 감지하고, 가능한 해법을 암시하는 휴리스틱(수식으로 표현됨)을 만든다. 저자는 이 활동을 결합을 균형화(그리고 재균형화)한다고 부른다. 마지막 장들은 일련의 사례 연구에 이 휴리스틱을 구체적으로 적용해 보인다.
크게 요약하면 이렇다. 모듈이 강하게 통합되어 있다(서로 많은 지식을 공유한다)면 거리를 줄이고, 약하게 통합되어 있다(서로 공유하는 지식이 적다)면 거리를 늘려라. 이는 더 작고 더 널리 흩어진 컴포넌트가 반드시 더 단순한 시스템을 만든다는 생각(마이크로서비스 지옥과 left-pad로 이어지는 생각)과 다르다.
하지만 균형 맞추기는 단순히 “이것저것 옮겨 담는” 데 그치지 않는다. 소프트웨어 시스템이 프랙탈 네트워크라는 점을 감안하면, 구조적 한계—그리고 이를 유지보수하는 이들이 감당할 수 있는 인지 부하—를 넘어 성장하도록 새로운 추상화 수준을 도입할 수 있다. 이 책 이론의 강점 중 하나는 동일한 원칙이 시스템의 모든 수준에 적용된다는 점이다. 소프트웨어 설계와 아키텍처 사이에—심지어 소프트웨어 시스템과 그들이 통합되는 인간 시스템 사이에도—딱 잘라 구분되는 경계가 없다. 단지 규모와 의미 수준, 관점만 다를 뿐이다.
시스템은 구성 요소와 목적을 가진다. 구성 요소 간의 상호작용(결합)은 시스템의 목적을 이루기 위해 필수적이다. 더 작은 스케일에서, 구성 요소는 거의 언제나 그 자체로 하위 시스템이다.
결합은 구성 요소가 지식이나 생명주기(라이프사이클), 혹은 둘 다를 공유해야 하는 데서 비롯된다.
복잡성은 사람이 시스템과 상호작용할 때 경험하는 인지 부하를 반영한다. 인지 부하가 클수록 시스템의 동작을 이해·통제·예측·변경하기가 더 어려워진다.
모듈은 시스템의 유연성을 높여 미래의 목표를 지원하도록 진화시킬 수 있게 설계된 구성 요소의 한 형태다.
시스템 설계는 개별 모듈을 고립적으로 평가하는 것이 아니라, 모듈들 간의 관계—어떻게 결합되어 있는가—를 통해 평가해야 한다. 구성 요소를 결합하는 방식에 따라 공유되는 지식의 유형과 양이 달라진다. 어떤 방식은 복잡성을 키우고, 다른 방식은 모듈성에 기여한다.
**통합 강도(Integration strength)**는 결합된 구성 요소들이 공유하는 지식의 유형과 범위를 모델링한다. 결합 강도에는 네 가지 수준이 있다.
**거리(Distance)**는 결합된 구성 요소들 사이로 지식이 “이동”하는 공간을 뜻한다. 이는 결합된 구성 요소에 영향을 미치는 변경을 구현하기 위해 필요한 조정·소통의 노력을 좌우한다. 거리는 여러 요인의 영향을 받는다.
**변동성(Volatility)**은 구성 요소의 예상 변경 빈도다.
**균형(Balance)**은 결합의 세 차원이 최적 상태에 있어, 설계가 시스템의 변동성이 큰 구성 요소들의 모듈성을 높여 주는 상태를 말한다. 이 차원들의 불균형한 조합은 인지 부하 증가로 이어지고, 결과적으로 복잡성을 높인다.
-- 상당한 양의 지식이
-- 먼 거리를 이동할 때 글로벌 복잡성이 생긴다
global_complexity := strength() AND distance()
-- 서로 관련 없는 컴포넌트가
-- 한자리에 함께 있을 때 로컬 복잡성이 생긴다
local_complexity := NOT strength() AND NOT distance()
-- 모듈성은 로컬/글로벌 복잡성의 부재다
complexity := local_complexity OR global_complexity
= NOT (strength() XOR distance())
modularity := NOT complexity
= strength() XOR distance()
-- 변동성이 큰 컴포넌트가 모듈식이고
-- 복잡한 컴포넌트가 변하지 않을 때 균형이다
balance := modularity OR NOT volatility()
= (strength() XOR distance()) OR NOT volatility()