Posts 실용주의프로그래머 2장 실용주의 접근법
Post
Cancel

실용주의프로그래머 2장 실용주의 접근법

Topic 8 - 좋은 설계의 핵심

좋은 설계는 나쁜 설계보다 바꾸기 쉽다.

ETC(Easy To Change)는 규칙이 아니라 가치

가치란 결정을 내릴때 도움을 주는 것이다. ETC는 가치로서 내제화되어야하며, 의식적으로 방금 한 일이 ETC 한지 항상 물어봐야한다. 일반적으로 상식선에서 추측이 가능하다. 하지만 결정하기 어려울때가 있는데, 이럴땐 다음 두가지 방법이 있다.

  • 코드가 교체되기 쉬워야한다. (결합도를 낮추고 응집도를 높여라)
  • 직관적으로 선택하고, 그 이유에 대해 기록해둬라. 그리고 추후 변경이 실제로 발생했을때 다시보며 피드백하라.

Topic 9 - DRY : 중복의 해악

소프트웨어를 신뢰성 높게 개발하는 유일한 길, 유지보수를 쉽게 만드는 유일한 길은 DRY 원칙을 따르는 것이다.

DRY : 반복하지 말라

DRY를 따르지 않으면 똑같은 것이 두 군데 이상 표현될 것이다. 그러면 하나를 바꾸면 나머지를 바꿔야하는데, 언제 잊어버릴지 모른다.

1) DRY는 코드 밖에서도

DRY는 똑같은 개념을 다른 곳에서 표현하면 안된다는 것이다. 코드의 어떤 부분을 바꿔야할때 문서도 바꿔야하는가? 그럼 DRY를 위반한 것이다.

2) 모든 코드 중복이 지식의 중복은 아니다

요구조건) 연령과 주문 수량은 모두 숫자이고, 0보다 커야한다

def validate_age(value):
	validate_type(value, :integer)
	validate_min_integer(value, 1)

def validate_quantity(value):
	validate_type(value, :integer)
	validate_min_integer(value, 1)

이때 두 함수가 표현하는 지식은 다르다. 다만 우연히 규칙이 같은 것일 뿐이다. 이건 우연이지 중복이 아니다.

3) 문서화 중복

과도하게 많은 주석은 함수의 의도를 두번 설명하게 한다. 이는 추후 함수를 변경해야할때 주석도 변경해야한다. 따라서 함수명, 변수명 등으로 코드로서 의도를 표현하는 것이 제일 좋다.

4) 데이터의 DRY 위반

자료구조 내에서 중복된 의미의 데이터가 포함될 수 있다.

  • ex) line 이라는 클래스에 start, end, length 변수가 있는 경우 -> length를 계산하는 메서드를 만들어라

하지만 계산로직이 복잡한 경우 캐싱을 위해 포함할 수도 있는데, 중요한 것은 자료구조 외부로 드러내지 않는 것이다.

  • ex) line 클래스 생성 시 start, end만 받고, length는 자동으로 계산되게

5) 표현상의 중복

다른 라이브러리나 API와 연결할때 해당 API에 표현된 지식들(스키마, 에러코드 등)을 우리 코드도 알아야한다. 이러한 중복은 다음과 같이 완화될 수 있다.

  • 내부 API에서 생기는 중복
    • 언어나 기술에 중립적인 형식으로 내부 API를 정의할 수 있는 도구를 찾아라 (ex : swagger 등)
    • 모든 API 정의를 하나의 중앙 저장소에 넣어두고 여러 팀이 공유할 수 있으면 좋다.
  • 외부 API에서 생기는 중복
    • 외부 API는 엄밀한 문서화가 되어있는 경우가 많다.
  • 데이터 저장소와의 중복
    • 영속성 프레임워크 등을 통해 데이터 스키마로부터 객체를 바로 생성해낼 수 있도록 한다.
    • 또는 JS 객체처럼 키-값 데이터 구조에 밀어넣는 것도 좋다. (이때는 간단한 데이터 검증 도구는 필요하다)

6) 개발자 간의 중복

같은 기능을 여러 개발자가 각자 개발할 수도 있다. 이러한 것을 발견하려면 소통과 코드리뷰가 중요하다.

