본문으로 건너뛰기

Tidy First?

Kent BeckISBN 9781098151232

Overview

코드를 정리(tidying)하는 방법과, “지금 정리할지/나중에 정리할지”를 비용 관점에서 판단하는 방법을 다루는 책.

Tidy First? cover

Connections

  • Software Engineering (MOC)

ⅰ. Tidyings

Guard Clauses

  • ➡️ 가드 절을 만든 후 추가적으로 조건을 헬퍼 함수나 변수로 추출할 수 있다
if(condition) {
 if(!condition2) {
  ...
 }
}
if(!condition) return
if(condition2) return
...

얼리 리턴이 항상 좋은 것은 아니다.

if(condition) {
 ...
}

Dead Code

  • 실행되지 않는 코드를 삭제
  • 버전 관리 시스템을 사용한다면 언제든 나중에 되돌릴 수 있다
  • ➡️ 죽은 코드를 제거한 후에는 코드를 정한 순서에 따라 재정렬할 수 있다

Normalize Symmetries

  • 코드 작성 시 일관된 패턴을 사용하여 혼란을 줄이세요. (eslint 규칙을 통한 강제)
  • ➡️ 병렬 코드를 읽기 순서대로 그룹화

New Interface, Old Implementation

  • 기존 인터페이스가 코드를 어렵고 복잡하게 만든다면 기존 인터페이스를 통해 새로운 인터페이스를 만들 수 있습니다
  • 중간(pass-through) 인터페이스 역할을 하는 이 인터페이스는 바꾸고 싶은대로 쉽게 변경할 수 있습니다

Reading Order

  • 읽을 때 읽는 독자를 고려해서 순서를 재정렬
  • 선언 순서가 민감한 언어일 경우 독자에게 중요한 부분만 순서를 변경
  • 여러 경험을 통해 적절히 판단해야 한다

Cohesion Order

  • 코드를 변경해야 할 때 변경해야 할 부분이 여러 곳에 흩어져 있다면 어려움이 있습니다
  • 변경되는 요소끼리 가까이 위치하도록 코드를 재정렬할 수 있습니

Move Declaration And Initialization Together

  • 변수의 선언부는 변수에 대한 설명을 제공합니다
  • 변수의 선언과 할당 부분이 서로 멀어지면 코드를 이해하기 어렵게 만들 수 있습니다
  • 변수의 종속성 순서를 고려하면서 최상위에 모두 선언하는 것이 좋은지, 사용하는 위치 근처가 적절한지 고민해야 합니다

Explaining Variables

  • 장황한 표현식을 변수로 추출하여 변수이름으로 설명하면 이해하기 더 쉽다
  • 정리 커밋과 동작 커밋을 분리

Explaining Constants

  • 여러 곳에서 이해되어야 하는 공통적인 값을 상수로 추출하여 한 곳에 모으고 관리
  • 여러 의미 혹은 모호한 의미를 가지는 상수 표현(ONE)을 피하기

Explicit Parameters

  • 매개변수를 명시적으로 사용하도록 하세요
  • ex. 함수에서 매개변수 전체를 받아서 일부만 사용하기 -> 사용할 매개변수만 명시적으로 입력받는 함수 만들기

Chunk Statements

  • 코드에서 맥락이 다른 부분을 빈 줄로 구분하세요
  • ex. 이것은 글쓰기나 시에서 단락을 구분하는 것과 유사

Extract Helper

  • 명확한 목적을 가지고 나머지와 상호작용을 하지 않는다면 이를 헬퍼 함수로 추출하여 사용할 수 있습니다
  • 코드 간의 결합을 위해 사용할 수도 있습니다(함께 실행되어야 하는 경우, 순서대로 실행되어야 하는 경우 등)

One Pile

  • “코드에서 가장 큰 비용은 작성하는 비용이 아닌 읽고 이해하는 비용이다”
  • 작은 코드 조각으로 나누는 것이 때로는 이해를 방해하기도 한다
  • 코드를 하나의 더미로 합친 후 다시 분해하는 과정을 통해 명확함을 되찾을 수 있다

Explaining Comments

  • 주석은 코드에서 모호한 부분을 설명하기 위한 것이다
  • 코드를 읽는 독자(동료 프로그래머, 혹은 누구나)를 고려하여 코드를 이해하기 위한 추가적인 맥락(배경지식)을 제시

