David Farley의 Modern Software Engineering
modern-software-engineering
- David Farley의 Modern Software Engineering
- Ch01. Introduction
- Ch02. What Is Engineering?
- Ch03. Fundamentals of an Engineering Approach
- Ch04. Working Iteratively
- Ch05. Feedback
- Ch06. Incrementalism
- Ch07. Empiricism
- Ch08. Being Experimental
- Ch09. Modularity
- Ch10. Cohesion
- Ch11. Separation of Concerns
- Ch12. Information Hiding and Abstraction
- Ch13. Managing Coupling
- Ch14. Testability
- Ch15. Deployability
- Ch16. Speed
- Ch17. Controlling Variables
David Farley의 Modern Software Engineering
Ch01. Introduction
TL;DR
- 소프트웨어 산업은 빠르게 성장했지만 진정한 엔지니어링 원칙을 적용하는 데는 뒤처져 있다.
- 소프트웨어 개발은 공예(craft)가 아닌 엔지니어링 분야로 접근해야 한다.
- 좋은 소프트웨어를 만들려면 학습을 최적화하고 복잡성을 관리해야 한다.
- 이 책은 특정 기술 스택이 아닌, 시간이 지나도 유효한 근본 원칙에 집중한다.
Key Ideas
- 소프트웨어 공학의 문제: 많은 프로젝트가 실패하거나 예산/일정을 초과한다. 원인은 기술이 아닌 접근 방식에 있다.
- 엔지니어링 vs 공예: 공예는 개인 기술에 의존하고, 엔지니어링은 체계적 원칙에 기반한다. 소프트웨어는 후자여야 한다.
- 학습의 중요성: 소프트웨어 개발은 본질적으로 탐색과 발견의 과정이다. 요구사항도 개발 중에 비로소 명확해진다.
- 복잡성은 적: 복잡성이 증가하면 변경 비용이 기하급수적으로 늘어난다. 복잡성을 관리하는 것이 핵심 역량이다.
Open Questions
- 우리 팀의 개발 방식은 엔지니어링에 가까운가, 공예에 가까운가?
- 현재 프로젝트에서 복잡성이 가장 빠르게 증가하는 영역은 어디인가?
Ch02. What Is Engineering?
TL;DR
- 엔지니어링은 과학적 방법을 실용적 문제에 적용하는 것이다.
- 핵심은 합리적 사고: 가설을 세우고, 실험하고, 증거로 판단한다.
- 소프트웨어에서 엔지니어링은 "생산"이 아니라 "설계"에 해당한다. 코드 작성 자체가 설계다.
- 생산(Production)은 컴파일러/빌드 시스템이 한다. 따라서 소프트웨어의 비용은 설계 비용이다.
Key Ideas
- 엔지니어링 = 과학의 실용적 적용: 순수 과학과 달리, 엔지니어링은 실제 문제를 해결하기 위해 지식을 적용한다.
- 소프트웨어는 설계 활동: 제조업에서는 설계 후 생산이 비용의 대부분이지만, 소프트웨어에서는 코드 작성(설계)이 비용의 대부분이다. 빌드/배포(생산)는 거의 무료다.
- 합리주의 vs 경험주의: 합리주의는 이성으로 진리를 찾고, 경험주의는 관찰과 실험으로 찾는다. 좋은 엔지니어는 둘 다 활용한다.
- 전문가 판단의 한계: 전문가의 직감도 편향될 수 있다. 데이터와 실험으로 보완해야 한다.
Open Questions
- 우리 팀에서 "직감"으로 내리는 결정과 "데이터"로 내리는 결정의 비율은?
- 소프트웨어 설계의 품질을 어떻게 측정할 수 있을까?
Ch03. Fundamentals of an Engineering Approach
TL;DR
- 소프트웨어 엔지니어링의 근본은 학습 최적화와 복잡성 관리 두 가지다.
- 학습 최적화: 반복(Iteration), 피드백(Feedback), 점진주의(Incrementalism), 경험주의(Empiricism), 실험주의(Experimental).
- 복잡성 관리: 모듈성(Modularity), 응집도(Cohesion), 관심사 분리(SoC), 정보 은닉(Information Hiding), 결합도 관리(Coupling).
- 이 원칙들은 독립적이 아니라 서로 강화하는 관계다.
Key Ideas
- 두 축의 균형: 학습만 빠르면 복잡해지고, 복잡성만 관리하면 느려진다. 둘의 균형이 핵심이다.
- 학습 최적화 5원칙: 반복 → 작은 단계로 나눔, 피드백 → 결과를 빠르게 확인, 점진주의 → 릴리즈 가능한 단위로 진행, 경험주의 → 증거 기반 판단, 실험주의 → 가설-검증 반복.
- 복잡성 관리 5원칙: 모듈성 → 독립 단위로 분리, 응집도 → 관련 기능 집중, 관심사 분리 → 다른 문제는 다르게, 정보 은닉 → 변경 영향 격리, 결합도 관리 → 의존성 최소화.
- 실천법은 원칙의 구현: CI/CD, TDD, 마이크로서비스 등은 이 원칙들의 구체적 실천이다.
Open Questions
- 우리 팀에서 가장 약한 원칙은 무엇인가?
- 학습 최적화와 복잡성 관리 중 현재 더 투자가 필요한 쪽은?
Ch04. Working Iteratively
TL;DR
- 반복적 작업은 큰 문제를 작은 단계로 나누어 각 단계에서 학습하고 방향을 수정하는 것이다.
- 빅뱅 접근은 실패 확률이 높다. 작은 단계로 나누면 실패 비용을 최소화하고 빠르게 복구할 수 있다.
- 반복의 핵심은 각 반복이 완결된 학습 사이클이 되어야 한다는 것이다.
- Waterfall은 반복 없이 한 방향으로만 진행하기 때문에 피드백이 너무 늦다.
Key Ideas
- 반복 = 학습 기회: 매 반복은 "우리가 올바른 방향으로 가고 있는가?"를 확인하는 기회다.
- 작은 배치의 우월성: 작은 변경은 이해하기 쉽고, 문제 발생 시 원인 추적이 빠르고, 롤백이 간단하다.
- 빅뱅의 위험: 오랜 기간 통합 없이 개발하면, 통합 시점에 예측 불가능한 문제가 폭발한다.
- 반복 주기 최적화: 반복 주기가 짧을수록 피드백이 빠르다. 이상적으로는 분~시간 단위(TDD + CI).
- 완전한 반복: 각 반복은 "아이디어 → 구현 → 피드백 → 학습"의 완전한 사이클이어야 한다.
Open Questions
- 현재 팀의 반복 주기는 얼마나 되는가? 더 짧게 만들 수 있는가?
- 반복 사이에서 실제로 학습이 발생하고 있는가, 아니면 형식적인 반복인가?
Ch05. Feedback
TL;DR
- 피드백은 소프트웨어 엔지니어링에서 학습을 가능하게 하는 핵심 메커니즘이다.
- 피드백 루프는 짧고 빠르고 자동화되어야 한다.
- TDD, CI/CD, 배포 파이프라인 등은 모두 피드백 루프를 단축하기 위한 실천법이다.
- 피드백은 코드 수준(테스트)부터 비즈니스 수준(A/B 테스트)까지 다중 레벨로 존재한다.
Key Ideas
- 피드백 루프의 속도가 학습 속도를 결정한다: 테스트 1초 vs 1시간은 개발자의 학습 속도에 결정적 차이를 만든다.
- 피드백의 종류:
- 코드 수준: 컴파일, 단위 테스트, 린트
- 통합 수준: 통합 테스트, CI
- 시스템 수준: E2E 테스트, 스테이징 배포
- 비즈니스 수준: A/B 테스트, 사용자 피드백, 모니터링
- 자동화가 핵심: 수동 피드백은 느리고 불규칙하다. 자동화된 파이프라인만이 지속 가능하다.
- 배포 파이프라인: 커밋부터 프로덕션까지 자동화된 피드백 경로. 각 단계가 "이 변경이 안전한가?"에 대한 피드백을 제공한다.
Open Questions
- 현재 팀의 피드백 루프에서 가장 느린 병목은 어디인가?
- 테스트 실행 시간을 10배 줄이면 개발 패턴이 어떻게 변할까?
Ch06. Incrementalism
TL;DR
- 점진주의는 작고 릴리즈 가능한 단위로 변경을 누적하여 큰 변화를 만드는 것이다.
- 각 변경은 그 자체로 완결되어야 하며, 시스템을 깨뜨리지 않아야 한다.
- 한 번에 큰 변경은 위험하다. 작은 변경의 연속이 더 안전하고 빠르다.
- 점진주의를 가능하게 하려면 모듈화된 설계와 자동화된 테스트가 전제 조건이다.
Key Ideas
- 릴리즈 가능한 단위: 모든 커밋은 릴리즈 가능한 상태여야 한다. "나중에 고칠 것"은 기술 부채다.
- 브랜치 전략: 장기 브랜치는 점진주의의 적이다. 트렁크 기반 개발이 점진주의와 가장 잘 맞는다.
- 피처 플래그: 미완성 기능도 안전하게 배포할 수 있게 한다. 점진적 릴리즈의 핵심 도구다.
- 호환성: 점진적 변경은 이전 버전과의 호환성을 유지해야 한다. API 변경 시 확장-마이그레이션-축소 패턴을 활용한다.
- Strangler Fig 패턴: 레거시 시스템을 한 번에 교체하지 않고 점진적으로 새 시스템으로 이전한다.
Open Questions
- 현재 브랜치의 평균 수명은 얼마인가? 이를 줄일 수 있는가?
- 점진적 릴리즈를 위한 피처 플래그 시스템이 있는가?
Ch07. Empiricism
TL;DR
- 경험주의는 가정이나 직감이 아닌, 관찰과 실험의 결과(증거)에 기반하여 의사결정하는 것이다.
- 소프트웨어 개발에서 많은 결정이 "그냥 그렇게 해왔으니까"로 이루어진다. 이것은 엔지니어링이 아니다.
- 측정이 경험주의의 출발점이다. 측정하지 않으면 개선 여부를 알 수 없다.
- 편견과 인지 편향을 인식하고 극복해야 진정한 경험주의적 판단이 가능하다.
Key Ideas
- 증거 기반 의사결정: "이 기술이 좋다"는 주장은 "어떤 상황에서, 어떤 측정 결과로?" 뒷받침되어야 한다.
- 측정의 중요성: 성능, 배포 빈도, 결함률, 리드 타임 등 객관적 지표로 판단한다.
- 인지 편향 극복: 확증 편향(원하는 결과만 보기), 앵커링(첫 정보에 치우침), 밴드왜건 효과(유행 따라가기) 등을 인식해야 한다.
- 가설 검증: "이 캐시를 추가하면 응답 시간이 30% 줄 것이다"처럼 구체적이고 반증 가능한 가설을 세운다.
- DORA 연구: Accelerate 연구는 소프트웨어 팀의 성과를 경험적으로 측정한 대표 사례다.
Open Questions
- 현재 팀에서 데이터 없이 내리는 결정에는 어떤 것들이 있는가?
- DORA 메트릭을 측정하고 있는가? 그렇지 않다면 무엇부터 측정을 시작할 수 있는가?
Ch08. Being Experimental
TL;DR
- 실험적 접근은 가설을 세우고, 실험을 설계하고, 결과로 판단하는 과학적 방법이다.
- 소프트웨어에서 실험 비용은 다른 엔지니어링 분야보다 압도적으로 낮다. 이 장점을 활용해야 한다.
- 안전하게 실패할 수 있는 환경을 만들면, 더 대담한 실험이 가능하다.
- TDD는 코드 수준에서의 실험이고, A/B 테스트는 비즈니스 수준에서의 실험이다.
Key Ideas
- 가설-검증 사이클: "이 변경이 X를 개선할 것이다" → 구현 → 측정 → 판단. 명확하고 반증 가능한 가설이 핵심이다.
- 실험 비용 최소화: 소프트웨어는 물리적 제약이 없어 실험 비용이 낮다. 빌드, 테스트, 배포 자동화로 더 낮출 수 있다.
- 안전한 실패: 실패 시 시스템이 망가지지 않는 환경(자동 롤백, 피처 플래그, 카나리 릴리즈)이 실험을 가능하게 한다.
- 탐색적 코딩: 스파이크(Spike)로 기술적 가능성을 탐색한 후, 검증된 접근법으로 프로덕션 코드를 작성한다.
- 실패로부터의 학습: 실패한 실험도 "이 방법은 작동하지 않는다"는 소중한 학습이다.
Open Questions
- 팀에서 "실험"과 "도박"의 경계를 어떻게 구분하는가?
- 실패한 실험에서 학습을 체계적으로 공유하고 있는가?
Ch09. Modularity
TL;DR
- 모듈성은 시스템을 독립적으로 변경 가능한 단위로 나누는 것이다. 복잡성 관리의 가장 기본 전략이다.
- 좋은 모듈은 명확한 경계, 안정적 인터페이스, 교체 가능성을 갖는다.
- 모듈의 크기보다 경계의 품질이 더 중요하다.
- 마이크로서비스는 모듈성의 극단적 형태지만, 모듈성의 유일한 방법은 아니다.
Key Ideas
- 모듈 = 독립적 변경 단위: 모듈 내부를 변경해도 다른 모듈에 영향이 없어야 한다.
- 경계의 중요성: 모듈 사이의 인터페이스가 불명확하면 모듈화의 이점이 사라진다. 경계를 넘는 통신은 잘 정의된 계약으로만 이루어져야 한다.
- 서비스 vs 모듈: 서비스(프로세스 경계)로 나누면 강제적 분리가 되지만, 네트워크 오버헤드와 분산 시스템 복잡성이 추가된다. 잘 설계된 모놀리스의 모듈이 나쁜 마이크로서비스보다 낫다.
- 교체 가능성(Replaceability): 좋은 모듈은 교체할 수 있다. 교체가 어렵다면 모듈 경계가 잘못된 것이다.
- 테스트와 모듈성: 모듈이 독립적이면 독립적으로 테스트할 수 있다. 테스트가 어렵다면 모듈성이 부족한 신호다.
Open Questions
- 현재 시스템에서 "변경할 때 여러 모듈을 동시에 수정해야 하는" 경우가 얼마나 자주 발생하는가?
- 모듈 경계는 어떤 기준으로 나누고 있는가? 기술적 기준인가, 도메인 기준인가?
Ch10. Cohesion
TL;DR
- 응집도는 모듈 내부의 요소들이 얼마나 밀접하게 관련되어 있는지를 나타낸다.
- 높은 응집도 = 모듈이 하나의 명확한 목적을 가진다. 관련 있는 것들을 함께 둔다.
- 낮은 응집도 = 서로 관련 없는 기능이 한 모듈에 모여 있다. 변경 이유가 여러 개다.
- 응집도는 단일 책임 원칙(SRP)과 직접 연결된다: 변경의 이유가 하나여야 한다.
Key Ideas
- 응집도의 스펙트럼: 기능적 응집(가장 높음) > 순차적 응집 > 통신적 응집 > 절차적 응집 > 시간적 응집 > 논리적 응집 > 우연적 응집(가장 낮음).
- 기능적 응집: 모듈의 모든 요소가 단일 기능을 수행하기 위해 존재한다. 이상적인 상태.
- 우연적 응집: "유틸리티 클래스"처럼 관련 없는 기능이 편의상 모여 있다. 피해야 할 상태.
- 변경의 이유로 판단: "이 모듈을 변경해야 하는 이유가 몇 가지인가?"가 응집도의 실용적 척도다.
- 패키지 구조와 응집도: 기능(feature) 기반 패키지가 계층(layer) 기반 패키지보다 응집도가 높다.
Open Questions
- 유틸리티 클래스가 많다면, 이를 적절한 도메인 모듈로 재배치할 수 있는가?
- 한 클래스/모듈의 변경 이유가 몇 가지인지 나열해 보았는가?
Ch11. Separation of Concerns
TL;DR
- 관심사 분리는 서로 다른 문제를 서로 다른 곳에서 처리하는 것이다.
- 각 관심사를 독립적으로 이해하고 변경할 수 있으면 복잡성이 줄어든다.
- 관심사 분리는 모듈성, 응집도, 결합도와 밀접하게 연관된 원칙이다.
- 포트와 어댑터(Hexagonal) 아키텍처는 비즈니스 로직과 인프라 관심사를 분리하는 대표적 패턴이다.
Key Ideas
- 관심사(Concern)란: 시스템이 해결해야 하는 각각의 문제 영역. 비즈니스 로직, 데이터 저장, UI, 인증 등이 각각의 관심사다.
- 관심사 분리의 이유: 한 관심사를 변경할 때 다른 관심사에 영향을 주지 않기 위해서다.
- 계층 구조: 전통적 방법. Presentation → Business Logic → Data Access. 하지만 비즈니스 로직이 인프라에 의존하게 되는 문제가 있다.
- 포트와 어댑터: 비즈니스 로직이 중심, 외부(DB, UI, API)가 어댑터. 비즈니스 로직은 포트(인터페이스)만 알고, 구체적 인프라를 모른다.
- 수직 슬라이스: 기능 단위로 모든 계층을 관통하는 슬라이스를 만든다. 기능 추가 시 한 슬라이스만 수정하면 된다.
- DDD의 Bounded Context: 같은 용어도 맥락에 따라 다른 의미를 가진다. 각 맥락(관심사)을 명시적으로 분리한다.
Open Questions
- 현재 시스템에서 비즈니스 로직이 인프라(DB, 프레임워크)에 직접 의존하고 있는 부분은?
- 관심사 분리가 잘 되어 있는지를 어떤 기준으로 판단하는가?
Ch12. Information Hiding and Abstraction
TL;DR
- 정보 은닉은 모듈의 구현 세부사항을 외부로부터 숨기는 것이다.
- 추상화는 복잡한 것의 핵심만 드러내고 나머지를 감추는 것이다.
- 안정적인 인터페이스를 노출하고 변동성이 큰 구현을 숨기면, 변경의 영향 범위를 최소화한다.
- 누출된 추상화(Leaky Abstraction)는 내부 세부사항이 외부로 새어나가는 것으로, 정보 은닉의 실패다.
Key Ideas
- David Parnas의 정보 은닉: 모듈은 "변경될 가능성이 높은 결정"을 내부에 숨겨야 한다. 1972년 논문에서 제안된 원칙이 지금도 유효하다.
- 인터페이스 = 계약: 모듈이 "무엇을 하는지"만 공개하고 "어떻게 하는지"는 숨긴다. 인터페이스가 안정적이면 내부 구현을 자유롭게 변경할 수 있다.
- 추상화 수준: 좋은 추상화는 적절한 수준에서 복잡성을 감춘다. 너무 낮으면 세부사항에 노출되고, 너무 높으면 유연성을 잃는다.
- 누출된 추상화: Joel Spolsky의 법칙 - "모든 비자명한 추상화는 어느 정도 누출된다." 완벽한 추상화는 없지만, 누출을 최소화하려는 노력이 필요하다.
- 캡슐화와 정보 은닉의 차이: 캡슐화는 데이터와 행동을 묶는 것(메커니즘), 정보 은닉은 변경될 것을 숨기는 것(원칙). 캡슐화는 정보 은닉을 구현하는 수단 중 하나다.
Open Questions
- 코드베이스에서 "구현 세부사항이 인터페이스로 새어나간" 대표적인 사례는?
- 추상화 수준을 적절히 설정하는 기준은 무엇인가?
Ch13. Managing Coupling
TL;DR
- 결합도는 모듈 간 의존성의 정도다. 한 모듈의 변경이 다른 모듈에 미치는 영향을 결정한다.
- 낮은 결합도가 목표다. 완전히 없앨 수는 없지만, 불필요한 결합을 제거해야 한다.
- DIP(의존성 역전 원칙)는 결합도를 관리하는 핵심 원칙이다.
- 이벤트 기반 아키텍처와 비동기 통신은 시간적 결합을 줄이는 방법이다.
Key Ideas
- 결합도의 종류:
- 내용 결합(가장 나쁨): 다른 모듈의 내부를 직접 참조.
- 공통 결합: 전역 데이터 공유.
- 제어 결합: 다른 모듈의 흐름을 제어하는 플래그 전달.
- 스탬프 결합: 필요 이상의 데이터 전달.
- 데이터 결합(가장 좋음): 필요한 데이터만 전달.
- DIP(의존성 역전 원칙): 상위 모듈이 하위 모듈에 의존하지 않고, 둘 다 추상화(인터페이스)에 의존한다.
- 시간적 결합: A가 끝나야 B가 시작되는 의존. 비동기 통신, 이벤트, 메시지 큐로 해소할 수 있다.
- 배포 결합: A를 배포할 때 B도 함께 배포해야 하는 의존. 독립 배포를 목표로 한다.
- 마이크로서비스와 결합: 서비스로 나눠도 API 계약, 공유 DB, 공유 라이브러리를 통해 결합될 수 있다.
Open Questions
- 현재 시스템에서 가장 결합도가 높은 모듈 쌍은 무엇인가?
- 배포 시 항상 함께 배포해야 하는 컴포넌트가 있는가?
Ch14. Testability
TL;DR
- 테스트 가능한 설계는 좋은 설계다. 테스트하기 어렵다면 설계에 문제가 있다는 신호다.
- TDD는 단순한 테스트 기법이 아니라 설계를 이끄는 방법이다.
- 테스트 피라미드: 단위 테스트(많이) > 통합 테스트(적당히) > E2E 테스트(적게).
- 포트와 어댑터 아키텍처는 테스트 가능성을 극대화한다. 비즈니스 로직을 외부 의존성 없이 테스트할 수 있다.
Key Ideas
- 테스트 가능성 = 설계 품질 지표: 테스트하려면 명확한 입출력, 낮은 결합도, 높은 응집도가 필요하다. 이것들은 좋은 설계의 특성이기도 하다.
- TDD의 3단계: Red(실패하는 테스트 작성) → Green(최소한의 구현) → Refactor(코드 개선). 이 사이클이 설계를 점진적으로 개선한다.
- 테스트 더블: Stub(고정 응답 반환), Mock(호출 검증), Fake(간단한 구현). 외부 의존성을 대체하여 테스트를 빠르고 안정적으로 만든다.
- 테스트 피라미드: 빠르고 안정적인 단위 테스트를 많이, 느리고 불안정한 E2E 테스트를 적게. 역삼각형(E2E 위주)은 안티패턴이다.
- 속성 기반 테스트: 특정 입출력 대신 "항상 참이어야 하는 속성"을 테스트한다. 예상치 못한 케이스를 발견하는 데 유용하다.
Open Questions
- 현재 테스트 피라미드의 모양이 어떤가? 이상적인 피라미드에 가까운가?
- 테스트하기 어려운 코드가 있다면, 그것은 어떤 설계 문제를 나타내는가?
Ch15. Deployability
TL;DR
- 배포는 일상적이고 지루한 이벤트여야 한다. 긴장되는 이벤트라면 뭔가 잘못된 것이다.
- 자동화된 배포 파이프라인이 핵심이다. 수동 배포는 느리고 오류가 발생하기 쉽다.
- 배포 빈도를 높이면 각 배포의 위험이 줄어든다. 작은 변경 + 자주 배포가 안전하다.
- 피처 플래그, 카나리 릴리즈, 블루/그린 배포로 안전한 배포를 구현한다.
Key Ideas
- 배포 파이프라인: 커밋 → 빌드 → 테스트 → 스테이징 → 프로덕션. 각 단계가 자동화된 피드백을 제공한다.
- 배포 ≠ 릴리즈: 배포는 코드를 환경에 설치하는 것, 릴리즈는 사용자에게 기능을 공개하는 것. 피처 플래그로 분리 가능하다.
- Continuous Delivery: 모든 커밋이 프로덕션에 배포 가능한 상태를 유지한다. 배포 결정은 비즈니스 결정이 된다.
- Continuous Deployment: Continuous Delivery에서 한 걸음 더, 모든 커밋이 자동으로 프로덕션에 배포된다.
- 롤백과 롤포워드: 문제 발생 시 이전 버전으로 돌아가거나(롤백), 빠르게 수정 배포(롤포워드). 둘 다 빠르게 가능해야 한다.
- 환경 일관성: 개발, 스테이징, 프로덕션 환경이 동일해야 "여기서는 되는데 거기서는 안 돼"를 방지한다.
Open Questions
- 현재 배포 주기는 얼마인가? 일일 배포를 할 수 있는가?
- 배포 실패 시 롤백까지 걸리는 시간은?
Ch16. Speed
TL;DR
- 빠른 개발 속도는 품질을 희생해서 얻는 것이 아니다. 오히려 품질이 속도를 만든다.
- "빠르게 더럽게"는 단기적으로도 느리다. 기술 부채는 즉시 이자를 청구한다.
- DORA 메트릭(배포 빈도, 리드 타임, 변경 실패율, 복구 시간)이 팀 성과의 핵심 지표다.
- 엘리트 팀은 속도와 안정성이 양립한다. 둘은 상충하지 않는다.
Key Ideas
- 속도 ≠ 서두름: 진정한 속도는 짧은 사이클 타임(아이디어→프로덕션)이다. 코딩 속도가 아니라 가치 전달 속도다.
- 품질이 속도를 만든다: 깨끗한 코드, 자동화된 테스트, 좋은 설계가 변경 비용을 낮춰 빠른 개발을 가능하게 한다.
- 기술 부채의 진짜 비용: 기술 부채는 "나중에 갚는다"가 아니라 "매일 이자를 낸다." 디버깅 시간 증가, 변경 공포, 배포 지연이 이자다.
- DORA 연구의 핵심 발견:
- 엘리트 팀: 하루 여러 번 배포, 리드 타임 1시간 미만, 변경 실패율 0~15%, 복구 1시간 미만.
- 속도와 안정성은 상충하지 않는다. 둘 다 높은 팀이 실재한다.
- 처리량(Throughput): 단위 시간당 완료된 가치의 양. 리드 타임을 줄이는 것이 처리량을 높이는 가장 효과적인 방법이다.
- 지속 가능한 속도: 번아웃 없이 장기적으로 유지할 수 있는 속도. 오버타임은 단기적으로도 생산성을 낮춘다.
Open Questions
- 현재 팀의 DORA 메트릭 수준은? 이를 개선할 수 있는 가장 큰 병목은?
- "빠르게 더럽게" 유혹을 느끼는 상황은 언제인가?
Ch17. Controlling Variables
TL;DR
- 변수를 통제해야 실험 결과를 신뢰할 수 있다. 여러 것을 동시에 바꾸면 원인을 알 수 없다.
- 소프트웨어에서 통제할 변수: 코드, 환경, 설정, 데이터.
- 인프라 코드화(IaC), 컨테이너, 불변 인프라가 환경 변수를 통제하는 핵심 도구다.
- 한 번에 하나만 변경하는 원칙이 디버깅과 실험의 기초다.
Key Ideas
- 과학적 실험과 변수 통제: 과학에서 대조군과 실험군을 나누듯, 소프트웨어에서도 변경 요소를 격리해야 한다.
- 환경 일관성: "내 컴퓨터에서는 되는데"를 방지한다. Docker, Kubernetes 등으로 환경을 코드로 정의하면 어디서든 동일한 환경을 재현할 수 있다.
- 불변 인프라(Immutable Infrastructure): 서버를 수정하지 않고, 새 이미지를 만들어 교체한다. 설정 드리프트를 방지한다.
- 인프라 코드화(Infrastructure as Code): Terraform, Pulumi 등으로 인프라를 버전 관리한다. 인프라 변경도 코드 리뷰와 테스트가 가능해진다.
- 한 번에 하나만: 코드 변경과 인프라 변경을 동시에 하면, 문제 발생 시 원인이 어디인지 모른다. 변경을 격리한다.
- 재현 가능성(Reproducibility): 빌드, 테스트, 배포 결과가 동일한 입력에 대해 항상 동일해야 한다.
Open Questions
- 현재 개발 환경과 프로덕션 환경의 차이점은 무엇인가?
- 빌드/배포 결과가 항상 재현 가능한가?