Home > Blog > tech

Clean Code คืออะไร? หลักการเขียนโค้ดสะอาด และ Refactoring เพื่อโค้ดที่ดีขึ้น 2026

clean code refactoring guide
Clean Code and Refactoring Guide 2026
2026-04-08 | tech | 3500 words

ในโลกของการพัฒนาซอฟต์แวร์ การเขียนโค้ดที่ "ทำงานได้" เป็นเพียงจุดเริ่มต้น แต่การเขียนโค้ดที่ "อ่านง่าย บำรุงรักษาง่าย และขยายต่อได้" คือสิ่งที่แยกนักพัฒนามืออาชีพออกจากมือสมัครเล่น Robert C. Martin (Uncle Bob) ผู้เขียนหนังสือ Clean Code กล่าวไว้ว่า "โค้ดที่ดีคือโค้ดที่อ่านแล้วเข้าใจได้ทันที" และ "อัตราส่วนเวลาที่ใช้อ่านโค้ดต่อเขียนโค้ดคือ 10 ต่อ 1" ดังนั้นการทำให้โค้ดอ่านง่ายจึงเป็นการลงทุนที่คุ้มค่าที่สุด

บทความนี้จะพาคุณเรียนรู้หลักการ Clean Code ตั้งแต่พื้นฐานจนถึงเทคนิค Refactoring ขั้นสูง ครอบคลุม SOLID Principles, Code Smells, Refactoring Techniques และ Best Practices ที่นักพัฒนาทุกคนควรรู้ในปี 2026

Clean Code คืออะไร?

Clean Code คือโค้ดที่มีคุณสมบัติดังนี้:

Meaningful Naming — การตั้งชื่อที่มีความหมาย

การตั้งชื่อเป็นสิ่งสำคัญที่สุดอย่างหนึ่งใน Clean Code ชื่อที่ดีสามารถลดความจำเป็นในการเขียน Comment ได้อย่างมาก และทำให้โค้ดอ่านเหมือนภาษาธรรมชาติ

# BAD — ชื่อไม่มีความหมาย
def calc(a, b, c):
    return a * b * (1 - c)

d = calc(100, 5, 0.1)
lst = [x for x in data if x > 0]
temp = get_data()

# GOOD — ชื่อบอกความหมายชัดเจน
def calculate_discounted_total(price, quantity, discount_rate):
    return price * quantity * (1 - discount_rate)

total_amount = calculate_discounted_total(
    price=100, quantity=5, discount_rate=0.1
)
positive_values = [value for value in measurements if value > 0]
user_profile = fetch_user_profile(user_id=42)
// BAD — ชื่อคลุมเครือ
class Data {
  process() { ... }
  handle() { ... }
  doStuff() { ... }
}

function check(x) { return x > 0 && x < 100; }

// GOOD — ชื่อชัดเจน
class OrderProcessor {
  calculateSubtotal() { ... }
  applyDiscount() { ... }
  generateInvoice() { ... }
}

function isWithinValidRange(age) { return age > 0 && age < 100; }

หลักการตั้งชื่อที่ดี

Functions — ฟังก์ชันที่ดี

ฟังก์ชันเป็นหน่วยพื้นฐานที่สุดของการจัดระเบียบโค้ด ฟังก์ชันที่ดีตามหลัก Clean Code ควรมีคุณสมบัติดังนี้:

1. ฟังก์ชันต้องเล็ก (Small Functions)

# BAD — ฟังก์ชันยาวเกินไป ทำหลายสิ่ง
def process_order(order):
    # Validate order
    if not order.items:
        raise ValueError("Empty order")
    if not order.customer:
        raise ValueError("No customer")
    for item in order.items:
        if item.quantity <= 0:
            raise ValueError(f"Invalid quantity for {item.name}")
        if item.price < 0:
            raise ValueError(f"Invalid price for {item.name}")

    # Calculate totals
    subtotal = 0
    for item in order.items:
        subtotal += item.price * item.quantity
    tax = subtotal * 0.07
    shipping = 50 if subtotal < 1000 else 0
    total = subtotal + tax + shipping

    # Save to database
    db.orders.insert({
        "customer_id": order.customer.id,
        "items": [item.to_dict() for item in order.items],
        "subtotal": subtotal,
        "tax": tax,
        "total": total,
        "status": "pending"
    })

    # Send email
    send_email(order.customer.email, "Order Confirmed", f"Total: {total}")

    return total