재사용하기 쉽게 만들어라

기존의 것을 찾아내고, 재사용하기 쉬운 환경을 조성해야한다. 재사용이 어려우면 중복의 위험을 감수해야한다.


Topic 10 - 직교성

컴퓨터 과학에서 직교성은 결합도 줄이기를 의미한다. 하나가 바뀌어도 나머지에 어떤 영향도 주지 않으면 서로 직교한다고 할 수 있다. 잘 설계가 되어있다면 데이터베이스 코드가 바뀌어도 사용자 인터페이스는 바뀌지 않을 것이다.

직교성의 장점

관련 없는 것들 간에 서로 영향이 없도록 하라

컴포넌트들이 각기 격리되어있으면 어느 하나를 바꿀 때 나머지를 걱정하지 않아도 된다. 직교성은 다음 두가지 큰 장점이 있다.

  • 생산성 향상
    • 변화를 국소화해서 개발 시간과 테스트 시간을 줄일 수 있다.
    • 직교성은 재사용하기도 쉽다
    • 직교적인 컴포넌트들을 결합하면 생산성 향상을 얻을 수 있다. 하나의 컴포넌트로 다양한 일을 할 수 있기 때문이다.
  • 리스크 감소
    • 직교성은 감염된 코드를 격리시켜준다.
    • 변경이 발생해도 시스템이 잘 깨지지 않는다.
    • 각각의 컴포넌트들을 테스트하기가 쉬워진다
    • 특정 제품, 플랫폼에 덜 종속적이 된다.

설계

설계가 직교적인지 확인하는 쉬운 방법은 ‘특정 기능에 대한 요구사항이 대폭 변경되었을때 몇개의 모듈이 영향을 받는가?’를 자문하는 것이다. 정답은 하나여야한다.

또한 현실 세계의 변화와 설계 사이의 결합도를 얼마나 줄였는지도 확인해야한다. 자신의 힘으로 제어할 수 없는 속성에 의존하면 안된다.

  • ex) 전화번호를 고객 식별자로 사용할 경우 -> 법이 변경되면 식별자를 모두 바꿔야한다.

툴킷과 라이브러리

같은 팀이 작성한 것이라도 라이브러리의 도입이 코드에 수용해서는 안될 변화를 가져오고, 직교성을 해친다면 도입하면 안된다.

EJB의 데코레이터 패턴이 좋은 라이브러리 예시이다.

코딩

코딩을 할 때 직교성을 유지할 수 있는 몇가지 기법이 있다.

  • 코드의 결합도를 줄여라
    • 불필요한 것은 다른 모듈에 보여주지말고, 다른 모듈의 구현에 의존하지 않는 코드를 작성하라
    • 데메테르 법칙을 따르려 노력해보자
    • 객체의 상태를 바꿔야하면, 객체가 직접 상태를 바꾸도록 해보자. (상태를 변경하는 메서드를 만들라는 뜻인듯…?)
  • 전역 데이터를 피하라
    • 전역 데이터를 읽기 전용으로 사용한다해도 전역 데이터가 변경되면 사용하는 모든 모듈에 영향이 간다
    • 필요한 컨텍스트를 모듈에 전달하는 식으로 해야 유지보수가 쉽다.
    • 싱글턴 객체를 일종의 전역 데이터로 남용하여 불필요한 결합을 만들 수 있다.
  • 유사한 함수를 피하라
    • 유사해보이는 함수를 여러개 구현해야할 수 있다. (ex : 시작과 끝은 같지만 중간이 다른 함수)
    • 이러한 함수는 전략 패턴을 사용하여 더 낫게 구현할 수 없을지 확인하라

기회가 있을 때마다 리팩터링하여 직교성을 개선하기 위해 노력하라.

테스트

직교적으로 설계된 시스템은 테스트하기가 더 쉽다. 통합 테스트보다 단위 테스트가 훨씬 쉽기 때문이다.

단위테스트를 작성하는 행위 자체가 직교성을 테스트할 수 있는 기회이다. 단위 테스트를 작성할 때 많은 모듈을 불러와야하면 결합도가 높다는 뜻이다.

