- 프로그래밍과 소프트웨어 엔지니어링의 가장 큰 차이는 시간, (규모)확장, 실전에서의 트레이드오프 세 가지이다.
- ‘소프트웨어 엔지니어링은 흐르는 시간 위에서 순간순간의 프로그래밍을 모두 합산한 것이다’
- 소프트웨어의 지속 가능성이 프로그래밍과 소프트웨어 엔지니어링 차이의 핵심.
- 기술적인 이유든 사업적인 이유든, 소프트웨어의 기대 생애 동안 요구되는 모든 가치 있는 변경에 대응할 수 있다면 지속가능하다고 말할 수 있다.
- 소프트웨어 엔지니어링은 또한 의사결정의 복잡성과 이해관계 측면에서도 프로그래밍과 차이가 난다.
- 주기적으로 여러 선택지 사이의 트레이드오프를 평가해야 한다.
- 소프트웨어 엔지니어링에서 만병통치약이란 찾기 어렵다.
- 때로는 유지보수에 도움되는 변경을 연기하거나 심지어 확장성이 떨어지는 정책을 받아들여야 할 때도 있음.
1. 시간과 변경

- 소프트웨어의 기대 수명이 길다면 업그레이드의 중요도가 높아진다.
- (기대 수명 스펙트럼의 최저점과 최고점 사이 어딘가에서 전환이 일어남)
- 초기부터 업그레이드를 계획하지 않은 프로젝트라면 이 전환이 아주 고통스러울 것이다.
- 그래서 업그레이드에 성공하든 중도에 포기하든, 이 고통을 한 번 겪고 나면 지레 겁먹고 이후의 업그레이드 비용을 과대 측정하여 ‘다시는 시도하지 않겠다’고 다짐하는 경우도 있다.
- 이런 결론에 이른 회사는 기존 코드를 버리고 새로 작성하거나 업그레이드를 완전히 포기하곤 한다.
1-1 하이럼의 법칙
- 하이럼의 법칙 : API 사용자가 충분히 많다면 API 명세에 적힌 내용은 중요하지 않다. 시스템에서 눈에 보이는 모든 행위를 누군가는 이용하게 될 것이기 때문이다.
- 만약 api 명세에 기입되지 않은 버그를 기능처럼 사용하는 사용자가 있을 수 있다.
- 버그를 수정하는 무해할 듯한 변경도 일부 사용자의 소프트웨어를 망가뜨릴 수 있음.
1-2 사례 : 해시 순서
- 해시 테이블의 원소 순서의 규칙을 발견한 사용자가 이를 소프트웨어 기능으로 사용한다면?
- 규칙을 변경하면 소프트웨어에 문제가 생길 것이다.
- 프로그램의 수명이 짧다면 기술적 문제를 겪지 않을 것이지만, 소프트웨어 엔지니어링 프로젝트에서 이런 의존성은 위험 요인에 해당한다.
- 코드 자체를 수명에 대한 요구사항이 다양한 제품으로 본다면 프로그래밍 스타일을 다음처럼 분류해볼 수 있다.
- 이용하는 api의 명세에 명시되지 않은, 즉 언제든 변할 수 있는 기능을 사용하는 코드는 ‘임시방편적인’ 혹은 ‘기발한’ 코드이다.
- 반대로 모범 사례를 따르고 미래에 대비한 코드는 ‘클린’하고 ‘유지보수 가능한’코드이다.
- 우리는 ‘기발한’이 칭찬으로 느껴진다면 프로그래밍이라 하고, 질채긍로 느껴진다면 소프트웨어 엔지니어라고 말한다.
1-3 변하지 않기를 목표로 하지 않는 이유
- 구글에서 오래전에 만들어둔 알고리즘과 데이터 구조는 최신 장비에서 효율이 떨어지기도 한다.
- 이전 시스템에 문제가 없더라도 시간이 흐르면 변경을 진행해야 할 이유가 자연스럽게 만들어지기도 함.
- 지속 가능성에 투자하지 않는 장기 프로젝트는 위험하다.
- 이러한 문제들에 대응할 수 있어야 하며, 직접 작성한 시스템의 문제든 시스템이 이용하는 기분 기술에 국한된 문제든 상관없이 주어진 기회를 최대한 살려야 한다.
- 변경은 본질적으로 좋지 않으므로 변경을 위한 변경은 삼가야 하지만 변화에 대응할 수는 있어야 한다.
2. 규모 확장과 효율성
- 비용이 너무 많이 드는 변경은 지연되기 쉽다.
- 변경 비용이 시간 흐름보다 가파르게 상승하는 시스템은 확장 가능하지 않다.
- 소프트웨어 조직에서 가장 중요한 자산인 코드베이스 자체도 확장가능해야 한다.
- 코드가 많아지고 변경 이력이 쌓이는 등이 이유로 빌드 시스템이나 버전 관리 시스템이 점점 느려진다면 어느 순간 더는 정상 운영할 수 없는 시점이 온다.
- 조직에서 코드를 작성하고 관리하는 데 필요한 모든 것이 총비용과 자원 소비 측면에서 확장가능해야 한다.
2-1 확장하기 어려운 정책들
- 새 위젯을 개발하여 프로젝트 리더가 오래된 위젯을 삭제하고 새 위젯을 일괄적으로 변경을 요청한다면?
- 이러한 방식은 작은 조직에서는 통할 지 몰라도 의존성 그래프가 조금만 더 깊고 넓어지면 실패한다.
- 팀이 의존하는 위젯 수가 늘어나면서 단 한번의 빌드 실패가 영향을 미치는 범위도 함께 늘어난다.
- 위 문제를 ‘확장 가능한 방식으로 푼다’함은 폐기를 처리하는 방식을 바꾸는 것이다.
- 마이그레이션 작업을 사용자에게 떠넘기는 대신 시스템 담당 팀 내부에서 스스로 처리할 수 있도록 하자.
- 구글의 인프라팀은 사내 사용자들이 새 버전으로 옮기도록 돕거나 직접 업데이트하되, 하위호환성을 유지한다.
- 사용자 입장에서 새 인프라로 바꾸라고 전가하는 방식은 당장 얻는 이익이 눈에 보이지 않으므로 의욕이 떨어져 적극적으로 움직이지 않음.
2-2 확장 가능한 정책들
- 비욘세 규칙 - 인프라를 변경하여 서비스가 중단되는 등의 문제가 발성하더라도, 같은 문제가 지속적 통합 시스템의 자동테스트에서 발견되지 않는다면 인프라팀의 책임이 아니다.
- 공통 CI 시스템에 추가하지 않은 테스트는 인프라팀이 책임지지 않는다는 뜻
- 이 규칙이 없었다면 인프라팀의 엔지니어는 코드가 조금이라도 영향받은 모든 팀을 일일이 찾아 물어보러 다녀야 함. 이 방식은 규모가 커질수록 감당할 수 없다.
- 전문성과 공유 포럼이 조직 확장에 기여하는 바가 크다.
- 엔지니어들이 포럼에 질문하고 답하는 과정에서 지식이 전파되고 새로운 전문가가 성장한다.
2-3 사례 : 컴파일러 업그레이드
- 인프라는 더 자주 변경할수록 변경하기가 오히려 쉬워진다.
- 컴파일러 업그레이드 등의 목적으로 코드를 한번 수정해두면 거의 예외없이 코드가 더 견고해지고 다음번 업그레이드 하기도 쉬워진다.
2-4 원점 회귀(왼쪽으로 옮기기)

