주요 내용
- Programming과 Software Engineering의 차이를 구분
- 코드의 기대 수명 동안 의존성, 기술, 제품 요구사항 변경에 대응할 역량 필요
- 하이럼의 법칙
- 조직의 반복 작업은 필요 인력 측면에서 확장 가능해야 함
- 일을 수행할 때는 책임감과 근거를 갖고 결정
- 객관적 데이터만으로 모든 일을 결정하기는 어려움
- 잘못을 인정하는 자세 필요
프로그래밍과 소프트웨어 엔지니어링의 가장 큰 차이는 time, scale, trade-off 라고 할 수 있습니다. 둘의 차이를 비교하고 이해하는 것이 이번 챕터의 목표입니다.
첫째, programming과 software engineering의 차이는 프로그램의 지속 시간에 있습니다. 코드의 수명이 길어질수록 '동작한다'와 '유지보수 가능하다'의 차이를 분명하게 인지해야 합니다. 프로그래밍은 코드의 수명이 짧으므로 단순히 동작만 하면 되지만 소프트웨어 엔지니어링의 경우 오랜 시간 생존하기 때문에 유지보수까지 고려해야 합니다.
하이럼의 법칙 (Hyrum's Law)
API 사용자가 충분히 많다면 API 명세에 적힌 내용은 중요하지 않다. 시스템에서 관측될 수 있는 모든 행동 양식은 다른 이들에게 달려있을 것이다.
최선의 의도, 최고의 엔지니어, 꼼꼼한 코드 리뷰가 뒷받침 되더라도 공표한 계약이나 모범 사례를 완벽하게 구현해냈다고 단정할 수 없다는 현실을 표현한 법칙입니다. API의 작은 변화도 이를 사용하고 있던 사용자들에겐 큰 파장이 일어날 수 있으므로 주의해야 합니다. 변경이 얼마나 유용할지를 분석할 때는 충돌을 조사/식별/해결하는 데 따르는 비용도 고려해야 합니다.
API의 명세에 명시되지 않은 즉, 언제든 변할 수 있는 기능을 사용하는 코드는 '기발한' 코드이다.
모범 사례를 따르고 미래에 대비한 코드는 '클린'하고 '유지보수 가능한' 코드이다.
코드의 기대 수명에 따라 선택해야 한다.
변경은 본질적으로 좋지 않지만 이를 피할 수는 없습니다. 그러므로 변경을 위한 변경은 삼가야 하지만 변화에 대응할 수는 있어야 합니다.
둘째, programming과 software engineering의 차이는 규모 확장에 있습니다. 코드를 작성하고 관리하는 데 활용하는 모든 것이 비용과 자원 소비 측면에서 확장 가능해야 합니다. 특히 반복적으로 수행하는 일이라면 인적 비용 측면에서도 확장 가능해야 합니다.
[확장성 문제를 고려하는 방법]
- 조직이 10배로 커지면 이 작업도 10배로 많아질까?
- 엔지니어가 해야 하는 일의 양이 조직이 커질수록 늘어날까?
- 코드 베이스가 커질수록 작업량도 늘어날까?
- 이 중 하나라도 해당할 경우 작업을 자동화 하거나 최적화할 수단이 있을까?
마지막이 “NO”라면 확장성에 문제가 있다.
조직에서 효과적으로 확장을 하기 위해서는 갈아타기 규칙 처럼 책임 소재를 달리하는 방법이 있습니다.
갈아타기 규칙 (Churn Rule)
마이그레이션의 책임 소재를 각 사용자가 아닌 인프라 팀으로 한다. 인프라 팀은 사내 사용자들이 새 버전으로 옮기도록 돕거나 직접 업데이트를 하되, 하위 호환성을 유지해야 한다.
또한 안정적인 확장을 위해서는 비욘세 규칙 처럼 개발자가 자신이 짠 코드의 테스트까지 책임 지는 자세가 필요합니다.
비욘세 규칙
인프라를 변경해 문제가 발생하더라도 해당 문제가 CI의 자동테스트에서 발견되지 않는 다면 인프라 팀의 책임이 아니다.
이러한 확장에 따르는 비용을 줄이기 위해서는 인프라를 자주 변경해야합니다. 최초의 업그레이드가 수정량이 가장 많으며, 업그레이드 할 수록 수정량이 적어집니다. 또한 개발 과정에서 문제를 일찍 발견할 수록 비용이 적게 듭니다. 개발 과정이 다음과 같다면 문제 발견 시점을 최대한 왼쪽으로 옮기는 원점 회귀(Shift Left) 전략이 필요합니다.
[설계 - 구현 - 리뷰 - 테스트 - 커밋 - 카나리 (canary) - 배포]
[코드 베이스의 유연성에 영향을 주는 요인]
- 전문성 (expertise): 충분한 지식
- 안정성 (stability): 규칙적인 릴리즈로 변경량 줄이기
- 순응 (conformity): 규칙적인 업그레이드
- 익숙함 (familiarity): 자동화
- 정책 (policy): 비욘세 규칙처럼 유용한 정책과 절차 갖추기
셋째, programming과 software engineering의 차이는 trade-off에 있습니다. 당시엔 최선의 선택일지라도 나중에는 오류가 될 수 있음을 인정해야 합니다. 그러므로 시스템의 생애 동안 과거의 결정을 수시로 재고해야 합니다. trade-off시엔 금융 비용뿐만 아니라 인적 비용, 기회 비용, 사회적 비용 등 종합적인 비용을 고려해야 합니다. 또한 최종 결정자는 필요하지만 결정의 책임은 모두의 것임을 명심해야 합니다.
[사례: 분산 빌드]
1. 빌드하느라 시간이 낭비됨
2. 자체 분산 빌드 시스템 개발
3. 처음엔 효과적이었으나 시간이 흐르고 분산 빌드 자체가 커짐, 개인이 빌드 효율을 위해 노력하지 않게됨
즉, 사소한 trade-off 조차 예기치 목한 부정적인 효과를 초래할 수 있음
[사례: 시간 vs 규모 확장]
우리 팀에만 해당하는 문제가 발생했을 때 의존성을 추가할까, fork하거나 다시 구현할까?
- fork한다면 확실하게 최적화할 수 있고 외부 솔루션에 의존하지 않고 완전한 통제가 가능해짐
- 하지만 확장성과 지속 가능성이 떨어짐
프로젝트의 수명이 짧거나 코드의 영향 범위가 제한적인게 확실할 경우 → fork O
데이터 구조, 직렬화 포맷, 네트워크 프로토콜 처럼 프로젝트 수명에 종속되지 않는 인터페이스 → fork X
Software engineering is programming integrated over time.
차원(고려해야 하는 주제의 큰 축)의 개수라는 점에서 programming과 software engineering은 다릅니다. 뭐가 더 좋고 나쁘다, 허접하고 훌륭하다는 관점으로 봐서는 안됩니다. 각각 적용되는 제약 사항, 가치, 모범 사례가 서로 다르다는 뜻입니다.
"이 코드의 예상 수명은?" 이라는 질문을 통해 둘을 구분할 수 있습니다. 소프트웨어 지속 가능성이 둘 차이의 핵심입니다. 또한 의사 결정의 복잡성과 이해관계 측면에서도 차이가 있습니다. 조직, 프로젝트 구성, 정책, 관례 등이 소프트웨어의 복잡성을 좌우합니다.
프로그래밍
소프트웨어 엔지니어링