버그 수정을 할 때에도, 하나의 모듈만 고치면 되는지 전체를 다 고쳐야하는지 점검해보아라.

더 알아보기

함수형 프로그래밍에서도 결합이 발생할 수 있는데, 한 함수의 결과를 다른 함수의 입력으로 사용하는 경우이다.

  • 이때는 함수 결과를 일반적인 포맷으로 하면 완화할 수 있을거 같음
  • 타입스크립트 사용도 도움이 된다고 함

Topic 11 - 가역성

프로젝트에서 정답은 하나가 아니다. 중요한 결정이 추후 변경될 수 있고, 큰 비용을 치뤄야하는 경우가 발생한다.

가역성

이 책에서 추천하는 방법들 (DRY 원칙, 결합도 줄이기 등)을 따른다면 중요하면서도 되돌릴 수 없는 결정의 수를 가능한한 줄일 수 있다.

최종 결정이란 없다

유연한 아키텍처

코드 뿐만이 아니라 아키텍처, 배포, 외부 제품과의 통합 영역도 유연하게 유지하여야한다. 아키텍처는 항상 변화하며 할 수 있는 것은 바꾸기 쉽게 만드는 것 뿐이다. 외부의 API를 추상화 계층 뒤로 숨기고, 코드를 여러 컴포넌트로 쪼개라.

유행을 좇지 마라.

어떤 미래가 펼쳐질지는 알 수 없으며, 코드 스스로 유연하게 대처 가능하도록 해야한다.


Topic 12 - 예광탄

프로젝트를 진행할때 마치 예광탄처럼 즉각적인 피드백을 얻는 것은 매우 중요하다.

프로젝트를 진행할때 우리는 미지의 것을 마주한다. 애매모호한 요구사항, 처음보는 라이브러리, 기술 등. 이럴때 전형적인 반응은 시스템을 극도로 명세화하는 것이다. 환경을 제약하고, 요구사항을 일일히 항목으로 만들어서 명세서를 만든 후 맞기를 기도한다.

하지만 실용주의 프로그래머는 소프트웨어 판 예광탄을 선호한다.

어둠 속에서 빛을 내는 코드

예광탄이 효과적인 이유는 일반 탄환과 동일한 환경 및 제약 조건에서 발사되기 때문이다. 코딩에서 동일한 효과를 얻으려면 우리를 요구사항으로부터 최종 시스템의 일부 측면까지 빨리, 눈에 보이게 반복적으로 도달하게 해줄 무언가를 찾아야한다.

시스템을 정의하는 중요한 요구사항을 찾아라. 의문이 드는 부분이나 가장 위험이 커 보이는 곳을 찾아라. 이런 부분의 코드를 가장 먼저 작성하도록 개발 우선순위를 정하라.

목표물을 찾기 위해 예광탄을 써라.

예시들

  • 프로젝트를 세팅할때 hello world 를 작성한 후 컴파일, 실행 해보는 것
  • 프로젝트 전체 아키텍처를 거치는 간단한 로직을 먼저 구현해보는 것

예광탄 코드는 여러 장점이 있다.

  • 사용자가 뭔가 작동하는 것을 일찍부터 보게 된다.
    • 사용자는 자신의 시스템에 진전이 있음을 보게 되어 기쁠 것이고, 점점 직접 기여하기 시작할 것이다.
    • 각 반복 주기마다 얼마나 목표에 가까워졌는지도 알려줄 것이다.
  • 개발자가 들어가서 일할 수 있는 구조를 얻는다
    • 무에서 유를 만드는 것은 어렵다. 따라서 일단 애플리케이션의 모든 요소간 상호 작용을 만들면, 이후 추가하는 것은 쉽고 일관성도 촉진된다.
  • 통합 작업을 수행할 기반이 생긴다.
    • 한번 시스템의 모든 요소를 연결하고 나면 새로 작성한 코드를 시스템에 붙이기가 쉬워진다.
  • 보여줄 것이 생긴다.
  • 진행상황에 대해 더 정확하게 감을 잡을 수 있다.

예광탄 코드 대 프로토타이핑

프토로타입은 최종 시스템의 어떤 특정한 측면을 탐사해보는 것이 목표이다. 따라서 프로토타입 방식을 따른다면 실험 이후 모두 버려야한다. 그리고 프로토타입을 통해 얻은 교훈으로 코드를 새로 작성한다.