Delete Redundant Comments

  • 주석은 단지 문자열에 불과하기 때문에 시스템이 변경됨에 따라 관리하기 어렵고, 쓸모가 없어질 수 있다
  • 코드의 동작을 단순히 설명하는 것과 같은 중복되는 주석을 제거하세요

ⅱ. Managing

Separate Tidying

  • 정리 PR과 작업 PR을 분리하면 변경사항을 되돌릴 때 뿐만 아니라 제 3자가 검토하기 더 수월해 집니다.
  • 작업 PR -> 정리 PR -> 정리 PR -> 작업 PR -> 작업 PR -> 정리 PR -> …

Chaining

  • 정리를 시작하면 연쇄적으로 다른 정리 작업을 이어서 수행하고 싶어질 수 있습니다
  • 스스로 여러 정리를 단계별로 수행하면서 실험해보는 것이 좋습니다
  • 상황에 따라 너무 이른 정리는 오히려 안좋을 수 있습니다

Batch Sizes

  • 배치당 정리 작업이 많을 수록 비용이 증가합니다
    • 코드 통합을 위한 지연 시간이 길어진다.
    • 코드가 충돌할 가능성이 높아진다
    • 코드 충돌은 코드 병합 비용 상승으로 이어진다
  • 코드를 검토하고 배포하는 비용이 크기 때문에 자주 검토하는 것도 어렵습니다
  • 강력한 신뢰와 문화를 갖춘 팀에서는 정리 작업에 대해 검토가 필요하지 않습니다.
  • 정리 비용 낮추기 -> 정리를 늘리기, 행동 변경 비용 줄이기 -> 검토 비용이 감소

Rhythm

  • 처음에는 많을 것들을 정리하겠지만, 조금의 시간이 지나고 나면 대부분의 변경 사항이 정리된 코드 영역에서 발생합니다.

Getting Untangled

코드 동작 변경에 대한 3가지 시나리오

  • 정리 없이 코드를 그대로 업로드: 검토하기 어려움. 잠재적인 오류 발생 가능성

  • 정리 작업과 변경 작업을 분리하여 커밋: 조금 더 나은 방법이지만, 많은 작업 요구

  • 작업을 되돌리고 먼저 정리하기: 더 많은 작업을 요구하지만, 일관된 커밋을 남길 수 있다

  • 재구현은 처음부터 다시 코드를 작성하면서 새로운 것을 깨달을 수 있는 잠재적 가능성이 있다

First, After, Later, Never

  • Never: 코드의 동작을 변경하지 않을 것이라면 정리를 전혀 하지 않아도 되나요?
    • 코드에서 동작을 변경할 필요가 전혀 없는 경우는 거의 없습니다
  • Later: 나중에 정리하는 것 이 더 가치있는 경우도 있습니다
    • 당장 정리해도 좋을 게 없는 많은 정리 작업이 쌓인 경우 나중에 정리하고 싶을 수 있습니다
    • 코드 정리를 통해 뭔가를 배우거나 보상을 받을 수 있습니다
    • 원할 때 조금씩 정리할 수 있습니다
    • “정리는 시스템 동작에 대한 향후 변경을 더 쉽게 만듭니다”
  • After: 정리를 먼저 한 이후에 행동을 바꾸는 것이 나을 수 있습니다
    • 같은 영역을 곧 다시 변경해야 하는 경우
    • 지금 정리하는 것이 비용이 더 저렴한 경우
    • 정리하는 비용은 행동을 변경하는 비용과 거의 비례합니다
  • First: 정리가 먼저인가?
    • 정리를 한다고 해서 변경이 더 쉬워지지 않는다면 먼저 정리하지 않아도 된다
    • 코드 변경 전 정리를 통해 코드를 더 빠르게 이해할 수 있다면 먼저 하세요
    • 코드가 단 한 번만 변경될 예정이라면 정리를 제한하세요. 시간을 들여 정리하는 것이 비효율적일 수 있습니다
    • ’‘정리가 필요하다’’고 추측하지 말고 코드에 근거하여 분석하고 정리가 필요한 이유를 찾으세요

ⅲTheory

