켄트 벡의 Tidy First? 책을 읽고 정리한 내용입니다.

PART 01. 코드 정리법

응집도를 높이는 배치

두 루틴에 결합도가 있으면 서로 옆에 두세요. 두 파일에 결합도가 있으면 같은 디렉토리에 넣습니다. 결합도를 제거할 수 있다면 그렇게 합니다.

응집도를 높이는 순서로 정리하면, 코드를 더 쉽게 변경할 수 있습니다. 때로는 응집도를 조금 개선해서 코드가 명확해지면, 결합도 제거를 막고 있던 장막이 걷힐 수도 있습니다. 그리고 응집도가 좋아지면 결합도 역시 덩달아 좋아집니다.

선언과 초기화를 함께 옮기기

타입이 포함된 선언과 초기화 코드가 떨어져 있다면 읽기 더 어려워집니다. 변수를 사용하기 직전에 선언하고 초기화합니다.

명시적인 매개변수

맵(map) 형태의 매개변수가 전달되는 경우가 흔합니다. 이렇게 하면, 코드를 읽으면서도 어떤 데이터가 필요한지 알기 어렵습니다. 매개변수를 명시적으로 드러나게 만든 다음, 함수를 연쇄적으로 호출할 수 있게 준비하세요.

도우미 추출

코드를 보다가 목적이 분명하고 나머지 코드와는 상호작용이 적은 코드 블록을 만날 때가 있습니다. 그 코드 블록을 출려내고, 도우미(helper)로 추출한 후에 이름을 붙입니다. 이때, 도우미의 이름은 작동 방식이 아니라 목적에 따라 짓습니다.

하나의 더미

다음과 같은 증상들이 있다면 새롭게 정리해야 합니다.

  • 길고 반복되는 인자(argument) 목록
  • 반복되는 코드, 그 중에서도 반복되는 조건문
  • 도우미에 대한 부적절한 이름
  • 공유되어 변경에 노출된 데이터 구조

PART 02. 관리

코드 정리 구분

변경 대상 중에는 프로그램 동작(B) 변경이 있습니다. 이러한 변경은 프로그램을 실행하면서 찾아낸 것입니다. 반면, 어떤 변경은 프로그램 구조(S) 변경입니다. 이런 종류의 변경은 코드를 자세히 살펴봐야만 관찰할 수 있습니다.

비슷한 코드끼리 정리하면 설명하는 도우미가 드러나고, 설명하는 도우미를 만들면 이어지는 동작 변경이 훨씬 쉬어집니다. 이렇게 되면 어떤 순서로 전개될지 보이고, 몇 수 앞을 짐잔할 수 있습니다.

변경할 때는 의도적으로 쉽게 변경하던가 또는 변경하기 쉽게 만들던가, 둘 중 하나를 목표로 삼는 것이 좋습니다.

연쇄적인 정리

코드 구조를 대대적으로 바꾸려고 코드 정리를 시작하는 경우가 많습니다. 너무 많이, 너무 빠르게 변경하지 않도록 주의하세요. 대개 작은 정리를 순차적으로 성공하는 것이 무리한 정리로 실패하는 것보다 시간을 아껴줍니다.

리듬

코드 정리는 몇 분에서 한 시간 정도면 충분합니다.

코드 정리 시점

다음 상황에는 코드 정리를 하지 마세요.

  • 앞으로 다시는 코드를 변경하지 않을 때
  • 설계를 개선하더라도 배울 것이 없을 때

다음 상황에서는 나중으로 정리를 미루세요.

  • 정리할 코드 분량이 많은데, 보상이 바로 보이지 않을 때
  • 코드 정리에 대한 보상이 잠재적일 때
  • 작은 묶음으로 여러 번에 나눠서 코드를 정리를 할 수 있을 때

다음 상황에서는 동작 변경 후에 정리하세요.

  • 다음 코드 정리까지 기다릴수록 비용이 더 불어날 때
  • 코드 정리를 하지 않으면 일을 끝냈다는 느낌이 들지 않을 때

다음 상황에서는 코드 정리 후에 동작 변경을 하세요.

  • 코드 정리를 했을 때, 코드 이해가 쉬어지거나 동작 변경이 쉬어지는 즉각적인 효과를 얻을 수 있을 때
  • 어떤 코드를 어떻게 정리해야 하는지 알고 있을 때

PART 03. 이론

요소들을 유익하게 관계 맺는 일

‘소프트웨어 설계의 의미’에 대해 ‘요소들을 유익하게 관계 맺는 일’이라고 말할 수 있습니다.

  • 요소: 요소는 하위 요소를 포함합니다.
    • 토큰
    • 식(expression)
    • 문(statement)
    • 함수
    • 객체/모듈
    • 시스템
  • 관계 맺기: 소프트웨어 설계에서 관계는 다음과 같은 것들이 있습니다.
    • 호출
    • 발행(publish)
    • 대기(listen)
    • 참조(변수의 값을 가져오기)
  • 요소들을 유익하게 관계 맺는 일
    • 설계를 구성하는 요소들과 그들의 관계, 그리고 그 관계에서 파생되는 이점이 바로 설계입니다.
    • 이러한 관점에서 소프트웨어 설계자는 오직 다음과 같은 일만 할 수 있습니다.
      • 요소를 만들고 삭제합니다.
      • 관계를 만들고 삭제합니다.
      • 관계의 이점을 높입니다.