프로토타입은 일종의 예시를 사용자에게 제시하고, 이후 최종 목표환경을 위한 코드로 다시 작성한다.

예광탄 코드는 애플리케이션이 전체적으로 어떻게 연결되는지 알려주고, 사용자에게는 애플리케이션의 요소들이 어떻게 상호작용하는지 보여주고 개발자에게는 아키텍쳐의 골격을 제시한다.

정리하면 프로토타입은 나중에 버리는 코드를 만들고, 예광탄 코드는 기능은 별로 없지만 완결된 코드이며 최종 시스템 골격 중 일부가 된다. 프로토타입은 예광탄을 발사하기 전에 먼저 수행하는 정찰 같은 것이다.


Topic 13 - 프로토타입과 포스트잇

프로토타입은 반드시 코드로 만들 필요는 없다. 그림이나 인터페이스 빌더 등을 통해 만들어 볼 수 있다.

프로토타입은 제한된 몇가지 질문에 답하기 위한 것이기에 세부 사항을 무시하고 실제 제품보다 매우 적은 비용으로 만들 수 있다. 만약 세부 사항을 포기하지 못하는 상황이라면 프로토타입을 만들고 있는 것이 맞는지 자문하라. 이럴때는 예광탄 코드가 적합할 수 있다.

프로토타입 대상

프로토타입으로 조사할 대상은 위험을 수반하는 모든 것이다. 이전에 해본 적 없는 것, 최종 시스템에 매우 중요한 것, 의심이 가는 것 등이 모두 대상이다.

  • 아키텍처
  • 기존 시스템에 추가할 새로운 기능
  • 외부 데이터의 구조 혹은 내용
  • 외부에서 가져온 도구나 컴포넌트
  • 성능 문제
  • 사용자 인터페이스 설계

프로토타입의 가치는 프로토타입 자체가 아니라 이를 통해 배우는 교훈에 있다.

프로토타이핑으로 학습하라

프로토타입을 어떻게 사용할 것인가?

프로토타입을 만들 때 무시해도 좋은 세부사항은 무엇일까?

  • 정확성 : 적절히 가짜 데이터 사용 가능
  • 완전성 : 제한된 방식으로만 동작해도 됨
  • 안정성 : 오류 검사를 무시해도 됨
  • 스타일 : 주석이나 문서가 많지 않아야함

Topic 14 - 도메인 언어

프로그래밍 언어로 사고방식이 제한될 수 있다. 따라서 도메인의 어휘를 사용하여 프로그래밍 하는 방식으로 좀 더 그 도메인에 가깝게 설계할 수 있다

문제 도메인에 가깝게 프로그래밍하라.

예시 :

  • 내부 도메인 언어 : RSpec, 피닉스라우터
    • 컴파일하고 실행할 수 있는 코드로, 실제 코드 안에 들어감
    • 호스트언어의 기능을 쓸 수 있다는 장점이 있지만, 호스트 언어의 문법과 의미론을 따라야한다
  • 외부 도메인 언어 : 큐컴버, 앤서블
    • 다른 언어나 데이터 구조로 변환하여 실행함
    • 호스트 언어에따른 제약은 없지만, 파서를 만들기에 리소스가 소비된다.

하지만 우리가 도메인 언어로 코드를 짠다해도 기획자는 읽지 않는다. 스스로 무엇을 원하는지 정확히 모르기 때문이다. 이때는 실제로 동작하는 것을 보여주면 스스로 원하는 것을 말할 것이다.

도메인언어를 사용하며 절약되는 시간보다 더 많은 시간을 쏟지마라. 기본적으로는 통용되는 yaml, json, csv 같은 외부 언어를 사용하라. 다만 외부언어 도입은 사용자가 직접 도메인 언어로 코드를 작성하는 경우에만 추천한다.


Topic 15 - 추정

얼마나 정확해야 충분히 정확한가?

모든 답은 추정치이다. 따라서 질문자의 원하는 것이 정확한 답을 요구하는지 큰 그림을 요구하는지 먼저 파악해야한다.