Beneficially Relating Elements

  • Elements: tokens -> expressions -> statements -> functions -> objects / modules -> systems
    • 요소들은 경계를 가지고 있습니다
    • 요소들은 하위 요소를 포함하고 있습니다
  • Relating
    • Invokes(호출하다)
    • Publishes(게시하다)
    • Listens(듣다)
    • Refers(참조하다)
  • Beneficially
    • 요소들은 관계 속에서 서로 이익을 줄 수 있다
    • ex. 함수가 다른 함수를 호출하여 동작을 간단하게 만들거나 다른 변수를 참조하여 값을 재사용할 수 있다
  • 디자인은 요소 간의 관계로부터 얻을 수 있는 이점에 대한 것이다
  • 디자이너는 이러한 요소와 관계를 관리하는 역할이다

Structure And Behavior

  • 행동: 소프트웨어가 수행하는 작업.
  • 구조: 구조가 좋은지 측정하는 것은 쉽지 않다
  • 좋은 구조에 올바른 투자를 했는지는 실제로 알 수 없지만 좋은 구조는 미래에 동작 변경에 대한 비용을 낮출 수 있다
  • 구조 변화와 행동 변화는 근본적으로 다르다

Economics: Time Value And Optionality

  • 시간 가치: 오늘의 1달러는 미래의 1달러보다 더 가치있다. 더 빨리 벌고 나중에 지출해야 합니다
  • 선택성: 불확실한 상황에서는 사물보다 옵션을 갖는 것이 더 낫다(쉽게 변경하고 확장할 수 있는 시스템). 더 많은 가치를 제공
  • 이 두 가지 전략은 충돌할 수 있다.
    • 지금 돈 벌기 -> 미래의 선택지 감소.
    • 지금 돈을 벌지 못한다 -> 미래에 선택할 기회가 사라질 수 있음
  • 소프트웨어를 설계할 때는 돈의 시간 가치와 미래의 선택성 구축을 모두 고려

A Dollar Today > A Dollar Tomorrow

  • 돈은 [언제] [얼마나 확실한지]에 따라 가치가 달라진다
  • 당장 쓸 수 없으면 가치가 낮다
  • 미래에 받을 돈은 당장 투자할 수 없기 때문에 가치가 낮습니다
  • 미래에 돈을 받지 못할 가능성은 가치를 낮춥니다
  • 오늘 1000만 달러 내고, 10년 후 2000만 달러 얻기
    • 미래에 대한 불확실성 존재, 10년 후 받지 못할 수 있음
  • 오늘 1200만 달러를 받고 10년 후에 1000만 달러 내기
    • 확실히 돈을 받을 수 있고, 투자할 기회가 생김

Options

  • 내가 할 수 있는 행동이 많을 수록 가치있다
  • 내가 한 행동이 많을 수록 가치있다
  • 내가 한 행동의 가치가 높을 수록 더 가치있다
  • 오늘 우리가 하는 디자인은 내일의 행동 변화를 ’구매’하는 ’선택지’에 대해 우리가 지불하는 프리미엄

Options Versus Cash Flows

  • 상황에 따라 먼저 정리하는 것이 경제적으로 유리할 수도 불리할 수 도 있다
  • 현금 흐름 관점: 가능성이 높은 돈은 빨리 벌고, 가능성이 낮은 돈은 나중에 쓰는 것이 좋다
    • -> 따라서 나중에 정리하는 것이 좋다
  • 옵션 관점: 지금 돈을 쓰고 나중에 더 많은 돈을 버는 것이 좋다.
    • -> 먼저 정리하는 것이 좋다
  • 정리 비용 + 정리 후 행동 변화 비용 < 정리하지 않은 행동 변화 비용
    • 확실히 정리를 먼저해야 할 때
  • 정리 비용 + 정리 후 행동 변화 비용 > 정리하지 않은 행동 변화 비용
    • 비용이 들더라도 먼저 정리하는 것이 합리적이다고 생각되는 경우가 있을 수 있다
    • 먼저 정리를 하는 것이 행동 변화를 즐겁게 만들어 긍정적 영향을 줄 수도 있다

