9. 더 화려한 단어 고르기
send deliver, dispatch, announce, distribute, route
find search, extract, locate, recover
start launch, create, begin, open
make Create, set up, build, generate, compose, add, new
10. tmp나 retval 같은 보편적인 이름 피하기
var euclidean_norm = function (v) {
var retval = 0.0;
for (var i =0; i < v.length; i+= 1)
retval += v[i] * v[i];
return Math.sqrt(retval);
};
retval += v[i];
sum_squares += v[i]; // 더해야 하는 '제곱'은 어디 있는가?
11. tmp나 retval 같은 보편적인 이름 피하기
if (right < left) {
tmp = right;
right = left;
left = tmp;
}
12. tmp나 retval 같은 보편적인 이름 피하기
for(int i =0; i < clubs.size(); i++)
for(int j =0; j < clubs[i].members.size(); j++)
for(int k =0; k < users.size(); k++)
if(clubs[i].members[k] == users[j])
cout << "user[" << j << "] is in club[" << i << "]" << endl;
13. tmp나 retval 같은 보편적인 이름 피하기
for(int ci =0; ci < clubs.size(); ci++)
for(int mi =0; mi < clubs[ci].members.size(); mi++)
for(int ui =0; ui < users.size(); ui++)
if(clubs[ci].members[ui] == users[mi])
cout << "user[" << mi << "]is in club[" << ci << "]" << endl;
if (clubs[ci].members[ui] == users[mi] # 버그! 처음 문자가 다름
14. 추상적인 이름보다 구체적인 이름을 선호하라
#define DISALLOW_EVIL_CONSTRUCTORS(ClassName)
ClassName(const ClassName&);
void operator=(const ClassName&);
class ClassName {
private:
DISALLOW_EVIL_CONSTRUCTORS(ClassName);
public:
...
};
15. 추상적인 이름보다 구체적인 이름을 선호하라
#define DISALLOW_COPY_AND_ASSIGN (ClassName)
ClassName(const ClassName&);
void operator=(const ClassName&);
class ClassName {
private:
DISALLOW_COPY_AND_ASSIGN(ClassName);
public:
...
};
16. 추상적인 이름보다 구체적인 이름을 선호하라
--run_locally
의도
디버깅 정보를 출력
문제
원격에서 디버깅 정보를 출력한다면
로컬에서 디버깅 정보를 출력하기 싫다면
원인
실제 작동보다 사용하는 환경을 기반으로 지은 이름
17. 추상적인 이름보다 구체적인 이름을 선호하라
--extra_logging
장점
무슨 역할을 하는지 이름만으로 명확
나중에 데이터베이스 설정까지 필요 해진다면?
--use_local_database 라는 추가 옵션 생성
18. 추가적인 정보를 이름에 추가하기
//Example: "af84ef845cd8"
string id;
19. 추가적인 정보를 이름에 추가하기
//Example: "af84ef845cd8"
string id; string hex_id;
20. 추가적인 정보를 이름에 추가하기
var start = (new Data()).getTime();
...
var elapsed = (new Data()).getTime() - start;
document.wrieln("Load time was: " + elapsed + " seconds");
21. 추가적인 정보를 이름에 추가하기
var start_ms = (new Data()).getTime();
...
var elapsed_ms = (new Data()).getTime() - start_ms;
document.wrieln("Load time was: " + elapsed_ms / 1000 + "
seconds");
22. 추가적인 정보를 이름에 추가하기
함수 인수 단위를 포함하게 재작성
Start(int delay) delay_secs
CreateCache(int size) size_mb
ThrottleDownload(float limit) max_kbps
Rotate(float angle) degrees_cw
23. 추가적인 정보를 이름에 추가하기
상황 변수명 더 나은 이름
패스워드가 plaintext에 담겨있고, 추가
password plaintext_password
작업 전에 암호화 되어야 함
출력하기전에 이스케이프 처리가 되어 comment unescaped_comme
야함 nt
Html의 바이트가 UTF-8로 변환되었음 html html_utf8
입력데이터가 url encoded 되었다 data data_urlenc
24. 이름은 얼마나 길어야 하는가
newNavigationControllerWrappingViewControllerForDataSourceOfClass
25. 이름은 얼마나 길어야 하는가
좁은 범위에서는 짧은 이름이 괜찮다.
if (debug) {
map<string, int> m;
LookupNamesNumbers(&m);
Print(m);
}
26. 이름은 얼마나 길어야 하는가
m이 클래스 멤버거나 전역변수라면?
LookUpNamesNumbers(&m);
Print(m);
27. 이름은 얼마나 길어야 하는가
단어 완성기능을 이용하라
VisualStudio 2008 Intellisense
단어 자동 완성 ctrl + space
매개변수 정보 ctrl + shift + space
멤버 출력 ctrl + j
28. 이름은 얼마나 길어야 하는가
팀에 새로 합류한 사람이 이해할 수 있는 약어인가?
eval, doc, str BEManager
29. 이름 포맷팅으로 의미를 전달하라
static const int kMaxOpenFiles = 100;
class LogReader {
public:
void OpenFile(string local_file);
private:
int offset_;
DISALLOW_COPY_AND_ASSIGN(LogReader);
};
30. 오해할 수 없는 이름들
본인이 지은 이름을 “다른사람들이 다른 의미로 해석할 수 있을까“ 라는
질문을 던져보며 철저하게 확인해야 한다.
37. 경계를 포함하는 한계값을 다룰 때는
min과 max를 사용하라
CART_TOO_BIG_LIMIT = 10
if shopping_cart.num_items() >= CART_TOO_BIG_LIMIT:
Error(“Too many items in cart.”)
10개를 포함하는가?
포함하지 않는가?
38. 경계를 포함하는 한계값을 다룰 때는
min과 max를 사용하라
MAX_ITEMS_IN_CART = 10
if shopping_cart.num_items() > MAX_ITEMS_IN_CART:
Error(“Too many items in cart.”)
47. 일관성과 간결성을 위해서 줄 바꿈을
재정렬하기
public class PerformanceTester {
public static final TcpConnectionSimulator wifi = new TcpConnectionSimulator(
500, /*Kbps*/
80, /*millisecs 대기시간*/
200, /*흔들림*/
1 /*패킷손실*/);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(
4000, /*Kbps*/
10, /*millisecs 대기시간*/
0, /*흔들림*/
0 /*패킷손실*/);
public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(
100, /*Kbps*/
400, /*millisecs 대기시간*/
250, /*흔들림*/
5 /*패킷손실*/);
}
48. 일관성과 간결성을 위해서 줄 바꿈을
재정렬하기
public class PerformanceTester {
public static final TcpConnectionSimulator wifi =
new TcpConnectionSimulator(
500, /*Kbps*/
80, /*millisecs 대기시간*/
200, /*흔들림*/
1 /*패킷손실*/);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(
4000, /*Kbps*/
10, /*millisecs 대기시간*/
0, /*흔들림*/
0 /*패킷손실*/);
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator(
100, /*Kbps*/
400, /*millisecs 대기시간*/
250, /*흔들림*/
5 /*패킷손실*/);
}
49. 일관성과 간결성을 위해서 줄 바꿈을
재정렬하기
public class PerformanceTester {
// TcpConnectionSimulator (처리량, 지연속도, 흔들림, 패킷_손실)
// [Kbps] [ms] [ms] [percent]
public static final TcpConnectionSimulator wifi =
new TcpConnectionSimulator( 500, 80, 200, 1);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(4000, 10, 0, 1);
public static final TcpConnectionSimulator cell =
new TcpConnectionSimulator( 100, 400, 250, 5);
}
50. 메소드를 활용하여 불규칙성을 정리하라
DatabaseConnection database_connection;
string error;
assert(ExpandFullName(database_connection, "Doug Adams", &error)
== "Mr. Douglas Adams");
assert(error == "");
assert(ExpandFullName(database_connection, "Jake Brown", &error)
== "Mr. Jacob Brown III");
assert(error == "");
assert(ExpandFullName(database_connection, "No such Guy", &error) == "");
assert(error == "no match found");
51. 메소드를 활용하여 불규칙성을 정리하라
헬퍼함수 사용
CheckFullName("Doug Adams", "Mr. Douglas Adams", "");
CheckFullName("Jake Brown", "Mr. Jacob Brown III", "");
CheckFullName("No such Guy", "", "no match found");
52. 메소드를 활용하여 불규칙성을 정리하라
추가적인 장점
중복된 코드가 없어져서 더 간결하다
테스트의 중요 부분들이 한눈에 보인다
새로운 테스트 추가가 훨씬 쉬워졌다
54. 코드를 문단으로 쪼개라
# 사용자 친구들의 이메일 주소를 읽어 들여 시스템에 존재하는 사용자와 매치시킨다.
# 그 다음 해당 사용자와 이미 친구인 사람들의 리스트를 나타낸다.
def suggest_new_friends(user, email_password):
friends = user.friends()
friend_emails = set(f.email for f in friends)
contacts = import_contacts(user.email, email_passwd)
contact_emails = set(c.email for c in contacts)
not_friends_emails = contact_emails - friend_emails
suggested_friends = User.objects.select(email_in=non_friend_emails)
display['user'] = user
display['friend'] = friends
display['suggested_friends'] = suggested_friends
return render("suggested_friends.html", display)
55. def suggest_new_friends(user, email_password):
# 사용자 친구들의 이메일 주소를 읽는다.
friends = user.friends()
friend_emails = set(f.email for f in friends)
# 이 사용자의 이메일 계정으로부터 모든 이메일 주소를 읽어들인다.
contacts = import_contacts(user.email, email_passwd)
contact_emails = set(c.email for c in contacts)
# 아직 친구가 아닌 사용자들을 찾는다.
not_friends_emails = contact_emails - friend_emails
suggested_friends = User.objects.select(email_in=non_friend_emails)
# 사용자 리스트를 화면에 출력한다.
display['user'] = user
display['friend'] = friends
display['suggested_friends'] = suggested_friends
return render("suggested_friends.html", display)
56. 개인적인 스타일 대 일관성
class Logger {
…
};
class Logger
{
…
}
58. 주석에 담아야 하는 대상
주석의 목적은 코드를 읽는 사람이 코드를 작성한 사람만큼
코드를 잘 이해하게 돕는데 있다.
59. 코드에서 빠르게 유추할 수 있는 내용은
주석으로 달지 말라
// 클래스 Account를 위한 정의
class Account {
public:
// 생성자
Account();
// profit에 새로운 값을 설정
void SetProfit(double profit);
// 이 어카운트의 profit을 반환
double GetProfit();
};
60. 설명 자체를 위한 설명을 달지 말라
// 주어진 이름과 깊이를 이용해서 서브트리[h1]에 있는 노드를 찾는다
Node* FindNodeInSubtree(Node* subtree, string name, int depth);
61. 설명 자체를 위한 설명을 달지 말라
// 주어진 ‘name’으로 노드를 찾거나 아니면 NULL 을 반환한다.
// 만약 depth <= 0이면 ‘subtree’만 검색된다.
// 만약 depth == N이면 N 레벨과 그 아래만 검색된다.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);
62. 나쁜 이름에 주석을 다는 대신 이름을 고쳐라
// 반환되는 항목의 수나 전체 바이트 수와 같이
// Request가 정하는 대로 Reply에 일정한 한계를 적용한다.
Void CleanReply(Request request, Reply reply);
63. 나쁜 이름에 주석을 다는 대신 이름을 고쳐라
// ‘reply’가 count/byte/등과 같이 ‘request’가 정하는 한계조건을
// 만족시키도록 한다.
Void EnforceLimitsFromRequest(Request request, Reply reply);
64. 나쁜 이름에 주석을 다는 대신 이름을 고쳐라
// 해당 키를 위한 핸들을 놓아준다.
// 이 함수는 실제 레지스트리를 수정하지는 않는다.
Void DeleteRegistry(RegistryKey* key);
65. 나쁜 이름에 주석을 다는 대신 이름을 고쳐라
Void ReleaseRegistryHandle(RegistryKey* key);
67. 나쁜 이름에 주석을 다는 대신 이름을 고쳐라
나쁜 코드 + 좋은 주석 < 좋은 코드
68. 생각을 기록하라
// 놀랍게도, 이 데이터에서 이진트리는 해시테이블보다 40% 정도 빠르다.
// 해시를 계산하는 비용이 좌/우 비교를 능가한다.
// 이 주먹구구식 논리는 몇 가지 단어를 생략할 수 있다.
// 상관없다. 100% 해결은 쉽지 않다.
// 이 클래스는 점점 엉망이 되어가고 있다. 어쩌면 ‘ResourceNode’ 하위 클래스를
// 만들어서 정리해야 할지도 모르겠다.
69. 생각을 기록하라
좋은 주석은 단순히
‘자신의 생각을 기록하는 것‘
만으로도 탄생할 수 있다.
70. 코드에 있는 결함을 설명하라
// TODO: 더 빠른 알고리즘을 사용하라.
// TODO: JPEG말고 다른 이미지 포맷도 처리할 수 있어야 한다.
71. 코드에 있는 결함을 설명하라
표시 보통의 의미
TODO: 아직 하지 않은 일
FIXME: 오동작을 일으킨다고 알려진 코드
HACK: 아름답지 않은 해결책
XXX: 위험! 여기 큰 문제가 있다
73. 상수에 대한 설명
# 이 상수값이 2 * num_processors보다 크거나 같으면 된다.
NUM_THREADS = 8
74. 상수에 대한 설명
# 이 상수값이 2 * num_processors보다 크거나 같으면 된다.
NUM_THREADS = 8
이제 이 코드를 읽는 사람은 상수값을 어떻게 변경해야 하는지 알게 되었다.
75. 상수에 대한 설명
// 합리적인 한계를 설정하라 – 그렇게 많이 읽을 수 잇는 사람은 어차피 없다.
Const int MAX_RSS_SUBSCRIPTIONS = 1000;
// 사용자들은 0.72가 크기/해상도 대비 최선이라고 생각한다.
image_quality = 0.72;
추후 상수를 고쳐야 할때 왜 이 값이었을지 고민할 필요가 없다.
76. 나올 것 같은 질문 예측하기
Struct Recorder {
vector<float> data;
…
void Clear() {
vecter<float>().swap(data); // 뭐? 그냥 data.clear()를 호출하지 않는 이유가 뭐지?
}
}
77. 나올 것 같은 질문 예측하기
// 벡터가 메모리를 반납하도록 강제한다(“STL swap trick”을 보라)
vecter<float>().swap(data);
78. 사람들이 쉽게 빠질 것 같은 함정을 경고하라
void SendEmail(string to, string subject, string body);
만약 이 함수가 1초이상 걸릴 수 있는 함수라면?
79. 사람들이 쉽게 빠질 것 같은 함정을 경고하라
// 외부 서비스를 호출하여 이메일 서비스를 호출한다(1분 이후 타임아웃된다.)
void SendEmail(string to, string subject, string body);
80. 큰 그림에 대한 주석
새롭게 팀에 합류한 사람을 위해
전체 구조를 이해할 수 있는 주석이 필요하다.
81. 큰 그림에 대한 주석
파일 수준 주석의 예
// 파일 시스템에 편리한 인터페이스를 제공하는 헬퍼 함수들을 담고있다.
// 파일의 퍼미션과 다른 자세한 세부 사항을 처리한다.
82. 글 쓰는 두려움을 떨쳐내라
// 이런 제길, 이 리스트 안에 중복된 항목이 있으면 이건 복잡해지잖아.
1. 마음에 떠오르는 생각을 무조건 적어본다.
2. 주석을 읽고 무엇이 개선되어야 하는지 확인한다.
3. 개선한다.
86. 모호한 대명사는 피하라
// Insert the data into the cache, but check if it’s too big first.
// 데이터를 캐시에 넣어라. 하지만 그것이 너무 큰지 먼저 확인하라
데이터가 큰지 확인하라는 거야?
캐시가 큰지 확인하라는 거야?
87. 모호한 대명사는 피하라
// Insert the data into the cache, but check if the data too big first.
// 데이터를 캐시에 넣어라. 하지만 그것이 너무 큰지 먼저 확인하라
88. 모호한 대명사는 피하라
// If the data is small enough, insert it into the cache
// 데이터가 충분히 작으면, 이를 캐시에 넣어라
Simple!
89. 엉터리 문장을 다듬어라
# 이 URL을 전에 이미 방문했는지에 따라서 다른 우선순위를 부여한다.
=> # 전에 방문하지 않은 URL에 높은 우선순위를 부여하라.
90. 함수의 동작을 명확하게 설명하라
// 이 파일에 담긴 줄 수를 반환한다.
int CountLines(string filename) { … }
“”빈 파일은 줄수가 0인가 1인가?
“hello”는 줄 수가 0인가 1인가?
“hellon”은 줄 수가 1인가 2인가?
“hellon world”는 줄 수가 1인가 2인가?
“hellonr crueln worldr”은 줄 수가 2, 3, 4 중 어느 것인가?
91. 함수의 동작을 명확하게 설명하라
// 파일 안에 새 줄을 나타내는 바이트(‘n’)가 몇 개 있는지 센다.
int CountLines(string filename) { … }
개행문자가 없으면 0이겠네?
캐리지리턴(r)는 무시되는군.
92. 구체적인 용법을 설명해주는 입/출력 예를
사용하라
// 입력된 ‘src’의 ‘chars’라는 접두사와 접미사를 제거한다.
String Strip(String src, String chars) { … }
chars가 제거되어야 하는 정확한 부분 문자열일까? 아니면 특정한
순서가 정해지지 않은 문자 집합일까?
src의 끝에 chars가 여러 번 있으면 어떻게 될까?
94. 코드의 의도를 명시하라
// 리스트를 역순으로 반복한다.
for (list<Product>::reverse_iterator it = products.rbegin(); it != products.rend(); ++it)
DisplayPrice(it->price);
이미 다 아는 사실의 반복
95. 코드의 의도를 명시하라
// 각 가격을 높은 값에서 낮은 값 순으로 나타낸다.
for (list<Product>::reverse_iterator it = products.rbegin(); it != products.rend(); ++it)
DisplayPrice(it->price);
96. 이름을 가진 함수 파라미터 주석
Connect(10, false);
10은 뭐고 false는 뭐지?
97. 이름을 가진 함수 파라미터 주석
파이썬의 경우
def Connect(timeout, use_encryption) : …
Connect(timeout = 10, use_encryption = False);
98. 이름을 가진 함수 파라미터 주석
C++의 경우
void Connect(int timeout, bool use_encryption) { … }
Connect(/* timeout_ms = */ 10, /* use_encryption = */ false);
99. 정보 축약형 단어를 사용하라
// 이 클래스는 데이터베이스와 동일한 정보를 담는 멤버를 가지고
// 있는데, 이는 속도를 향상시키는 데 사용된다. 나중에 이 클래스가
// 읽히면, 멤버들이 어떤 값을 가졌는지 확인하고, 만약 값이 있으면
// 그 값이 반환된다. 값이 없으면 데이터베이스에서 값이 읽혀져서
// 나중에 이용될 수 있게 멤버에 저장된다.
100. 정보 축약형 단어를 사용하라
// 이 클래스는 데이터베이스에 대한 캐시 계층으로 기능한다.