사용하는 단위에 따라서 답의 정확도 해석이 달라질 수 있다. 예를들어 130일이 걸린다고 하면 정확히 130일이라는 느낌이 들지만, 대략 6달이라고하면 5~6개월로 해석된다. 아래 단위를 추천한다. (ex : 125일이 걸릴 거 같으면 대략 6달이라고 말하자)

  • 1 ~ 15일 -> 일
  • 3~6주 -> 주
  • 8 ~ 20주 -> 달
  • 20주 이상 -> 추정치를 말하기 전에 다시 한번 생각해보자

추정치는 어디에서 나오는가?

모든 추정치는 문제의 모델에 기반한다. 그런데 모델을 작성하기 전에 그 일을 해본 사람에게 물어봐라. 다른 사람의 경험을 바탕으로 성공적인 추정치를 낼 수 있다.

무엇을 묻고있는지 이해하라

어떤 추정을 하건 상대방이 무엇을 묻고 있는지 먼저 이해해야한다. 도메인에 존재하는 조건에 대해서도 알아야한다. 상대방이 말해줄 때도 있지만 말하지 않더라도 미리 조건을 생각해보는 습관이 있어야한다.

시스템의 모델을 만들어라

의뢰인의 요청을 이해한 후에는 간단하게 모델을 만들어보라. 모델을 만듦으로써 표현에 드러나지 않은 패턴이나 프로세스를 발견할 수 있고, 초기 질문과는 다른 제안을 할 수도 있다.

모델이 정교해질수록 추정은 정확해지지만, 정확히 비례하진 않는다. 따라서 적절한 수준을 골라야한다.

모델을 컴포넌트로 나눠라

다 만든 모델을 컴포넌트로 분해하여 서로 어떻게 상호작용하는지 수식으로 기술해야한다. 각 노드 별로 전체 시스템에 얼마나 기여하는지 매개변수를 찾을 수 있을 것이다.

각 매개 변수에 값을 할당하라

가장 큰 영향을 미치는 매개변수를 찾아서 이 매개변수의 값을 최대한 정확하게 산출해내라. ex) 대기열시스템에서는 실제 시간당 요청수를 측정하거나, 비슷한 시스템을 찾아봐라

답을 계산하라

주요 매개변수들의 값을 변경시켜가며 여러 번 계산해보고 어느 것이 가장 잘 들어맞는지 찾아내라. 이때 너무 정확한 값이 나오면 아마 잘못계산한 것이다.

여러분의 추정 실력을 기록하라

계산한 추정치를 기록하고 나중에 얼마나 잘 들어맞는지 평가해봐라.

프로젝트 일정 추정하기

PERT 기법

아무 문제 없다면 10시간, 현실적으로는 18시간, 날씨가 나쁘면 30시간 정도 걸릴거 같습니다.

낙관적 추정치와 가장 가능성 높은 추정치, 비관적 추정치를 갖는다. 과업은 의존성에 따라 네트워크 형태로 배열한 후, 간단한 통계 기법으로 전체 프로젝트의 예상 최소 및 최대 소요 시간을 계산한다.

단순히 값을 부풀리는 것보다, 범위로 추정하는 것이 오류를 피할 수 있는 좋은 방법이다. PERT 속의 통계가 범위로 표현한 불확실성을 분산시켜주고, 전체 프로젝트에 대하여 더 나은 추정치를 얻을 수 있다.

코끼리 먹기

기능을 매우 작은 단위로 나누어 다음과 같은 방식으로 개발하면 추정치는 점점 더 정확해질 것이다.

  • 요구사항 확인하기
  • 위험 분석 후 위험도가 높은 부분을 우선 하기
  • 설계, 구현, 통합
  • 사용자와 함께 검증하기

한 iteration이 끝났을때 리뷰를 거쳐 총 몇번의 iteration이 필요할 지, 각 iteration 마다 뭘할지 추측할 수 있을 것이다.

이 방법은 경영진에게 인기는 없지만 더 정확한 일정을 전달할 수 있다.

This post is licensed under CC BY 4.0 by the author.

실용주의프로그래머 1장 실용주의 철학

실용주의프로그래머 3장 기본도구

Comments powered by Disqus.