# GOOD — แยกเป็นฟังก์ชันย่อย แต่ละตัวทำสิ่งเดียว
def process_order(order):
    validate_order(order)
    totals = calculate_order_totals(order)
    order_id = save_order(order, totals)
    notify_customer(order.customer, totals)
    return order_id

def validate_order(order):
    if not order.items:
        raise ValueError("Empty order")
    if not order.customer:
        raise ValueError("No customer")
    for item in order.items:
        validate_order_item(item)

def validate_order_item(item):
    if item.quantity <= 0:
        raise ValueError(f"Invalid quantity for {item.name}")
    if item.price < 0:
        raise ValueError(f"Invalid price for {item.name}")

def calculate_order_totals(order):
    subtotal = sum(item.price * item.quantity for item in order.items)
    tax = subtotal * TAX_RATE
    shipping = 0 if subtotal >= FREE_SHIPPING_THRESHOLD else SHIPPING_FEE
    return OrderTotals(subtotal=subtotal, tax=tax, shipping=shipping)

2. ฟังก์ชันต้องทำสิ่งเดียว (Single Responsibility)

ฟังก์ชันแต่ละตัวควรทำสิ่งเดียว ทำมันให้ดี และทำแค่สิ่งนั้น ถ้าฟังก์ชันมีคำว่า "and" ในชื่อ มักเป็นสัญญาณว่าทำหลายสิ่งเกินไป

3. จำนวน Arguments ให้น้อย

# BAD — Arguments มากเกินไป
def create_user(name, email, password, age, phone, address,
                city, country, zip_code, role, department):
    ...

# GOOD — ใช้ Object/Dict รวมกลุ่ม
@dataclass
class CreateUserRequest:
    name: str
    email: str
    password: str
    age: int
    contact: ContactInfo
    role: str = "user"

def create_user(request: CreateUserRequest):
    ...

DRY, KISS, YAGNI — หลักการพื้นฐาน 3 ข้อ

DRY — Don't Repeat Yourself

ทุกความรู้ (Knowledge) ในระบบควรมีการแสดงออกที่เป็นเอกภาพ ไม่ซ้ำซ้อน เมื่อต้องเปลี่ยนแปลง แก้ที่เดียวจบ

# BAD — Code ซ้ำซ้อน
def get_active_users():
    users = db.query("SELECT * FROM users WHERE active = 1")
    result = []
    for user in users:
        result.append({
            "id": user.id,
            "name": user.name,
            "email": user.email,
            "created": user.created_at.isoformat()
        })
    return result

def get_admin_users():
    users = db.query("SELECT * FROM users WHERE role = 'admin'")
    result = []
    for user in users:
        result.append({
            "id": user.id,
            "name": user.name,
            "email": user.email,
            "created": user.created_at.isoformat()
        })
    return result

# GOOD — DRY: แยก Logic ที่ซ้ำออกมา
def format_user(user):
    return {
        "id": user.id,
        "name": user.name,
        "email": user.email,
        "created": user.created_at.isoformat()
    }

def get_users(condition: str):
    users = db.query(f"SELECT * FROM users WHERE {condition}")
    return [format_user(u) for u in users]

def get_active_users():
    return get_users("active = 1")

def get_admin_users():
    return get_users("role = 'admin'")

KISS — Keep It Simple, Stupid

เลือกวิธีที่ง่ายที่สุดที่ทำงานได้ อย่าสร้างความซับซ้อนที่ไม่จำเป็น

// BAD — Over-engineering
class UserValidatorFactory {
  static create(type) {
    const validators = new Map();
    validators.set('email', new EmailValidatorStrategy());
    validators.set('phone', new PhoneValidatorStrategy());
    return new CompositeValidator(
      validators.get(type) || new DefaultValidatorStrategy()
    );
  }
}

// GOOD — KISS: ง่ายพอสำหรับความต้องการจริง
function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

function validatePhone(phone) {
  return /^\d{10}$/.test(phone);
}

YAGNI — You Aren't Gonna Need It

อย่าเขียนโค้ดสำหรับฟีเจอร์ที่ยังไม่ต้องการ เขียนเฉพาะสิ่งที่ต้องใช้ตอนนี้ เมื่อความต้องการเปลี่ยนค่อย Refactor ในอนาคตจะง่ายกว่ามาก ถ้าโค้ดสะอาดตั้งแต่แรก