예시를 들어보겠습니다. 한 객체가 하나의 함수에서 다른 객체를 두 번 호출하는 예시입니다.

def caller():
  return box.width() * box.height()

설계 관점에서 Box.area()라는 새로운 요소를 만들고 caller() 함수와 box 객체 사이의 관계를 조정했습니다. 이제 두 요소는 하나의 함수 호출로 연결됩니다.

def caller():
  return Box.area()

class Box:
  def area():
    return width() * height()

구조와 동작

소프트웨어는 두 가지 방식으로 가치를 만듭니다.

  • 현재 소프트웨어가 하는 일(예: 급여 계산, 주문 배송, 알림 발송 등)
  • 미래에 새로운 일을 시킬 수 있는 가능성

시스템 구조는 동작에 영향을 미치지 않습니다. 예를 들면, 하나의 큰 기능으로 구성하든, 수많은 작은 기능으로 구성하든, 동작의 결과로 같은 급여가 계산됩니다. 구조는 미래의 기회를 만듭니다. 구조에 따라 급여 계산에 새로운 국가를 추가하는 것이 쉬워질 수도 있고, 어려워질 수도 있습니다.

옵션과 현금흐름 비교

  • 현금흐름할인: 먼저 돈을 벌고, 나중에 돈을 쓰라고 말합니다. 코드 정리를 먼저 하지 마세요.
  • 옵션: 나중에 더 많은 돈을 벌기 위해, 지금 돈을 쓰라고 말합니다. 옵션이 생길 일이 명백하다면, 코드 정리를 선행하세요.

다음 수식이 성립하면, 무조건 코드 정리를 먼저 하세요.

  • 비용(코드 정리) + 비용(코드 정리 후 동작 변경) < 비용(바로 동작 변경)

소프트웨어 설계는 인간관계 속에서 벌어지는 활동이고, 코드 정리의 차원에서 코드와 자신과의 관계에 대해 이야기하고 있기 때문에, 코드 정리를 먼저 하면 이후의 행동 변화가 더 즐거워진다는 이유로 코드 정리를 먼저 할 수도 있습니다. 다만, 경제적 인센티브에 반하는 행동을 하고 있을 수도 있다는 사실을 항상 인식하세요.

연습을 꾸준히 한다면, 제품의 생존과 번영이 걸려 있는 중요한 순간에 이를 때 언제, 어떻게 설계해야 하고 언제 설계하지 말아야 하는지 직감적으로 알 수 있게 됩니다.

결합도

한 요소를 변경하면 다른 요소도 함께 변경해야 하는 경우, 두 요소는 특정 변경과 관련하여 서로 결합되어 있는 것입니다.

두 요소가 결합되어 있는지 여부를 판단하려면, 먼저 어떤 변경이 발생했거나 발생할 가능성이 있는지 알아야 합니다(테스트해 보려면 하나의 커밋에서 어떤 파일 쌍이 함께 나타나는 경향이 있는지 살펴보세요. 그런 파일들은 결합되어 있습니다).

만약 결합도가 두 요소만의 문제라면, 악몽이 아니었을 겁니다. 그보다는, 결합도를 주목하는 이유는 결합도가 가진 두 가지 성질 때문입니다.

  • 일대다(1-N): 어떤 변경이 일어나면, 한 요소는 여러 요소와 결합이 일어 납니다.
  • 연쇄작용(Cascading): 일단 변경이 일어나면, 한 요소에서 다른 요소로 파급되고, 그 변경은 그 자체로 또 다른 변경을 촉발하고, 스스로도 변경을 촉발할 수 있습니다.

일대다 문제는 개발 도구를 이용하면 어느 정도 해결할 수도 있습니다. 함수 이름과 모든 호출 코드를 변경하는 리팩터링 자동화 기능이 있다면 한 번에 변경할 수 있습니다.

연쇄적인 변경이 더 큰 문제입니다. 변경 비용은 멱법칙 분포를 따릅니다. 바로 이러한 연쇄적인 변경 비용 때문에 멱법칙 분포가 만들어집니다. 소프트웨어 설계를 해서 연쇄적인 변경의 환률과 규모를 줄일 수 있습니다.

결합도와 결합도 제거

한 종류의 코드 변경에 대한 결합도를 줄일수록 다른 종류의 코드 변경에 대한 결합도가 커집니다. 이것이 의미하는 실질적인 의미는 모든 결합을 다 색출하듯 없애려고 애쓰지 말아야 한다는 것입니다. 그렇게 해서 만들어진 결합도는 그만한 가치가 없습니다.

결론

  • 비용: 코드를 정리하면 비용이 줄까요? 아니면 나중에 하는 편이 나을까요? 아니면 줄일 가능성이 있을까요?
  • 수익: 코드를 정리하면 수익이 더 커질까요? 혹은 더 빨리 발생하거나 커질 가능성이 있나요?
  • 결합도: 코드를 정리하면 변경에 필요한 요소의 수가 줄어드나요?
  • 응집도: 코드를 정리하면 변경을 더 작고 좁은 범위로 집중시켜 더 적은 수의 요소만 다룰 수 있을까요?