# Clean Code

## 😢 ปัญหา

เคยอ่านโค้ดแล้วกร่นด่าในใจกันไหม "นี่มันโค้ดบ้าอะไร?", "ทำไมเขียนแบบนี้ฟร๊า?", "จะเริ่มจากตรงไหนดี?" บลาๆ ซึ่งถ้าเกิดเหตุการณ์แบบนี้ขึ้นนั่นแสดงว่า **เราจะทำงานได้ช้าลง** เพราะต้องเสียเวลานั่งทำความเข้าใจเจ้าโค้ดพวกนั้น และเลวร้ายที่สุดคือการที่พบว่า **ลบโค้ดพวกนั้นทิ้งแล้วเขียนใหม่ดีกว่า** นั่นแสดงว่าทีมต้องเสียเวลาไปทำงานเดิมซ้ำสอง

โค้ด !@#$%^&\* พวกนั้นเราเรียกมันว่า **Bad Code** ซึ่งเราควรจะเปลี่ยนมาเขียน **Good Code** กัน

## ❓ ทำไมต้องแก้ Bad Code ?

ถ้าในโปรเจคมี Bad Code อยู่เยอะๆ คนอื่นๆในทีมก็จะเห็น Bad Code เหล่านั้น เมื่อเห็นเรื่อยๆเห็นบ่อยๆ มันก็มีโอกาสสูงที่ทีมจะคิดว่านั่นคือเรื่องธรรมดา เลยทำให้ทีมบ่มนิสัยผลิตแต่โค้ดที่กากๆออกมา ทำให้**โปรเจคนี้และโปรเจคต่อๆไปเราก็จะเจอแต่โค้ดที่ทำให้ทีมทำงานได้ช้าลงตลอดไป**

สมมุติว่าเราต้องเพิ่มความสามารถใหม่ให้โค้ดหรือต้องไปแก้ bug กับงานที่มี Bad Code เยอะๆ จะได้อารมณ์ประมาณภาพด้านล่างนี้

> **เส้นสีเขียว** - คือเราต้องเดินไปไหนบ้างถึงจะไปเจอจุดที่ต้องไปเขียนโค้ดจริงๆ\
> **โซนสีส้ม** - คือจุดที่ต้องเข้าไปเขียนโค้ด

![ภาพจาก ronjeffries.com](/files/-LrKUKZC4t0wS7zDA35L)

จะเห็นว่า **Bad Code** ต้องเสียเวลาไปหาก่อนว่าจุดที่เราจะทำงานอยู่ตรงไหน และต้องเสียเวลาทำความเข้าใจก่อนว่ามันต้องแก้ตรงไหนบ้าง ซึ่งถ้ามั่วมากๆก็อาจจะทำให้ไปแก้ผิดจุดได้

คราวนี้มาดูงานที่มีแต่ **Good Code** ที่ทุกอย่างเป็นระเบียบเรียบร้อยกันบ้าง

![ภาพจาก ronjeffries.com](/files/-LqpyJEvEObIhwoPuvG9)

จะเห็นว่า **Good Code** ทำให้เราเข้าถึงจุดของงานได้เร็ว และ ลดโอกาสเกิดข้อผิดพลาดลง

## 🤔 ดูยังไงว่าโค้ดเราดีหรือยัง ?

การดูว่าโค้ดเราดีหรือเปล่าให้ดูจาก **คำด่าต่อนาที** ตอนทำ **Code Review**

![](/files/-LsZgRcCygkf3qGB6E-_)

{% hint style="info" %}
**Code Review**\
คือชั่วโมงที่เราเอาโค้ดทั้งของเราและของคนอื่นๆมากางออก แล้วไล่ดูว่ามีโค้ดจุดไหนที่ต้องปรับแก้ ตรงไหนควรสร้าง code standard บลาๆ ซึ่งวัตถุประสงค์ของมันคือ**ช่วยให้ทีมได้เรียนรู้และไม่ทำโค้ดกากๆออกมา**
{% endhint %}

## 😄 วิธีแก้ปัญหา

> ก็อย่าเขียน **Bad Code** แต่แรกดิ !! (ไม่บอกก็รู้เฟร้ย 🤣)

