{clean code}

Volodymyr Kupriienko

Our plan 🎯

What is clean code?

1.

# Intro

Analyze examples

2.

Define heuristic rules

3.

Multithreading

3.

Architecture

4.

Code Examples

Special for the Front-end team 🙌🏻

# Intro
# Intro

Clean code is

Software development practice described in book written by Robert C. Martin (uncle Bob) 

Чистий код можуть читати і удосконалювати інші розробники, крім його вихідного автора.

Чистий код - це код, над яким ретельно попрацювали. Хтось не пошкодував часу, щоб зробити його простим і чітким. Хтось приділив належну увагу всім дрібницям і поставився до коду з душею.

What does this code do? 🧐

func main() {
  c := &User{Name: "Kate", Ch: make(chan string)}
  arrEmployees := []*Employee{
    {Name: "John", Type: 1},
    {Name: "Liza", Type: 2},
    {Name: "Garry", Type: 2},
  }

  go func() {
    for _, e := range arrEmployees {
      if e.Type == 2 {
        c.Ch <- fmt.Sprintf("Hello, I'm %s", e.Name)
        time.Sleep(time.Second)
        c.Ch <- fmt.Sprintf("Waiting time is 5m")
        break
      }
    }

    close(c.Ch)
  }()

  for m := range c.Ch {
    fmt.Println(m)
  }
}
type User = {...}

type Employee = {...}

const c: User = {
    name: "Kate",
    ch: (m: string) => console.log(m),
};

const arrEmployees: Employee[] = [
    {name: "John", type: 1},
    {name: "Liza", type: 2},
    {name: "Garry", type: 2},
]

for (let e of arrEmployees) {
    if (e.type == 2) {
        c.ch(`Hello, I'm ${e.name}`)
        setTimeout(
          () => c.ch("Waiting time is 5m"),
          1000
        )
        break
    }
}

# Intro

1

Naming

Let's figure it out by cleanup 🧹

  • Short and descriptive
  • Ubiquitous language
  • Context

Naming

# Naming

Let's fix naming 🪄🎩

-type User struct {
-  Name string
-  Ch   chan string
}
+type Passenger struct {
+  FirstName           string
+  NotificationChannel chan string
}

-type Employee struct {
-  Name string
-  Type int
}
+type DriverRank int
+
+const (
+  DriverRankStandard DriverRank = 1
+  DriverRankPremium  DriverRank = 2
+)
+
+type Driver struct {
+  FirstName string
+  Rank      DriverRank
}

-c := &User{Name: "Kate", Ch: make(chan string)}
+passenger := &Passenger{
+  FirstName: "Kate",
+  NotificationChannel: make(chan string)
+}

-arrEmployees := []*Employee{
-  {Name: "John", Type: 1},
-  {Name: "Liza", Type: 2},
-  {Name: "Garry", Type: 2},
}
+availableDrivers := []*Driver{
+  {FirstName: "John", Rank: DriverRankStandard},
+  {FirstName: "Liza", Rank: DriverRankPremium},
+  {FirstName: "Garry", Rank: DriverRankPremium},
}
-type User = {
-    name: string
-    ch: (m: string) => void
}
+type channel = (update: string) => void
+
+type Passenger = {
+    firstName: string
+    notificationChannel: channel
}

-type Employee = {
-    name: string
-    type: number
}
+enum Rank {
+    STANDARD = 1,
+    PREMIUM = 2,
+}
+
+type Driver = {
+    firstName: string
+    rank: Rank
}

-const c: User = {
-    name: "Kate",
-    ch: (m: string) => console.log(m),
};
+const passengerKate: Passenger = {
+    firstName: "Kate",
+    notificationChannel: (update: string) => console.log(update),
};

-const arrEmployees: Employee[] = [
-    {name: "John", type: 1},
-    {name: "Liza", type: 2},
-    {name: "Garry", type: 2},
]
+const availableDrivers: Driver[] = [
+     {firstName: "John", rank: Rank.STANDARD},
+     {firstName: "Liza", rank: Rank.PREMIUM},
+     {firstName: "Garry", rank: Rank.PREMIUM},
]
# Naming

