Saturday 21 May 2016

Notes and excerpts from the book : Clean Code

Hi, thanks for viewing my blog. Here is my new blog -> https://sairaghavak.gitlab.io

Here is my notes and direct excerpts drawn from the Book chapter wise.

Chapter 1: Clean Code

There are some exceptionally well written definitions of clean code by distinguishable, profound and efficient programmers in this book. I would like to mix parts of each definition and create a hybrid one out of them.

Clean code is the code that is clear, straightforward, focussed, undistracting, highly expressive, without any duplication. Can be easily read and enhanced by a developer other than its original author.

  • The only valid measurement of code quality : WTFs/minute.
  • The ratio of time spent reading vs writing is well over 10:1. We are constantly reading old code as part of the effort to write new code.

Chapter 2: Meaningful names

  • One difference between a smart programmer and a professional programmer is that the professional understands that Clarity is King.
  • Say What you mean. Mean what you say.
  • Our goal as authors is to make our code as easy as possible to understand.
meaningful names do's and don'ts

Chapter 3: Functions

  • Should be small
    - The first rule of functions is that they should be small.
    - The second rile of functions is that they should be smaller than that.
    - Functions should hardly ever be 20 lines long.
  • Blocks and Indenting - Blocks within if statements, else statements, while statements and so on should be 1 line long. Probably that 1 line should be a function call. This helps the functions easier to read and understand.
  • Do one thing - Functions should do one thing. They should do it well. They should do it only.
  • Reading code from Top to Bottom : The stepdown rule - We want the code to be read like a top-down narrative.
  • Use Descriptive Names - A long descriptive name is btter than a short enigmatic name.
  • Function Arguments - Ideal number of aruguments for a function is 0. Next comes 1, followed by 2. 3 arguments should be avoided wherever possible. More than 3 requires a very special justification
  • Flag arguments - Flag arguments are ugly. Passing a boolean value into a function is a truly terrible practice.
  • Argument Objects - Always check and try to see if the arguments of a function can be fit into a separate class and pass this as argument instead.
  • Extract try/catch blocks - try and catch blocks should contain a single lines. A try should have a call to other function and catch should have a single line logger message.
  • Error handling is one thing
    - Functions should do one thing.
    - Error handling is one thing. Thus a function that handles error should do nothing else.
    - This means that if the keyword try exist in a function, it should be the very first word in the function and there should be nothing after the catch/finally blocks.
  • Don't Repeat Yourself(DRY)
    - Avoid duplication. OOP(Object Oriented Programming), AOP(Aspect Oriented Programming), COP(Composite Oriented Programming) are meant to avoid duplication.
  • Structured Programming
    - There should only be one return statement in a function, no break or continue statements in a loop and never ever any goto statements.
    - The code should be in a way to read from top down approach or The Stepdown Rule.

Chapter 4: Comments

  • Don't comment bad code ------ Rewrite it.
  • Inaccurate comments are far worse than, no comments at all. They delude and mislead.
  • When you find yourself in a position where you need to write comment, think it through and see whether there isn't some way to turn the tables and express yourself in code.
  • It is ridiculous to have a rule that says that every function must have a javadoc or every variable must have a comment. Comments like this just clutter up the code, propagate lies, and tend to general confusion and disorganization.

Chapter 5: Formatting

  • Code formatting is important. Code formatting is about communication, and communication is the professional developers first order of bussiness.
  • Source file should not be more than 500 lines(considering the statistics from various popular projects like JUnit, FitNesse, TestNG, JDepend, Apache Ant, Maven).
  • Good to have files less than 200 lines. Although this should not be the hard and fast rule, it should be considered very desirable.
  • Small files are relatively easier to read an understand than large files are.
  • Code should always be written like a newspaper article, where in the high level information comes first and low level details are presented at last.
  • Nearly all code is read left to right and top to bottom. Each line represents an expression or a clause, and each group of lines represents a complete thought. Those thoughts should be separated from each other with blank lines.
  • Author says there should not be no more than 120 characters in a horizontal line.
  • Without indentation programs would be virtually unreadable by humans.

Chapter 6: Objects and Data Structures

  • Objects hide their data behind abstractions and expose operations/functions that operate on that data.
  • The Law of Demeter says that a module should not know about innards of the objects it manipulates. In other words, talk to friends, not to strangers.
  • DTO's are very useful structures, especially when communicating with databases or passing messages from sockets, and so on. They often become the first in a series of translation pages that convert raw data in a database into objects in the application code.

