-
Notifications
You must be signed in to change notification settings - Fork 1
/
2024-10-23-book-notes:-a-philosophy-of-software-design-.html
725 lines (719 loc) · 44.3 KB
/
2024-10-23-book-notes:-a-philosophy-of-software-design-.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="description" content="This is a personal note for the book "A Philosophy of Software Design" (2018) by John Ousterhout">
<link rel="alternate"
type="application/rss+xml"
href="https://chenyo.me/rss.xml"
title="RSS feed for https://chenyo.me">
<title>Book notes: A Philosophy of Software Design</title>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'],['\\(','\\)']]}});
</script>
<meta name="author" content="chenyo">
<meta name="referrer" content="no-referrer">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="assets/style.css" type="text/css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/>
<link rel="favicon" type="image/x-icon" href="favicon.ico">
<script src="assets/search.js"></script></head>
<body>
<div id="preamble" class="status">
<header>
<h1><a href="https://chenyo.me">Chenyo's org-static-blog</a></h1>
<nav>
<a href="https://chenyo.me">Home</a>
<a href="archive.html">Archive</a>
<a href="tags.html">Tags</a>
<div id="search-container">
<input type="text" id="search-input" placeholder="Search anywhere...">
<i class="fas fa-search search-icon"></i>
</div>
</nav>
</header></div>
<div id="content">
<div class="post-date">23 Oct 2024</div><h1 class="post-title"><a href="https://chenyo.me/2024-10-23-book-notes:-a-philosophy-of-software-design-.html">Book notes: A Philosophy of Software Design</a></h1>
<nav id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#org0047a6c">1. Introduction</a></li>
<li><a href="#org5e4815d">2. Complexity</a>
<ul>
<li><a href="#org4171659">2.1. Definition</a></li>
<li><a href="#orgda5850f">2.2. Symptoms</a></li>
<li><a href="#orge7cd90b">2.3. Causes</a></li>
</ul>
</li>
<li><a href="#org4526627">3. Programming mindsets</a>
<ul>
<li><a href="#org8891fe0">3.1. Strategic programming</a></li>
</ul>
</li>
<li><a href="#org99fc0ab">4. Modular design</a>
<ul>
<li><a href="#org5185435">4.1. Interface</a></li>
<li><a href="#orgf90a710">4.2. Abstraction</a></li>
</ul>
</li>
<li><a href="#org8050b6d">5. Information hiding</a>
<ul>
<li><a href="#org00dfaa5">5.1. Information leakage</a></li>
<li><a href="#orgc638438">5.2. Temporal decomposition</a></li>
</ul>
</li>
<li><a href="#org5200a4b">6. General-Purpose modules</a>
<ul>
<li><a href="#org90c7d9d">6.1. Example: GUI text editor design</a></li>
</ul>
</li>
<li><a href="#org1a9b571">7. Layers of abstractions</a>
<ul>
<li><a href="#org93fdc1c">7.1. Pass-through methods</a></li>
<li><a href="#org229bdb5">7.2. Pass-through variables</a></li>
<li><a href="#orgf897c14">7.3. Acceptable interface duplication</a></li>
</ul>
</li>
<li><a href="#orgf863796">8. Combine or separate functionality</a></li>
<li><a href="#org615f6d2">9. Exception handling</a>
<ul>
<li><a href="#orga7f7974">9.1. Define errors out of existence</a></li>
<li><a href="#org7ab2088">9.2. Exception masking</a></li>
<li><a href="#org049389f">9.3. Exception aggregation</a></li>
</ul>
</li>
<li><a href="#org900e81a">10. Design it twice</a></li>
<li><a href="#orgfa4c08c">11. Write comments</a>
<ul>
<li><a href="#orgbd95ce2">11.1. Why write comments</a></li>
<li><a href="#org269a3c0">11.2. What are good comments</a>
<ul>
<li><a href="#org5f38c1b">11.2.1. Lower-level comments</a></li>
<li><a href="#org74c7a02">11.2.2. Higher-level comments</a></li>
<li><a href="#orgd53dc56">11.2.3. Interface comments</a></li>
<li><a href="#orga495a56">11.2.4. Implementation comments</a></li>
<li><a href="#orgea0e7b6">11.2.5. Cross-module comments</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#org1c5f413">12. Choose names</a>
<ul>
<li><a href="#org0e67aa5">12.1. Precision</a></li>
<li><a href="#org2d62f1e">12.2. Consistency</a></li>
</ul>
</li>
</ul>
</div>
</nav>
<p>
This is a personal note for the book “A Philosophy of Software Design” (2018, John Ousterhout).
John Ousterhout is the author of Tcl scripting language and the Raft protocol.
This book works together with <a href="https://web.stanford.edu/~ouster/cs190-winter24/info/">Stanford CS190</a>.
</p>
<div id="outline-container-org0047a6c" class="outline-2">
<h2 id="org0047a6c"><span class="section-number-2">1.</span> Introduction</h2>
<div class="outline-text-2" id="text-1">
<ul class="org-ul">
<li>The most fundamental problem in CS is <b><b>problem decomposition</b></b>: how to divide a complex problem into pieces that can be solved independently.</li>
<li>Complexity increases <b><b>inevitably</b></b> over the life of the program, but simpler designs allow to build larger systems before complexity becomes overwhelming.</li>
<li>Two general approaches:
<ul class="org-ul">
<li>Making code more obvious, e.g., eliminating special cases, using identifiers in a consistent fashion.</li>
<li>Encapsulate the complexity, i.e., divide a system into independent modules.</li>
</ul></li>
<li>Waterfall model: each phase of a project e.g., requirement, design, coding, testing, completes before the next phase starts, such that the entire system is designed once.
<ul class="org-ul">
<li>Does not work for software since the problem of the initial design do not become apparent until implementation is well underway; then the developers need to patch around the problems, resulting in an explosion of complexity.</li>
</ul></li>
<li>Agile/Incremental development: in each iteration the design focuses on a small subset of the overall functionality, so that problems can be fixed when the system is small and later features benefit from previous experience.
<ul class="org-ul">
<li>Developers should always think about design issues and complexity, and use complexity to guide the design.</li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-org5e4815d" class="outline-2">
<h2 id="org5e4815d"><span class="section-number-2">2.</span> Complexity</h2>
<div class="outline-text-2" id="text-2">
<ul class="org-ul">
<li>Complexity is <b><b>incremental</b></b>.</li>
<li>Developers must adopt a <b><b>zero tolerance</b></b> philosophy.</li>
</ul>
</div>
<div id="outline-container-org4171659" class="outline-3">
<h3 id="org4171659"><span class="section-number-3">2.1.</span> Definition</h3>
<div class="outline-text-3" id="text-2-1">
<ul class="org-ul">
<li>Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.</li>
<li>\(C = \sum_p(c_pt_p)\): The overall complexity is determined by the complexity of each part \(p\) weighted by the fraction of time developers working on that part.
<ul class="org-ul">
<li>Isolating complexity in a place where it will never be seen is as good as eliminating the complexity.</li>
</ul></li>
<li>Complexity is more apparent to readers than writers: if you write a piece of code that seems simple to you, but others think it is complex, then it is complex.</li>
</ul>
</div>
</div>
<div id="outline-container-orgda5850f" class="outline-3">
<h3 id="orgda5850f"><span class="section-number-3">2.2.</span> Symptoms</h3>
<div class="outline-text-3" id="text-2-2">
<ul class="org-ul">
<li>Change amplification: a seemingly simple change requires code modifications in many different places.</li>
<li>Cognitive load: developers have to spend more time learning the required information to complete a task, which leads to greater risk of bugs.
<ul class="org-ul">
<li>E.g., a function allocates memory and returns a pointer to the memory, but assumes the caller should free the memory.</li>
<li>Sometimes an approach that requires more lines of code is actually simpler as it reduces cognitive load.</li>
</ul></li>
<li>Unknown unknowns (the worst!): it is not obvious which pieces of code must be modified or which information must have to complete the task.</li>
<li>The goal of a good design is for a system to be <b><b>obvious</b></b>: a developer can make a quick guess about what to do and yet to be confident that the guess is correct.</li>
</ul>
</div>
</div>
<div id="outline-container-orge7cd90b" class="outline-3">
<h3 id="orge7cd90b"><span class="section-number-3">2.3.</span> Causes</h3>
<div class="outline-text-3" id="text-2-3">
<ul class="org-ul">
<li>Complexity is caused by two things: <b><b>dependencies</b></b> and <b><b>obscurity</b></b>.</li>
<li>A dependency exists when a code cannot be understood and modified in isolation.
<ul class="org-ul">
<li>E.g., in network protocols both senders and receivers must conform to the protocol, changing code for the sender almost always requires corresponding changes at the receiver.</li>
<li>Dependencies are fundamental and cannot be completely eliminated, the goal is to make the dependencies remain simple and obvious (e.g., variable renaming are detected by compilers).</li>
</ul></li>
<li>Obscurity occurs when important information is not obvious, e.g., a variable is too generic or the documentation is inadequate.
<ul class="org-ul">
<li>Obscurity is often associated with non-obvious dependencies.</li>
<li>Inconsistency is also a major contributor, e.g., the same variable name is used for two different purposes.</li>
<li>The need for extensive documentation is often a red flag that the design is complex.</li>
</ul></li>
<li>Dependencies lead to change amplification and cognitive load; obscurity creates unknown unknowns and cognitive load.</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org4526627" class="outline-2">
<h2 id="org4526627"><span class="section-number-2">3.</span> Programming mindsets</h2>
<div class="outline-text-2" id="text-3">
<ul class="org-ul">
<li>Tactical programming: the main focus is to get something working, e.g., a new feature or a bug fix.</li>
<li>Tactical programming is short-sighted, e.g., each task contributes a reasonable compromise to finish the current task quickly.</li>
<li>Once a code base turns to spaghetti, it is nearly impossible to fix.</li>
</ul>
</div>
<div id="outline-container-org8891fe0" class="outline-3">
<h3 id="org8891fe0"><span class="section-number-3">3.1.</span> Strategic programming</h3>
<div class="outline-text-3" id="text-3-1">
<ul class="org-ul">
<li>Requires an investment mindset to improve the system design rather than taking the fastest path to finish the current task.</li>
<li>Proactive investment: try a couple of alternative designs and pick the cleanest one; imagine a few ways in which the system might need to be changed in the future; write documentation.</li>
<li>Reactive investments: continuously make small improvements to the system design when a design problem becomes obvious rather than patching around it.</li>
<li>The ideal design tends to emerge in bits and pieces, thus the best approach is to make lots of <b><b>small</b></b> investments on a <b><b>continual</b></b> basis, e.g., 10-20% of total development time on investments.</li>
</ul>
<figure id="orgf8611f6">
<img src="https://csruiliu.github.io/blog/images/strategic-tactical.jpeg" alt="strategic-tactical.jpeg" align="center" width="400px">
<figcaption><span class="figure-number">Figure 1: </span>Strategic vs Tactical programming (<a href="https://csruiliu.github.io/blog/images/strategic-tactical.jpeg">Source</a>)</figcaption>
</figure>
</div>
</div>
</div>
<div id="outline-container-org99fc0ab" class="outline-2">
<h2 id="org99fc0ab"><span class="section-number-2">4.</span> Modular design</h2>
<div class="outline-text-2" id="text-4">
<ul class="org-ul">
<li>The goal of modular design is to minimize the dependencies between modules.</li>
<li>Each module consists of two parts: interface and implementation. The interface describes what the module does, the implementation specifies how it does.
<ul class="org-ul">
<li>The interface consists of everything that a developer working on a different module must know in order to use the given module.</li>
<li>The implementation consists of the code that carries out the promises made by the interface.</li>
</ul></li>
<li>The best modules are <b><b>deep</b></b>, i.e., those whose interfaces are much simpler than their implementations.
<ul class="org-ul">
<li>In such cases the modification in the implementation is less likely to affect other modules.</li>
</ul></li>
<li>Small modules tend to be shallow, because the benefit they provide is negated by the cost of learning and using their interfaces.</li>
<li>Classitis refers to a mistaken view that developers should minimize the amount of functionality in each new class.
<ul class="org-ul">
<li>It may result in classes that are individually simple, but increases the overall complexity.</li>
</ul></li>
</ul>
</div>
<div id="outline-container-org5185435" class="outline-3">
<h3 id="org5185435"><span class="section-number-3">4.1.</span> Interface</h3>
<div class="outline-text-3" id="text-4-1">
<ul class="org-ul">
<li>A module interface contains two information: formal and informal.</li>
<li>The formal part for a method is its signature; The formal interface for a class consists of the signatures for all public methods and variables.</li>
<li>The informal part includes its high-level behavior and usage constraints; they can only be described using comments and cannot be ensured by the programming languages.
<ul class="org-ul">
<li>Informal aspects are larger and more complex than the formal aspects for most interfaces.</li>
</ul></li>
<li>While providing choice is good, interfaces should be designed to make the <b><b>common case</b></b> as simple as possible (c.f. \(C = \sum_p(c_pt_p)\)).</li>
</ul>
</div>
</div>
<div id="outline-container-orgf90a710" class="outline-3">
<h3 id="orgf90a710"><span class="section-number-3">4.2.</span> Abstraction</h3>
<div class="outline-text-3" id="text-4-2">
<ul class="org-ul">
<li>An abstraction is a simplified view of an entity, which omits unimportant details.
<ul class="org-ul">
<li>The more unimportant details that are omitted from an abstraction, the better, otherwise the abstraction increases the cognitive load.</li>
<li>A detail can only be omitted if it is unimportant, otherwise obscurity occurs.</li>
</ul></li>
<li>In modular programming, each module provides an abstraction in form of its interface.</li>
<li>The key to designing abstractions is to understand what is important.
<ul class="org-ul">
<li>E.g., how to choose storage blocks for a file is unimportant to users, but the rules for flushing data to secondary storage is important for databases, hence it must be visible in the file system interface.</li>
<li>Garbage collectors in Go and Java do not have interface at all.</li>
</ul></li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org8050b6d" class="outline-2">
<h2 id="org8050b6d"><span class="section-number-2">5.</span> Information hiding</h2>
<div class="outline-text-2" id="text-5">
<ul class="org-ul">
<li>Information hiding is the most important technique for achieving deep modules.</li>
<li>Each module should encapsulate a few design information (e.g., data structures, low-level details) in the module implementation but not appear in its interface.</li>
<li>Information hiding simplifies the module interface and makes it easier to evolve the system as a design change related a hidden information only affects that module.</li>
<li>Making an item <code>private</code> is not the same as information hiding, as the information about the private items can still be exposed through public methods such as <code>getter</code> and <code>setter</code>.</li>
<li>If a particular information is only needed by a few of a class’s users, it can be <b><b>partially</b></b> hidden if it is accessed through separate methods, so that it is still invisible in the most common use cases.
<ul class="org-ul">
<li>E.g., modules should provide adequate <b><b>defaults</b></b> and only callers that want to override the default need to be aware of the parameter.</li>
</ul></li>
</ul>
</div>
<div id="outline-container-org00dfaa5" class="outline-3">
<h3 id="org00dfaa5"><span class="section-number-3">5.1.</span> Information leakage</h3>
<div class="outline-text-3" id="text-5-1">
<ul class="org-ul">
<li>The leakage occurs when a design decision is reflected in multiple modules. thus creating dependencies between the modules.
<ul class="org-ul">
<li>Interface information is by definition has been leaked.</li>
</ul></li>
<li>Information can be leaked even if it does not appear in the interface, i.e., back-door leakage.
<ul class="org-ul">
<li>E.g., two classes read and write the same file format, then if the format changes, both classes need to be modified; such leakage is more pernicious than interface leakage as it is not obvious.</li>
</ul></li>
<li>If affected classes are relatively small and closely tied to the leaked information, they may need to be <b><b>merged</b></b> into a single class.
<ul class="org-ul">
<li>The bigger class is deeper as the entire computation is easier to be abstracted in the interface compared to separate sub-computations.</li>
</ul></li>
<li>One may also pull the leaked information out of all affected classes and create a new class to encapsulate the information, i.e., replace back-door leakage with interface leakage.</li>
<li>One should avoid exposing internal data structures (e.g., return by reference) as such approach makes more work for callers, and make the module shallow.
<ul class="org-ul">
<li>E.g., instead of writing <code>getParams()</code> which returns a map of all parameters, one should have <code>getParameter(String name)</code> and <code>getIntParameter(String name)</code> to return a specific parameter and throw an exception if the name does not exist or cannot be converted.</li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-orgc638438" class="outline-3">
<h3 id="orgc638438"><span class="section-number-3">5.2.</span> Temporal decomposition</h3>
<div class="outline-text-3" id="text-5-2">
<ul class="org-ul">
<li>Temporal decomposition is a common cause of information leakage.</li>
<li>It decompose the system into operations corresponding to the execution order.
<ul class="org-ul">
<li>E.g., A file-reading application is broken into 3 classes: read, modify and write, then both reading and writing steps have knowledge about the file format.</li>
<li>The solution is to combine the core mechanisms for reading and writing into a single class.</li>
</ul></li>
<li>Orders should not be reflected in the module structure unless different stages use totally different information.</li>
<li>One should focus on the <b><b>knowledge</b></b> needed to perform each task, not the order in which tasks occur.</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org5200a4b" class="outline-2">
<h2 id="org5200a4b"><span class="section-number-2">6.</span> General-Purpose modules</h2>
<div class="outline-text-2" id="text-6">
<ul class="org-ul">
<li>General-purpose modules can be used to address a broad range of problems such that it may find unanticipated uses in the future (cf. investment mindset).</li>
<li>Special-purpose modules are specialized for today’s needs, and can be refactored to make it general-purpose when additional uses are required (cf. incremental software development).</li>
<li>The author recommends a “somewhat general-purpose” fashion: the functionality should reflect the current needs, but the interface should be general enough to support multiple uses.</li>
<li>The following questions can be asked to find the balance between general-purpose and special-purpose approach:
<ul class="org-ul">
<li>What is the simplest interface that will cover all current needs?</li>
<li>How many situations will a method be used?</li>
<li>Is the API easy to use for the current needs (not go too far)?</li>
</ul></li>
</ul>
</div>
<div id="outline-container-org90c7d9d" class="outline-3">
<h3 id="org90c7d9d"><span class="section-number-3">6.1.</span> Example: GUI text editor design</h3>
<div class="outline-text-3" id="text-6-1">
<ul class="org-ul">
<li>Specialized design: use individual method in the text class to support each high-level features, e.g., <code>backspace(Cursor cursor)</code> deletes the character before the cursor; <code>delete(Cursor cursor)</code> deletes the character after the cursor; <code>deleteSelection(Selection selection)</code> deletes the selected section.</li>
<li>The specialized design creates a high cognitive load for the UI developers: the implementation ends up with a large number of shallow methods so a UI developer had to learn all of them.
<ul class="org-ul">
<li>E.g., <code>backspace</code> provides a false abstraction as it does not hide the information about which character to delete.</li>
</ul></li>
<li>The specialized design also creates information leakage: abstractions related to the UI such as backspace key and selection, are reflected in the text class, increasing the cognitive load for the text class developers.</li>
<li>General-purpose design define API only in terms of <b><b>basic</b></b> text features without reflecting the higher-level operations.
<ul class="org-ul">
<li>Only three methods are needed to modify a text: <code>insert(Position position, String newText)</code>, <code>delete(Position start, Position end)</code> and <code>changePosition(Position position, int numChars)</code>.
<ul class="org-ul">
<li>The new API uses a more generic <code>Position</code> to replace a specific user interface <code>Cursor</code>.</li>
<li>The delete key can be implemented as <code>text.delete(cursor, text.ChangePosition(cursor, 1))</code>, the backspace key can be implemented as <code>text.delete(cursor, text.ChangePosition(cursor, -1))</code>.</li>
</ul></li>
</ul></li>
<li>The new design is more obvious, e.g., the UI developer knows which character to delete from the interface, and also has less code overall.</li>
<li>The general-purpose methods can also be used for new feature, e.g., search and replace text.</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org1a9b571" class="outline-2">
<h2 id="org1a9b571"><span class="section-number-2">7.</span> Layers of abstractions</h2>
<div class="outline-text-2" id="text-7">
<ul class="org-ul">
<li>Software systems are composed into layers, where higher layers use the facilities provided by lower layers; each layer provides an abstraction different from the layers above or below it.
<ul class="org-ul">
<li>E.g., a file in the uppermost layer is an array of bytes and is a memory cache of fixed-size disk blocks in the next lower layer.</li>
</ul></li>
<li>A system contains adjacent layers with similar abstractions is a red flag of class decomposition problem.</li>
<li>The internal representations should be different from the abstractions that appear in the interface; if the interface and the implementation have similar abstractions, the class is shallow.</li>
<li>It is more important for a module to have a simple interface than a simple implementation to benefit more user.
<ul class="org-ul">
<li>Simple implementation example: throw an exception when don’t know how to handle the condition; define configuration parameters (developers should compute reasonable defaults automatically for configuration parameters).</li>
<li>Simple interface example: make a text editor GUI character-oriented rather on line-oriented so users can insert and delete arbitrary ranges of text.</li>
</ul></li>
</ul>
</div>
<div id="outline-container-org93fdc1c" class="outline-3">
<h3 id="org93fdc1c"><span class="section-number-3">7.1.</span> Pass-through methods</h3>
<div class="outline-text-3" id="text-7-1">
<ul class="org-ul">
<li>A pass-through method is a method that does little except invoke another method, whose signature is similar or identical to the callee function.</li>
<li>Pass-through methods usually indicates there is not a clean <b><b>division of responsibility</b></b> between classes.</li>
<li>Pass-through methods also create dependencies between classes.</li>
<li>The solution is to refactor the classes, e.g., expose the lower level class directly to the higher level (b), redistribute the functionality (c) or merge them (d):</li>
</ul>
<figure id="org7282617">
<img src="./static/pass-through-methods.png" alt="pass-through-methods.png" align="center" width="400px">
<figcaption><span class="figure-number">Figure 2: </span>Refactor pass-through methods</figcaption>
</figure>
</div>
</div>
<div id="outline-container-org229bdb5" class="outline-3">
<h3 id="org229bdb5"><span class="section-number-3">7.2.</span> Pass-through variables</h3>
<div class="outline-text-3" id="text-7-2">
<ul class="org-ul">
<li>A pass-through variable is a variable that is passed down through a long chain of methods.</li>
<li>Pass-through variables add complexity as all intermediate methods must be aware of the existence and need to be modified when a new variable is used.</li>
<li>The author’s solution is to introduce a <b><b>context</b></b> object which stores all application’s global states, e.g., configuration options and timeout value, and there is one context object per system instance.</li>
<li>To avoid passing through the context variable, a reference to the context can be saved in other objects.
<ul class="org-ul">
<li>When a new object is created, the context reference is passed to the constructor.</li>
</ul></li>
<li>Contexts should be immutable to avoid thread-safety issues and may create non-obvious dependencies.</li>
</ul>
</div>
</div>
<div id="outline-container-orgf897c14" class="outline-3">
<h3 id="orgf897c14"><span class="section-number-3">7.3.</span> Acceptable interface duplication</h3>
<div class="outline-text-3" id="text-7-3">
<ul class="org-ul">
<li>Dispatcher: a method that uses its arguments to select a specific method to invoke and passes most of its arguments.
<ul class="org-ul">
<li>E.g., when a web server receives an HTTP request, it invokes a dispatcher to examine the URL and selects a specific method to handle the request.</li>
</ul></li>
<li>Polymorphous methods, e.g., <code>len(string)</code> and <code>len(array)</code> reduces cognitive load; they are usally in the same layer and do not invoke each other.</li>
<li>Decorator: a wrapper that takes an existing object and extends its functionality.</li>
<li>Decorators are often shallow and contain pass-through methods, one should consider following alternatives before using them:
<ul class="org-ul">
<li>Add the new functionality directly to the class if it is relatively general-purpose; or merge it with the specific use case if it is specialized.</li>
<li>Merge the new functionality with an existing decorator to make the existing decorator deeper.</li>
<li>Implement it as a stand-alone class independent of the base class, e.g., <code>Window</code> and <code>ScrollableWindow</code>.</li>
</ul></li>
</ul>
</div>
</div>
</div>
<div id="outline-container-orgf863796" class="outline-2">
<h2 id="orgf863796"><span class="section-number-2">8.</span> Combine or separate functionality</h2>
<div class="outline-text-2" id="text-8">
<ul class="org-ul">
<li>The goal is to reduce the system complexity as a <b><b>whole</b></b> and improve its modularity.
<ul class="org-ul">
<li>Subdividing components create additional complexity, e.g. additional code.</li>
<li>Developers should separate one general-purpose code from special-purpose code, each special-purpose code should go in a different module, e.g., pull the special-purpose code into higher layers.
<ul class="org-ul">
<li>A general-purpose mechanism provides <b><b>interfaces</b></b> for special-purpose code to override.</li>
<li>Each special-purpose code implements particular logic which is unaware by other code, including the general-purpose mechanism.</li>
</ul></li>
<li>Combining codes is most beneficial if they are closely related:
<ul class="org-ul">
<li>They share information, e.g., HTTP request reader and parser.</li>
<li>They share repeated pattern, e.g., may <code>goto</code> same cleanup code.</li>
<li>The combination simplifies the interface, e.g., each code implement a part of the solution.</li>
<li>They are used together bi-directionally, e.g., a specific error message which is only invoked by one method.</li>
<li>They overlap conceptually in that there is single higher-level category including both code.</li>
</ul></li>
<li>Each method should do one thing and do it <b><b>completely</b></b>.
<ul class="org-ul">
<li>The length itself is rarely a good reason for splitting up methods.</li>
<li>If a method is subdivided, users should be able to understand the child method independently, which typically means the child method is relatively general-purpose, otherwise conjoined methods are created.</li>
</ul></li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-org615f6d2" class="outline-2">
<h2 id="org615f6d2"><span class="section-number-2">9.</span> Exception handling</h2>
<div class="outline-text-2" id="text-9">
<ul class="org-ul">
<li>Exceptions refer to any uncommon condition that alters the normal control flow.
<ul class="org-ul">
<li>E.g., bad arguments, an I/O operation fails, server timeout, packet loss, unprepared condition.</li>
</ul></li>
<li>It’s difficult to ensure that exception handling code really works, especially in distributed data-intensive systems.</li>
<li>Classes with lots of exceptions have complex interfaces and are shallow.</li>
<li>The best way is to reduce the number of places where exceptions have to be handled.</li>
<li>The author proposes 4 techniques: define errors out of existence; exception handling; exception aggregation; crash.
<ul class="org-ul">
<li>For errors that are not worth trying to handle, or occur infrequently, abortion is the simplest thing to do; e.g., there is nothing the application can do when an out-of-memory exception occurs.</li>
</ul></li>
<li>Same as exceptions, special cases can result in code riddled with <code>if</code> statements, they should be eliminated by designing the normal case in a way that automatically handles the special cases.</li>
</ul>
</div>
<div id="outline-container-orga7f7974" class="outline-3">
<h3 id="orga7f7974"><span class="section-number-3">9.1.</span> Define errors out of existence</h3>
<div class="outline-text-3" id="text-9-1">
<ul class="org-ul">
<li>Example 1: instead of throwing an exception when a key does not exist in <code>remove</code>, simply return to ensure the key no longer exists.</li>
<li>Example 2: instead of throwing an exception when trying to delete a file that is still open in other processes, mark the file for deletion to deny any processes open the old file later, and delete the file after all processed have closed the file.</li>
<li>Example 3: instead of throwing an <code>IndexOutOfBoundsExeception</code> when <code>substring(s, begin, end)</code> takes out-of-range arguments, return the overlap substring.</li>
</ul>
</div>
</div>
<div id="outline-container-org7ab2088" class="outline-3">
<h3 id="org7ab2088"><span class="section-number-3">9.2.</span> Exception masking</h3>
<div class="outline-text-3" id="text-9-2">
<ul class="org-ul">
<li>An exceptional condition is detected and handled at a low level in the system so that higher levels need not be aware of the condition.</li>
<li>E.g., when a TCP packet is lost, the packet is resent within the implementation and clients are unaware of the dropped packets (they notice the hanging and can abort manually).</li>
<li>Exception masking results in deeper classes and pulls complexity downward.</li>
</ul>
</div>
</div>
<div id="outline-container-org049389f" class="outline-3">
<h3 id="org049389f"><span class="section-number-3">9.3.</span> Exception aggregation</h3>
<div class="outline-text-3" id="text-9-3">
<ul class="org-ul">
<li>Exception aggregation handles many special-purpose exceptions with a single general-purpose handler.</li>
<li>Example 1: instead of catching the exception for each individual missing parameter, let the single top-level exception handler aggregate the error message with a single <b><b>top-level</b></b> try-catch block.
<ul class="org-ul">
<li>The top-level handler encapsulates knowledge about how to generate error responses, but knows nothing about specific errors.</li>
<li>Each service knows how to generate errors, but does not know how to send the response.</li>
</ul></li>
<li>Example 2: promote rare exceptions (e.g., corrupted files) to more common exceptions (e.g., server crashes) so that the same handler can be used.</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-org900e81a" class="outline-2">
<h2 id="org900e81a"><span class="section-number-2">10.</span> Design it twice</h2>
<div class="outline-text-2" id="text-10">
<ul class="org-ul">
<li>Rather than picking the first idea that comes to mind, try to pick several approaches that are radically <b><b>different</b></b> from each other.
<ul class="org-ul">
<li>No need to pin down every feature of each alternative.</li>
</ul></li>
<li>Even if you are certain that there is only one reasonable approach, consider a second design anyway, no matter how bad it will be.
<ul class="org-ul">
<li>It will be instructive to think about the weaknesses of the second design and contrast with other designs.</li>
<li>It’s easier to identify the best approach if one can compare a few alternatives.</li>
</ul></li>
<li>Make a list of the pros and cons of each rough design, e.g.,
<ul class="org-ul">
<li>Does one design have a simpler/ more general-purpose interface than another?</li>
<li>Does one interface enable a more efficient implementation?</li>
</ul></li>
<li>The design-it-twice principle can be applied at many levels in a system, e.g., decompose into modules, pick an interface, design an implementation (simplicity and performance).</li>
<li>No-one is good enough to get it right with their first try in large software system design.</li>
<li>The process of devising and comparing multiple approaches teach one about the factors that make designs better or worse.</li>
</ul>
</div>
</div>
<div id="outline-container-orgfa4c08c" class="outline-2">
<h2 id="orgfa4c08c"><span class="section-number-2">11.</span> Write comments</h2>
<div class="outline-text-2" id="text-11">
</div>
<div id="outline-container-orgbd95ce2" class="outline-3">
<h3 id="orgbd95ce2"><span class="section-number-3">11.1.</span> Why write comments</h3>
<div class="outline-text-3" id="text-11-1">
<ul class="org-ul">
<li>The correct process of writing comments will improve the system design.</li>
<li>A significant amount of design information that was in the mind of the designer cannot be represented in code, e.g., the <b><b>high-level</b></b> description of a method, the motivation for a particular design.</li>
<li>Comments are fundamental to abstractions: if users must read the code to use it, then there is no abstraction.
<ul class="org-ul">
<li>If there is no comment, the only abstraction of a method is its declaration which misses too much essential information to provide a useful abstraction by itself; e.g., whether <code>end</code> is inclusive.</li>
</ul></li>
<li>Good comments reduce cognitive load and unknown unknowns.</li>
</ul>
</div>
</div>
<div id="outline-container-org269a3c0" class="outline-3">
<h3 id="org269a3c0"><span class="section-number-3">11.2.</span> What are good comments</h3>
<div class="outline-text-3" id="text-11-2">
<ul class="org-ul">
<li>Follow the comment conventions, e.g., Doxygen for C++, godoc for Go.</li>
<li>Comments categories:
<ul class="org-ul">
<li>Interface: a comment block that precedes the declaration; describes the interface, e.g., overall behavior or abstraction, arguments, return values, side effects or exceptions and any requirements the caller must satisfy.</li>
<li>Data structure member: a comment next to the declaration of a field in a data structure, e.g., a variable.</li>
<li>Implementation comment: a comment inside the code to describe how the code work internally.</li>
<li>Cross-module comment: a comment describing dependencies that cross module boundaries.</li>
</ul></li>
<li>The interface and the data structure member comments are the most important and should be always present.</li>
<li>Don’t repeat the code: if someone who has never seen the code can also write the comment by just looking at the code next to the comment, then the comment has no value.</li>
<li>Don’t use the same words in the comment that appear in the name of the entity being described, pick words that provide additional information.</li>
<li>Comments should augment the code by providing information at a different level of detail.
<ul class="org-ul">
<li>Lower-level: add precision by clarifying the exact meaning of the code.</li>
<li>Higher-level: offer intuition behind the code.</li>
</ul></li>
</ul>
</div>
<div id="outline-container-org5f38c1b" class="outline-4">
<h4 id="org5f38c1b"><span class="section-number-4">11.2.1.</span> Lower-level comments</h4>
<div class="outline-text-4" id="text-11-2-1">
<ul class="org-ul">
<li>Precision is most useful when commenting variable declarations.</li>
<li>Missing details include: variable units; boundary conditions (inclusive/exclusive); whether a null value is permitted and what does it imply; who is responsible for a resource release; variable invariants that always true.
<ul class="org-ul">
<li>Avoid vague comments, e.g., “current”, not explicitly state the keys and values in a map.</li>
</ul></li>
<li>Comments on a variable focuses on what the variable <b><b>represents</b></b>, not how it will be modified.
<ul class="org-ul">
<li>E.g., instead of documenting when a boolean variable is toggled to true/false, document what true/false mean.</li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-org74c7a02" class="outline-4">
<h4 id="org74c7a02"><span class="section-number-4">11.2.2.</span> Higher-level comments</h4>
<div class="outline-text-4" id="text-11-2-2">
<ul class="org-ul">
<li>Help the reader understand the overall intent and code structure, usually inside the code.</li>
<li>More difficult to write than lower-level comments as one must think about the code in a different way.
<ul class="org-ul">
<li>Comments can include why we need this code; what the code does on a high-level.</li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-orgd53dc56" class="outline-4">
<h4 id="orgd53dc56"><span class="section-number-4">11.2.3.</span> Interface comments</h4>
<div class="outline-text-4" id="text-11-2-3">
<ul class="org-ul">
<li>Separate interface comments from implementation comments: interface comments provide information for someone to use rather than maintain the entity.</li>
<li>A class interface comment documents the overall capability and limitation of a class and what each instance represents.</li>
<li>A method interface comment include both higher-level and lower-level information.
<ul class="org-ul">
<li>Starts with one or two sentences describing the method behavior and performance (e.g., whether concurrently).</li>
<li>Must be very precise about each argument and the return value, and must mention any constraint and dependencies on the arguments.</li>
<li>Must document any side effects that affect the system future behavior, e.g., modify a system data structure which can be read by other methods.</li>
<li>Must document any preconditions that must be satisfied before a method is invoked.</li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-orga495a56" class="outline-4">
<h4 id="orga495a56"><span class="section-number-4">11.2.4.</span> Implementation comments</h4>
<div class="outline-text-4" id="text-11-2-4">
<ul class="org-ul">
<li>The main goal is to help readers understand <b><b>what</b></b> the code is doing and <b><b>why</b></b> the code is needed (e.g., refer to a bug report), not how.</li>
<li>For long methods, add a comment before each of the major blocks to provide a abstract description of what the block does.</li>
<li>For complex loops, add a comment before the loop to describe what happens in each iteration at an intuitive level.</li>
<li>Document the local variables if they are used over a large span of code.</li>
</ul>
</div>
</div>
<div id="outline-container-orgea0e7b6" class="outline-4">
<h4 id="orgea0e7b6"><span class="section-number-4">11.2.5.</span> Cross-module comments</h4>
<div class="outline-text-4" id="text-11-2-5">
<ul class="org-ul">
<li>Real systems involve design decisions that affect multiple classes.</li>
<li>The biggest challenge of documenting cross-module decisions is to find a place.
<ul class="org-ul">
<li>E.g., when a new state is introduced, multiple updates are required to handle the state; an obvious place to document all required updates is inside the state enum where a new state will be added.</li>
</ul></li>
<li>When there is no obvious place, e.g., all updates depend on each other, the author recommends documenting them in a central design notes.
<ul class="org-ul">
<li>The file is divided up into clearly labeled section, one for each major topic.</li>
<li>Then in any code that relates to a topic, add a short comment “see ”X“ in designNotes”.</li>
<li>The disadvantage of this approach is to keep is up-to-date as the system envolves.</li>
</ul></li>
</ul>
</div>
</div>
</div>
</div>
<div id="outline-container-org1c5f413" class="outline-2">
<h2 id="org1c5f413"><span class="section-number-2">12.</span> Choose names</h2>
<div class="outline-text-2" id="text-12">
<ul class="org-ul">
<li>A good name conveys information about what the underlying entity is and is <b><b>not</b></b>.
<ul class="org-ul">
<li>Ask yourself: if someone sees this name in isolation without seeing its declaration or documentation, how closely can they guess?</li>
</ul></li>
<li>A good name has two properties: precision and consistency.</li>
<li>The greater the distance between a name’s declaration and its last usage, the longer the name should be.</li>
</ul>
</div>
<div id="outline-container-org0e67aa5" class="outline-3">
<h3 id="org0e67aa5"><span class="section-number-3">12.1.</span> Precision</h3>
<div class="outline-text-3" id="text-12-1">
<ul class="org-ul">
<li>Vague name examples: “count”, “status”, “x”, “result” in a no return method.</li>
<li>It’s fine to use generic “i/j” in a loop as long as the loop does not span large.</li>
<li>A name may also become too specific, e.g., <code>delete(Range Selection)</code> suggests the argument must be a selection, but it can also be passed with unselected range.</li>
<li>If you find it difficult to come up with a name that is precise, intuitive and not too long, then it is a red flag that the variable may not have a clear design.
<ul class="org-ul">
<li>Consider alternative factorings, e.g., separate the representation into multiple variables.</li>
</ul></li>
</ul>
</div>
</div>
<div id="outline-container-org2d62f1e" class="outline-3">
<h3 id="org2d62f1e"><span class="section-number-3">12.2.</span> Consistency</h3>
<div class="outline-text-3" id="text-12-2">
<ul class="org-ul">
<li>Always use the common name for and <b><b>only</b></b> for the given purpose, e.g., <code>ch</code> for a channel.</li>
<li>Make sure the purpose is narrow enough that all variables with the same name have the same behavior.
<ul class="org-ul">
<li>E.g., a <code>block</code> purpose is not narrow as it can have different behaviors for physical and logical blocks.</li>
</ul></li>
<li>When you need multiple variables that refer to the same general thing, use the common name for each variable and add a distinguishing <b><b>prefix</b></b>, e.g., <code>srcBlock</code> and <code>dstBlock</code>.</li>
<li>Always use <code>i</code> in outmost loops and <code>j</code> for nested loops.</li>
</ul>
</div>
</div>
</div>
<div class="taglist"><a href="https://chenyo.me/tags.html">Tags</a>: <a href="https://chenyo.me/tag-book.html">book</a> <a href="https://chenyo.me/tag-software.html">software</a> <a href="https://chenyo.me/tag-design.html">design</a> </div></div>
<div id="postamble" class="status"><div id="search-results"></div>
<footer>
<div class="footer-content">
<div class="footer-left">
<p>© 2024 chenyo. Some rights reserved.</p>
<a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/">
<img alt="Creative Commons License" style="border-width: 0"
src="https://i.creativecommons.org/l/by-nc/4.0/88x31.png"/>
</a>
</div>
<div class="social-links">
<a href="https://t.me/chenyo17" target="_blank" rel="noopener noreferrer">
<i class="fab fa-telegram"></i>
</a>
<a href="https://github.com/chenyo-17" target="_blank" rel="noopener noreferrer">
<i class="fab fa-github"></i>
</a>
</div>
</footer></div>
</body>
</html>