Summary of "The art of readable code"

276 Views

January 15, 25

スライド概要

profile-image

しがない博士学生 / fish shell user

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

THE ART OF READABLE CODE by Dustin Boswell, Trevor Foucher Publisher: O’Reilly Media (2011) 2022/02/13

2.

1. Code Should Be Easy to Understand ✦ The main question What makes code “better”? ✦ The fundamental theorem of readability Code should be written to minimize the time it would take for someone else to understand it

3.

PART 1 Surface-Level Improvements

4.

2. Packing Information into Names ✦ More concrete/precise word GetPage → FetchPage Size → Height, NumNodes, MemoryBytes Stop → Kill, Pause ✦ Examples find → search, extract, locate retval → sum_squared start → launch, create, begin, open tmp → user_info, tmp_file make → create, build, generate (i, j, k) → (club_i, member_j, user_k)

5.

3. Names That Can’t Be Misconstrued (Mis-understood / Mis-interpreted) ✦ Rephrasing words that can be interpreted in two ways filter → select (to pick out), exclude (to get rid of) length → max_length, max_chars limit → max_*, min_* ✦ Appropriate pair of words inclusive range start last inclusive/exclusive range begin end

6.

4. Aesthetics ✦ Aligning columns public class PerformanceTester { // TcpConnectionSimulator(throughput, latency, jitter, packet_loss) // [Kbps] [ms] [ms] [percent] } public static final TcpConnectionSimulator wifi = new TcpConnectionSimulator(500, 80, 200, 1); public static final TcpConnectionSimulator t3_fiber = new TcpConnectionSimulator(45000, 10, 0, 0); public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(100, 400, 250, 5); terms units values

7.

5. Knowing what to comment ✦ Principle: good code > bad code + comment ✦ Commenting on constants - calculation NUM_THREADS = 8 # as long as it’s >= 2 * num_processors, that’s enough. - reasonable values // Impose a reasonable limit - no human can read that much anyway. const int MAX_RSS_SUBSCRIPTIONS = 1000; - highly tuned values image_quality = 0.72; // users thought 0.72 gave the best size/quality tradeoff

8.
[beta]
6. Making Comments Precise and Compact
✦ Comments with high information-to-space ratio
- to use mathematical expression
// The int is the CategoryType.
// The first float in the inner pair is the 'score',
// the second is the 'weight'.
typedef hash_map<int, pair<float, float> > ScoreMap;
// CategoryType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;

- to polish sentences
// Depending on whether we’ve already crawled this URL before, give it a different priority.

// Give higher priority to URLs we’ve never crawled before.

9.

PART 2 Simplifying Loops and Logic

10.

7. Making Control Flow Easy to Read ✦ Removing nesting by returning early if (user_result == SUCCESS) { if (permission_result != SUCCESS) { reply.WriteErrors("error reading permissions”); reply.Done(); return; } reply.WriteErrors(""); } else { reply.WriteErrors(user_result); } reply.Done(); if (user_result != SUCCESS) { reply.WriteErrors(user_result); reply.Done(); return; } if (permission_result != SUCCESS) { reply.WriteErrors(permission_result); reply.Done(); return; } reply.WriteErrors(""); reply.Done();

11.

8. Breaking Down Giant Expressions ✦ Using summary variables if (request.user.id == document.owner_id) { // user can edit this document... } if (request.user.id != document.owner_id) { // document is read-only... } boolean user_owns_document = (request.user.id == document.owner_id); if (user_owns_document) { // user can edit this document... } if (!user_owns_document) { // document is read-only... }

12.

9. Variables and Readability ✦ Eliminating variables ✦ Shrinking the scope of variables ✦ Using for loop instead of while loop var setFirstEmptyInput = function (new_value) { var found = false; var i = 1; var elem = document.getElementById('input' + i); while (elem !== null) { if (elem.value === '') { found = true; break; } i++; elem = document.getElementById('input' + i); } if (found) elem.value = new_value; return elem; }; var setFirstEmptyInput = function (new_value) { for (var i = 1; true; i++) { var elem = document.getElementById('input' + i); if (elem === null) return null; // Search Failed. No empty input found. if (elem.value === '') { elem.value = new_value; return elem; } } };

13.

PART 3 Reorganizing Your Code

14.

10. Extracting Unrelated Subproblems ✦ Separating the generic code from the project-specific code CHARS_TO_REMOVE = re.compile(r"['\.]+") CHARS_TO_DASH = re.compile(r”[^a-z0-9]+”) business = Business() business.name = request.POST["name"] url_path_name = business.name.lower() url_path_name = re.sub(r"['\.]", "", url_path_name) url_path_name = re.sub(r"[^a-z0-9]+", “-", url_path_name) url_path_name = url_path_name.strip(“-") business.url = "/biz/" + url_path_name business.date_created = datetime.datetime.utcnow() business.save_to_database() def make_url_friendly(text): text = text.lower() text = CHARS_TO_REMOVE.sub('', text) text = CHARS_TO_DASH.sub('-', text) return text.strip("-") business = Business() business.name = request.POST["name"] business.url = "/biz/" + make_url_friendly(business.name) business.date_created = datetime.datetime.utcnow() business.save_to_database()

15.
[beta]
11. One Task at a Time
✦ Organizing tasks
void UpdateCounts(HttpDownload hd) {
// Figure out the Exit State, if available.
if (!hd.has_event_log() || !hd.event_log().has_exit_state()) {
counts["Exit State"]["unknown"]++;
} else {
string state_str = ExitStateTypeName(hd.event_log().exit_state());
counts["Exit State"][state_str]++;
}

void UpdateCounts(HttpDownload hd) {
// Task: define default values for each of the values we want to extract
string exit_state
= “unknown";
string http_response = “unknown”;
string content_type = “unknown”;

// If there are no HTTP headers at all,
// use "unknown" for the remaining elements.
if (!hd.has_http_headers()) {
counts["Http Response"]["unknown"]++;
counts["Content-Type"]["unknown"]++;
return;
}

// Task: try to extract each value from HttpDownload, one by one
if (hd.has_event_log() && hd.event_log().has_exit_state()) {
exit_state = ExitStateTypeName(hd.event_log().exit_state());
}
if (hd.has_http_headers() && hd.http_headers().has_response_code()) {
http_response = StringPrintf(“%d”,, hd.http_headers().response_code());
}
if (hd.has_http_headers() && hd.http_headers().has_content_type()) {
content_type = ContentTypeMime(hd.http_headers().content_type());
}

HttpHeaders headers = hd.http_headers();
// Log the HTTP response, if known, otherwise log "unknown"
if (!headers.has_response_code()) {
counts["Http Response"]["unknown"]++;
} else {
string code = StringPrintf("%d", headers.response_code());
counts["Http Response"][code]++;
}
// Log the Content-Type if known, otherwise log "unknown"
if (!headers.has_content_type()) {
counts["Content-Type"]["unknown"]++;
} else {
string content_type = ContentTypeMime(headers.content_type());
counts["Content-Type"][content_type]++;
}
}

// Task: update counts[]
counts["Exit State"][exit_state]++;
counts["Http Response"][http_response]++;
counts[“Content-Type"][content_type]++;
}

16.
[beta]
12. Turning Thoughts into Code
✦ Describing logic clearly
$is_admin = is_admin_request();
if ($document) {

if (is_admin_request()) {
// authorized

if (!$is_admin && ($document[‘username'] != $_SESSION[‘username'])) {

} elseif ($document && ($document['username'] == $_SESSION['username'])){

return not_authorized();

// authorized

}
} else {

} else {

if (!$is_admin) {
return not_authorized();

return not_authorized();
}

}
}

In short, there are two ways you can be authorized:
1) you are an admin
2) you own the current document (if there is one)
Otherwise, you are not authorized.

✦ Explaining what a program is doing in plain English
- to your colleagues
- to rubber duck (cf. rubber duck debugging)

17.

13. Writing Less Code ✦ Overengineering - we tend to overestimate how many features are truly essential - we tend to underestimate how much effort it takes - rethink requirements to solve the easiest version of the problem ✦ Keeping your codebase small - create as much generic “utility” code as possible - remove unused code or useless features ✦ Being familiar with the libraries - get familiar with standard libraries by periodically reading through their entire APIs - use UNIX tools instead of coding

18.

Slide Recipes ✦ Theme: Silver chars / lead background (with lower contrast than W/B) ✦ Font: Cica (monospaced) ✦ Table GetPage → FetchPage Size → Stop → no border GetPage → FetchPage Height, NumNodes, MemoryBytes Size → Height, NumNodes, MemoryBytes Kill, Pause Stop → Kill, Pause ✦ Code Blocks NUM_THREADS = 8 overlay NUM_THREADS = 8