การเขียน **Good Code** นั้นเป็นเรื่องของความเอาใจใส่ในคุณภาพของโค้ด ซึ่งการเขียนโค้ดมันก็เหมือนกับคุณเขียนหนังสือซักเล่มนั่นแหละ ถ้าคุณเขียนแล้วคนอื่นเอาไปอ่านแล้วไม่เข้าใจ มันก็หมายถึงคุณเขียนได้ไม่ดี #โค้ดก็เช่นกัน

งั้นเดี๋ยวเราไปดูมาตรฐานสากลของ **Good Code** แล้วเทียบกับ **Bad Code** กันเรย

### 🔥 การเขียนเงื่อนไข

* **อย่าเขียนเงื่อนไขที่ให้คนอ่านต้องแปลความหมาย** เพราะมันจะเสียเวลาทำความเข้าใจ และอาจะเข้าใจผิดได้

> ลองดูโค้ดด้านล่างแล้วทำความเข้าใจดูครับว่ามันจะสื่อว่าอะไร ?\
> แล้วลองเปรียบเทียบ Bad Code กับ Good Code ดู

**Bad Code**

```csharp
if(Math.Pow(banaCa.Extana + 3 / 1.2, 2) >= Policy && (FullName.StartWith("LUNA") || LastName == "Covan" ))
{
   // เข้าผับได้
}
else
{
   // เข้าผับไม่ได้
}
```

**Good Code**

```csharp
var canEnterThePub = Math.Pow(banaCa.Extana + 3 / 1.2, 2) >= Policy && (FullName.StartWith("LUNA") || LastName == "Covan" );
if(canEnterThePub)
{
   // เข้าผับได้
}
else
{
   // เข้าผับไม่ได้
}
```

> ใจความของตัวอย่างคือ เราเข้าใจเงื่อนไขได้เร็วขนาดไหน\
> \* **Bad Code** เราต้องไล่ดูทีละเงื่อนไขทีละตัวใน if เพื่อจะได้รู้ว่ามันกำลังตรวจเรื่องอะไร\
> \* **Good Code** เราไม่ต้องไล่ดูเงื่อนไขเลย เพราะอ่านใน if ปุ๊ปเราก็เข้าใจได้ทันที

### 🔥 คอมเมนต์

* **อย่าเขียนคอมเมนต์!!** ถ้าเขียนคอมเมนต์แสดงว่าโค้ดคุณไม่ได้เข้าใจง่ายพอ คุณเลยต้องเขียนคอมเมนต์ทิ้งไว้ให้ชนรุ่นหลังได้เข้าใจ วิธีแก้คือสร้าง method ที่ทำให้เข้าใจได้ง่ายแทน
* **อย่าคอมเมนต์โค้ดที่เผื่อจะได้ใช้** เพราะคนอื่นไม่รู้สาเหตุหรอกว่าคุณคอมมเมนต์มันไว้ทำไม และเขาจะไม่กล้าลบมันแล้วมันจะกลายเป็นขยะค้างไว้แบบนั้น **ลบมันทิ้งซะ เพราะยังไงก็อยู่ใน commit อยู่แล้ว**

**Bad Code**

```csharp
/// <summary>
/// เปลี่ยนแปลงที่อยู่ผู้ใช้ใหม่
/// </summary>
/// <param name="email">อีเมล์ผู้ใช้</param>
/// <param name="address">ข้อมูลที่อยู่ใหม่</param>
public void UpsertAddress(string email, Address address)
{
    // ตรวจสอบหาข้อผิดพลาด
    if(address == null)
    {
        throw new Exception("Address เป็น null");
    }

    // ค้นหาผู้ใช้และตรวจว่ามีผู้ใช้ในระบบหรือไม่
    var user = Database.User.Get(it => it.Email == email);
    if(user == null)
    {
        throw new Exception("หาผู้ใช้ไม่พบ");
    }
    else
    {
        address.UserId = user.Id;
    }

    // ดึงข้อมูลที่อยู่ผู้ใช้มาทำการแก้ไข
    if(Database.Address.Get(it => it.UserId == user.Id) == null)
    {
        Database.Address.Insert(address);
    }
    else
    {
        Database.Address.Update(address);
    }

    // เก็บเอาไว้ก่อนเผื่อได้ใช้ในอนาคต
    //if(user.Setting.SendNotificationWhenAddressChanged)
    //{
    //    Email.Send(email, address, "คุณได้ทำการเปลี่ยนที่อยู่ใหม่");
    //}
}
```