Code Smells — กลิ่นของโค้ดที่ไม่ดี

Code Smells คือสัญญาณที่บอกว่าโค้ดอาจมีปัญหาและควร Refactor แม้ว่าโค้ดจะยังทำงานได้ถูกต้อง Martin Fowler ได้จำแนก Code Smells ไว้หลายประเภท ต่อไปนี้คือ Smells ที่พบบ่อยที่สุด:

Code Smellคำอธิบายวิธีแก้
Long Methodฟังก์ชันที่ยาวเกิน 20-30 บรรทัด ทำหลายสิ่งExtract Method
God ClassClass ที่ใหญ่เกินไป รู้ทุกเรื่อง ทำทุกอย่างExtract Class, SRP
Feature EnvyMethod ที่ใช้ข้อมูลของ Class อื่นมากกว่าของตัวเองMove Method
Primitive Obsessionใช้ Primitive types แทน Value ObjectsReplace with Value Object
Shotgun Surgeryเปลี่ยนฟีเจอร์หนึ่ง ต้องแก้หลายไฟล์Move Method, Extract Class
Data Clumpsกลุ่มข้อมูลที่ปรากฏด้วยกันเสมอExtract Class
Switch StatementsSwitch/if-else ยาวๆ ที่ซ้ำหลายที่Polymorphism
Dead Codeโค้ดที่ไม่ถูกเรียกใช้เลยลบทิ้ง
Comments (บางกรณี)Comment ที่อธิบายว่าโค้ดทำอะไร แทนที่จะทำไมRename, Extract Method
Duplicated Codeโค้ดที่ซ้ำกันหลายที่Extract Method, Template Method

ตัวอย่าง God Class และการแก้ไข

# BAD — God Class: ทำทุกอย่าง
class UserManager:
    def create_user(self, data): ...
    def update_user(self, user_id, data): ...
    def delete_user(self, user_id): ...
    def validate_email(self, email): ...
    def hash_password(self, password): ...
    def send_welcome_email(self, user): ...
    def send_password_reset(self, user): ...
    def generate_report(self, start_date, end_date): ...
    def export_to_csv(self, users): ...
    def calculate_subscription(self, user): ...
    def process_payment(self, user, amount): ...
    def log_activity(self, user, action): ...

# GOOD — แยกตาม Responsibility
class UserRepository:
    def create(self, data): ...
    def update(self, user_id, data): ...
    def delete(self, user_id): ...
    def find_by_id(self, user_id): ...

class UserValidator:
    def validate_email(self, email): ...
    def validate_password(self, password): ...

class AuthService:
    def hash_password(self, password): ...
    def verify_password(self, password, hashed): ...

class EmailService:
    def send_welcome_email(self, user): ...
    def send_password_reset(self, user): ...

class UserReportService:
    def generate_report(self, start_date, end_date): ...
    def export_to_csv(self, users): ...

Refactoring Techniques — เทคนิคการปรับปรุงโค้ด

Refactoring คือกระบวนการปรับปรุงโครงสร้างภายในของโค้ด โดยไม่เปลี่ยนแปลง Behavior ภายนอก เป็นเหมือนการทำความสะอาดบ้านโดยไม่เปลี่ยนผังห้อง Martin Fowler ผู้เขียนหนังสือ "Refactoring" ได้รวบรวมเทคนิค Refactoring ไว้มากมาย ต่อไปนี้คือเทคนิคที่สำคัญที่สุด:

Extract Method

แยกโค้ดที่ทำงานเฉพาะทางออกจากฟังก์ชันใหญ่มาเป็นฟังก์ชันย่อย เป็นเทคนิคที่ใช้บ่อยที่สุดในการ Refactor

// BEFORE: โค้ดยาวปนกัน
function printInvoice(invoice) {
  console.log("==== INVOICE ====");
  console.log(`Date: ${invoice.date}`);
  console.log(`Customer: ${invoice.customer.name}`);

  let total = 0;
  for (const item of invoice.items) {
    const itemTotal = item.price * item.quantity;
    total += itemTotal;
    console.log(`  ${item.name}: ${item.quantity} x ${item.price} = ${itemTotal}`);
  }

  const tax = total * 0.07;
  const grandTotal = total + tax;
  console.log(`Subtotal: ${total}`);
  console.log(`Tax (7%): ${tax}`);
  console.log(`Total: ${grandTotal}`);
}