Let's fix naming 🪄🕊

go func() {
-  for _, e := range arrEmployees {
-    if e.Type == 2 {
-      c.Ch <- fmt.Sprintf("Hello, I'm %s", e.Name)
      
      time.Sleep(time.Second)
      
-      c.Ch <- fmt.Sprintf("Waiting time is 5m")
      
      break
    }
  }

-  close(c.Ch)
+  for _, driver := range availableDrivers {
+    if driver.Rank == DriverRankPremium {
+      passenger.NotificationChannel
+        <- fmt.Sprintf("Hello, I'm %s", driver.Name)
      
      time.Sleep(time.Second)
    
+      passenger.NotificationChannel
+        <- fmt.Sprintf("Waiting time is 5m")
      
      break
    }
  }

+  close(passenger.NotificationChannel)
}()

-for m := range c.Ch {
-  fmt.Println(m)
}
+for update := range passenger.NotificationChannel {
+  fmt.Println(update)
}
-for (let e of arrEmployees) {
-  if (e.type == 2) {
-    c.ch(`Hello, I'm ${e.name}`)
      
    setTimeout(
-      () => c.ch("Waiting time is 5m"),
      1000
     )
      
    break
  }
}
+for (let driver of availableDrivers) {
+  if (driver.rank == Rank.PREMIUM) {
+    passenger.notificationChannel(
+      `Hello, I'm ${driver.name}`
+    ) 
      
    setTimeout(
+      () => passenger.notificationChannel(
+        "Waiting time is 5m"
+      ),
      1000
    )
      
    break
  }
}
# Naming

1

Naming ✅

2

Methods

What next? 🤩

# Methods

Methods

  • Short
  • Single responsibility
  • No side effects
  • Param objects & no flags
  • Don't repeat yourself

Let's split it by methods 🖖🏻

# Methods
+func callTaxi(passenger *Passenger) {
  for _, driver := range availableDrivers {
    if driver.Rank == DriverRankPremium {
      passenger.NotificationChannel <- fmt.Sprintf(
        "Hello, I'm %s",
        driver.Name
      )
      
      time.Sleep(time.Second)
    
      passenger.NotificationChannel
        <- fmt.Sprintf("Waiting time is 5m")
      
      break
    }
  }

  close(passenger.NotificationChannel)
+}

+func notifyPassengerOnUpdates(passenger *Passenger) {
  for update := range passenger.NotificationChannel {
    fmt.Println(update)
  }
+}
+const callTaxi = (passenger: Passenger) => {
  for (let driver of this.availableDrivers) {
    if (driver.rank == Rank.PREMIUM) {
      passenger.notificationChannel(
        `Hello, I'm ${driver.name}`
      ) 

      setTimeout(
        () => passenger.notificationChannel(
          "Waiting time is 5m"
        ),
        1000
      )

      break
      }
    }
+}

Let's split it by methods ✋🏻

# Methods
func callTaxi(customer *Customer) {
  for _, driver := range availableDrivers {
    if driver.Rank == DriverRankPremium {
      passenger.NotificationChannel <- fmt.Sprintf(
        "Hello, I'm %s",
        driver.Name
      )
      
      time.Sleep(time.Second)
    
      passenger.NotificationChannel
        <- fmt.Sprintf("Waiting time is 5m")
      
      break
    }
  }

  close(passenger.NotificationChannel)
}

func notifyPassengerOnUpdates(passenger *Passenger) {
  for update := range passenger.NotificationChannel {
    fmt.Println(update)
  }
}
const callTaxi = (passenger: Passenger) => {
  for (let driver of this.availableDrivers) {
    if (driver.rank == Rank.PREMIUM) {
      passenger.notificationChannel(
        `Hello, I'm ${driver.name}`
      ) 

      setTimeout(
        () => passenger.notificationChannel(
          "Waiting time is 5m"
        ),
        1000
      )

      break
      }
    }
}