Chapter 7: Error Handling

  • Error handling is important, but if it obscures logic, its wrong.
  • It is just one of those things that we all have to do when we program.
  • Each exception that you throw should provide enough context to determine the source and location of an error. Always create informative error messages and pass them along with your exceptions.
  • We can simplify our code considerably by wrapping the API that we are calling and making sure that it returns a common exception type. In fact, wrapping third party API's is a best practice. When you wrap a third-party API, you minimize your dependencies upon it.
  • Often a single exception class is fine for a particular area of code. The information sent with the exception can distinguish the errors.
  • Don't return null: There are some methods which return null and it is a huge headache for the caller method to check if the callee has returned null or not and do actions accordingly. Why return null? Try to avoid returning null as much as possible.
  • Don't pass null : Returning null from methods is bad, but passing null into methods is worse.

Chapter 8: Boundaries

  • We should avoid letting too much of our code know about the third-party particulars. Its better to depend on something you control than on something you don't control.
  • Use an Adapter pattern or wrap them whenever you are using a third-party APIs.
  • Code at boundaries needs clear separation and tests that define expectations.

Chapter 9: Unit Tests

  • 3 Laws of TDD(Test driven development)
    1. You may not write production code until you have written a failing unit test.
    2. You may not write any more of a unit test than is sufficient to fail; and compilation failures are failures.
    3. You may not write more production code than is sufficient to pass the currently failing unit test.
  • Build-Operate-Check
    - Each test should be split into 3 parts. First part builds up the test data, the second part operates on that test data, and third part checks that the operation yielded the expected results.
  • Clean tests should follow FIRST principle that is Fast, Indepedent, Repeatable, Self-Validating and Timely.

Chapter 10: Classes

  • Classes should begin with a list of variables. Public static constants if any should come first. Then private static variables, followed by private instance variables.
  • With functions we measured size by counting physical lines. With classes we use a different measure. We count responsibilities.
  • The name of a class should describe what responsibilities it fulfills. In fact, naming is probably the first way of helping determine class size. If cannot derive a concise name for a class, then its likely too large.
  • Classes should obey the Single Responsibility Principle(SRP) which states that a class or module should have one and only one, reason to change. Trying to identify responsibilities(reasons to change) often helps us recognize and create better abstractions in our code.
  • Classes should also obey the Open Closed Principle (OCP) which states that they should be open for extension but closed for modification.
  • Cohesion
    - Classes should have a small number of instance variables.
    - A class in which each variable is used by each method is maximally cohesive.
    - When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole.

Chapter 11: Systems

  • Whether you are designing systems or individual modules never forget to use the simplest thing that can possibly work.
  • Ensure that the system has Separation of Concerns.

Chapter 12: Emergence

  • Kent Beck's 4 rules of Simple Design helps us create well-design software.
    1. Run all the tests - A non testable system is useless and questionable, though it has a perfect design depicting how it works on paper.
    2. Contains no duplication - Duplication is the primary enemy of a well-designed system.
    3. Expresses the intent of the programmer - Make your code easy to understand for other programmers.
    4. Minimizes the number of classes and methods - A well-designed system is one which is refactored a lot.

Chapter 13: Concurrency - Skipped this chapter deliberately, will be adding notes here after reding this Java Concurrency in practice

Chapter 14: Successive Refinement

  • To write clean code, you must first write dirty code and then clean it.
  • Using test driven development, I am not allowed to make a change to the system that breaks the system. Every change I make must keep the system working as it worked before.
  • Much of the good software design is simply about partitioning - Creating appropriate places to put different kinds of code. This separation of concerns makes the code much simpler to understand and maintain.
  • Continuously keep your code as clean and simple as it can be. Never let the rot get started.

Chapter 15: JUnit Internals

Here the author refactors a code example and explains the flaws in each revision of code.

Chapter 16: Refactoring SerialDate

An example scenario in which author describes how the code has been refactored for class SerialDate.

  • First, Make it Work -> Make all the unit tests pass
  • Then Make it Right -> Make the code clean by refactoring.

Chapter 16: Smells and Heuristics

Here the author describes a lot of code smell categories which is a nice gist of bad code and good code, specifies how to identify them.

In conclusion, I would say that this book is language agnostic and must read for professional software developers.

Labels