// AFTER: Extract Method
function printInvoice(invoice) {
  printHeader(invoice);
  const subtotal = printLineItems(invoice.items);
  printTotals(subtotal);
}

function printHeader(invoice) {
  console.log("==== INVOICE ====");
  console.log(`Date: ${invoice.date}`);
  console.log(`Customer: ${invoice.customer.name}`);
}

function printLineItems(items) {
  let total = 0;
  for (const item of items) {
    const itemTotal = item.price * item.quantity;
    total += itemTotal;
    console.log(`  ${item.name}: ${item.quantity} x ${item.price} = ${itemTotal}`);
  }
  return total;
}

function printTotals(subtotal) {
  const tax = subtotal * TAX_RATE;
  console.log(`Subtotal: ${subtotal}`);
  console.log(`Tax (${TAX_RATE * 100}%): ${tax}`);
  console.log(`Total: ${subtotal + tax}`);
}

Replace Conditional with Polymorphism

# BEFORE: Switch/if-else ยาว
def calculate_shipping(order):
    if order.shipping_type == "standard":
        if order.weight <= 5:
            return 50
        else:
            return 50 + (order.weight - 5) * 10
    elif order.shipping_type == "express":
        if order.weight <= 5:
            return 100
        else:
            return 100 + (order.weight - 5) * 20
    elif order.shipping_type == "overnight":
        return 300 + order.weight * 30

# AFTER: Polymorphism
from abc import ABC, abstractmethod

class ShippingStrategy(ABC):
    @abstractmethod
    def calculate(self, order) -> float:
        pass

class StandardShipping(ShippingStrategy):
    BASE_COST = 50
    EXTRA_PER_KG = 10

    def calculate(self, order):
        extra = max(0, order.weight - 5) * self.EXTRA_PER_KG
        return self.BASE_COST + extra

class ExpressShipping(ShippingStrategy):
    BASE_COST = 100
    EXTRA_PER_KG = 20

    def calculate(self, order):
        extra = max(0, order.weight - 5) * self.EXTRA_PER_KG
        return self.BASE_COST + extra

class OvernightShipping(ShippingStrategy):
    BASE_COST = 300
    PER_KG = 30

    def calculate(self, order):
        return self.BASE_COST + order.weight * self.PER_KG

# Usage
SHIPPING_STRATEGIES = {
    "standard": StandardShipping(),
    "express": ExpressShipping(),
    "overnight": OvernightShipping(),
}

def calculate_shipping(order):
    strategy = SHIPPING_STRATEGIES[order.shipping_type]
    return strategy.calculate(order)

Extract Class

// BEFORE: Class ที่มี Responsibility มากเกินไป
class User {
  constructor(name, email, street, city, zipCode, country) {
    this.name = name;
    this.email = email;
    this.street = street;
    this.city = city;
    this.zipCode = zipCode;
    this.country = country;
  }

  getFullAddress() {
    return `${this.street}, ${this.city} ${this.zipCode}, ${this.country}`;
  }

  isInternational() {
    return this.country !== 'Thailand';
  }
}

// AFTER: Extract Address class
class Address {
  constructor(street, city, zipCode, country) {
    this.street = street;
    this.city = city;
    this.zipCode = zipCode;
    this.country = country;
  }

  getFullAddress() {
    return `${this.street}, ${this.city} ${this.zipCode}, ${this.country}`;
  }

  isInternational() {
    return this.country !== 'Thailand';
  }
}

class User {
  constructor(name, email, address) {
    this.name = name;
    this.email = email;
    this.address = address;  // Composition
  }
}

SOLID Principles — หลักการ 5 ข้อ

SOLID เป็นหลักการออกแบบ Object-Oriented ที่ Robert C. Martin รวบรวมไว้ เป็นรากฐานสำคัญของ Clean Code และ Clean Architecture:

S — Single Responsibility Principle (SRP)

Class ควรมีเหตุผลเดียวในการเปลี่ยนแปลง หมายความว่าแต่ละ Class ควรรับผิดชอบเพียง Responsibility เดียว ตัวอย่างที่ชัดเจนคือการแยก God Class ออกเป็นหลาย Class ตาม Responsibility ดังที่แสดงในหัวข้อ Code Smells ข้างต้น

