이 시리즈를 통틀어 필자가 제시한 많은 예제들은 최종 책임 시점으로 의사결정을 미루는 린 소프트웨어(Lean Software) 개념에 의존한다. 하지만 언제 그 시점이 오는지 어떻게 아는가? 그리고 더 나은 경로가 생겨나기 전에 설계 의사결정을 앞으로 언제까지 미룰 수 있는지 아는 방법은 무엇인가? 어느 종류의 프로젝트가 창발적 설계의 이점을 극대화하는가? 이 최종 Evolutionary architecture and emergent design 회에서는 이러한 질문에 답한 다음에 전체적으로 시리즈에서 일부 기본 요점들을 검토한다.
장을 마련하기 위해 도널드 럼스펠드(Donald Rumsfeld)의 시로 시작한다.
모른다는 것을 아는 것이 있다. 다시 말해서, 우리가 모른다는 것을 이제 우리가 아는 것이 있다는 것이다. 하지만 모른다는 것을 모르는 것도 있다. 우리가 모른다는 것을 우리가 모르는 것이 있다.
—Donald Rumsfeld, 전 미국 국방부 장관
Rumsfeld는 모르는 것의 범주를 구별하여, 우리가 존재하는 것을 알지만 찾지 못한 것을 모르는 것으로 시작한다. 하지만, 그는 또한 우리가 찾기 위해 우리의 지식이나 경험을 아주 넘어서기 때문에 알지도 못하는 것, 즉 "모르는 것을 모르는 것"의 존재를 인식한다.
Rumsfeld는 전쟁의 불확실성에 대해 논한 것이었지만, 그는 소프트웨어에 대해서도 이야기할 수도 있었다. 사소하지 않은 소프트웨어가 무엇이든지 간에 이를 써봤으면, 모르는 것을 모르는 것의 문제에 연관될 수 있다 — 이는 소프트웨어 설계의 가장 큰 문제점 중 하나이다. 독자는 솔루션을 구현하면서 접할 문제에 대해 충실히 이해하고 있다고 생각하지만, 필연적으로 예상치 못한 문제점들이 발생한다. 오픈 소스 프레임워크와 상호작용하는 것은 다시 말해서, 생각했던 것처럼 간단하지가 않거나 문제점에 미묘한 차이가 더 많아서 원래 보여지는 것보다 훨씬 더 세심한 사고를 요구한다.
소프트웨어는 장애 허용 한계가 낮기 때문에, — 이는 실제 시스템보다 훨씬 더 낮다 — 예상치 못한 설계 문제가 끊임없이 발생한다. 예를 들어, 독자 주변 건물을 고려해보자. 건물을 건설하는 것은 다수의 사람들에 의해 수 개월(또는 수 년)에 걸쳐 완성되는 프로젝트이다. 이제 유사한 기간에 걸쳐 구축된 다수의 사람들이 만든 소프트웨어 프로젝트를 생각해보자. 이러한 프로젝트는 범위가 유사하지만, 실제 건물은 구조적 결함을 엄청나게 더 허용한다. 전등 스위치 판이 배선이 들어있는 벽의 구멍을 완전히 메우지 않는다 해도 건물은 무너지지 않을 것이다. 하지만 소프트웨어에서는 작은 결함으로 인해 중단이 발생할 수 있다. 원자와 비교하면 비트의 결함 허용 한계는 용서되지 않는다. 물론, 소프트웨어(부드럽게 되는)는 또한 수정하기에 더 간편하다. 즉, 구멍을 보수하고(버그를 수정하고) 즉시 집을 다시 제작할 수 있다.
비행기의 날개가 떨어지면 범죄 수사 공학자들이 비행기에 날개가 부착된 곳을 먼저 살펴볼 것이다. 즉, 실제 시스템에서 오류가 기능에 근접한 성향은 흔히 높다. 하지만, 소프트웨어에서 관련 없어 보이는 떨어진 수 백 또는 수 천 개의 코드 행들에 대해 분명해지지 않는 불안정성을 초래하는 코드 행을 실행하는 것이 일반적이다.
각 부분이 무수히 많게 때로는 예상치 못한 방식으로 서로 다른 부분과 상호작용하기 때문에 소프트웨어에 대해 선행 설계(design up front)하는 것은 어렵다. 이러한 상호작용의 포함을 예측하는 것은 우리의 현재 능력을 벗어난다. 창발적 설계는 놀라운 복잡성의 필연성을 수용하고 변경의 피해 효과를 경감하려고 노력한다.
창발적 설계는 바이너리 상태가 아니다. 설계가 100퍼센트 애자일 또는 0퍼센트 애자일이라고 단언하여 말할 수 없는 반면 이는 범위 내에 존재한다. 이는 다음과 같이 그림 1에 나와있다.
그림 1. 그림 1. 설계의 범위
그림 1의 맨 왼쪽에 기존의 BDUF(Big Design Up Front)가 있으며, 이는 일반적인 많은 개발 방법론에서 구체화된다. 가장 다듬어진 구현체(straw-man) 형태의 BDUF가 설계 아티팩트를 작성하는 상아탑 설계자를 구체화하여, 불운한 코더가 변경사항 없이 구현하도록 이를 바닥의 구멍으로 떨어뜨린다. 이러한 설계 방법론은 모방이 아닌 형태로 코딩을 시작하기 전에 흥미로운 사항을 전부 발견하기 위해 부지런히 노력한다. 이는 소프트웨어 설계의 예측적이고 예방적인 모델이다.
그림 1의 오른쪽에 독자가 고등학교에서 수행한 코딩 종류를 표현한다. 즉, 작동될 때까지 이를 해킹한 다음에 변경하기 위해 계속 해킹한다. 이는 소규모에서는 잘 작동하지만(기본적으로 머리 속에서 문제를 생각할 만한 소규모인 경우에 한함) 그 이상의 규모로 조정되지 않는다.
창발적 설계는 이러한 두 가지 극단 사이에 속하지만, 왼쪽보다는 오른쪽으로 더 향한다. 창발적 설계는 소프트웨어를 설계하기 위한 응답적이고 반응적인 방법이다.
매우 많은 조직들이 매우 많은 실패와 능력 이하의 성과를 내는 프로젝트에 직면할 때에 BDUF를 계속 사용하는 것은 혼란스럽다. BDUF를 성공적으로 사용할 수 없다고 제안하는 것은 아니다. (실제로 과거에 필자는 이러한 프로젝트를 많이 수행했다.) 하지만 개발 방법론을 위한 추적 기록이 불량하고 수 십 년이 되었다. Fred Brooks의 중요한 책인 Mythical Man Month는 이러한 스타일로 소프트웨어를 빌드하는 것에 관한 문제를 논의했으며, 이는 1975년에 출판되었다(참고자료 참조).
이는 은유적으로 더 전통적인 엔지니어링에서 설계가 작동하는 방법과 일치하므로 팀들이 이러한 개발 스타일을 시도했던 것은 당연하다. 위젯 또는 iPod을 만드는 중이라면 원자를 리팩토링할 수 없으므로 모든 선행 설계를 수행해야 한다. 원본 Intel Pentium 프로세서를 생각해보자. 이는 릴리스된 이후로 부동 소수점 연산처리 단위에서 버그가 발견되어, 모든 운영체제 제작자들이 해당 문제점을 수용하기 위해 특수 코드를 써야 했다. 한 번 제조 프로세스가 완료되면, 하드웨어에 더 이상 변경을 수행할 수 없다. 소프트웨어는 극과 극으로 상반된다. 즉, 대부분 소프트웨어 프로젝트의 수명에 초기 릴리스 이후에 향상판을 통해 버그 수정 및 기타 "유지보수" 활동이 나타난다. 원자 대신에 비트를 처리하고, 비트는 영향 받을 가능성이 더 엄청나다.
애자일 설계는 프로젝트 또는 단계의 초반에 설계를 무시하려고 노력하지 않는다. 문제의 본질적 성향에 대해 가장 적게 알고 있을 때, 이는 프로세스의 초반에 가능한 한 적게 수행하려고 노력한다. 필자는 종종 다음 질문을 받는다. "프로젝트의 시작에서 어느 정도의 설계가 적합하다고 결정하는 방법은 무엇인가?" 다른 종류의 프로젝트는 다른 범주가 있고, 이는 대부분의 기술자 외의 사람들이 인식하는 것보다 유형과 복잡도 면에서 다양하다. 독자는 기본적으로 두 가지 사항의 균형을 잡으려고 노력하고 있다. 즉, 초기 의사결정이 향후에 변경을 방지할 만큼 충분히 정확하고, 이후 변경의 비용은 이점이 문제가 될 만큼 충분히 클 때 선행 설계가 효과가 있다는 점이다. 그러므로 차후 변경의 비용이 많이 드는 개발 기술과 결합된 초기 단계에서 훌륭한 지식이 필요하다. 사람들이 초기에 정확한 의사결정을 내리는 그들의 능력을 과대평가하고 나중에 끊임없이 늘어나는 결과에 고통 받기 때문에 애자일 설계는 이러한 개념에 반대한다.
다음과 같이 선행 설계를 더 많이 수행해야 하는 프로젝트의 일부 예제가 나와 있다.
- 연도별로 예상되는 변경이 없는 극도로 안정적인 요구사항을 갖춘 것.
- 안전 상의 이유로 철저히 고립된 환경(예를 들어, 우주 공간 여행, 수중 탐험)을 위한 것.
- 이전에 정확히 동일한 부분의 소프트웨어를 동일한 그룹의 사람들과 범위 변경 없이 작성한 프로젝트.(이에 대해 절대적으로 정확한 견적이 확보될 것이다.)
- 해당 환경과 함께 제한조건을 반드시 고려하도록 임베드된 시스템과 같이 고도로 제한된 환경에 대한 프로젝트. (필자는 동작적 기능이 가능한 한 많이 나타나도록 계속해서 노력할 것이다.)
선행 설계를 너무 많이 수행해선 안 되는 프로젝트의 유형에는 다음이 포함된다.
- 고도로 변동성이 높아 대부분의 비즈니스 애플리케이션과 같이 요구사항을 변경하는(월별 또는 주별로) 프로젝트.
- 시장 조건과 같은 외부 요인에 응답해야 하는 것.
- 많은 기술 또는 비즈니스 세부사항에 대해 아직 확신이 없는 프로젝트. 이는 가상으로 모든 프로젝트를 포괄하여 Rumsfeld의 "모르는 것을 모르는 것"에 다시 귀 기울이는 것을 참고하자.
- "완료"의 인위적인 차이라기 보다는 개발에 구독의 혜택을 누리는 프로젝트. 소프트웨어 프로젝트는 어느 때라도 완료되지 않으므로, 독자는 실제로 항상 구독을 구매하는 중이고, 이를 빨리 인식하면 할수록 이와 같은 조치를 빨리 취할 수 있다.
의사결정을 내리는 적합한 시기를 선택하는 것은 어렵지만 중요하다. 모든 프로젝트는 고유하므로, 구체적인 조언은 쓸모가 없게 된다. 하지만, 어느 정도 일반적인 가이드라인은 다음과 같이 존재한다.
- 작동하는 "가장 간단한 것"이 한참 작동하고 있는 중이지만, 이것이 해결하는 중이었던 문제는 중요성 또는 복잡성 면에서 엄청나게 증가하는 것에 주목하자. 예를 들어, 비동기 작동의 기본 배경에 대해 간단한 메시징 큐로서 데이터베이스를 사용해 왔다고 가정하자. 하지만, 성능이 문제가 되기 시작하는 것과 동일한 시점에 이제 새로운 몇 가지 요구사항들이 다양한 비동기 작동에 추가될 것이다. 독자의 솔루션이 해당 기준에 더 이상 부합되지 않기 때문에, 지금이 바로 "작동하는 가장 간단한 것" 의사 결정을 다시 논의할 시간이다.
- 문제의 전체적인 범위를 살펴볼 때, 창발적 설계 기술로 치우치는 경향을 보일 집단을 분리하려고 시도하자. 예를 들어, 독자가 지오코딩 지원이 필요한 애플리케이션에서 작업을 하고 있고, 이전에 지오코딩 라이브러리로 작업한 적이 아예 없다고 가정하자. 독자는 추정을 위해서 충분한 이해를 갖추도록 몇 가지 스파이크(간단한 직접적인 연구 및 개발 프로젝트)를 수행해야 한다. 불완전한 이해를 기반으로 하는 어느 아키텍처의(나중에 변경하기 어려움) 의사결정도 내리는 것을 방지하기 위해 노력하며, 더 나은 이해를 기반으로 개선할 수 있을지 여부를 확인하기 위해 나머지 애플리케이션과 이러한 분리된 부분 사이의 인터페이스 지점을 다시 논의하자.
- 애플리케이션 유동체 사이에 상호작용 지점을 보존하기 위해 시도하자. SOAP(Simple Object Access Protocol)와 같은 프로토콜의 바람직하지 않은 부작용 중 하나는 견고한 구조와 강력한 유형을 고집하는 것이다. 가변성의 비밀은 구체성이 더 많은 것이 아니라 더 적은 것이며, 이에 대해 인식하는 것은 REST(Representational State Transfer) 및 관련 기술에 더 많은 관심을 가지도록 이끌고 있다. API를 빌드할 때에, 합당한 가장 제네릭한 종류를 허용하려고 시도하자. 이는 통합에도 적용된다.
필자는 이 시리즈를 이전 18회의 주요 테마를 요약하여 마무리할 것이다.
"The Relationship between code and design"으로 돌아가서 필자는 일반적으로 설계로 생각하는 아티팩트(화이트보드 다이어그램, 통합된 모델링 언어(Unified Modeling Language) 및 기타 등등)가 아니라 프로젝트에 대해 전체 소스 코드로 구성된 소프트웨어 설계를 제안하는 Jack Reeves의 글을 인용하였다. 그는 소스 코드를 제조 팀이 설계를 원자로 변환해야 하는 전부를 지정하는 엔지니어가 작성한 계획과 비교한다. 우리의 계획은 소스 코드이며, 이는 컴파일러가 비트로 변환한다. 코드가 설계이면, 사용하는 컴퓨터 언어와 프레임워크는 우리가 설계 가능한 대상에 대한 원자재를 정의한다.
더 강력한 언어로 제공되는 영향력으로 인해 고급 기능의 두려움이 상쇄된다. 비록 독자는 저비용 개발자의 준비된 공급이 있기 때문에 10년 이상 된 언어에서 선택하고 표준화하는 것이 장점이긴 하지만, 더 현대적인 언어와 도구를 사용하고 있는 경쟁 상대만큼 빠르게 이동할 수 없다는 사실도 받아들여야 한다. 많은 조직들은 혁신과 정상 상태 둘 다를 희생하고 표준화를 너무 강조한다. 필자는 하나 이상의 프로젝트가 문제에 대해 엄청나게 부적합한 표준 프레임워크 세트를 사용하도록 강제 실행하여, 이는 우연한 소음으로 인해 모호해지기 때문에 어느 종류의 설계 검색에나 해가 되는 것을 확인했다.
이는 모든 개발자가 그의 자체적인 도구 스택을 선택하도록 허용해야 한다는 것을 의미하지 않는다. 이는 표준화로 제공되는 실제 가치에 주의를 기울여 합당한 대안을 고려해야 한다는 것을 의미한다. 예를 들어, 아마도 프로젝트에 대해 Java 기술을 계속 사용하려 할 수도 있지만 빌드하고 테스트하며 다른 개발자 태스크를 위한 더 고급 도구의 사용을 시작할 수 있다. 코드는 소프트웨어 프로젝트 어디서나 나타나고, 이는 모두 설계를 의식적이든지 아니든지 구현한다.
많은 방법론들은 불확실성을 방지하려고 한다. 원자로 건설하는 중이라면 특정한 모양으로 한 번 강제 실행되면 원자의 구성을 변경하는 비용이 많이 들기 때문에 불확실성은 바람직하지 않다. 하지만 비트로 빌드하는 중이라면 변경은 간편하고 매우 바람직하다. 변경을 방지하는 것은 소프트웨어에서 어려울 뿐만 아니라 바람직하지도 않다.
애자일 방법론은 단위 테스팅, 리팩토링, 지속적 통합(Continuous Integration) 및 반복적 개발과 같은 기술을 사용하여 변경이 더 간편해지는 방법을 찾으려고 노력한다. 창발적 설계는 설계에 대한 애자일 철학을 구현한다. 설계 의사결정이 발생할 때에 본인 스스로에게 다음을 물어보자.
- 지금 이 의사결정을 내려야 하는가?
- 이 의사결정을 안전하게 미룰 수 있을까?
- 의사결정을 되돌리기 위해 무엇을 할 수 있는가?
독자가 코드를 간편하게 리팩토링할 수 있는 환경에 있다면, 큰 문제 없이 이를 수정할 수 있기 때문에 임시로 차선의 의사결정을 내리는 것이 그렇게 무서운 일은 아니다. 변경에 적응하기 위해 프로젝트를 설정하면 과정 정정에 최적화되어 있기 때문에 의사결정을 미루는 것은 해가 되지 않는다. 변경을 수용하는 것은 냉정한 객관성으로 의사결정을 살펴보고 일을 더 어렵게 만드는 의사결정을 변경하기 위한 기능이 필요하다.
창발적 설계의 또 다른 측면은 코드에 이미 존재하는 유용한 설계 요소를 확인하고, 과거의 문제에 효율적인 솔루션으로서 필자가 "Leveraging reusable code, Part 1"에서 정의한 관용적 패턴으로 이를 채취하는 기능이다. 이러한 검색된 패턴은 회사가 비즈니스를 수행하는 방법에 대해 유용한 것을 캡처하는 설계 요소이기 때문에 회사의 보배이다. 독자가 쓰는 안타까울 정도로 적은 양의 코드는 고유한 가치를 요약한다. 즉, 나머지는 대부분 데이터베이스로 밀어 넣고 빼내는 것, HTML 빌드하는 것 등등에 불과하다. 관용적 패턴은 유용성의 중대한 기준들 중 하나에 이미 부합하기 때문에 JAD(Joint Application Design) 또는 상아탑 설계에서 지어낸 것보다 더 가치가 있다. 즉, 이는 실제 문제를 해결하기 위해 이미 사용되었다.
독자는 다양한 방식으로 이러한 패턴을 채취할 수 있다. API를 작성할 수 있으며, 이는 기계적으로 간편하지만 크게 두드러지지는 않는다 — 이는 독자가 사용하는 다른 모든 API와 비슷하다. 또한 메타프로그래밍 및 속성을 사용하여 관용적 패턴의 일부 카테고리를 캡처할 수도 있다("Leveraging reusable code, Part 2" 참조). 필자는 또한 "Using DSLs", "유연한 인터페이스", "Building DSLs in Groovy" 및 "JRuby로 DSL 작성하기"에서 도메인 특정 언어(DSL)를 사용하는 도메인 패턴을 캡처링하는 것도 논의했다.
항상 유용한 설계 요소를 인식하기 위해 노력하고, 그렇게 수행하기에 인위적으로 어렵게 만드는 의사결정을 방지하기 위해 노력하자. 예를 들어, 아키텍처의 요소를 애플리케이션에 추가하는 것(예: 확장성을 위한 계층 또는 서비스 지향 아키텍처에서 서비스)은 이러한 요소를 사용하여 시작할 때가 아니라 이를 코드 기반에 추가하자 마자 코드를 더 복잡하게 만든다. 이를 사용하기 시작할 때까지는 우연한 복잡성이기 때문에 적합한 시점에 복잡성을 추가하도록 하자.
이 시리즈의 목표 중 한 가지는 필자 스스로가 다른 방식의 설계와 진행하는 문서를 살펴보도록 강제로 실행하는 것이었다. 독자도 승객 좌석에서 승차를 즐겼기를 바란다. 필자는 머무르는 것을 포기하는 것이 아니라 주제를 변경하는 것이다. 기능적 사고에 대한 필자의 다음 developerWorks 시리즈에 대해서도 계속 주목하자.
교육
- The Productive Programmer(Neal
Ford 저, O'Reilly Media, 2008년): Neal Ford의 가장 최신 저서에서 이 시리즈의 다양한 주제에 대해 확장한다.
-
Donald Rumsfeld의 시: Rumsfeld의 선언 중 일부는 시 형식 및 음악으로 설정되어 있다.
- The Mythical Man Month, 2d ed. (Fred Brooks저, Addison-Wesley, 1995년):
이 책은 소프트웨어 개발에서 중요한 작업이며, 비직관적이지만 소프트웨어 프로젝트의 진정한 기이한 사항의 일부를 보여준다.
-
이런 기술 주제와 다른 기술 주제에 대한 서적 정보는 Java technology bookstore를
참조한다.
-
developerWorks Java 기술 영역: Java 프로그래밍과 관련된 모든 주제를 다루는 여러 편의 기사를 찾아보자.
토론
- developerWorks 커뮤니티에 참여하자. 개발자가 이끌고 있는 블로그, 포럼, 그룹 및 Wiki를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

Neal Ford는 글로벌 IT 컨설팅 업체인 ThoughtWorks의 소프트웨어 아키텍트이자 Meme Wrangler이다. 애플리케이션, 교육용 자료, 매거진 기사 및 비디오/DVD 프리젠테이션을 설계 및 개발하며 다양한 기술과 관련된 서적의 저자 또는 편집자이기도 하다. 최근에 출판된 책으로는 The Productive Programmer가 있다. 대규모 엔터프라이즈 애플리케이션의 설계 및 빌드에 많은 관심을 가지고 있는 그는 전세계의 개발자 컨퍼런스에서 국제적으로 인정 받고 있는 연사로도 활동하고 있다. 그의 웹 사이트를 살펴보자.