- 개발 과정에서 문제를 일찍 발견할수록 비용이 적게 든다.
- 버그 발견 시점을 왼쪽으로 옮기는 행위를 원점 회귀라고 부름.
- 코드 커밋 전에 정적 검사나 코드리뷰로 찾아낸 버그는 프로덕션 이후에 발견한 버그보다 훨씬 싸게 고칠 수 있다.
- 가능한 한 많은 버그를 그래프 왼쪽에서 잡기 위해 절차나 도구를 활용하여 다층 방어 전략 필요
3. 트레이드오프와 비용
- 비용 - 금융, 리소스, 인적, 거래, 기회, 사회적 비용 등등이 있다.
3-1 사례 : 화이트보드 마커
- 화이트 보드에 마커가 말라있거나 사용할 수 없는 상태여서 회의에 방해된 경험이 있는가?
- 구글은 사무용품과 일상적인 개발 시 드는 경비부터 글로벌 규모의 서비스를 준비하고 운영하는 방법에 이르기까지, 우리가 하는 모든 일과 관련한 비용/이윤 트레이트오프에 동일한 수준의 관심을 두고 명확히 계량하려 한다.
- 종종 구글의 문화는 데이터 주도적이라고 하는데 실상은 데이터가 없을 때조차 근거, 선례, 논증을 거쳐 결정을 내린다.
- 좋은 엔지니어링 결정이란 결국 가용한 모든 근거 자료에 적절한 가중치를 부여하고, 이러한 풍부한 지식을 바탕으로 균형점을 잡는 일이다.
- 결국 엔지니어링 조직의 선택을 결정짓는 요인은 다음으로 압축된다.
- 반드시 해야 하는 일 (법적 요구사항, 고객 요구사항)
- 근거에 기반하여 당시 내릴 수 있는 최선의 선택 (적절한 결정권자가 확정)
- 의사결정이 ‘내가 시켰으니까’가 되어서는 안된다.
3-2 의사결정을 위한 근거자료
- 근거 자료의 가중치를 정하는 시나리오는 주로 다음의 두 가지이다.
- 관련한 정량적 데이터를 모두 ‘측정’할 수 있거나 최소한 ‘추정’이라도 할 수 있는 경우
- 측정하기 어렵거나 측정 방법을 모르는 정량적 데이터. (엔지니어 시간이 얼마나 들지 모르겠다 등)
- 첫 번째 결정 유형에선 결함이 파고들 여지가 적으나, 두 번째 결정 유형에서는 쉬운 답이 나오기 어렵다.
- 그래서 절충안을 찾기 위해 경험, 리더십, 선례에 기대야 한다.
- 구글은 정량화하기 어려운 정보를 정량화하는 데 도움되는 연구에 투자한다.
- 하지만 구글이 줄 수 있는 가장 보편적인 제안은 모든 것이 측정 가능하거나 예측 가능하지 않다는 사실을 인정하고, 그런 결정에도 똑같은 우선순위와 관심을 두라는 것이다.
3-3 사례 : 분산 빌드
- 구글에서 로컬 컴퓨터로 빌드하는 경우 빌드 시간동안의 생산적인 시간을 낭비하게 되는 문제로, 분산 빌드 시스템을 도입했다.
- 분산 빌드 시스템으로 엔지니어 생산성을 크게 끌어올렸지만, 시간이 흐르면서 분산 빌드 자체가 폭발적으로 커지게 되었다.
- 로컬 빌드 시절에는 엔지니어 각자가 빌드 시간 단축에 신경 썼는데, 분산 빌드로 넘어오면서 관심이 멀어지게 되었기 때문.
- 빌드를 최적화해도 혜택을 직접 누리게 되는 사람이 거의 사라졌기 때문에 모두가 자원을 분별없이 소비하는 상황이 되었다.
- 종합적으로, 분산 빌드 시스템을 도입하여 절약한 비용이 시스템을 구축하고 유지보수하는 데 드는 부정적인 비용보다 훨씬 커졌다.
- 결국 시스템의 목표와 제약을 다시 정하고, 모범 사례를 만들고, 새로운 생태계를 위한 도구와 유지보수에도 투자해야 한다는 사실을 깨우침.
- ‘엔지니어 시간을 만회하기 위해 연산 자원 확보에 x원 사용하겠다’ 같은 사소한 트레이드오프조차 예기치 못한 부정적인 효과를 초래할 수 있음.
3-4 사례 : 시간과 규모 확장 사이에서 결정하기
3-5 결정 재고하기와 잘못 인정하기
- 잘 드러나지는 않지만 잘못했음을 인정할 수 있게 해주는 능력 역시 데이터 중심 문화가 주는 커다란 장점이다.
- 결정 시점에 가용한 데이터만을 활용한다는 한계 때문에 새로운 데이터를 얻어 상황이 바뀌거나 가정이 무너진다면 기존 결정에 오류가 생길 수 있다.
- 혹은 당시에는 옳았지만 지금은 아닐 수 있음.
- 시간은 기술 의존성과 소프트웨어 시스템뿐 아니라 의사결정에 활용되는 데이터도 달라지게 하기 때문
- 구글은 데이터에 기초한 의사결정을 강력히 지지한다.
- 데이터 자체도 시간이 지나면 변하고 새로운 데이터가 나타날 수 있음을 안다.
- 그러므로 시스템의 생애 동안 과거에 내린 결정을 수시로 재고해봐야 한다.
- 이때 중요한 것은 결정권자에게 잘못을 인정할 권리가 있느냐다.
- 어떤 사람의 직관에는 맞지 않을지 모르지만, 잘못을 인정할 줄 아는 리더가 더 존경받는다.
4. 소프트웨어 엔지니어링 vs 프로그래밍
- 이 둘에 적용되는 제약 사항, 가치, 모범 사례가 서로 다르다.
- 차이의 대부분은 시간 흐름에 따른 코드 관리, 시간 흐름에 따른 규모 확장의 영향, 이런 관점에서의 의사결정 방식에 있다.
- 프로그래밍은 코드를 생산하는 즉각적인 행위이다.
- 소프트웨어 엔지니어링은 활용 가치가 남아 있는 한 오랫동안 코드를 유용하게 관리하고 팀 간 협업을 가능케 하는 정책, 관례, 도구 모두를 아우르는 종합적인 개념이다.