O — Open/Closed Principle (OCP)

Class ควรเปิดให้ขยาย (Extension) แต่ปิดไม่ให้แก้ไข (Modification) เมื่อต้องการเพิ่มฟีเจอร์ใหม่ ควรขยายด้วยการสร้าง Class ใหม่ ไม่ใช่แก้ไข Class เดิม

# OCP — เพิ่ม Shipping type ใหม่ได้โดยไม่ต้องแก้โค้ดเดิม
class SameDayShipping(ShippingStrategy):
    def calculate(self, order):
        return 500 + order.weight * 50

# เพิ่มเข้า Registry ได้เลย ไม่ต้องแก้ if-else
SHIPPING_STRATEGIES["same_day"] = SameDayShipping()

L — Liskov Substitution Principle (LSP)

Subclass ต้องสามารถใช้แทน Parent class ได้โดยไม่ทำลาย Behavior ถ้า Subclass ทำให้โปรแกรมพัง แสดงว่าผิดหลัก LSP

# BAD — ผิด LSP: Square ไม่ควร inherit Rectangle
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)

    # ปัญหา: set width แล้ว height ไม่เปลี่ยนตาม
    # ทำให้ใช้แทน Rectangle ไม่ได้อย่างถูกต้อง

# GOOD — ใช้ Composition หรือ Interface แทน
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    def area(self):
        return self.width * self.height

class Square(Shape):
    def __init__(self, side):
        self.side = side
    def area(self):
        return self.side ** 2

I — Interface Segregation Principle (ISP)

Client ไม่ควรถูกบังคับให้ Implement Interface ที่ไม่ใช้ ควรแยก Interface ใหญ่เป็นหลาย Interface เล็กๆ

# BAD — Interface ใหญ่เกินไป
class Worker(ABC):
    @abstractmethod
    def work(self): pass
    @abstractmethod
    def eat(self): pass
    @abstractmethod
    def sleep(self): pass

class Robot(Worker):
    def work(self): ...
    def eat(self): raise NotImplementedError  # Robot ไม่กินข้าว!
    def sleep(self): raise NotImplementedError

# GOOD — แยก Interface
class Workable(ABC):
    @abstractmethod
    def work(self): pass

class Feedable(ABC):
    @abstractmethod
    def eat(self): pass

class Human(Workable, Feedable):
    def work(self): ...
    def eat(self): ...

class Robot(Workable):
    def work(self): ...

D — Dependency Inversion Principle (DIP)

Module ระดับสูง ไม่ควรขึ้นอยู่กับ Module ระดับต่ำ ทั้งคู่ควรขึ้นอยู่กับ Abstraction

# BAD — High-level module ขึ้นกับ Low-level module
class OrderService:
    def __init__(self):
        self.db = MySQLDatabase()  # ผูกกับ MySQL โดยตรง
        self.mailer = GmailSender()  # ผูกกับ Gmail โดยตรง

# GOOD — Dependency Inversion
class OrderService:
    def __init__(self, db: Database, mailer: EmailSender):
        self.db = db
        self.mailer = mailer

# Inject dependencies
service = OrderService(
    db=PostgresDatabase(),
    mailer=SendGridMailer()
)
# เปลี่ยน Implementation ได้โดยไม่ต้องแก้ OrderService

Comments — เมื่อไหร่ควรเขียน Comment

Comment ที่ดีคือ Comment ที่ไม่ต้องเขียน เพราะโค้ดอ่านรู้เรื่องด้วยตัวเอง แต่มีบางกรณีที่ Comment มีประโยชน์:

# BAD — Comment ที่ไม่จำเป็น (อธิบายสิ่งที่เห็นชัดอยู่แล้ว)
# เพิ่ม 1 เข้า counter
counter += 1

# ตรวจสอบว่า user เป็น null หรือไม่
if user is None:
    return None

# GOOD — Comment ที่มีประโยชน์ (อธิบาย "ทำไม" ไม่ใช่ "อะไร")
# ต้อง +1 เพราะ API ใช้ 1-based index (ไม่ใช่ 0-based)
page_number = index + 1

# Timeout 30s เพราะ payment gateway ของ Bank X มี SLA 25s
PAYMENT_TIMEOUT = 30

