
대형 소프트웨어 시스템을 실제로 작업하는 엔지니어만이 설계 과정에 의미 있게 참여할 수 있다. 그 이유는 시스템의 구체적인 세부 사항을 깊이 이해하지 않고는 제대로 된 소프트웨어 설계를 할 수 없기 때문이다. 즉, 대부분의 실무적인 소프트웨어 설계 문제에서 일반적인 소프트웨어 설계 조언(generic software design advice)은 보통 쓸모가 없다.
일반적인 소프트웨어 설계(Generic software design)
일반적인 소프트웨어 설계란 무엇인가? 이는 “문제에 맞춰 설계하는(designing to the problem)” 방식이다: 도메인(domain)에 대한 합리적인 이해는 있지만 기존 코드베이스(codebase)에 대한 지식은 거의 없는 상태에서 주는 조언이다. 불행히도 소프트웨어 책이나 블로그 포스트에서 읽을 수 있는 조언은 바로 이 유형뿐이다. 엔지니어들이 일반적인 소프트웨어 설계 조언을 좋아하는 이유는 모든 기술 전문가들이 “전문 용어로 떠들기(talking shop)”를 즐기는 것과 같다. 하지만 이런 일반 조언을 일상적인 실무 문제에 적용할 때는 매우 조심해야 한다.
실제 작업을 할 때는 구체적인 요소들이 일반적인 요소들을 압도한다. 지금 코드가 어떻게 생겼는지 명확히 아는 것이 일반적인 디자인 패턴(design patterns)이나 원칙(principles)을 잘 아는 것보다 훨씬, 훨씬 더 중요하다. 예를 들어:
- 대형 코드베이스에서는 “좋은 설계”보다 일관성(consistency)이 더 중요하다. 여기서는 그 이유를 자세히 논하지 않겠지만, 대형 기존 코드베이스에서 엔지니어들이 저지르는 실수(Mistakes engineers make in large established codebases)에서 길게 다뤘다.
- 실제 코드베이스는 보통 복잡하고 예측하기 어려운 부작용들로 가득 차 있다. 변경을 안전하게 하려면 구현 선택지가 극히 제한적인 몇 가지로 좁혀진다.
- 대형 공유 코드베이스는 결코 단일 설계를 반영하지 않고, 항상 여러 소프트웨어 설계 사이의 중간 상태에 있다. 따라서 개별 변경 후 코드베이스가 어떻게 유지될지가 이상적인 “북쪽 별(north star)”을 향해 가는 것보다 훨씬 더 중요하다.
전체 시스템을 마음대로 다시 쓸 수 있는 세상이라면 일반적인 소프트웨어 설계 조언이 훨씬 실용적일 것이다. 일부 프로젝트는 그렇다! 하지만 소프트웨어 엔지니어링 작업의 대부분은 안전하게 다시 쓸 수 없는 시스템에서 이뤄진다. 이런 시스템들은 “소프트웨어 설계”에 의존할 수 없고, 대신 내부 일관성과 엔지니어들의 신중함에 의지해야 한다.
구체적인 소프트웨어 설계(Concrete software design)
그렇다면 좋은 소프트웨어 설계는 어떻게 생겼을까?
경험상 가장 유용한 소프트웨어 설계는 시스템을 깊이 이해하는 소수 엔지니어들 사이에서 일어나는데, 그들은 매일 그걸 작업하는 사람들이기 때문이다. 이런 설계 논의는 외부인에게는 종종 정말 지루하다. 왜냐하면 누구나 이해하고 의견 낼 수 있는 일반 원칙이 아니라, 시스템의 난해한 구체적 세부 사항(esoteric concrete details)을 중심으로 돌아가기 때문이다.
논의되는 주제는 “DRY( Don’t Repeat Yourself)가 WET( Write Everything Twice)보다 나은가” 같은 게 아니라, “이 새 동작을 A 서브시스템(subsystem A)에 넣을 수 있을까? 안 돼, B 정보(information B)가 필요하고 C 컨텍스트(context C)에서 그 서브시스템에 안 보이니까, D 서브시스템(subsystem D)을 다시 써야 하는데, E 서브시스템(subsystem E)을 여기서 여기서 쪼개면…” 같은 것이다.
설계에 대한 깊은 철학적 논점은 논의에서 거의 중요하지 않다. 대신 가장 중요한 기여는 구체적 사실에 대한 작은 오해를 지적하는 거다. 예를 들어: “아, B가 C 컨텍스트에 안 된다고 생각했어? 그런데 최근에 C를 리팩토링(refactored)해서 필요하면 B를 스레드(thread)로 넣을 수 있게 됐어”.
일반적인 소프트웨어 설계가 유용한 때(When generic software design is useful)
실무적인 소프트웨어 설계 문제에는 일반적인 소프트웨어 설계 조언이 도움이 되지 않지만, 그렇다고 완전히 쓸모없다는 건 아니다.
일반적인 소프트웨어 설계 조언은 완전히 새로운 프로젝트를 만들 때 유용하다. 위에서 주장했듯이 기존 시스템에서 새 기능을 설계할 때는 시스템의 구체적 요소들이 지배적이지만, 새 시스템을 설계할 때는 그런 구체적 요소가 없으므로 일반 조언에 전적으로 의지할 수 있다.
일반적인 소프트웨어 설계 조언은 구체적 설계 결정에서 최종 선택을 할 때(tie-breaking)도 유용하다. 일반 설계부터 시작해야 한다고 생각하지 않지만, 여러 현실적 후보 경로가 모두 괜찮아 보일 때는 일반 원칙들이 그 사이에서 선택을 도와줄 수 있다.
이건 특히 회사 전체 차원에서 더 맞는 말이다. 즉, 일반적인 소프트웨어 설계 조언은 서로 다른 코드베이스 간 일관성을 확보하는 데 도움이 된다. 이것이 공식적인 “소프트웨어 아키텍트(software architect)” 역할의 가장 유용한 기능 중 하나다: 개별 엔지니어들이 자신의 구체적 결정을 같은 방향으로 최종 선택할 수 있도록 일반 원칙 세트를 제공하는 것이다.
일반적인 소프트웨어 설계 원칙은 회사 전체 아키텍처 결정도 안내할 수 있다. 서비스를 우리 데이터센터에서 돌릴까, 클라우드(cloud)에서 할까? k8s(Kubernetes)를 쓸까? AWS인가 Azure인가? 범위가 넓어질수록 개별 서비스의 구체적 세부 사항은 거의 중요하지 않게 되고, 어차피 어마어마한 작업량이 들기 때문이다. 그래도 이런 결정에서도 구체적 세부 사항은 아주 중요하다. 클라우드에서는 할 수 없는 일(예: 맞춤형 하드웨어 세팅에 의존하기)이 있고, 자체 데이터센터에서는 할 수 없는 일(예: 서비스를 12개 지역의 엣지(edge)에 배포하기)이 있다. 코드베이스의 구체적 세부 사항이 그런 것에 의존한다면, 회사 전체 아키텍처 결정을 할 때 무시하면 큰코다친다.
아키텍트와 로컬 미니멈(Architects and local minima)
이런 이유들은 일반적인 소프트웨어 설계를 하는 좋은 이유들이다. 회사들이 일반적인 소프트웨어 설계를 하는 나쁜 이유 하나는, 현업 소프트웨어 엔지니어가 아닌 사람들에게 그냥 정말 좋은 아이디어처럼 들리기 때문이다. 일단 시작하면 인센티브 때문에 멈추기 어렵다. 많은 테크 회사들이 이런 로컬 미니멈(local minimum)에 빠진다.
왜 최고 연봉 받는 소프트웨어 엔지니어들을 가장 추상적이고 영향력 큰 결정만 하게 하지 않나? 구조 엔지니어들은 벽돌 쌓는 대신 도면 그리게 하고 싶지 않나, 뭐 그런 식으로. 구조 공학이 그렇게 되는진 모르겠지만, 소프트웨어 공학은 그렇지 않다는 건 안다. 실제로 소프트웨어 아키텍처 조언은 현장 사람들이 종종 무시해야 한다. 현재 시스템의 현실 맥락에서 그걸 실제 구현 가능한 걸로 번역할 방법이 없기 때문이다.
하지만 효과 없는 관행치고는 “최고 엔지니어들은 그냥 일반 설계만 하라”는 게 놀랍게도 튼튼하다. 아키텍트들은 스킨 인 더 게임(skin in the game)이 없기 때문이다. 왜냐하면 그들의 설계는 실제 엔지니어링 팀에 넘겨져 구현되기 때문이다. 그런 설계는 완벽히 구현될 수 없으므로, 아키텍트들은 성공하면 공을 주장할 수 있고(어차피 내 설계였으니까), 실패하면 책임을 피할 수 있다(저 바보들이 내 설계를 제대로 따랐으면!).
요약(Summary)
대형 기존 코드베이스에서 일할 때, 유용한 소프트웨어 설계 논의는 많은 사람들이 믿는 것보다 훨씬, 훨씬 더 구체적이다. 보통 개별 파일이나 심지어 코드 라인 단위로 이야기한다. 따라서 코드베이스를 깊이 잘 알아야 유용한 소프트웨어 설계를 할 수 있고(실제로 그건 거의 항상 적극적인 기여자(active contributor)가 되어야 한다는 뜻이다).
완전히 일반적인 아키텍처는 쓸모없지 않지만, 그 역할은 (a) 완전 새 시스템을 위한 잘 포장된 길(paved paths)을 제시하는 것, (b) 기존 시스템에서의 결정 최종 선택(tie-breaking), (c) 회사들이 큰 기술 선택을 하는 걸 돕는 데로 제한되어야 한다.
개인적으로, 프로젝트 초기 설계를 전담하는 형식적인 “큰 그림 소프트웨어 아키텍트(big-picture software architect)” 역할은 실패할 운명이라고 본다. 좋은 아이디어처럼 들리고(아키텍트에게는 공만 챙기고 책임은 안 지는 좋은 딜이지만), 실제 코드를 쓰는 엔지니어링 팀에 거의 가치를 주지 못한다.
개인적으로 소프트웨어 프로젝트 설계를 제안한 사람이 그 프로젝트의 성공이나 실패에 책임을 져야 한다고 믿는다. 그렇게 되면 소프트웨어 시스템을 설계하는 사람들이 바로 소프트웨어 시스템을 실제로 출시(ship)하는 법을 아는 사람들이 빠르게 된다. 또한 코드베이스의 모든 거친 모서리(rough edges)와 흉터(warts)를 고려해야 하는 진짜 소프트웨어 설계자들이 그 어려운 설계 작업에 대한 공을 받게 될 것이다.
원문: You can’t design software you don’t work on
blog by ash에서 더 알아보기
구독을 신청하면 최신 게시물을 이메일로 받아볼 수 있습니다.
