Skip to content

[Learning Purpos] Clean Code by Robert C. Martin Summary

Notifications You must be signed in to change notification settings

tamagossi/clean-code-summary

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 

Repository files navigation

Greetings 👋:👋:👋:,

This is the summary of 📖  Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin 📖. I am trying to write this summary to strengthen my understanding of this book and try to implement Feynman Technique.

I hope I can make the sentence as simple as easy to understand for non-native English speakers (like me), so feel free to contribute, comment, and pull requests if there are any grammatical mistake 😁

⚠️  If you think that this repo should not be public caused by copyright infringement. Feel free to DM me on any platform I have. ⚠️

⚠️  This summary is also based on my understanding, it will be has a lot of flaws caused by my lack of experiences, and english language ability. So then, improvements are very welcome ⚠️


Clean Code Summary

Table of Contents


Chapter 1 - Clean Code

Chapter 1 - Clean Code

Creating a clean code is important, it might help you to add more features in the future easily. Have you ever been slowed down by someone else's messy code? That's why writing a good code is important. So, what is clean code?

Clean Code ...

  • is elegant and efficient
  • is simple and direct
  • has meaningful names and is self-documenting
  • is pleasing to read, and reads like well-written prose
  • does one thing well
  • does not tempt the developer to write bad code - on the flip side, when others change bad code, they tend to make it worse
  • never hides the designer's intent but is rather full of crisp abstractions and straightforward lines of control
  • is when each method you read turns out to be pretty much what you expected (Least Astonishment Principle)
  • can be read, and enhanced by a developer other than its original author
  • has unit and acceptance tests
  • is minimal (KISS and YAGNI)
  • does not repeat itself (DRY Principle)

Chapter 2 - Naming

Chapter 2 - Naming

