🏆 Can follow intermediate guidelines for improving readability

Implementation → Code Quality → Readability →

Intermediate

Lay out the code so that it adheres to the logical structure. The code should read like a story. Just like we use section breaks, chapters and paragraphs to organize a story, use classes, methods, indentation and line spacing in your code to group related segments of the code. For example, you can use blank lines to group related statements together. Sometimes, the correctness of your code does not depend on the order in which you perform certain intermediary steps. Nevertheless, this order may affect the clarity of the story you are trying to tell. Choose the order that makes the story most readable.

Avoid things that would make the reader go ‘huh?’, such as,

  • unused parameters in the method signature
  • similar things look different
  • different things that look similar
  • multiple statements in the same line
  • data flow anomalies such as, pre-assigning values to variables and modifying it without any use of the pre-assigned value

As the old adage goes, "keep it simple, stupid” (KISS). Do not try to write ‘clever’ code. For example, do not dismiss the brute-force yet simple solution in favor of a complicated one because of some ‘supposed benefits’ such as 'better reusability' unless you have a strong justification.

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. --Brian W. Kernighan

Programs must be written for people to read, and only incidentally for machines to execute. --Abelson and Sussman

Optimizing code prematurely has several drawbacks:

  • We may not know which parts are the real performance bottlenecks. This is especially the case when the code undergoes transformations (e.g. compiling, minifying, transpiling, etc.) before it becomes an executable. Ideally, you should use a profiler tool to identify the actual bottlenecks of the code first, and optimize only those parts.
  • Optimizing can complicate the code, affecting correctness and understandability
  • Hand-optimized code can be harder for the compiler to optimize (the simpler the code, the easier for the compiler to optimize it). In many cases a compiler can do a better job of optimizing the runtime code if you don't get in the way by trying to hand-optimize the source code.

A popular saying in the industry is make it work, make it right, make it fast which means in most cases getting the code to perform correctly should take priority over optimizing it. If the code doesn't work correctly, it has no value on matter how fast/efficient it it.

Premature optimization is the root of all evil in programming. --Donald Knuth

Note that there are cases where optimizing takes priority over other things e.g. when writing code for resource-constrained environments. This guideline simply a caution that you should optimize only when it is really needed.

Avoid varying the level of abstraction within a code fragment. Note: The Productive Programmer (by Neal Ford) calls this the SLAP principle i.e. Single Level of Abstraction Per method.

Example:

readData();
salary = basic*rise+1000;
tax = (taxable?salary*0.07:0);
displayResult();

readData();
processData();
displayResult();

Design → Design Fundamentals → Abstraction →

What

Abstraction is a technique for dealing with complexity. It works by establishing a level of complexity (or an aspect) we are interested in, and suppressing the more complex details below that level (or irrelevant to that aspect).

Most programs are written to solve complex problems involving large amounts of intricate details. It is impossible to deal with all these details at the same time. The guiding principle of abstraction stipulates that we capture only details that are relevant to the current perspective or the task at hand.

Ignoring lower level data items and thinking in terms of bigger entities is called data abstraction.

📦 Within a certain software component, we might deal with a user data type, while ignoring the details contained in the user data item such as name, and date of birth. These details have been ‘abstracted away’ as they do not affect the task of that software component.

Control abstraction abstracts away details of the actual control flow to focus on tasks at a simplified level.

📦 print(“Hello”) is an abstraction of the actual output mechanism within the computer.

For example,

Abstraction can be applied repeatedly to obtain progressively higher levels of abstractions.

📦 An example of different levels of data abstraction: a File is a data item that is at a higher level than an array and an array is at a higher level than a bit.

📦 An example of different levels of control abstraction: execute(Game) is at a higher level than print(Char) which is at a higher than an Assembly language instruction MOV.

Putting all details in one place can create lengthy methods, but it is preferred over creating many small methods because it makes the code easier to understand.

False

Explanation: If you are using abstraction properly, you DON’T need to see all details to understand something. The whole point of using abstraction is to be able to understand things without knowing as little details as possible. This is why we recommend single level of abstraction per method and top-down coding.

What are the drawbacks of trying to optimize code too soon?

  • a. We may not know which parts are the real performance bottleneck
  • b. When we optimize code manually, it becomes harder for the compiler to optimize
  • c. Optimizing can complicate code
  • d. Optimizing can lead to more error-prone code

a, b, c, d

This is a common saying among programmers

b