Reversible Structure Changes

  • 결정의 가역적(바뀔 수 있는)으로 만드는 것의 가치
  • 일반적으로 구조 변경은 되돌릴 수 있지만, 행동 변경은 되돌리기 어렵다
  • 되돌릴 수 없는 결정은 신중해야 한다.
    • 큰 장점에 따르는 큰 단점이 있을 수 있다
  • 반면 되돌릴 수 있는 결정은 잘못된 결정이라 판단되면 취소할 수 있기 때문에 신중을 기하는 노력을 줄여도 된다.
    • 몇 가지 장점이 있으며, 단점이 적다.
  • 되돌리기 어려운 설계 변경 -> 프로토 타입을 구현하고 점진적으로 전환
  • 설계 결정이 코드 베이스 전체에 전파되어야 하는 경우 -> 하나씩 정리하면서 빠져 나오기

Coupling

  • 결합(Coupling)은 소프트웨어 비용을 증가시키므로, 결합을 줄이는 정리를 통해 변경 비용을 낮출 수 있다
  • 결합이 있는 경우 한 요소를 변경할 때 다른 요소도 함께 변경해야 한다
  • 결합은 여러 개의 요소와 결합될 수 있다 (1-N 문제)
    • 자동화된 리팩토링 도구를 사용해 어느 정도 해결 할 수 있다
    • ex. 함수 이름 변경 시 함수 호출되는 부분 이름을 한 번에 변경
  • 결합은 변경사항이 한 요소에서 다른 요소로 계속 전파될 수 있다 (Cascading)

Constantine’s Equivalence

  • 소프트웨어 비용은 소프트웨어를 변경하는 비용과 거의 같다

  • 변경 비용은 큰 변경 비용과 거의 동일하다(=작은 변경 비용은 적다)

  • 큰 변경 비용은 결합과 거의 동일하다

  • 소프트웨어 비용을 줄이기 위해서는 결합을 줄여야 한다.

  • 비용(소프트웨어) ≒ 비용(변경) ≒ 비용(큰 변화) ≒ 결합

Coupling Versus Decoupling

  • 왜 커플링이 존재 있는 건가?
    • 일부 동작을 구현하기 위해 결합된 방법으로 빠르게 구현할 수 있다
    • 지금까지는 결합이 문제가 되지 않았을 수 있다.
    • 일부는 불가피한 결합도 있다
  • 한 클래스에 대해 결합을 줄이면 다른 클래스의 결합은 늘어날 수 있습니다
    • 모든 결합을 없애기 위해 노력할 필요는 없습니다
  • 결합된 방식: 초기 개발 비용⬇️, 미래에 변경 발생 시 비용⬆️ -> 미래에 더 큰 비용 지출 = 지출 지연(discounted cash flow)
  • 분리된 방식: 초기 개발 비용⬆️, 미래에 변경 발생 시 비용⬇️ -> 초기에 더 큰 비용 지출
  • 트레이드오프: 결합이 늘어날수록 분리하기 위한 비용이 증가하고, 분리가 늘어날수록 결합하기 위한 비용이 증가합니다
    • 모든 결합을 없애는 비용은 크다
    • 양 쪽 모두 시간이 지남에 따라 비용이 증가합니다
    • 적절한 수준으로 결합과 분리

Cohesion

  • 결합된 요소들을 하나의 상위 요소(모듈, 클래스 등)로 묶어 응집성 높이기
  • 결합된 요소를 하위 요소로 묶어 추출
    • ex. 도우미 함수 추출
  • 결합되지 않은 하위 요소를 다른 곳에 두기
    • 하위 요소의 기능(역할)에 따라 적절한 위치로 이동

Conclusion

  • 비용 측면
    • 정리로 인해 비용이 작아지는가?
    • 정리로 인해 비용이 나중에 발생하는가?
    • 정리로 인해 비용이 발생할 가능성이 낮아지는가?
  • 수익 측면
    • 정리로 인해 수익이 커지는가?
    • 정리로 인해 수익이 더 빨리 발생하는가?
    • 정리로 인해 수익이 생길 가능성이 더 높아지는가?
  • 결합 측면
    • 정리로 인해 코드 변경 시 더 적은 요소들을 변경해야 하는가?
  • 응집력 측면
    • 정리로 인해 변경해야 할 요소 또는 범위가 더 작아지는가?
  • 프로그래머 측면
    • 정리로 인해 프로그래밍하기가 더 쉽거나 즐거워지는가?