1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2024-11-14 16:13:14 +01:00
.profile/posts/clean-code.md

260 lines
8.6 KiB
Markdown
Raw Permalink Normal View History

2021-11-09 15:14:31 +01:00
---
2021-12-04 15:52:51 +01:00
title: '🧼 Clean Code'
2022-02-23 11:51:00 +01:00
description: 'What is "Clean Code", what are "Design Patterns", and why is it so important today? Tips and tricks to make your code more readable and maintainable in the long term.'
isPublished: true
publishedOn: '2022-02-23T08:00:18.758Z'
2021-11-09 15:14:31 +01:00
---
Hello! 👋
2022-08-27 02:30:55 +02:00
Have you already heard of "**Clean Code**" or "**Design Patterns**"?
2021-11-09 15:14:31 +01:00
Even if you know what it is about, this blog post will probably still be useful to you, I will share some tips and tricks to make your code more readable and maintainable in the long term.
**Note:** Sources used to write this blog post are available at the [end of this post](#sources).
2022-08-27 02:30:55 +02:00
## Definition: Clean Code
2021-11-09 15:14:31 +01:00
A clean code is a code that is **easy** to **read** and easy to **understand**.
2021-12-04 15:52:51 +01:00
But I promise it is not a code that is easy to write, in fact it is really **hard to write Clean Code**.
2021-11-09 15:14:31 +01:00
2021-12-04 15:52:51 +01:00
We could ask ourselves, what is **easy** to **read** and easy to **understand** ?
2021-11-09 15:14:31 +01:00
It depends of many factors, and is somewhat relative to each one of us. The **perfect** Clean code **doesn't exist**, but we can try to be **as perfect as possible**.
2022-08-27 02:30:55 +02:00
## Why is it so important?
2021-11-09 15:14:31 +01:00
Code like that works great, but it is not enough, even if the code will be read by the computer and understood by the machine, we should not forget that the code is **written by human** and will be also **read by human** not only a machine.
2021-11-09 15:14:31 +01:00
For example the [Linux kernel](https://www.kernel.org/), is one of the biggest open source project with many contributors worldwide. Last data shows that it is about **20 millions** lines of code.
2022-02-23 11:51:00 +01:00
With a project of this magnitude, we can't let everyone do what they want and however they want, **we must set rules and conventions** to get everyone to agree, this allows to add features faster and will reduce possible bugs as **developers** will not struggle as much to understand the code.
2021-11-09 15:14:31 +01:00
## Definition: Design Patterns
2021-11-09 15:14:31 +01:00
These **rules** and **conventions** are so called **Design Patterns**.
A software design pattern is a general way of **solving a problem** by applying a **well-known solution**.
Design patterns are formalized **best practices** that the programmer can use to solve common problems when designing an application or system.
## How to write Clean Code and famous Design Patterns
To show you the rules and conventions, I will write the examples in the [TypeScript](https://www.typescriptlang.org/) programming language but it is relevant to any programming language.
### Naming variables
We all know that **variables** are used everywhere in **programming**, good variable names allow us to better understand the intention of the code.
#### Same vocabulary for the same type of variable
##### Example (bad way)
```typescript
function getUserInfo(): User
function getUserDetails(): User
function getUserData(): User
```
##### Example (good way)
```typescript
function getUser(): User
```
---
#### Avoid "Magic Numbers"
##### Example (bad way)
```typescript
2022-02-23 11:51:00 +01:00
// What does 86400000 mean?
2021-11-09 15:14:31 +01:00
setTimeout(restart, 86400000)
```
##### Example (good way)
```typescript
const MILLISECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000
setTimeout(restart, MILLISECONDS_IN_ONE_DAY)
2021-11-09 15:14:31 +01:00
```
---
#### Explicit is better than implicit (no abbreviations or acronyms)
##### Example (bad way)
```typescript
const u = getUser()
const s = getSubscription()
const t = charge(u, s)
```
##### Example (good way)
```typescript
const user = getUser()
const subscription = getSubscription()
const transaction = charge(user, subscription)
```
---
#### As short as possible, as long as necessary
##### Example (bad way)
```typescript
interface Car {
carModel: string
carColor: 'red' | 'blue' | 'yellow'
}
const printCar = (car: Car): void => {
console.log(`${car.carModel} (${car.carColor})`)
}
```
##### Example (good way)
```typescript
interface Car {
model: string
color: 'red' | 'blue' | 'yellow'
}
const printCar = (car: Car): void => {
console.log(`${car.model} (${car.color})`)
}
```
---
#### Boolean names
The name of a boolean variable should be a question, and the answer should be true or false. We can use prefixes like `is`, `has`, `can` to make it more explicit and we should avoid negation.
2021-11-09 15:14:31 +01:00
##### Example (bad way)
```typescript
let person = true
let age = true
let dance = true
function isEmailNotUsed(email: string): boolean
```
##### Example (good way)
```typescript
let isPerson = true
let hasAge = true
let canDance = true
function isEmailUsed(email: string): boolean
```
---
### DRY (Don't Repeat Yourself)
When we copy/paste the same lines of code, we should better abstract it in a function, that we can reuse later without having to copy/paste the lines of code, that makes the code more maintainable afterwards, because if we need to change the behavior of this piece of code, we won't need to change it in several places, but only when declaring the function.
---
### KISS (Keep It Simple Stupid)
As we have just said, we will prefer to abstract the code in multiple functions, rather than leaving everything in the same place, but a function should not do "too much", and we should rather separate it into several distinct functions.
We have to keep it as simple as possible, not to implement features that are not requested, and to divide the functions as much as possible into small functions.
### Example (bad way)
```typescript
import fs from 'node:fs'
import path from 'node:path'
const createFile = async (
name: string,
isTemporary: boolean = false
): Promise<void> => {
2021-11-09 15:14:31 +01:00
if (isTemporary) {
return await fs.promises.writeFile(path.join('temporary', name), '')
}
return await fs.promises.writeFile(name, '')
}
```
`createFile` is a function that does 2 things so it is better to split it in 2 separated functions.
### Example (good way)
```typescript
import fs from 'node:fs'
import path from 'node:path'
const createFile = async (name: string): Promise<void> => {
2021-11-09 15:14:31 +01:00
await fs.promises.writeFile(name, '')
}
const createTemporaryFile = async (name: string): Promise<void> => {
2021-11-09 15:14:31 +01:00
await createFile(path.join('temporary', name))
}
```
---
### TDD (Test Driven Development)
Test-driven development (TDD) is a software development process relying on software requirements being converted to test cases before software is fully developed, and tracking all software development by repeatedly testing the software against all test cases. This is as opposed to software being developed first and test cases created later.
We first write tests that should fails because there are no implementation, and then we write the code implementation to make the tests succeeds.
The End To End (e2e) and Unit tests should document what is the behavior intended for the code.
---
### Avoid comments
One of the most important rule of "Clean Code": If you need to add **comments**, it's because your code is **not clean**.
2021-11-09 15:14:31 +01:00
I know that might be counter intuitive at first, as most developers will advice you to add comments to your code, to document what it does.
The thing is that you should choose good variable names, break down features in multiple functions, so that others developers can read your code and understand it just by reading the functions names etc.
You can write comments, but that should only be used documenting how to use a function but not for the implementation itself and in places where you can't be more explicit.
In fact, as we saw in the [TDD section](#tdd-test-driven-development), automated tests can document what a function should returns, and how the code should behave, so that should already improve code maintainability.
Having a good comment explaining a difficult code is better than nothing with a bad written code, difficult to understand.
#### Example (bad way)
```typescript
// Check if subscription is active
if (subscription.endDate > Date.now()) {
}
```
#### Example (good way)
```typescript
const isSubscriptionActive = subscription.endDate > Date.now()
if (isSubscriptionActive) {
}
```
Here we are creating a new variable `isSubscriptionActive` that allows us to avoid the need of a comment to understand what the code does.
---
## Conclusion
2021-12-04 15:52:51 +01:00
We can't write the perfect clean code understandable by everyone but we can **write code that is as perfect as possible to ease maintaibility** for yourself and others developers.
2021-11-09 15:14:31 +01:00
## Sources
- [Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin](https://books.google.fr/books/about/Clean_Code.html?id=hjEFCAAAQBAJ)
- [Software Design Pattern (Wikipedia)](https://en.wikipedia.org/wiki/Software_design_pattern)
- [TDD - Test-driven development (Wikipedia)](https://en.wikipedia.org/wiki/Test-driven_development)
- [github.com/labs42io/clean-code-typescript](https://github.com/labs42io/clean-code-typescript)