CH 1. 소프트웨어 엔지니어링이란?
프로그래밍과 소프트웨어 엔지니어링의 가장 큰 차이는 시간, (규모) 확장, 실전에서의 트레이드오프, 이렇게 세 가지이다.
- 시간: 시간의 흐름과 언젠가 변경될 가능성에 더 신경 써야 함
- 확장: 소프트웨어 자체뿐 아니라 제작하는 조직까지 양 측면 모두에서의 확장과 효율에 더 집중해야 함
- 트레이드오프: 대체로 수명과 성장 속도를 정밀하게 예측하기 어려운 상황에서, 결과에 더 큰 영향을 주는 보다 복잡한 결정을 내려야 함
시간
소프트웨어 엔지니어링에서 프로그래밍이 큰 비중을 차지하지만 결국 프로그래밍은 새로운 소프트웨어를 제작하는 수단이다.
코드의 예상 수명에 대해 생각하라. 단명하는 시스템은 '그저' 프로그래밍 문제와 다를 게 없지만 수명이 길어질수록 변경이라는 요소가 점점 중요해진다. 이것이 소프트웨어 엔지니어링과 프로그래밍을 가르는 핵심이다.
이 차이가 우리가 말하는 소프트웨어의 지속 가능성의 핵심이다. 지속 가능하다는 것은 소프트웨어의 기대 생애 동안 요구되는 모든 가치 있는 변경에 대응할 수 있다는 것이다.
확장
소프트웨어 엔지니어링은 팀 업무이다. 초기에는 여러 버전의 프로그램을 여러 사람이 참여해 개발하는 것이라고 정의했다. 그만큼 시간과 참여 인원에서 소프트웨어 엔지니어링과 프로그래밍은 차이가 난다.
트레이드오프
소프트웨어 엔지니어링에선근 주기적으로 여러 선택지 사이의 트레이드오프를 평가해야 한다. 지속 가능성을 잃지 않으면서 조직, 제품, 개발 워크플로의 규모를 확장하는 비용을 관리하여 합리적인 결정을 내려야 한다.
1.1 시간과 변경
기대 수명이 짧다면 유지보수가 필요없지만 기대 수명이 길다면 유지보수의 중요성이 크다. 그만큼 단명하는 프로그램용 코드와 수명이 훨씬 긴 프로젝트가 만들어내는 코드는 다르며, 수명이 길어질수록 '동작한다'와 '유지보수 가능하다'의 차이는 분명하다.
1.1.1 하이럼의 법칙
하이럼의 법칙은 '동작한다'와 '유지보수 가능하다'를 구분 짓는 가장 중요한 요인이다.
하이럼의 법칙
API 사용자가 충분히 많다면 API 명세에 적힌 내용은 중요하지 않습니다. 시스템에서 눈에 보이는 모든 행위(동작)를 누군가는 이용하게 될 것이기 때문입니다.
하이럼의 법칙은 최선의 의도, 최고의 엔지니어, 꼼꼼한 코드 리뷰가 뒷받침되더라도 공표한 계약(명세)이나 모범 사례를 완벽하게 구현해냈다고 단정할 수 없다는 현실을 표현한 말이다.
1.1.2 사례: 해시 순서
해시 컨테이너의 반복 순서에 의존하더라도 프로그램의 수명이 짧다면 기술적 문제를 전혀 겪지 않겠지만 소프트웨어 엔지니어링 프로젝트에서는 이런 의존성은 위험 요인에 해당한다.
따라서 '당장 돌아가야 한다'라는 생각으로 작성한 코드와 '언제까지고 작동해야 한다'라는 생각으로 작성한 코드의 차이를 생각해보면 어떤 관계인지가 분명하게 그려진다.
1.1.3 '변하지 않기'를 목표로 하지 않는 이유
C 언어는 상당히 안정적이여서 순수한 C 언어 프로젝트라면 리펙터링이나 난해한 업그레이드 없이 버틸 수도 있다. 하지만 대부분의 프로젝트는 기반 기수르이 변화를 훨씬 많이 겪는다. 대다수의 프로그래밍 언어와 런타임은 C 언어보다 빠르게 변하며 순수 C로 구현한 라이브러리조차 새로운 기능을 지원하기 위해 변화하여 라이브러리 사용자들에게 영향을 준다.
1.2 규모 확장과 효율성
'코드베이스의 수명이 다할 때까지 직면하는 변화가 몰고 오는 모든 변경을 안전하게 처리할 수 있다면 그 코드베이스는 지속 가능하다.' 이 풀이에는 비용이라는 요소가 숨어 있다. 비용이 너무 많이 드는 변경은 지연되기 쉬우며 변경 비용이 시간 흐름보다 가파르게 상승하는 시스템은 분명 확장 가능하지 않다.
1.2.1 확장하기 어려운 정책들
조직이 10배 혹은 100배로 커졌을 때 작업을 자동화하거나 최적화할 수단이 없다면 확장성에 문제가 있는 것이다.
개발 브랜치를 활용하는 전통적인 방법 역시 확장성 문제를 겪는 정책의 좋은 예이다. 모든 팀과 모든 기능을 독립된 개발 브랜치로 나누고 모든 브랜치는 '완료'되려면 트렁크로 머지되고 테스트되어야 하므로 아직 다른 개발 브랜치에서 작업 중인 엔지니어들은 다시 동기화하고 테스트하느라 값진 시간을 허비한다.
따라서 규모가 확장되면 다른 방식이 필요하다. (16장에서 소개)
1.2.2 확장 가능한 정책들
어떤 정책이 조직 성장에 따르는 비용이 적을까? 구글 내부 정책 중 하나로 '인프라를 변경하여 서비스가 중단되는 등의 문제가 발생하더라도, 같은 문제가 지속적 통합 시스템의 자동 테스트에서 발견되지 않는다면 인프라팀의 책임이 아니다'라는 정책이 있다. 이는 비욘세 규칙이며 친근하게 표현하면 '네가 좋아했다면 CI 테스트를 준비해뒀어야지'라는 뜻이다.
1.2.3 사례: 컴파일러 업그레이드
2006년 구글에서는 앞선 5년 동안 컴파일러를 업그레이드하지 않아서 대부분의 엔지니어가 컴파일러를 변경해본 경험이 없고 작성된 코드들도 단 하나의 컴파일러 버전만 겪어본 상태였다. 이 때문에 크고 작은 하이럼의 법칙 문제들이 튀어나와서 특정 컴파일러 버전에 더 깊이 의존하게 되었고 이 의존성들을 끊기가 매우 고통스러웠다.
구글은 고통을 겪은 뒤, 규모 문제를 극복할 기술을 찾고 조직을 변화시켜 큰 규모가 장점이 되도록 노력했다. 그 노력은 자동화(한 사람이 더 많은 일을 수행), 통합과 일관성(저수준 변경이 영향을 미치는 범위 제한), 전문성(적은 인원으로 더 많은 일을 수행)으로 이어졌다.
그렇게 2006년의 업그레이드를 포함한 많은 경험을 통해 코드베이스의 유연성에 영향을 주는 여러 요인을 찾아냈다.
- 전문성: 여러 방법에 대한 충분한 지식
- 안정성: 더 규칙적으로 릴리스하여 릴리스 사이의 변경량 감소
- 순응: 업그레이드를 겪지 않은 코드가 많지 않음
- 익숙함: 중복되는 작업 자동화
- 정책: 비욘세 규칙과 같은 유용한 정책과 절차를 갖춤
1.2.4 원점 회귀(왼쪽으로 옮기기)
개발 과정에서 문제를 일찍 발견할수록 비용이 적게 든다는 사실은 널리 받아들여지는 진실이다.
문제 발견 시점을 왼쪽으로 이동시킬수록 수정 비용이 줄어드는데, 이 행위를 원점 회귀라 한다.
1.3 트레이드오프와 비용
비용은 단순히 금액만을 지칭하는 것이 아니며, 다음의 요소들까지 모두 포괄한다.
- 금융 비용 (예: 돈)
- 리소스 비용 (예: CPU 시간)
- 인적 비용 (예: 엔지니어링 노력)
- 거래 비용 (예: 조치를 취하는 비용)
- 기회 비용 (예: 조치를 취하지 않는 비용)
- 사회적 비용 (예: 선택이 사회 전체에 미치는 비용)
특히, 소프트웨어 엔지니어링처럼 매우 창의적이고 수익성 높은 분야에서는 금융 비용보다는 인적 비용이 제한 요소일 가능성이 크다. 그래서 엔지니어들이 행복을 느끼게 만들고 일에 집중하고 참여할 수 있게 해주면 효율이 높아진다.
1.3.1 사례: 화이트보드 마커
구글에서는 화이트보드 마커를 포함하여 다양한 사무용품을 구비해둔 사물함이 비치되어 있다. -> 끊김 없는 브레인스토밍이 중요하므로 확실한 균형점을 잡아 놓은 것이다.
이처럼 구글은 사무용품과 일상적인 개발 시 드는 경비부터 글로벌 규모의 서비스를 준비하고 운영하는 방법에 이르기까지, 우리가 하는 모든 일과 관련한 비용/이윤 트레이등로프에 동일한 수준의 관심을 두고 명확히 계량하려 한다.
결국 엔지니어링 조직의 선택을 결정짓는 요인은 다음으로 압축된다.
- 반드시 해야 하는 일(법적 요구사항, 고객 요구사항)
- 근거에 기반하여 당시 내릴 수 있는 최선의 선택(적절한 결정권자가 확정)
의사결정이 '내가 시켰으니까'가 되어서는 안 된다.
1.3.2 의사결정을 위한 근거 자료
근거 자료의 가중치를 정하는 시나리오는 다음 두 가지다.
- 관련한 정량적 데이터를 모두 '측정'할 수 있거나 최소한 '추정'이라도 할 수 있는 경우 (CPU, 네트워크, 금액과 메모리 등)
- 측정하기 어렵거나 측정 밥법을 모르는 정량적 데이터 (엉망으로 설계된 API의 엔지니어링 비용을 측정할 방법?, 선택한 제품의 사회적 영향?)
첫 번째는 결함이 파고들 여지가 적다.환산표가 갖춰진다면 모든 엔지니어가 자신에게 필요한 분석을 할 수 있다.
두 번째는 쉬운 답이 나오기 어려워서 경험, 리더십, 선례에 기대야 한다.
1.3.3 사례: 분산 빌드
오늘 날에도 약 60~70% 개발자가 빌드를 로컬에서 실행한다고 한다.
2000년대 중반 구글은 온전히 로컬 빌드에 의존했다. 코드베이스가 커져가면서 컴파일 시간도 꾸준히 늘어나 더 크고 강력한 로컬 머신을 구입하는 지출이 커졌다. 더욱 컴파일 시간 상승은 곧 일할 시간이 줄어든다는 뜻이라서 우회적으로 인건비를 상승시키는 효과도 있었다.
결국 구글은 자체 분산 빌드 시스템을 개발했다. 하지만 최적화의 혜택이 병렬 빌드 시스템 속으로 가려지면서 모두가 자원을 분별없이 소비하는 상황이 생기기도 했다.
종합적으로는, 분산 빌드 시스템을 도입하여 절약한 비용이 시스템을 구축하고 유지보수하는 데 드는 부정적인 비용보다 훨씬 컸다. 그러나 우리는 어느 정도일지 예측해내지 못하였다.
1.3.4 사례: 시간과 규모 확장 사이에서 결정하기
때로는 시간과 규모 확장 문제가 충돌을 일으킨다. 그래서 다음과 같은 기본적인 질문에도 간단히 답할 수 없다. '우리 팀에만 해당하는 문제가 생겼어. 해결하려면 의존성을 추가하는 게 좋을까, 아니면 포크하거나 다시 구현하는 게 좋을까?"
이 문제의 정답도 고정적이지 않다. 프로젝트의 수명이 짧다면 포크해도 위험이 적다. 포크한 코드의 영향 범위가 제한적인 게 확실할 때도 포크하는 게 유리할 것이다. 반면 데이터 구조, 직렬화 포맷, 네트워크 프로토콜처럼 프로젝트의 수명에 종속되지 않는 인터페이스라면 포크를 피하는 게 좋다.
1.3.5 결정 재고하기와 잘못 인정하기
잘 드러나지는 않지만 잘못했음을 인정할 수 있게 해주는 능력 역시 데이터 중심 문화가 주는 커다란 장점이다.
데이터에 기초한 의사결정을 강력히 지지하지만 데이터 자체도 시간이 지나면 변하고 새로운 데이터가 나타날 수 있으므로 과거에 내린 결정을 수시로 재고해봐야 한다.
1.4 소프트웨어 엔지니어링 vs 프로그래밍
소프트웨어 엔지니어링과 프로그래밍에 적용되는 제약 사항, 가치, 모범 사례가 서로 다르다. 단 며칠만 활용할 프로젝트라면 통합 테스트나 지속적 배포(CD)에 목맬 필요가 없다. 이처럼, 소프트웨어 엔지니어링 프로젝트에서 활용하는 유의적 버전과 의존성 관리 같은 장기적 고려사항은 단명하는 프로그래밍 프로젝트에는 도입하지 말아야 한다.
따라서 서로 관련은 깊지만 상이한 두 용어인 '프로그래밍'과 '소프트웨어 엔지니어링'은 구별해야 한다.
'도서 리뷰 > IT 도서 리뷰' 카테고리의 다른 글
[IT 도서 리뷰] 구글 엔지니어는 이렇게 일한다 (CH. 3) (0) | 2023.01.19 |
---|---|
[IT 도서 리뷰] 구글 엔지니어는 이렇게 일한다 (CH. 2) (0) | 2023.01.19 |
[IT 도서 리뷰] 읽기 좋은 코드가 좋은 코드다 (Part. 4) (0) | 2022.07.26 |
[IT 도서 리뷰] 읽기 좋은 코드가 좋은 코드다 (Part. 3) (0) | 2022.07.24 |
[IT 도서 리뷰] 읽기 좋은 코드가 좋은 코드다 (Part. 2) (0) | 2022.07.19 |