# Workaround สำหรับ bug ใน library v2.3
# ref: https://github.com/lib/issues/1234
result = process(data, legacy_mode=True)

Error Handling — จัดการข้อผิดพลาดอย่างถูกต้อง

# BAD — Error handling ที่ไม่ดี
def get_user(user_id):
    try:
        user = db.query(f"SELECT * FROM users WHERE id = {user_id}")
        data = external_api.get(f"/profiles/{user_id}")
        return {**user, **data}
    except:  # Catch ทุกอย่าง — อันตราย!
        return None  # ไม่รู้ว่าผิดตรงไหน

# GOOD — Error handling ที่ชัดเจน
def get_user(user_id: int) -> User:
    try:
        user = user_repository.find_by_id(user_id)
    except DatabaseConnectionError as e:
        logger.error(f"Database error fetching user {user_id}: {e}")
        raise ServiceUnavailableError("Database is unavailable") from e

    if user is None:
        raise UserNotFoundError(f"User {user_id} not found")

    try:
        profile = profile_api.get_profile(user_id)
        user.profile = profile
    except ExternalAPIError as e:
        logger.warning(f"Could not fetch profile for user {user_id}: {e}")
        # Profile is optional — continue without it

    return user
หลักการ Error Handling ที่ดี: อย่า catch Exception กว้างเกินไป, อย่า return null เมื่อเกิดข้อผิดพลาด (ใช้ Exception แทน), Log ข้อผิดพลาดพร้อมบริบทที่เพียงพอ, แยก Error ที่ Recoverable กับ Unrecoverable ออกจากกัน

Formatting and Consistency — ความสม่ำเสมอของโค้ด

โค้ดที่อ่านง่ายต้องมี Format ที่สม่ำเสมอทั้งโปรเจกต์ ใช้เครื่องมือ Auto-formatter เพื่อให้ทั้งทีมใช้รูปแบบเดียวกัน:

# เครื่องมือ Formatting ยอดนิยม
# Python: Black + isort + flake8
pip install black isort flake8
black .                    # Format โค้ด
isort .                    # จัดเรียง imports
flake8 .                   # ตรวจ style

# JavaScript/TypeScript: Prettier + ESLint
npx prettier --write .
npx eslint --fix .

# Go: gofmt (built-in)
gofmt -w .

# Rust: rustfmt (built-in)
cargo fmt

# ตั้งค่าใน CI/CD ให้ตรวจ Format ทุก PR
# .github/workflows/lint.yml
# - run: black --check .
# - run: npx prettier --check .

Code Review Best Practices

Code Review เป็นกระบวนการสำคัญในการรักษาคุณภาพโค้ดของทีม ต่อไปนี้คือ Best practices:

Refactoring Legacy Code อย่างปลอดภัย

Legacy Code คือโค้ดที่ไม่มี Tests ทำให้การแก้ไขเสี่ยงต่อการทำลาย Functionality ที่ใช้งานอยู่ Michael Feathers ผู้เขียน "Working Effectively with Legacy Code" แนะนำขั้นตอนดังนี้:

  1. ระบุจุดที่ต้อง Refactor — เลือกส่วนที่เปลี่ยนบ่อยหรือมีบั๊กบ่อย ไม่ต้อง Refactor ทุกอย่างพร้อมกัน
  2. เขียน Characterization Tests — เทสต์ที่บันทึก Behavior ปัจจุบัน แม้จะไม่แน่ใจว่า Behavior นั้นถูกต้อง เพื่อให้มั่นใจว่า Refactoring ไม่เปลี่ยน Behavior
  3. Refactor ทีละเล็ก — เปลี่ยนทีละน้อย รันเทสต์บ่อยๆ ไม่ทำ Big Bang Refactoring
  4. ใช้ IDE Refactoring Tools — เครื่องมือ Refactoring ใน IDE ปลอดภัยกว่าการแก้ด้วยมือ เช่น Rename, Extract Method, Move Class
# ขั้นตอนที่ 1: เขียน Characterization Test
def test_legacy_calculate_price_current_behavior():
    """บันทึก Behavior ปัจจุบัน (ยังไม่รู้ว่าถูกหรือผิด)"""
    result = legacy_calculate_price(100, "premium", True)
    assert result == 85.0  # บันทึกค่าที่ได้จริง

    result = legacy_calculate_price(100, "standard", False)
    assert result == 100.0

