Clean Code

마틴 파울러나, 스캇 마이어스, 켄트 벡과 같은 네임드들의 책을 읽어봤지만, 로버트 C.마틴이 책을 제일 쉽고 간결하게 쓰는 것 같다. 밥 아저씨의 책 중에 제일 유명한 클린 코드를 읽고 요약해봤다.

네임드들이 말하는 깨끗한 코드

  • 비야네 스트롭스트룹(C++ 창시자, The C++ Programming Language 저자)
    • 보기에 즐거운 코드
    • 효율적인 코드
    • 나쁜 코드는 나쁜 코드를 이끈다.(깨진 창문 이론)
    • 오류 처리가 철저한 코드
    • 한 가지를 잘 하는 코드
  • 그래디 부치(UML 개발자, Object Oriented Analysis and Design with Application 저자)
    • 잘 쓴 문장처럼 읽혀야 하는 코드
    • 명쾌한 추상화
  • 데이브 토마스 형님(OTI 창립자, 이클립스 전략의 대부)
    • 고치기 쉬운 코드
    • 테스트 케이스가 있는 코드
    • 작은 코드
    • 인간이 읽기 좋은 코드
  • 마이클 페더스(Working Effectively with Legacy Code 저자)
    • 주의 깊게 작성한 코드
    • 깔끔하고 단정한 코드
  • 론 제프리스(Extreme Programming Installed, Extreme Programming Adventure in C# 저자)
    • 중복을 피하라.
    • 한 기능만 수행하라.
    • 제대로 표현하라.
    • 작게 추상화하라.
  • 워드 커닝햄(Wiki 창시자, Fit 창시자, Extreme Programming 창시자, 객체지향의 정신적 지도자)
    • 읽으면서 짐작한대로 동작하는 코드
    • 문제를 풀기 위한 언어처럼 보이는 코드
  • 코드는 요구사항을 표현하는 언어이다.
  • 나중은 결코 오지 않는다. 깨끗한 코드를 위한 노력은 지금 당장 해야 한다.
  • 코드 읽는 시간:코드 작성 시간 = 약 10:1
  • 체크아웃할 때보다 나은 코드로 체크인 한다. 캠프장은 도착했을 때보다 떠날 때 더 깨끗해야한다는 보이스카웃 규칙을 떠올리자.

의미 있는 이름

  • 의도를 분명히 밝혀라.
    • 알 수 없이 줄인 이름
    • 자료구조를 단순히 적은 이름
    • 정의하지 않고 그대로 사용한 상수
    • 나만 알 수 있는 이름
  • 그릇된 정보를 피하라.
    • 자료구조와는 다른 이름(Map인데 이름에 List를 넣는다거나)
    • 레거시 의미와는 다른 의미로 사용
  • 의미있게 구분하라.
    • 단순히 컴파일러를 통과하기 위한 이름(a1, a2, a3…)을 사용하지 말라.
    • Object, Data, Info, Manager, Processor와 같은 불분명한 이름을 덧붙여 사용하지 말라.
  • 검색하기 쉬운 이름을 사용하라.
    • 상수를 그대로 사용하지 말 것.
    • 이름의 길이는 범위 크기에 비례할 것(간단한 메서드의 로컬 변수에만 줄인 이름을 사용)
  • 인코딩한 이름을 사용하지 말라.
    • Abstract와 Implementation을 구분하기 위해 인코딩을 사용한다면, Implementation에 인코딩을 사용하는 것이 낫다.(ClassImpl)
  • 헝가리안 표기법의 시대는 갔다.
    • 컴파일러가 타입 점검을 하기 때문에 프로그래머는 굳이 타입을 표기할 필요가 없다.
    • 최근 IDE는 컴파일하기 전에 타입 점검을 지원한다.
    • IDE에서 색상으로 구분해주기 때문에, 멤버 변수 접두어는 더이상 필요 없다.
  • 클래스 이름
    • 명사나 명사구를 사용한다.
  • 메소드 이름
    • 동사나 동사구를 사용한다.
    • 접근자는 get, 변경자는 set, 조건자는 is를 붙인다.(javabean 표준)
    • 생성자를 오버로딩할 때에는 Static Factory Method를 사용한다.
  • 한 개념에 일관성 있는 한 단어를 사용하라. Manager, Controller, Driver등을 의미 없이 섞어 쓰지 말라.
  • 동일하지 않은 개념을 동일한 이름으로 정의하지 말라. Add, Insert, Append는 각각 다른 의미이다.
  • 의미가 불명확하다면 맥락을 추가하라. Name으로 의미 표현이 부족하면 LastName과 같이 맥락을 추가하라.
  • 불필요한 맥락(엔진 이름, 작성자 이름 등)은 제거하라. 검색도 잘 안되고, IDE의 자동완성 기능이 의미 없게 된다.

함수

  • 작아야 한다.
  • if/else, while 등에 들어가는 블록은 1줄(함수 호출)이어야 한다.
  • 함수의 들여쓰기(중첩 구조)는 2단을 넘어서면 안된다.
  • 한가지만 해야 한다.
    • 추상화 수준이 하나여야 한다.
    • 추상화 수준이 여러개 섞여 있으면, 이해도 어렵고 분리도 어렵다.
    • 의도한 동작 안에 초기화와 같이 외부로 드러나지 않는 부수적인 동작이 숨어있지 않도록 해야한다. 하나의 역할만 해야 한다.
  • 코드를 읽어 나감에 따라 등장하는 함수의 추상화 수준은 점점 낮아져야 한다.
  • switch 문은 SRP에 위배된다. Abstract Factory등과 같은 패턴을 사용해 최소화 하도록 한다.
  • 서술적인 이름을 사용하라. 간결하면 좋지만 의미를 충분히 드러내기 위함이라면 길어도 좋다.
  • 일관성 있는 이름을 사용하라.
  • 함수 인수의 갯수는 적으면 적을 수록 좋다. 3개를 넘어가면 의미 파악도 어렵고, 테스트가 어려워진다.
  • 함수와 인수 사에에 추상화 수준도 동일하게 맞춰야 한다.
  • 1개의 인수를 가지는 함수에서, 함수 이름은 동사 인수는 명사의 쌍을 이루어야 한다. 함수의 의미를 명확하게 하기 위해서 동사구로 표현할 수 있지만, 기본적으로 동사+명사의 쌍이다.
  • 1개의 인수를 가지는 함수는 명령인지 조회인지 이벤트인지 의미를 명확하게 파악해서 각각의 이름 형식을 일관되게 맞춰야 한다.
    • 조회(명사+동사)
      • boolean fileExists(string fileName)
    • 명령(동사+명사)
      • InputStream openFile(string fileName)
    • 이벤트(on+명사+동사)
      • onFileOpen(string fileName)
  • 1개의 인수를 가지는 함수인데 명령, 조회, 이벤트를 나타내지 않는다면 리팩토링 해라.
  • 함수의 의미가 명확해지지 않기 때문에 출력 인수는 최대한 피한다. 변환 함수에서는 출력 인수를 사용해 변환 결과를 얻지 말고, 반환 값 또는 객체 내 필드를 사용해 변환 결과를 얻어라.
  • 플래그 인수는 존재 자체가 SRP 위반이다.
  • 함수가 여러개의 인수를 가진다면, 인수를 묶어 객체로 만들 수 있는지 고려하라. 인수들은 자연스럽게 개념을 표현하게 된다.
  • 명령 함수가 오류코드를 반환하면 중첩코드가 만들어진다. 예외 처리를 사용하라.
  • 오류코드는 보통 enum으로 표현되고, 변경에 취약하다(재컴파일/재배치 발생). 예외 처리를 사용하면 이를 없앨 수 있다.
  • try/catch 블록에 그대로 코드를 넣지 말고, 함수로 뽑아내라. 코드도 깔끔해지고 이해도 쉽다.
  • 구조적 프로그래밍의 원칙이 절대적인 것은 아니다. return, break, continue, goto 등도 적절히 사용할 수 있다.
  • 시스템은 구현의 대상이 아니라 풀어갈 이야기이다.

주석

  • 코드로 의도를 표현하는데 실패했다는 것을 의미한다. 필요악이다.
  • 좋은 주석
    • 저작권 정보, 소유권 정보 등 법적인 내용을 표시하는 주석
    • 지엽적인 설명이 아닌, 전체적인 의도를 설명하는 주석
    • 중요성(경고 포함)을 강조하는 주석
    • To-do 주석
  • 나쁜 주석
    • 간결하지 않고, 주절거리는 주석
    • 같은 이야기 반복하는 주석
    • 오해의 여지가 있는 명확하지 않은 주석
    • 의무적으로 다는 주석
    • 이력을 기록하는 주석. 요새는 소스 관리 시스템에서 다 해준다.
    • 있으나 마나한 주석
    • 위치를 표시하는 주석. -여기서부터 XX임
    • 닫는 괄화에 다는 주석. 함수가 길다는 의미이며 함수를 줄이려는 노력이 먼저다.
    • 저자를 표시하는 주석. 요새는 소스 관리 시스템에서 다 해준다.
    • 주석으로 처리한 코드. 요새는 소스 관리 시스템에서 다 추적 가능하다.
    • HTML로 표시한 주석. 극혐. HTML로 표시하는 역할은 그 역할을 하는 도구에 맡기는게 맞다.
    • 전역 정보. 가까이에 있는 코드만 기술하라.
    • 주석 자체가 다시 설명을 요구하는 애매한 주석

형식 맞추기

  • 코드 형식은 의사소통의 일환이다.
  • 파일 당 코드 라인의 수는 최대한 적게 유지하고 500라인을 넘지 않도록 한다.
  • 코드의 가로 길이는 최대한 작게 유지하고 120자를 넘지 않도록 한다.
  • 코드는 신문 기사를 작성하는 것과 같이 제목-추상적 내용-구체적 내용의 순서로 작성한다.
  • 개념과 개념 사이는 빈 행으로 구분한다.
  • 서로 밀접한 관련이 있는 코드들은 한 파일 내에 가까이 묶는다.
  • 변수는 사용 위치에 최대한 가까운 곳에 선언한다.
  • 루프를 제어하는 변수는 루프 문 내부에 선언한다.
  • 인스턴스 변수들은 클래스의 맨 처음(Java) 또는 맨 나중(C++ - Scissors Rule)에 선언하며, 빈 행을 두지 않는다.
  • 호출 하는 함수 뒤에 호출 되는 함수를 가까이 배치한다.
  • 의미 있는 상수라면, 저차원 함수 내에 선언하지 말고, 클래스 또는 고차원 함수 내에서 선언하고 넘겨주는 방식을 사용한다.
  • 연산자의 앞뒤에 공백을 넣는다.
  • 함수 인수들 사이에 공백을 넣는다.
  • 코드를 가로 정렬하면 연산자만 부각되기 때문에 의미가 흐려진다. 정렬하지 않는다.
  • 함수를 한줄로 표현하고 싶은 유혹을 떨쳐버리고 들여쓰기를 해서 명확히 표현하자. 가독성에 좋다.

객체와 자료 구조

  • 아무 생각 없이 get/set 메소드를 넣는다고 해서 캡슐화가 되는 것이 아니다. 캡슐화를 위해서는 추상화가 필요하다.
  • 추상 인터페이스를 통해 사용자가 구현에 신경쓰지 않고 자료를 조작할 수 있어야 진정한 캡슐화가 되었다고 할 수 있다.
  • 절차 지향적 코드는 새로운 데이터를 추가하기는 어려우나(수정 사항이 많음), 새로운 함수를 추가하기는 쉽다.
  • 객체 지향적 코드는 새로운 데이터를 추가하기는 쉬우나, 새로운 함수를 추가하기는 어렵다(수정 사항이 많음).
  • 클래스 C의 메소드 f가 있다면 f가 호출할 수 있는 객체는
    • 클래스 C
    • f가 생성한 객체
    • f의 인수로 넘어온 객체
    • 클래스 C의 인스턴스 변수
  • 해당 객체의 메소드를 호출했을 때 또 다른 객체를 반환한다면, 반환된 객체의 메소드는 호출하면 안된다.(디미터의 법칙)
  • 즉, C.f()만 사용해야 하며, C.f().f1().f2()와 같이 사용하면 안된다.(Train Wreck)
  • 그러나 해당 객체가 반환하는 것이 자료구조(Data Transfer Object - 메소드 없이 공개된 데이터)라면 큰 상관 없다.

오류 처리

  • 오류 코드 리턴 대신 예외를 사용하라.
  • 예외가 발생할 코드의 경우 try-catch-finally 문으로 시작하라.
  • 상위 예외처리 함수에서 모든 예외를 열거해서 처리하고 있다면, 하위 함수에서 예외가 추가될 때마다 예외 처리 함수를 수정해야 한다.(OCP 위반) 따라서 - 미확인 예외를 사용하도록 한다.
  • 예외에 디버깅을 위한 정보를 전달하라.
  • 외부 API가 발생시키는 예외를 처리할 때에는, DIP에 따라 외부 API를 감싼다. 외부 API에 대한 의존성이 역전된다.
  • 예외 처리 때문에 정상 흐름이 잘 읽히지 않는다면, 예외 상황을 캡슐화 하는 클래스를 만들거나 객체를 조작해 예외 처리를 없앤다.(null 객체 등 - Special Case Pattern)
  • null은 전달하지도, 반환하지도 말라.

경계

  • 외부 코드를 사용할 때에는, 우리가 사용하고자 하는 대로 인터페이스(Adaptor)를 작성하고, 그 인터페이스에 맞춰 외부 코드를 작성하여 의존성을 역전시킨다.
  • 외부 코드를 사용하여 테스트 케이스 작성하고 테스트 함으로써 외부 코드를 학습한다.

단위 테스트

  • TDD의 3가지 법칙
    1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
    2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
    3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
  • 테스트 코드의 중요도 = 구현 코드의 중요도
  • 테스트 코드가 잘 되어 있으면 구현 코드 변경이 두렵지 않다.
  • 테스트 코드에서는 가독성이 가장 중요하다. 단순, 간결하면서도 표현력이 풍부해야 한다.
  • 테스트 코드에는 테스트와 관계 없는 코드를 넣지 않는다.
  • 테스트 코드는 BUILD-OPERATE-CHECK의 절차를 따라 작성한다.
  • 테스트 코드는, 필요하다면 API를 추상화(테스트를 위한 언어를 작성한다는 개념) 하여 작성한다.
  • 테스트 함수 1개 당 1개의 개념만 테스트하자.
  • 테스트 함수 1개 당 assert 문을 1개 두도록 하면, 1개의 개념만 테스트한다는 의미를 가질 수 있고, 읽고 이해하기 쉽다.
  • FIRST 원칙
    • 빠른(Fast)
      • 테스트는 자주해야 하므로, 빠르게 수행되어야 한다.
    • 독립적인(Independent)
      • 테스트는 서로 독립적이어야 한다.
    • 반복 가능한(Repeatable)
      • 테스트는 반복 가능해야 한다.
    • 셀프 검증(Self-Validating)
      • 테스트는 성공/혹은 실패만 리턴해야 한다.
    • 시점(Time)
      • 테스트는 적시(구현 전)에 작성해야 한다.

클래스

  • 클래스 정의 순서
    1. 정적 공개 상수
    2. 정적 비 공개 상수
    3. 비 공개 인스턴스 변수
    4. 추상화된 공개 함수
    5. 추상화된 공개 함수에서 사용된 구체적인 비 공개 함수
  • 클래스 정의 순서에 따르면 클래스가 주제 - 주요 행위 - 상세 행위로 표현되기 때문에 마치 신문 기사처럼 쉽게 읽힌다.
  • 클래스는 작아야 한다.(책임의 수가 적어야 하고 25 단어 내로 설명 가능해야 한다.)
  • 클래스 이름은 클래스의 책임을 표현해야 한다. 책임이 간결해야 이름도 간결해 진다.
  • 모호한 클래스 이름(Processor, Manager, Super)은 클래스의 책임이 모호하다는 것을 의미한다.
  • 단일 책임 원칙(Single Responsibility Principle)
    • 클래스나 모듈은 단 하나의 책임(단 하나의 메소드가 아니다)을 갖는다.
    • 클래스나 모듈을 변경할 이유는 단 하나뿐이어야 한다.
  • 응집도(Cohesion)
    • 클래스의 인스턴스 변수 수가 적어야 한다.
    • 클래스의 메소드는 인스턴스 변수를 1개 이상 사용해야 하며, 더 많이 사용할수록 응집도가 높다.
    • 몇몇 메소드만이 사용하는 인스턴스 변수가 보이면, 대부분 클래스를 분리해야할 경우이다.
    • 응집도를 유지하면 작은 클래스가 여럿 나온다.
  • 개방 폐쇄의 원칙(Open-Closed Principle)
    • 확장에 개방적, 수정에 폐쇄적이어 한다.
    • 추상 클래스와 구현 클래스를 분리하여 구현 변경에 따른 영향을 없앤다.
    • 추상 인터페이스를 사용하면, 세부 구현이 분리되므로 클래스의 테스트 코드를 작성하기도 쉽다.
  • 의존성 역전의 원칙(Dependency Inversion Principle)
    • 자주 변경되고 우리가 제어하기 어려운 상세 구현에 의존하지 말고, 우리가 제시한 추상화된 인터페이스에 의존하도록 해야 한다.

시스템

  • 시스템 생성과 시스템 사용을 분리해야 한다.
  • 초기화 지연 기법은 생성과 사용을 뒤섞어 SRP원칙을 위배할 수도 있다.
  • 생성과 사용을 분리시키는 방법
    • Main 분리
      • 생성과 관련된 동작은 Main에서 모두 하고, 어플리케이션에서는 생성된 객체를 사용만 한다.
    • Abstract Factory 패턴
      • 생성 시점은 어플리케이션에서 결정하지만, 실제 생성은 팩토리에서 하기 때문에 어플리케이션에서는 생성된 객체를 사용만 한다.
    • 의존성 주입(Dependency Injection)
      • 클래스의 외부에서 의존 클래스를 생성한 뒤에 주입(생성자의 파라미터, setter 메소드, 메소드 등을 이용)한다.
  • 처음부터 확장을 고려한 시스템을 만들 수 없고, 점진적, 반복적 개선이 필요하다. 이것이 애자일의 철학.
  • 시스템에서 관심사(도메인 영역)를 잘 분리해 내야 한다.

창발성(하다보면 자연스럽게 발현됨)

  • 켄트 백이 말하는 설계를 단순하게 만드는 법칙 4가지(중요도 순)
    1. 모든 테스트를 실행한다.
      - 의존성 주입(DI), 의존성 역전의 원칙(DIP), Abstraction, Interface 등을 사용하여 결합도를 낮춰야 테스트가 쉽다.
      - 테스트가 잘 되도록 코드를 작성하고, 실제로 모든 테스트를 한다면, 저절로 OOP가 달성된다.
      - 테스트 케이스를 모두 돌릴 수 있으면, 리팩토링도 두렵지 않다.

    2. 중복 코드를 없앤다.
      - 단순한 중복이라도 모두 없앤다.
      - Template Method 패턴(중복 Method를 상위 클래스에 정의하고 상위 클래스를 상속)을 사용할 수 있다.

    3. 의도를 표현한다.
      - 좋은 이름을 사용하라.
      - 함수 및 클래스의 크기를 줄여라.
      - 표준 명칭을 사용하라.(패턴을 적용했다면, 클래스 이름에 패턴 명을 넣어라)
      - 단위 테스트를 모두 작성하라.
      - 꼼꼼해야 한다.(장인 정신)

    4. 클래스와 메소드 수를 최소화 한다.
      - 1,2,3번을 다 하고나서 클래스와 메소드 수를 줄이라.

동시성

  • 구조적 관점
    • 컴포넌트의 동작 시점이 제한되지 않기 때문에, 모듈의 독립성을 높이고 결합도를 줄일 수 있다.
  • 성능 관점
    • 멀티 프로세싱이 가능한 환경이라면, 컴포넌트를 병렬로 동작시켜 처리 능력을 향상시킬 수 있다.
  • 늘 성능이 향상 되는 것은 아니다.
  • 동시성을 고려하면 설계가 크게 달라질 수 있다.
  • 오히려 부하를 발생시킬 수 있다.
  • 복잡하다.
  • 동시성 관련한 문제는 재현하기 어렵고, 일회성 문제로 치부하기 쉽다.
  • 동시성 관련 코드를 분리하라.(SRP)
  • 공유되는 자료를 최대한 줄이고, 사본을 활욜하라.
  • 쓰레드는 가능한 독립적으로 구현하고, 다른 쓰레드와 자료 공유를 최대한 줄인다.
  • 라이브러리의 컬렉션이 쓰레드에 안전한지 확인하라.

냄새와 휴리스틱

  • 주석
    • 부적절한 정보
    • 쓸모 없는 주석
    • 중복된 주석
    • 성의 없는 주석
    • 주석 처리된 코드
  • 환경
    • 여러단계로 된 빌드
    • 여러단계로 된 테스트
  • 함수
    • 너무 많은 인수
    • 출력 인수
    • 플래그 인수
    • 죽은 함수
  • 일반
    • 한 소스 파일에 여러 언어를 사용
    • 당연히 지원해야 하는 동작을 지원하지 않음(최소 놀람의 원칙 위배)
    • 경계를 올바로 처리 하지 않음
    • 안전절차를 무시
    • 중복
      • 다형성 이용
    • 올바르지 않은 추상화 수준
    • 기초 클래스가 파생 클래스에 의존
    • 과도한 정보
      • 작고 간결하게
    • 죽은 코드
    • 수직 분리
      • 연관된 코드는 수직으로 가깝게 배치
    • 일관성 부족(최소 놀람의 원칙 위배)
    • 잡동사니
      • 비어있는 기본 생성자 따위
    • 인위적 결합
      • 이유 없이 결합시키지 않는다.
    • 기능 욕심
      • 다른 클래스의 기능을 탐냄
    • 플래그(선택자) 인수
    • 모호한 의도
      • 개행 안함, 헝가리안 표기법, 매직 넘버
    • 잘못 지운 책임
    • 부적절한 static 함수
      • 재정의 가능성이 있는 함수인데 static으로 선언
    • 서술적 변수의 부재
      • 연속된 처리의 해독을 쉽게 하기 위해, 중간 요약을 하는 서술적 변수를 사용
    • 이름과 기능이 불일치하는 함수
    • 이해하지 못하고 사용한 알고리즘
    • 물리적으로 드러나지 않은 논리적 의존성 : 논리적으로 의존한다면, 물리적인 인터페이스를 정의해 의존하라.
    • if/else 또는 switch/case
      • 다향성을 이용해 제거하라.
    • 표준 표기법을 따르지 않음
    • 명명된 상수로 정의되지 않은 매직 넘버
    • 이유가 명확하지 않은 결정
    • 구조적 강제성 없는 관례 : 따르지 않으면 그만인 관례보다는 구조적으로 강제되도록 한다.
    • 캡슐화되지 않은 조건
      • if (x && y && z) 하지 말고 if (a()) 처럼 캡슐화 하라.
    • 부정 조건
    • 숨겨진 시간적 결합 : 함수 인수를 드러내어 함수가 호출되는 순서(시간)를 명확히 한다.
    • 일관성 없음
    • 캡슐화되지 않은 경계 조건 : 경계 조건은 한 곳에서 정의하여 처리한다. 코드 여기저기에 index+1, index-1를 늘어놓지 않는다.
    • 단계적이지 않은 함수의 추상화 수준
    • 흩어져 있는 기본값 상수 또는 설정 관련 상수 : 최상위 단계에 위치시키고, 저차원 함수에서는 인수로 넘겨받아 처리한다.
    • 오지랖 넓은 코드를 없애고, 부끄럼 많은 코드를 작성하자.
      • 디미터의 법칙. 자신이 직접 사용하는 모듈만 호출할 것. 추이적 탐색하지 말것.
    • 자바
      • 긴 import 목록을 피하고 와일드 카드를 사용하라.
      • 상수는 상속하지 말고 static import를 사용하라.
      • 상수 대신 Enum을 활용하라. Enum은 method와 field를 지원한다.
    • 이름
      • 서술적인 이름을 사용하라.
      • 적절한 추상화 수준에서 이름을 선택하라.
      • 상세 구현을 드러내는 이름은 피한다.(추상화!)
      • 가능하면 표준 명명법(디자인 패턴 명, toXX, isXX 등)을 사용하라
      • 명확한 이름
      • 긴 범위는 긴 이름을 사용할 것
      • 인코딩을 하지 말고 풀어 쓸 것
      • 이름으로 부수 효과를 설명하라.
    • 테스트
      • 충분한 테스트 케이스를 만들어라.
      • 커버리지 도구를 사용하라.
      • 사소한 테스트를 건너뛰지 마라.
      • 무시한 테스트는 모호함을 뜻한다.
      • 경계조건을 테스트하라.
      • 버그 주변은 철저히 테스트 하라.
      • 실패 패턴을 살펴라.
      • 테스트 커버리지 패턴을 살펴라.
      • 테스트는 빨라야 한다.