**Good Code**

```csharp
public void UpsertAddress(string email, Address address)
{
    var user = getUserByEmail(email);
    updateAddress(user, address);
}

private UserProfile getUserByEmail(string email) ...
private void updateAddress(UserProfile user, Address address) ...
```

> ลดการเสียเวลาในการอ่านโค้ด โดยการสร้าง method เพื่อให้คนอ่านเห็นเป็นภาษามนุษย์มากขึ้น ไม่ใช่ต้องไปไล่อ่านโค้ดเพื่อทำความเข้าใจว่าเจ้า 10 บรรทัดนี้คืออะไร
>
> จากตัวอย่างจะสังเกตุได้ว่า\
> ใน Good Code บรรทัด 3 มันแทนที่ 14-19 ใน Bad Code ได้หมดเลย (และอ่านเป็นภาษามนุษย์ด้วย)\
> ใน Good Code บรรทัด 4 มันแทนที่ 25-33 ใน Bad Code ได้หมดเลย (และอ่านเป็นภาษามนุษย์ด้วย)\
> ส่วน 8-12 และ 20-23 ใน Bad Code มันถูกย้ายไปจัดการใน updateAddress() เรียบร้อยแล้ว\
> และ 35-39 ถ้าไม่ได้ใช้ก็ลบทิ้งซะอย่างเก็บมันไว้ คนอื่นจะไม่กล้ายุ่งกับโค้ดคุณ

### 🔥 การตั้งชื่อทั่วไป

* เวลาจะตั้งชื่ออะไรก็แล้ว **ต้องตั้งให้มันสื่อความหมาย** อ่านแล้วเข้าใจว่ามันมีไว้เพื่ออะไร
* ชื่อต้อง **อ่านออกเสียงได้** ถ้ามันออกเสียงไม่ได้เวลาจะบอกคนอื่นว่ามันผิดที่ไหน คุณจะบอกเขาว่ายังไง?
* **หลีกเลี่ยงคำย่อ** เพราะทุกคนย่อคำไม่เหมือนกัน เช่น Student คุณจะย่อว่าอะไร (std? โรคติดต่อทางเพศงั้นเหรอ?)
* ชื่อควร**ตั้งชื่อตามพฤติกรรม** เช่น ชื่อ**คลาสควรเป็นคำนาม**, ชื่อ **method ควรขึ้นด้วยคำกิริยา**
* **อย่าตั้งชื่อยาว** เพราะมันจะเสียเวลาทำความเข้าใจ
* **อย่าตั้งชื่อเป็นนิเสธ** เพราะคนอ่านจะ งง (แค่คำว่านิเสธก็ งง แล้ว)
* **อย่างตั้งชื่อกำกวม** เพราะมันจะสับสนว่าตัวไหนเป็นตัวไหนกันแน่

**Bad Code**

```csharp
double m; // ตัวแปรตัวนี้ใช้ทำอะไร ?
string actpwd; // ไอ้นี่มันอ่านว่ายังไง? ย่อจากอะไร?
string generatedSaltValueForEncryptTheAccountPasswordForThisUserOnly; // เสียเวลาอ่านจุง
string data; // กำกวมเกิน สรุปมันคือ data เรื่องอะไร?
```

**Good Code**

```csharp
double money; // อ่านแล้วรู้เลยว่าใช้เก็บเรื่องเงิน
string accountPassword; // อ่านแล้วรู้เลยว่าเก็บรหัสผ่านของผู้ใช้
string salt4Password; // อ่านแล้วรู้เลยว่าเก็บรหัสผ่าน
string userData; // อ่านแล้วรู้เลยว่าเก็บข้อมูลผู้ใช้
```

{% hint style="info" %}
**เกร็ดความรู้**\
คลีนโค้ดบางสำนักไม่แนะนำให้ตั้งชื่อแบบใส่ตัวเลขแทนคำอ่านนะ เช่น Good Code บรรทัดที่ 3 อาจจะเขียนเต็มๆไปเลยก็ได้ saltForPassword
{% endhint %}