# ขั้นตอนที่ 2: Refactor ทีละน้อย (เทสต์ยังผ่าน)
# ขั้นตอนที่ 3: เขียนเทสต์ใหม่สำหรับ Behavior ที่ถูกต้อง

Technical Debt Management — จัดการหนี้ทางเทคนิค

Technical Debt คือ "ต้นทุนในอนาคต" ที่เกิดจากการตัดสินใจทางเทคนิคที่เร่งรีบในปัจจุบัน เหมือนการกู้เงิน ยิ่งปล่อยนานดอกเบี้ยยิ่งสูง การจัดการ Technical Debt ที่ดีคือ:

Refactoring Tools — เครื่องมือช่วย Refactor

IDE สมัยใหม่มีเครื่องมือ Refactoring ที่ทรงพลัง ช่วยให้ Refactor ได้ปลอดภัยและรวดเร็ว ควรเรียนรู้ Keyboard shortcuts เพื่อใช้งานได้คล่อง:

เครื่องมือฟีเจอร์Keyboard Shortcut (VS Code)
Rename Symbolเปลี่ยนชื่อตัวแปร/ฟังก์ชันทั้งโปรเจกต์F2
Extract Methodแยกโค้ดที่เลือกเป็นฟังก์ชันใหม่Ctrl+Shift+R
Extract Variableแยก Expression เป็นตัวแปรCtrl+Shift+R
Inline Variableแทนที่ตัวแปรด้วยค่าCtrl+Shift+R
Move to Fileย้าย Class/Function ไปไฟล์อื่นRight-click menu
Auto Importเพิ่ม Import อัตโนมัติCtrl+.
# เครื่องมือ Static Analysis ที่ช่วยตรวจ Code Quality

# Python
pip install pylint mypy ruff
pylint app/                # ตรวจ Code quality
mypy app/                  # ตรวจ Type errors
ruff check app/            # ตรวจ Linting (เร็วมาก)

# JavaScript/TypeScript
npx eslint . --ext .ts,.tsx
npx tsc --noEmit           # Type check

# Go
go vet ./...               # ตรวจ Code issues
golangci-lint run          # ตรวจหลาย Linters พร้อมกัน

# Rust
cargo clippy               # Linter ที่ดีที่สุดของ Rust

Clean Architecture Overview

Clean Architecture ที่ Robert C. Martin เสนอ แบ่งระบบเป็นชั้นวงแหวน โดยชั้นในไม่ขึ้นอยู่กับชั้นนอก:

# ตัวอย่างโครงสร้างโฟลเดอร์ตาม Clean Architecture
project/
├── domain/               # Entities + Use Cases (ชั้นใน)
│   ├── entities/
│   │   ├── user.py
│   │   └── order.py
│   ├── usecases/
│   │   ├── create_order.py
│   │   └── get_user.py
│   └── repositories/     # Interface (Abstract)
│       ├── user_repo.py
│       └── order_repo.py
├── infrastructure/       # Frameworks & Drivers (ชั้นนอก)
│   ├── database/
│   │   ├── postgres_user_repo.py   # Implementation
│   │   └── postgres_order_repo.py
│   ├── api/
│   │   └── fastapi_app.py
│   └── email/
│       └── sendgrid_service.py
└── tests/

สรุป — Checklist สำหรับ Clean Code

การเขียน Clean Code ไม่ใช่สิ่งที่ทำครั้งเดียวจบ แต่เป็นวินัยที่ต้องฝึกฝนทุกวัน ต่อไปนี้คือ Checklist ที่สามารถใช้ตรวจสอบโค้ดของตัวเองได้:

จำไว้ว่า Clean Code ไม่ได้หมายความว่าต้องสมบูรณ์แบบตั้งแต่แรก แต่หมายความว่าต้องดีขึ้นเรื่อยๆ ทุกครั้งที่แตะโค้ดนั้น ปฏิบัติตาม Boy Scout Rule อย่างสม่ำเสมอ แล้วโค้ดของคุณจะสะอาดขึ้นเรื่อยๆ เริ่มวันนี้ด้วยการ Refactor ฟังก์ชันยาวๆ สักตัวหนึ่ง แล้วคุณจะเห็นว่า Clean Code เปลี่ยนชีวิตการเขียนโปรแกรมได้จริง


Back to Blog | iCafe Forex | SiamLanCard | Siam2R