Naming is easy yet hard to choose when you coding something, it might indicates that you are writing a good code or a bad code. There are several indicator to follow when naming a variable, function, or classes:

  1. Use Intention-Revealing Name

    ❌  int d;
    ✅  int daySinceCreated;
    ✅  int dayLeft;
    
    ❌  function calc(int num1, int num2) { ... }
    ✅  function multiply(int num1, int num2) { ... }
    
  2. Make Meaningful, Descriptive, and Non-ambigous Name

    ❌  string date1, date2;
    ✅  string startDate, endDate;
    
    ❌  float pft, profit
    ✅  float profitBeforeTax, profitAfterTax
    
    ❌  let productData, productInfo
    ✅  let productData, productDescription
    
  3. Use Pronounceable Name (Don't use abbreviation/acronyms)

    ❌  let rltdJobPst;
    ✅  let relatedJobPost;
    
  4. Use Nouns For Classes, Variables and Use Verb For Function

    ❌  let setCurrentJobPost;
    ✅  let currentJobPost;
    
    ❌  function jobpost() { ... }
    ✅  function getJobPost() { ... }
    
  5. Pick a Single Word per Concept and Use It Widely Across The Entire App

    ❌  function findJobPostById { ... }
    ❌  function fetchJobPostDetail { ... }
    ❌  function getJobPosts { ... }
    ❌  function findCurrentJobPost { ... }
    
    ✅  function getJobPostById { ... }
    ✅  function getJobPostDetail { ... }
    ✅  function getJobPosts { ... }
    ✅  function getCurrentJobPost { ... }
    
  6. Avoid Unnecessary Word

    ❌  JobPost[] jobPostArray
    ❌  JobPost[] jobPostList
    ❌  JobPost[] sevenJobPostList
    
    // We already know the variable will store list/array. So do this instead
    ✅  JobPost[] jobPosts 
    
  7. Avoid Meaningless Prefix/Suffix

    ❌  const projectInfo
    ❌  const jobPostData
    
    ✅  const project
    ✅  const jobPost 
    

And, last but not least

  1. Follow common naming convention

    If you are coding with Javascript use camelCase convention. If you are coding with Python use snack_case. If your are write html use kebab-case


Chapter 3 - Function

Chapter 3 - Function

These are some several rules, to make your function cleaner:

  1. A function should be small and short

    Make the function not so big so the it fits into your screen, so you can read all the function does without scrolling vertically or horizontally. This approach will make you grasp the purpose of the function in the first place from the first line into the last line.

    It should be readable from top to bottom as a paraghraph

➖➖➖

  1. A function should do just one thing

    Function should only do one thing, They should do it well, and they should do it only


    Function should be only do the verb that the function name suggest. If you can still extract a function from the function, so it is not small enoung and do more than one thing.

    ❌ function order(int id, boolean clearCart, boolean flushOrderSession) {...}
    
    ✅ function order(int id) {...}
    ✅ function clearCart(int id) {...}
    ✅ function flushOrderSession(int id) {...}
    

➖➖➖

  1. Use one level of abstraction

    Make sure that your function has only one level of abstraction. From my understanding, your function shouldn't has a nested condition since reading a nested is will confused you. If you have a nested condition within your function, you better break the function into smaller part;

    // ❌ Avoid a function which has nesterd condition
    createUser(user) {
        let shouldDoAMilitaryWork;
        let isSmoking;
        
            if(user.gender === GENDER.MAN) {
                if(user.age > 17 && user.age < 40) {
                    shouldDoAMilitaryWork = true;
                } else {
                    shouldDoAMilitaryWork = false;
                }
    
                if(user.isSmoking) {
                    isSmoking = false;
                } else {
                    if(user.isHealty) {
                        ...
                    } else {
                        ...
                    }
                }
            } else {
                ...
            }
    }
    
    // ✅ Do like this instead
    
    createUser(user) {
        const shouldDoAMilitaryWork = isShouldDoAMilitaryWork(user);
        const isSmoking = isUserSmoking(user)
    }
    
    isShouldDoAMilitaryWork(user) { ... }
    isUserSmoking(user) { ... }
    
    

➖➖➖

  1. Avoid switch statement

    //TODO: Need to read once more to fully understand this one Use polymorphism and bury the switch statement in an abstract factory.

➖➖➖

  1. Use descriptive name

    The name of the function should be clear, it suggest one thing. And from the name of the function the reader should have a clue what does the function is doing.

    Usually the function is in verb form. Since the function is will doing something, but sometime the function name also could be in question format. It is okay to make a long function name, as long as the is no shorter way to describe what does the function is doing

    ❌ function onHandleChange() {...} // Not describing what does the function do
    
    ✅ function setUserName(String name) {...}
    ✅ function isUserActive() {...}
    

➖➖➖

  1. Function Argument

    Some point you should have to consider when creating a function:

    • Better to use no argument
    • You can add one argument or two. But better to avoid three or more argument
    • If you really need to pass three or more argument, consider to pass a map/object
    • Avoid flag argument. Avoid a flag which contains boolean value

    ❌ function saveUser(name, address, age, gender) {...}
    ✅ function saveUser(user) {...}
    
    ❌ function saveUser(isFromEdit) {...}
    ✅ better to make seperate scenario that will invoke same function instead of avoid flag 
    

➖➖➖

  1. Have No Side Effect

    Your function should has no side effect, it should only do what the name suggest. Don't do anything else

    function isUserValid(username, password) {
        const isValid = // some operation to check user within databse
    
        if (isValid) {
            createSession(x); // ❌ this cause a side effect
        }
        return isValid;
    }
    

➖➖➖

  1. Output Arguments

    Avoid setting an output as argument to a function, it ambigous and hard to add changes in the future. Do something like this instead:

    ❌ activateJob(job) { ... }
    ✅ job.activate() { ... }
    

➖➖➖

  1. Command Query Separation

    Your function should do a command or a query. But not both

    ❌ boolean setRole() { ... }
    ✅ void setRole() { ... }
    

➖➖➖

  1. Prefers Exception to Error Code

    Prepare an exception block to catch an error, rather than to leave the error code as is. It is a best practice to catch error and handling it in certain way.


    transformUserToStaffFormat () { 
        {}.map() // ❌ Will caused error with no handler
    }
    
    ✅ transformUserToStaffFormat () { 
        try {
            {}.map()
        } catch (error) {
            handleErrro(error)
        }
    }
    

➖➖➖

  1. Don't Repeat Yourself

    When you write a function, it should minimize or even remove repeated code or purpose since a function should represent one purpose. And also, please noted that when you are wiriting function you should take a notice that you shouldn't do repeated code


Chapter 4 - Comment

Chapter 4 - Comments

Generally, comments is bad. You should avoid comment in your code as possible. From Chapter 2 - Naming and Chapter 3 - Function you should already aware that your code should self explanatory, it should express the purpose. So avoid using comment in your code.

But there is some exception to white comment:

  • TODO comment
  • Legal comment
  • The necessary comment in case the code couldn't speak it self (ex: regex pattern)
  • Clarification and Amplification comment

Clarification and amplication comment example:

const mapResponseToForm = () => {
    ... 

    // This is the best way so far to avoid to prevent race condition. Don't remove
    setTimeOut(() => {
        setFormTypeBasedOnProjectCategory(response.project_category);
    }, 1000)
}


// Avoid using this function unless you really need to kill entire app
const killProcess() {
    ...
}


In conclusion, Don't leave a comment in your code!!!


Chapter 5 - Code Formatting

Chapter 5 - Code Formatting

Code formatting is important. It is too important to ignore, code formatting is about communication, and the communication is the professional developer's first order of business. It is even more important than getting it work since the requirements might be changed tommorow, but code realibility will be the core of your application in the future for maintainibility or future changes

The Newspaper Metaphor

Think like a well-written newspaper. You read it vertically, at the top you expect the overall view of the story telling you about, and then you will face the first paragraph which tells you the synopsis of the story. And as you continue down, you will more dig dive into the detail.

You should think as like writing a well-writter newspaper when you are coding. The code you are writing should self documentary to be able to tell the story to the readers.

Everyone might have their own style of formatting, but you might follow this rules (or convention, I think..):

  1. User vertical spacing, density, and distance to make it easy to read your file
  2. Group code by their functionality. Related function must be close. Related code must appear vertically dense. Additionally: me personally like to order function alphabetically to easily search the suggested function
  3. Avoid too long files. My personal preference is too keep one file around (or shorter than) 1000 lines
  4. Avoid too-wide code lines. Make sure the code are visible in a one screen, so you have a good picture of the file is telling you about
  5. Write high0level code first, followed lower level code. Keep the function which call another above the invoked function, so then you can read the flow top to bottom
  6. Follow proper identation across code files. Personally I prefer to follow language-formatting-convention

Chapter 6 - Object & Data Structures

Chapter 6 - Object & Data Structure

When creating an application. We have to choose the right choice between Object and Data Structure. There is no the most good of all time between the two. but the usage may be different based on your case. Here what to remember when choosing Object or a Data Structure:

  • Object exposes behaviour and hide data, it easy to add new kinds of data wihout changing behaviour. But, it hard to add new behaviour to existing
  • While Data Structure exposes data and no significat behaviour. This make it easy to add new behaviours, but it hards to add new data structures to existing function

The Law of Demeter

This law is telling about how or how should function invoked by others. The Law of Demeter says that a method f of a class C should only call the methods of there:

  • C
  • An object create by f
  • An object passed as an argument to f
  • An object held in an instance variable of C

Chapter 7 - Error Handling

Chapter 7 - Error Handling

Clean code is readable, but it must also be robutst. One way to write robust clean code if we see error handling as a seperate concern. These are some tips when dealing with error handling:

  • Seperate your logic and your error handling. We should have a clear seperation between error handling and bussiness logic. Avoid using if statement to check if the code is contains error or not
  • Provide Context. The error handling should tell the readers that receive the error the aduquate context. It should whats is going on, why the code is error, where the error happens
  • Map unknown error. Wrap all errors and exceptions raised from external systems
  • Don't Return or Pass Null. Avoid returning null

Chapter 8 - Boundaries

Chapter 8 - Boundaries

Third Party Code

When you use third party code, you will be presented with many capabilities of the code, usually third party code makers target a wide audience. But as users, they only needed a few abilities that were just what they needed. In the book it is mentioned that, a good way to use third party code is to create an `Adapter` or `Boundary Class` to create an interface from the third party that we use to make the code cleaner. For example:

When you installing axios, and read the documentation. You may read these API related to HTTP request (or even more than these if you deep dive into the API):

    // Instance methods
    axios.request(config);
    axios.get(url[, config]);
    axios.delete(url[, config]);
    axios.head(url[, config]);
    axios.options(url[, config]);
    axios.post(url[, data[, config]]);
    axios.put(url[, data[, config]]);
    axios.patch(url[, data[, config]]);
    axios.getUri([config]);

Let say, you just need the get method and the delete method. Yes, of course you can do such a axio.get within your app to do a HTTP Get Request. But, it is not clean, why?

Because we have no control over the 3rd party code, we have no control over whether the code will stay the same for a long time or not. The problem is, if the code changes in the near future and it changes the way we use the API. So, we have to change all the code that uses that code in many places. Hence, it is better to use it like this:

// ✅ request-adapter-service.js

import axios from 'axios';

export default class RequestAdapterService {
	sendGetRequest(URL, params) {
		return this.reqClient.get(URL, { params });
	}

	sendDeleteRequest(URL, requestBody) {
		return this.reqClient.delete(URL, requestBody);
	}
}
//some-feature.js

const service = new RequestAdapterService();


const response = service.sendGetRequest('http://fakeapi.com', { limit: 2});

with class RequestAdapterService we limit the usage of axios for only the things that we need. and also, we can control what we have to do with the sendGetRequest API. And it's in one place, where (let say) axios changes in the future. We just need to change the file request-adapter-service

➖➖➖

Using Code That Does Not Exist

One of the benefits of using Boundaries is that we can guess or prepare for development using something we don't know yet. We can create an interface that can bridge the real API (which we don't have yet) with the API we expect

➖➖➖

Testing The 3rd Party Libraries

We've talked about 3rd party code above, we as developers may be familiar with using 3rd party code. sometimes, we spend a lot of time just studying documentation and trying things out. But there is some better way to understand the 3rd party code.

Learning Test is a technique to learn 3rd party library. This way we ensure that the library we are going to use will match the expected output we want. And this test can be a reference to find out the library can still be in accordance with the development of the application that we make

➖➖➖

Clean Boundaries

Clear at boundaries needs clear separationn and tests that define expectation. avoid letting too much of our code know about the third party particulars. Better to depend on something you can control than on something you don’t contro


About

[Learning Purpos] Clean Code by Robert C. Martin Summary

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published