Ящо це метод, завжди застосовую до нього прийом “вилучення методу”; у результаті в мене залишається основний метод, який більш чітко пояснює, що саме він робить, і декілька підметодів, що пояснюють, як він це робить.

Let's split it by methods 🖖🏻

# Methods
func callTaxi(passenger *Passenger) {
-  for _, driver := range availableDrivers {
-    if driver.Rank == DriverRankPremium {
+    driver := findDriver(DriverRankPremium)

      passenger.NotificationChannel <- fmt.Sprintf(
        "Hello, I'm %s",
        driver.Name
      )
      
      time.Sleep(time.Second)
    
      passenger.NotificationChannel
        <- fmt.Sprintf("Waiting time is 5m")
      
-      break
-    }
-  }

  close(passenger.NotificationChannel)
}

+func findDriver(rank DriverRank) *Driver {
+  availableDrivers := []*Driver{
+    {FirstName: "John", Rank: DriverRankStandard},
+    {FirstName: "Liza", Rank: DriverRankPremium},
+    {FirstName: "Garry", Rank: DriverRankPremium},
+  }
+
+  for _, driver := range availableDrivers {
+    if driver.Rank == rank {
+      return driver
+    }
+  }
+
+  return nil
+}

func notifyPassengerOnUpdates(passenger *Passenger) {
  for update := range passenger.NotificationChannel {
    fmt.Println(update)
  }
}
const callTaxi = (passenger: Passenger) => {
-  for (let driver of this.availableDrivers) {
-    if (driver.rank == Rank.PREMIUM) {
+     const driver = findDriver(Rank.PREMIUM)

      passenger.notificationChannel(
        `Hello, I'm ${driver.name}`
      ) 

      setTimeout(
        () => passenger.notificationChannel(
          "Waiting time is 5m"
        ),
        1000
      )

-      break
-      }
-    }
}

+const findDriver = (rank: Rank): Driver => {
+    const availableDrivers: Driver[] = [
+        {firstName: "John", rank: Rank.STANDARD},
+        {firstName: "Liza", rank: Rank.PREMIUM},
+        {firstName: "Garry", rank: Rank.PREMIUM},
+    ]
+
+    return availableDrivers.find(
+       (driver: Driver) => driver.rank === rank
+     )
+}

3

Tests

2

Methods

1

Naming ✅

OK, that's all? 🙉

  • TDD
  • Scenarios design
  • Refactoring
  • Good quality

Tests

# Tests

Код без тестів не можна вважати чистим, хоч бияким елегантним він не був і хоч би я добре читався.

Process

Write test scenarios 💭

1.

# Tests

Write code 👨🏻‍💻

2.

Run tests 🚀

3.

Fix issues 🪲

3.

Refactor code 🧹

4.

Repeat 😉

5.

Закон Леблана: потім означає ніколи

1

Naming ✅

2

Methods

3

Tests

Formatting

  • Reduces MR size
  • Reduces conflicts
  • Can be automated
  • Code should be like article

5

Comments

4

Formatting

1

Naming ✅

2

Methods

3

Tests

Code must be self-documented

# Comments

Avoid muttering

# Comments

Avoid commented code

# Comments

Good comments

  • Use var/methods instead
  • Describe intentions
  • TODO comments
# Comments

5

Comments ✅

4

Formatting

6

Objects & error handling

1

Naming ✅

2

Methods

3

Tests

Objects & Structs

  • Name by state, not behavior
  • DTO
  • Law of Demeter

Error handling

  • Add context
  • Don't return error codes
  • Don't return bool or null
  • Separate domain logic and error handling

The Boy Scout Rule

1

Naming matters

2

We are reading code mostly

3

We work in a team

5

Improves stability and reliability

4

Boosts development process

6

All loves good quality code

Why it's important? 🤔

Book

💙💛

Ukrainian

Original

And remember

👇🏻

That's all✨

Clean Code

By Volodymyr Kupriienko

Clean Code

  • 146