### 🔥 การตั้งชื่อ Methods & Functions

> กฎในการตั้งชื่อทั่วไปทั้งหมดก็ใช้กับการตั้งชื่อ Methods & Functions ด้วยนะจ๊ะ

* **ขึ้นต้นด้วยคำกิริยา** เพราะคนเรียกใช้อ่านแล้วรู้เลยว่าใช้ทำอะไร (คนไทยอาจะไม่มีผลเท่าไหร่แต่ฝรั่งนี่มีสูงเลย)
* **อย่ารับ arguments เยอะ** เพราะคนเรียกใช้ method อาจ งง ใส่ผิด และเวลาเพิ่ม/ลดละก็อวกแตก
* ตั้งชื่อให้**สื่อว่างานของมันคืออะไร** ไม่งั้นคนเรียกใช้ method จะมีคำถาม

**Bad Code**

```csharp
AccountNameChanger(string name) // ต้องอ่านทั้งหมดถึงจะเข้าใจ
CreateAccount(string uName, string pwd, ...) // Arguments จะเยอะไปไหน

ChangeAccountPasswordAndDeleteIfNotValid() // เอ็งจะเปลี่ยนรหัสผ่านหรือจะลบผู้ใช้กันแน่ ?
```

**Good Code**

```csharp
ChangeAccountName(string name) // อ่านแล้วเข้าใจเลยเพราะเห็นคำกิริยาก่อนเพื่อน
CreateAccount(AccountInfo account) // คนเรียกไม่สับสน

ChangeAccountPassword() // อ่านแล้วรู้เลยว่าแค่เปลี่ยนรหัสผ่าน
DeleteAccountIfNoBirthDate() // อ่านแล้วรู้เลยว่าจะลบผู้ใช้กรณีไหน
```

### 🔥 Layout

* ควรจัด**วาง Layout ให้ตรงกับมาตรฐานของภาษาที่ใช้** เพราะโค้ดของทั้งทีมจะได้เหมือนกัน และเวลาทำงานกับ Git ก็จะไม่เกิด conflict ด้วย

**Bad Code**

```csharp
if( conditionA ){
   // Do something-1
}
else if(conditionB)
{
   // Do something-2
}
else
   // Do something-3

// สรุปเอ็งจะใช้ layout ของ { } และการเว้นวรรคแบบไหนกันแน่?
```

**Good Code**

```csharp
if(conditionA)
{
   // Do something-1
}
else if(conditionB)
{
   // Do something-2
}
else
{
   // Do something-3
}
```

## 🎯 บทสรุป

เรื่อง **Clean Code** มันเป็นเรื่องความเอาใจใส่ในผลงานของตัวเอง ซึ่งผลลัพท์ที่ได้มันจะทำให้เราไม่เสียเวลาหลงทางในเขาวงกต และมันไม่ได้มีแค่เท่านี้หรอก อันนี้เป็นเพียงน้ำจิ้มส่วนนึงเท่านั้น และแต่ละภาษาก็มีหลักในการทำ clean code ในรูปแบบของเขาอยู่เช่นกัน เลยขอดึงเฉพาะตัวที่เป็นกลางๆมาให้ดูก่อน ไว้มีโอกาสจะเอามาเพิ่มให้อ่านนะจุ๊

{% hint style="danger" %}
**ทำทันที**\
เรื่องการ Clean Code ห้ามปล่อยทิ้งไว้ เพราะ **คุณไม่กลับมาแก้หรอก!!** ดังนั้นจงจำไว้ **"ทำ-ทัน-ที"**
{% endhint %}

{% hint style="info" %}
**เกร็ดความรู้**\
มาตรฐานทั้งหมดที่ว่านี้ ในแต่ละภาษามันจะมี Coding Standard ของเขาเองอยู่นะ ลองไปหาอ่านได้ เช่น มาตรฐานของฝั่ง .NET สามารถอ่านได้จากลิงค์นี้ครับ [Microsoft document](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/)
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.saladpuk.com/basic/clean-code.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
