วันนี้บีมหยิบเนื้อหาบางส่วนจากหนังสือที่อ่านเมื่อปีที่แล้วที่ช่วยให้ผมคิดและเขียนโค้ดที่สะอาดขึ้นกว่าเดิมเยอะเลย ละเข้าใจที่มาที่ไปของหลายๆ Framework ที่มาจากแนวคิด Clean Code นิแหละ
เล่มนี้เป็น 1 ในหนังสือโค้ดที่แนะนำกันว่าควรอ่าน 🤔 จะจริงมั้ยนะ 55 ถ้าให้ตอบสั้นๆก็คิดว่าจริง แต่เรื่องนี้มี Discuss ท้ายบทความ ไปดูเนื้อหากันก่อนดีกว่าลุยย
ปล. ตัวอย่างโค้ดในบทความนี้เป็นภาษา Swift นะค้าบชินมือคนเขียน 55 อ่านไม่ยาก

คนเขียนเล่มนี้คือ Robert C. Martin หรือฉายา ลุงบ๊อบ (Uncle Bob) ตำนาน Software Engineer ของวงการ ที่ทำงานมากว่า 50 ปี
เบื้องหลังคนที่ผลักดัน Best Pratices หลายๆอย่างที่ทุกวันนี้เราใช้กันพวก SOLID, OOP จนกลายเป็นพื้นฐานที่ทุกคนยอมรับ เป็นคนให้กำเนิดแนวคิดการพัฒนา Software สมัยใหม่อย่าง Clean Code, Clean Architecture และ Agile Manifesto
ผลงานลุงแต่ละอันคือเปลี่ยนโลกของ Software Development ทั้งนั้น
ปัจจุบันลุงก็กลายเป็นแรงบรรดาลใจของ Programmer ทั่วโลกไปแล้วกราบบ ทุกวันนี้เป็น Consult เดินสายบรรยายแทนแล้ว 555 เจอลุงได้ตามมหาลัย
นิยาม Clean Code
ปรมาจารย์ศิลปะการต่อสู้แต่ละสำนักไม่ได้เห็นตรงกันว่าท่าไหนดีที่สุด เช่นเดียวกันกับโปรแกรมเมอร์ก็มีมุมมองต่างกันว่าโค้ดแบบไหนถึงจะเรียกว่า Clean
— Robert C. Martin (Clean Code, 2008)
จากการที่ลุงไปถามเพื่อนๆกูรูในวงการมาหลายๆคน แต่ละคนก็ตอบว่า
- ทำแค่อย่างเดียวได้ดี
- อ่านแล้วรู้ว่าคนเขียนต้องการจะสื่ออะไร
- คนอื่นเข้ามาแก้ง่าย
- อ่านละรู้ว่าคนเขียน ตั้งใจเขียน
- ไม่ใช้ซ้ำ, ทำแค่อย่างเดียว, อ่านเข้าใจ, ไม่ซับซ้อนมากไป
- ทำได้ตรงที่ต้องการ ไม่มีเซอร์ไพรซ์โผล่มา
คือแต่ละคนก็มี Code ที่ชอบต่างกันแต่อ่านๆแล้วมันก็ตีความคล้ายๆกันว่า “สะอาด เป็นระเบียบ ตรงไปตรงมา”
เหตุผลที่ลุงบ๊อบ เชื่อว่าการดูแลรักษาโค้ดให้สะอาดสำคัญมาก เพราะถ้าปล่อยให้โค้ดเละแล้วคิดว่าค่อยกลับมาแก้ สิ่งที่จะเกิดขึ้นคือเราจะไม่กลับมาแก้อีกแล้ว Leblanc’s law: Later equals never” 😂
ลุงเล่าประสบการณ์ทำงานว่า เค้าไฟว์กับ Manager มาหลายคนเพื่อปกป้องโค้ดตัวเองสุดชีวิต ไม่ยอมทำวิธีที่ง่ายและเร็วแบบที่ Manager อยากได้เพราะจะทำให้โค้ดเสียคุณภาพในระยะยาว
ในหนังสือ Clean Coder อีกเล่มนึงลุงบ็อบเล่าว่า เขาเคยปฏิเสธงานที่ละเมิดคุณภาพโค้ดจนเสี่ยงถูกไล่ออก และบางครั้งก็เลือกลาออกเอง เพราะไม่ยอมประนีประนอมกับคุณภาพ สุดจัด
ทำยังไงถึงเขียน Code ที่ Clean ได้?
ลุงใช้เปรียบเทียบคนเขียน Clean Code ว่าเป็น “Craftsmanship” หรือว่างานช่างฝีมือที่ใช้ความปราณีต ช่างแกะสลัก หรือช่างทำนาฬิกา ที่ต้องตั้งใจทำทุกขั้นตอน
บีมว่าสิ่งสำคัญที่สุดเลยคือเราต้องตั้งใจ เราต้องแคร์! ตั้งใจอยากให้งานเราดีที่สุด เชื่อว่าอ่านมาถึงตรงนี้ก็ต้องตั้งใจระดับนึงแล้ว ถือว่าผ่าน 👍
มีความตั้งใจเป็นก้าวแรก และการจะเรียนรู้เป็น Craftsmanship ใช้อีกสองอย่างคือ
1. ความรู้ : เราต้องรู้จักแพทเทิน พื้นฐานว่าอะไรถูกผิด ควรทำหรือไม่ควรทำก่อน (ก็คือ 90% ของบทความนี้เลย)
2. การฝึกฝน: การสังเกตุ การฝึกฝน หรือ ใช้เซนต์ (Gut Feeling)
3. อย่างที่สามผมเติมให้คือ ความตั้งใจ
ลุงบอกบางคนมีพรสวรรค์แค่มองก็รู้และทำได้สวยเลย ยินดีด้วย ส่วนถ้าไม่มีไม่เป็นไรสามารถเรียนรู้และฝึกฝนได้คับ 💯
💡 วิธีนึงทีลุงเอามาใช้ กฎลูกเสือ (Boy Scout rule): “Leave the campground cleaner than you found it” ทุกที่ๆเราเข้าไปแก้ทำอะไร ถ้าเราทำให้โค้ดดีขึ้นสะอาดขึ้นได้ให้เราทำเลยเล็กๆน้อยๆก็ยังดี

Names
การตั้งชื่อให้ดี คือพื้นฐานที่สำคัญที่สุดของโค้ดที่มีคุณภาพ
โปรแกรมเมอร์จะเข้าใจว่าการตั้งชื่อ (ให้ดี) ปวดหัวขนาดไหน 55 ลุงบอกส่วนที่ยากที่สุดครึ่งนึงของ Clean Code อยู่ที่การตั้งชื่อนี่แหละ
ชื่อที่ดีต้องอ่านแล้วเข้าใจได้เลยไม่ต้องไล่โค้ดเพิ่ม เขียนสั้นดีที่สุด แต่ถ้าทำไม่ได้เขียนยาวๆแล้วเข้าใจก็ถือว่าดีเหมือนกัน
// ✅✅✅ ชื่อสั้นกระชับอ่านเข้าใจทันที
let maxLoginAttempts = 3
// ✅✅ ชื่อยาวและอธิบายครบ
let maximumNumberLoginAttemptsBeforeAccountLocked = 3
// ❌ ชื่อสั้นแต่ไม่สื่ออะไร
let max = 3
// ❌❌❌ ชื่อไม่สื่ออ่านแล้วงงกว่าเดิม
let mxlg = 3
ชื่อที่ดีหน้าตาเป็นยังไงนะ?
1. Intension-Reavealing name: ชื่อควรบอกว่ามันทำอะไร
// ❌ ไม่ดี: อ่านแล้วไม่รู้เอาไปใช้ทำอะไร
let date = Date()
// ✅ ดี: อ่านแล้วเข้าใจทันที
let customerSignupDate = Date()
2. Avoid Disinformation: ไม่ใช้คำที่ไม่สื่ออะไร
ถ้าเป็น Type Array เติม s ให้ดีที่สุด อย่าใช้ list ตามท้ายเพราะมันแปลได้ทั้ง Array หรือ list ในภาษาอังกฤษก็ได้แบบคำว่า todo list
// ❌ ไม่ดี: ไม่รู้ย่อมากจากอะไร
let lst: [String] = ["Somchai"]
// ❌ ไม่ดี: เข้าใจได้หลายแบบ
let accountsList: [String] = ["Somchai"]
// ✅ ดี: ชัดเจน
let accounts: [String] = ["Somchai"]
3. Meaningful Distinction: แตกต่างได้ต้องมีความหมาย
ถ้าจะสร้างตัวแปรใหม่ อย่าใส่พวก a, an, the, info, data แค่ให้มันไม่ซ้ำกัน ให้ใช้คำใหม่ไปเลย
คือคำพวกนี้เราจะเจอเยอะ แบบ accout, accoutData อย่าขี้เกียจคิดชื่อใหม่เลย คิดเถอะๆ 55
// ❌ ไม่ดี: เติมตัวเลขแค่ให้ไม่ซ้ำ
let report = SalesReport()
let report2 = SalesReport()
// ✅ ดี: ชัดเจนว่า Report อะไร
let dailyReport = SalesReport()
let monthlyReport = SalesReport()
// ❌ ไม่ดี: "Info" ไม่ได้บอกอะไรเพิ่ม
var users: [User] = []
var usersInfo: [User] = []
// ✅ ดี: ชัดเจนเก็บ User แบบไหน
var activeUsers: [User] = []
var bannedUsers: [User] = []
4. Pronounecable Name: ใช้ชื่อที่อ่านออกเสียงได้
// ❌ ไม่ดี: ต้องแปลในหัว
let urslang: String = "Thai"
// ✅ ดี: อ่านออกชัดเจน
let userLanguage: String = "Thai"
5. Searchable Names: ใช้ชื่อที่เสิชเจอง่าย
// ❌ ไม่ดี: เสิชทีเจออยู่ 100 ที่
let max: Int = 10
// ✅ ดี: เสิชเจอตรงจุด
let maxApiTransactionCall: Int = 10
6. Avoid Encoding: ระวังตัวย่อ
เลี่ยงใช้พวก txt, str, imp นำหน้าหรือใช้ Hungarian Notation พวกชื่อแบบ _firstName เป็นการตั้งชื่อยุคเก่าลุงบอกเลิกใช้กันได้แล้ว อ่านยาก 55
// ❌ ไม่ดี
let strfirstName: String = "Beamtan"
let _firstName: String = "Beamtan"
// ✅ ดี
let firstName: String = "Beamtan"
7. Avoid Mental Mapping: ระวังใช้ชื่อที่เราต้องจำในหัวระหว่างไล่โค้ด
// ❌ ไม่ดี: ต้องมองกลับไปดูว่า i คืออะไร
for i in week:
if i == "monday"
// ✅ ดี: รู้ว่าเราใช้ตัวแปร day ไม่ต้องกลับขึ้นไปดูใหม่
for day in week:
if day == "monday"
8. Class Name: ใช้ชื่อคลาสเป็น Noun
ความหมายคือเป็นคำๆเดียวไปเลย เพราะถ้ามีพวก manager, super หมายความว่ามันทำหลายหน้าที่แน่นอน
// ❌ ไม่ดี: ไม่รู้เอาไว้ทำอะไรแน่
class CustomerManager
// ❌ ไม่ดี: ไม่ควรใช้ verb ตั้งชื่อ class
class CreateCustomer
// ✅ ดี: ตั้งชื่อ class ด้วย noun
class Customer
9. Method Name: ใช้ชื่อฟังชั่นเป็น Verb
// ❌ ไม่ดี: ดูแล้วไม่รู้ว่าคือ function
func totalPrice() -> Double {}
func userProfile() -> User {}
// ✅ ดี: ตั้งชื่อ function ด้วย verb
func calculateTotalPrice() -> Double {}
func fetchUserProfile() -> User {}
10. Pick One word Per Concept: 1 คอนเซ็ปใช้แค่ 1 คำ
ข้อนี้ช่วยได้เยอะเลยเวลาไล่โค้ด ไม่ต้องเดาว่าฟังชั่นนี้ทำอะไร เห็น get รู้เลยเรียก API
// ❌ ไม่ดี: เรียก api เหมือนกันแต่ใช้คำต่างกันไปหมด
func getUserProfile() -> String {}
func fetchUserId() -> String {}
func pullUserImage() -> String {}
// ✅ ดี: ใช้ get แทนการเรียก api ทั้งหมด
func getUserProfile() -> String {}
func getUserId() -> String {}
func getUserImage() -> String {}
11. Use Solution Domain Names: ใช้ศัพท์เทคนิคที่โปรแกรมเมอร์เข้าใจ ถ้าเป็นปัญหาเทคนิคอล
ในภาษา Swift - ViewController เป็นชื่อมาตรฐานของการตั้งชื่อ Class หน้า UI เห็นปุ๊ปไม่ต้องเข้าไปดูก็เข้าใจ
// ❌ ไม่ดี: ไม่ใช้คำมาตรฐาน
class LoginScreen: UIViewController {
func login() {}
}
// ✅ ดี: iOS programmer อ่านแล้วรู้ทันที
class LoginViewController: UIViewController {
func handleLoginButtonTapped() {}
}
12. Use Problem Domain Names: ใช้ศัพท์ Business ถ้าเรากำลังแก้ปัญหา Business
// ❌ ไม่ดี: ทำงานเหมือนกันก็จริงแต่ไม่รู้ว่าทำอะไรข้างใน
class UserProcessor {
func processUserData(for user: User) {}
}
// ✅ ดี: อ่านแล้วรู้ว่าเป็นการจองตั๋วของผู้โดยสาร
class TicketBookingService {
func reserveSeat(for passenger: Passenger) {}
}
13. Don’t Add Gratuitous Context: อย่าใส่คำเกินความจำเป็น
ตัวอย่างเป็น Project ที่เราทำชื่อ “Awesome” เลยใส่ชื่อโปรเจคไปใน Class, Function
// ❌ ไม่ดี
class AwesomeCustomer {
let awesomeCustomerAddress: [String]
func awesomeGetCustomer() {}
}
Functions
Function should do one thing. They should do it well. They should do it only.
— Robert C. Martin (Clean Code, 2008)
Function ที่ดีควรทำแค่อย่างเดียวให้ดีที่สุด
💡 ตอนเขียน Draft แรกไม่ควรไปโฟกัสเรื่อง Clean มากเพราะเราต้องแก้ปัญหาให้ได้ก่อน โค้ดจะดูยาวยุ่งเหยิงเป็นธรรมดา จากนั้นเขาค่อย Refine, Refactor อย่าลืมว่าการทำให้ใช้ได้ก่อน สำคัญที่สุด
ฟังชั่นที่ดีเป็นยังไง?
1. Small is better, Do one thing: ฟังชั่นควรทำแค่อย่างเดียว
ไม่ควรเกิน 150 บรรทัดประมาณๆ เกินกว่านั้นน่าจะ Do many things ละ 55 ข้อนี้เรื่องจริงนะฟังชั่นเล็กมันเทสง่ายย เชื่อพ้ม🤫
❌ ยัดหลายหน้าที่ในฟังก์ชันเดียว
func applyDiscountAndLogAndSave(price: Double) -> Double {
let discounted = price * 0.9
print(discounted)
saveToDB(discounted)
return discounted
}
✅ แยกหน้าที่
func applyDiscount(_ price: Double) -> Double {
return price * 0.9
}
func logDiscount(_ value: Double) {
print("Discounted: \(value)")
}
func saveDiscount(_ value: Double) {
saveToDB(value)
}
2. ฟังชั่นไม่ซ้อนลึก (Indent) เกิน 2 ชั้น
❌ Indent ซ้อนหลายชั้นอ่านยาก
func process(_ numbers: [Int]) {
for number in numbers {
if number > 0 {
print("isPositive")
if number % 2 == 0 {
print("isEven")
if number < 100 {
print("isLessThan100")
}
}
}
}
}
✅ สร้างตัวแปรแล้วแยกเงื่อนไขเอา
func process(numbers: [Int]) {
for number in numbers {
let isPositive = number > 0
let isEven = number % 2 == 0
let isLessThan100 = number < 100
if isPositive {
print("isPositive")
}
if isEven {
print("isEven")
}
if isLessThan100 {
print("isLessThan100")
}
}
}
3. ไม่ใส่ Argument เยอะ
❌ Argument เยอะเกิน
func createUser(
name: String,
email: String,
age: Int,
address: String,
phone: String
) {}
✅ เมื่อไหร่เจอที่ต้องส่งเข้าไปเยอะๆ สร้าง Object ใหม่แล้วส่งเข้าไปแทน
func createUser(_ input: NewUser) {}
4. No Flag Argument: ไม่ส่ง Boolean เข้าไปใน Function
❌ ใช้ Boolean flag
- เหมือนเป็นเรื่องดีเพราะจะลด If-Else ใน Function หลัก แต่เพิ่มความปวดหัวตอนไล่โค้ดมาก
- ที่สำคัญคือ Test ยาก นึกภาพตอน Test เราต้องเพิ่ม Condition True/False รวมๆแล้วกลายเป็นมี 4 Case ใน Function เดียว
func exportReport(fileName: String, isPDF: Bool) {
if isPDF {
print("Export \(fileName) as PDF")
} else {
print("Export \(fileName) as Excel")
}
}
✅ แก้โดยแยกเป็นสองฟังชั่น ตาม Condition
- อ่านง่าย
- เทสง่าย
- ข้อเสียอาจจะทำให้ Function หลักบรรทัดยาวขึ้นหน่อย แต่แลกกับ
- Readability
- Single Responsibility
func exportPDF(fileName: String) {
print("Export \(fileName) as PDF")
}
func exportExcel(fileName: String) {
print("Export \(fileName) as Excel")
}
5. One level of abstraction per function: แยกชั้นใช้งานกับรายละเอียด
❌ ทำทุกอย่างในที่เดียว
func showUserProfile() {
renderHeader()
let db = Database()
db.connect()
let user = db.query("SELECT *")
renderBody(user)
}
✅ แยกชั้นใช้งาน (High-Level) กับชั้นรายละเอียด (Low-Level) ข้างบนเตรียมของ ข้างล่างทำส่วนรายละเอียด
// ชั้นใช้งาน (High-Level)
func showUserProfile() {
renderHeader()
let user = fetchUser()
renderBody(user)
}
// ชั้นรายละเอียด (Low Level)
func fetchUser() -> User {
let db = Database()
db.connect()
return db.query("SELECT *")
}
6. ไม่เขียน Function ที่ทำงานซ้ำ (Do not repeat yourself)
❌ มีหลายฟังชั่นเปลี่ยนสีเหมือนกัน (Duplicate)
func changeColorYellow() {
self.color = yellow
}
func changeColorBlue() {
self.color = blue
}
✅ รวบเป็นฟังชั่นเดียวแล้วส่ง parameter สีมาแทน
func changeColor(color: Color) {
self.color = color
}
7. ไม่เขียนให้มี Side Effect
❌ ชื่อ Function ว่า Save แต่มี Delete โผล่มา
func saveUser(user: User) {
database.deleteAllUsers()
database.save(user)
}
✅ ชื่อบอกว่ายังไง ข้างในก็ทำตามนั้น
func saveUser(_ user: User) {
database.save(user)
}
8. ตั้งชื่อยาวแต่อ่านรู้เรื่อง > ดีกว่าชื่อสั้นๆแต่งง
❌ อ่านแล้วไม่เข้าใจว่าทำอะไร
func calc() -> Double {}
✅ ยาวขึ้นแต่เข้าใจ ดีกว่าเยอะมาก
func calculateTotalPriceIncludingTax() -> Double {}
Comments
Don’t comment bad code – rewrite it.
— Robert C. Martin (Clean Code, 2008)
ถ้าเรา Code ยังต้องมี Comment มาอธิบายแปลว่าที่เรายังเขียนไม่ดีพอ
เล่มนี้เลยบอกว่า ถ้าไม่จำเป็นจริงๆไม่ควรเขียน โค้ดที่ดีไม่ควรมีคอมเม้นอธิบาย ให้กลับไปเขียนโค้ดที่อ่านแล้วเข้าใจแทน เพราะโค้ดที่ดีต้องอ่านแล้วต้องเข้าใจว่าคนเขียนจะสื่ออะไร
ส่วนโค้ดที่ Comment ทิ้งเอาไว้คือควรลบออกทุกกรณี ถ้าอยากกลับไปดูให้ใช้ Version Control (Git) ดูแทน
✅ โค้ดที่อธิบายด้วยตัวมันเองไม่ได้
// ใช้สูตรเดิมเพราะ API ใหม่ยังไม่รองรับบน iOS เวอร์ชันต่ำกว่า 15
let result = legacyCalculatePrice(items)
// อ้างอิงจาก: https://en.wikipedia.org/
func shortestPath() {}
// FIXME: ลบโค้ดนี้ตอน API แก้บั๊กแล้ว (ticket #1234)
func fetchData() {}
❌ คอมเม้นต์ไม่บอกอะไรเพิ่ม, ปิดโค้ดไว้เฉยๆ
// ฟังชั่นนี้ลบ User ออกจาก Database
func saveUser(_ user: User) {
database.save(user)
}
// ปิดไว้ก่อนแก้บัคอยู่
// processPayment()
// sendInvoice()
updateInventory()
Formatting
“Your style and discipline survives, even though your code does not”
— Robert C. Martin (Clean Code, 2008)
เรื่อง Format เป็นเรื่องนึงที่ลุงให้ความสำคัญมาก วันนึงโค้ดเราอาจจะโดนแก้ไปหมดแล้ว แต่สไตล์ของโค้ดเราจะอยู่ต่อไป
ลุงบอกว่าทุกคนมี Format ที่ตัวเองชอบส่วนตัวกันทั้งนั้นแหละ บางคนชอบเว้น 1 ช่อง บางคนอาจจะเว้น 4 ช่อง แต่เมื่อ Programmer ทำงานเป็นทีม เราต้องใช้กฎของทีม ✅
💡 ก่อนจะ Code เราควรหาข้อตกลง Formatting ให้ชัดเจน รวมไปถึงวิธีการตั้งชื่อตัวแปร หรือชื่อฟังชั่น คลาส อื่นๆด้วย ลุงแนะนำว่าหา Practice ที่มันเป็นมาตรฐานของภาษานั้นๆที่มันมี Document ชัดเจนหรือคนใช้กันเยอะๆ – อ่านเรื่อง Code Convention เพิ่ม
โค้ดที่ดีควรจะอ่านแล้วเหมือนเป็นคนๆเดียวกันเขียน

จัดเรียงแบบไหนดี?
ไอเดียคือการอ่านโค้ดควรเหมือนอ่านหนังสือพิมพ์จากบนลงล่าง ซ้ายไปขวา อ่านหัวข้อภาพใหญ่ข้างบนสุดก่อนแล้วค่อยตามไปอ่านรายละเอียดข้างล่าง (High-Level -> Low-Level)
ขนาด File
มีไม่เกิน 200-500 บรรทัดต่อไฟล์
แนวตั้ง
- เขียนฟังชั่นที่มีรายละเอียดน้อย (High-Level) ไว้ข้างบน แล้วเรียกต่อลงไปเรื่อยๆ ฟังชั่นรายละเอียด (Low-Level) อยู่ข้างล่าง
- เอาตัวแปรไว้ด้านบน
- ถ้ามี Function ที่เรียกต่อกัน วางที่จะเรียกต่อไว้ล่าง Function แรก
- เว้นช่องว่างระหว่าง Function
- วางเป็นกรุ๊ป วางตัวแปร + Function ที่มีคอนเซปเหมือนกันไว้ด้วยกัน
แนวนอน
ความกว้างบรรทัดละ 100-120 ตัวกำลังดี มากกว่านี้เราต้องกวาดสายตาออกด้านข้างแทน (โค้ดที่ดีเน้นอ่านจากบนลงล่าง 💯)
Objects
เวลาเรียกใช้ object ให้เขียนแยกหลายบรรทัดดีกว่าเขียน 1 line ต่อๆ chain ไปเรื่อยๆ ลุงเรียก object แบบนี้ว่า รถไฟชนกัน Train wreck 55 🚂
❌ รถไฟชนกัน (Train Wreck)
let outputDir: String = ctxt.getOptions().getScratchDir().getAbsolutePath()
✅ แยกทีละบรรทัดอ่านง่ายกว่า
let a = ctxt.getOptions()
let b = a.getScratchDir()
let outputDir: String = b.getAbsolutePath()
Unit tests
Test code is just as important as production code, as clean as production code
— Robert C. Martin (Clean Code, 2008)
ควรดูแลโค้ดใน Unit Test ให้ดีเหมือนโค้ดใน Production
คอนเซ็ปต์ก็คือทำตามกฎ 3 อย่างของ Test Driven Development (TDD)
- ห้ามเขียน Production โค้ดจนกว่าจะเขียน Fail test
- ห้ามเขียน Unit test เกินกว่าที่จำเป็น เพื่อให้มัน Fail
- ห้ามเขียน Production โค้ดเกินกว่าที่จำเป็น เพื่อให้ผ่าน Unit test ที่กำลัง Fail ตอนนี้
หัวใจของ Unit Test คือก่อนที่จะเพิ่มอะไรใหม่ๆ Make sure ว่าเราต้องรู้ว่าโค้ดเราจะเฟลแบบไหนได้บ้าง
Clean Tests
Goal ของการเขียนใน Unit test คือการเขียนให้อ่านง่ายสำคัญที่สุด (Readability)
- เขียนให้ชัดเจนว่าเทสอะไร (Clarity)
- ไม่เขียนให้ซับซ้อนเกินจำเป็น (Simplicity)
- ใช้โค้ดน้อยที่สุดที่ยังชัดเจนอยู่ (Density of expression)
วิธีเขียน Clean Test จะใช้หลักการ F.I.R.S.T
- Fast – เทสต้องเร็ว
- Indepentdent – ต้องจบในตัวเอง ไม่พึ่งพาเทสตัวอื่น
- Reapeatable – รันได้เหมือนเดิมทุกรอบ ไม่เกี่ยวกับเครื่อง, เวลา, API และอื่นๆ
- Self-Validating – Output เป็น True/False เท่านั้น
- Timely – ควรเขียนเทสก่อนหรือระหว่างเราทำฟีเจอร์
ส่วนการจัดวางเทสแบบ 1 Concept ต่อ 1 Function ดีกว่า Test table ถึงจะเขียนเยอะกว่าแต่อ่านเข้าใจง่ายกว่าเยอะเลย (Readability)
✅ One concept per Function
หนึ่งเคสต่อหนึ่งฟังชั่น ง่ายตอน Debug ง่ายตอนเขียน กลับมาเพิ่มลบเคสก็ง่าย รักเลย
final class DiscountCalculatorTests: XCTestCase {
func testNoDiscount() {
// ... ///
}
func testFivePercentDiscount() {
// ... ///
}
func testTenPercentDiscountForVIPCustomer() {
// ... ///
}
}
❌ Test Table
คือการใส่ Case ที่จะเทสไว้ใน Array แล้ว Forloop เทส
ถ้ามีเป็น 100 เคสนี่หากันตาแตก
func testDiscountCalculator_Table() {
let cases: [(price: Double, isVIP: Bool, expected: Double)] = [
(90, false, 0),
(150, false, 7.5),
(200, true, 20)
]
for testCase in cases {
// ... //
}
}
Classes
Class should be small
The first rule of classes is that they should be small.
The second rule of classes is that they should be smaller than that.— Robert C. Martin (Clean Code, 2008)
- ฟังก์ชั่น (Function) ใช้ “จำนวนบรรทัด” เป็นตัววัดว่ามันใหญ่ไปรึยัง
- คลาส (Class) ใช้ “จำนวนหน้าที่ (responsibilities)” เป็นตัววัดว่าคลาสเราใหญ่ไปรึยัง
Single Responsibility
โครงสร้างของ Class ควรทำตามหลัก SOLID ข้อแรก Single Responsibility (SRP)
“A class should have one, and only one reason to change. and one responsibility.”
การตั้งชื่อก็สำคัญ
- ชื่อแบบ
InvoicePrinterชัดเจนรู้ว่ามันทำอะไร (Single Purpose) - ชื่อแบบ
SuperDashboardอ่านแล้วรู้ว่าทำหน้าที่กว้างเกินไปขัดกับ SRP
❌ Multi Responsibilities
class UserManager {
func createUser() {}
func deleteUser() {}
func saveToDatabase() {}
func sendWelcomeEmail() {}
}
✅ Single Responsibility
class UserRepository {
func save(_ user: User)
func delete(_ user: User)
}
class EmailService {
func sendWelcomeEmail(to user: User)
}
Class ที่ดีต้องมีความเชื่อมโยงกันสูง High Cohesion (high) ✅
Cohesion คือ ตัววัดการเชื่อมโยงของ Method, Variable ทุกอย่างภายในคลาส ยิ่งมันเชื่อมกันเกี่ยวข้องกันมากเท่าไหร่ Cohesion ก็ยิ่งมาก ถือว่าดี 👍
วิธีทำให้มี Cohesion สูง
- ทำคลาสให้เล็ก (Small)
- ทำให้ทุก Function กับตัวแปรใน Class มันโดนเรียกใช้กัน
- ทำให้คลาสมีหน้าที่เดียว (Single Responsibility)
ปัญหาคือยิ่ง Class ใหญ่มันยิ่งลดความเชื่อมโยงกัน (Cohesion) วิธีแก้ก็ให้แตก Class ออกมาอีก ทำให้ Cohesion กลับมาสูงเหมือนเดิม
✅ แยกหน้าที่กันคนละ Class ไปเลย (High Cohesion)
// จัดการการแสดงผล
class UserView {
func display(user: User) {
print("Name: \(user.name), Email: \(user.email)")
}
}
// จัดการโหลดข้อมูล
class UserService {
func fetchUser() -> User {
return User(name: "Beam", email: "beam@example.com")
}
}
Class ที่ดีต้องพึ่งพาภายนอกน้อย Low Coupling ✅
Coupling คือ การพึ่งพาคลาสหรือโมดูลอื่นๆ (ข้างนอก) แบบตรงๆ ยิ่งเชื่อมหลายๆตัวทำให้คลาสเราเป็นเหนียวหนึบขยับตัวยาก ถือว่าไม่ดี 👎
วิธีทำให้มี Coupling ต่ำ ทำให้แต่ละ Class มันเชื่อมกันตรงๆน้อยที่สุด โดยพระเอกของเราก็คือ Dependency Injection ที่ทำให้เป็นการเชื่อมต่อแบบหลวมๆ
✅ ใช้ UserDisplay Interface คุยกับคลาสอื่นแทน
// Low Coupling
class UserManager {
let display: UserDisplay
init(display: UserDisplay) {
self.display = display
}
func printUser() {
let user = User(name: "Beam", email: "beam@example.com")
display.show(user: user)
}
}
Low Cohesion + High Coupling ตัวอย่างที่ไม่ดี ❌
- เคสนี้ UserManager ผูกติดกับ User จะใช้ object ชนิดอื่นก็ไม่ได้ = High Coupling
- และยังมีตัวแปร cat ที่ไม่ได้ใช้ คลาสมีความไม่เกี่ยวกันมากขึ้น = Low Cohesion
class UserManager {
let user = User(name: "Beamtan", email: "beam@example.com")
let cat = User(name: "cat", email: "cat@example.com")
func loadAndShowUser() {
print("Name: \(user.name), Email: \(user.email)")
}
}
UserManager().loadAndShowUser()
วิธีออกแบบโค้ดที่สะอาดและดูแลง่าย

ลุงบอกว่ากฏ 4 ข้อ Simple Design ของเพื่อนลุง Kent Beck (ผู้คิดค้น Extreme Programming) จะช่วยให้เราออกแบบได้ดีขึ้น
เรียงความสำคัญจากมากสุดไปน้อยสุด
- Runs all the tests — ระบบต้องผ่านการทดสอบทั้งหมด เพื่อให้มั่นใจว่าโค้ดไม่พัง
- Contains no duplication — ไม่มีโค้ดซ้ำ
- Expresses the intent of the programmer — โค้ดต้องสื่อความหมายชัดเจน อ่านแล้วเข้าใจว่าคนเขียนจะสื่ออะไร
- Choosing good name
- Small classes, functions
- Code convention
- Minimizes the number of classes and methods — เพิ่ม class และ method เท่าที่จำเป็น
แนวคิดและวิธีดูแลโปรเจค (Successive Refinement)
Bad schedules can be redone. Bad requirements can be redefined. Bad team dynamics can be repaired. But bad code rots and ferments, becoming an inexorable weight that drags the team down.
— Robert C. Martin (Clean Code, 2008)
หนึ่งในการทำลายโปรเจคคือการเปลี่ยน Structure ของทั้ง Project ที่จะมาในรูปแบบที่เรียกว่า “Improvement” 😂 บางโปรเจคหลังจากเจอ Improvement เป๋ไปเลย ไม่เคยกลับไปเป็นเหมือนเดิมได้อีกเลย (เปลี่ยนทีเดียวหนักๆ)
โปรเจคที่เรียบร้อยไม่ได้เกิดจากฮึดใส่แรงครั้งเดียวจบ แต่เป็นการขัดเกลาทีละนิด (Successive Refinement) อาจจะเป็นวัน หลายวัน หรือเดือน
Refector เครื่องมือที่ช่วย Successive Refinement
การ Refactor ไม่ใช่การเขียนใหม่ แต่เป็นการปรับโค้ดเก่าให้ดีขึ้น โดยที่ยังทำทุกอย่างเหมือนเดิม (Refactor NOT Rewrite) โดยวิธีสังเกตว่าตรงไหนต้อง Refactor เราต้องดมกลิ่น Code Smell เอา
Code Smell สั้นๆคือมันเป็นโค้ดที่อยู่ผิดที่ผิดทาง ทำงานได้ก็จริงแต่ถ้าอยู่ไปนานๆจะเริ่มมีปัญหาตามออกมา Smelly!
ถ้าตามทฤษฎีเปะๆ ต้องทำควบคู่กับ TDD (Test Driven Development) หรือการเขียนเทสแล้วค่อยเขียนโค้ดให้รันเทสผ่าน
หัวใจของการ Refactor
- เขียนให้ทำงานได้ก่อน (First, make it work) – อย่าเพิ่งคิดมากเรื่องความสวยหรือ Performance ในตอนแรกทำให้มันใช้ได้ก่อน
- แล้วค่อยเราค่อยทำให้มันสะอาด (Then, make it right) – ปรับโครงสร้าง วิธีการวาง ทำให้อ่านง่าย Maintain ได้
- ใช้กฏลูกเสือ (ฺBoy scout rule) – ทุกครั้งที่เข้าไปแก้ไขโค้ด ควรทำให้มันดีขึ้นนิดหน่อย
สรุป
- โค้ดที่ใช้งานได้เป็นแค่ครึ่งแรกของการเดินทาง (You are not done when it works, you are done when it’s right)
- ให้โฟกัสแก้ปัญหาก่อนแรก โค้ดจะเละจะ Duplicate ยังไงก็ไม่เป็นไร
- เสร็จแล้วให้ใช้เวลาเท่าๆกับครึ่งแรก หรือมากกว่าในการ Clean
- เทคนิคทั้งหมดในเล่มนี้ มีไว้เพื่อเป็น “แนวทาง“
- เป้าหมายสูงสุดของเล่มนี้คือ การเขียนโค้ดที่อ่านง่าย ใครมาอ่านก็เข้าใจสิ่งเราเขียน
- โค้ดที่คนอื่นอ่านไม่เข้าใจ เขาก็แก้ไม่ได้ เขาแก้ไม่ได้เขาก็เสียเวลา พังเป็นทอดๆ สุดท้ายพังไปยันธุรกิจ
ดราม่าจากเล่มนี้
ดราม่าเล่มนี้มาจากลัทธิ Clean Code บางคนมองว่าเกิดจากคนที่อ่านเล่มนี้จบแล้ว กลับไปอ่านโค้ดของทีมตัวเองบอกว่าทำไมไม่คลีนเลย ไม่เป็นไปตามกฏของหนังสือเล่มนี้อย่างนั้นอย่างนี้ 555
ในบทที่ 1 เป็นบทที่ไม่มีโค้ดแม่แต่ตัวเดียว แต่เต็มไปด้วยความเห็นของเพื่อนร่วมอาชีพของลุงว่า Clean Code สำหรับคนอื่นเป็นยังไง ลุงเขาจะสื่อว่าแต่ละคนก็มี Clean Code ในแบบตัวเองไม่เหมือนกัน
Clean Code ในเล่มนี้ไม่ใช่ Best Practice หรือสิ่งที่ต้องยึดเป็นกฏตายตัวตลอดไป มันเป็นเครื่องมือนึงที่ Uncle คิดขึ้นมาเพื่อเอาไปใช้ให้ถูกที่ถูกเวลาต่างหาก ของแทร่ 💯
เราควรรู้เอาไว้บ้างเพื่อรู้ว่าหน้าตาความ Clean ของคนเก่งๆมันเป็นยังไง ถ้าไม่รู้เลยเราจะแยกแยะไม่เป็นก็หลงทางอีก
และประเด็นอื่นๆที่คนไม่ได้เห็นด้วยเรื่องวิธีที่ลุงใช้ รวมถึงบอกว่า Clean Code ของลุงทำให้ Performance ไม่ดีเท่าการเขียนแบบสั้นๆ ก็เป็นเรื่องถกกันในหลายเว็ปบอร์ด
ผมนึกถึงคำพูดของปิกาโซ่ที่บอกว่า “เรียนรู้กฎให้เชี่ยวชาญเหมือนมืออาชีพ เพื่อที่จะได้ทำลายมันเหมือนศิลปิน“ เข้าใจตามนี้สบายตัว
Learn the rules like a pro, so you can break them like an artist
– Pablo Picasso
ความเห็นหลังอ่านจบ
หนังสือเล่มนี้ไม่ได้สอนวิธีเขียนโค้ดขั้นเทพ หรือวิธีเขียนโค้ดให้ Performance เร็วที่สุด แต่สอนพื้นฐานที่สุดที่โปรแกรมเมอร์ทุกคนควรจะเข้าใจ ก็คือการเขียนโค้ดที่สะอาดมีคุณภาพและแก้ไขง่าย
ลุงเชื่อว่าการเขียนโค้ดสะอาดคือหน้าที่ความรับผิดชอบ (Work Ethic) ของนักพัฒนา
ในหนังสือมีเทคนิคอีกเยอะพร้อมขั้นตอนละเอียดแทบจะเป็น Code ทั้งเล่มเลย แนะนำว่าถ้าอยากเห็นตัวอย่างละเอียดลองไปอ่านเพิ่มดู (แต่ในหนังสือเขาใช้ภาษา Java อธิบายนะ)
💡ถ้าถามว่า “ถ้าเราไม่สนใจไม่อยากทำตามละผิดมั้ย?” ตำตอบคือไม่ผิดเลย เราสามารถเขียน Code ที่ดีได้โดยไม่ต้องเขียน Clean Code แบบลุงเลยก็ได้ แต่แค่การเขียน Style เดียวกันทำให้ง่ายต่อการ Maintain และยิ่งคนเขียนเหมือนกันเยอะๆก็ยิ่งง่ายต่อการไปแก้ไขต่อ จบปิ๊ง
สิ่งที่ไม่เห็นด้วย
ถึงจะเป็นเล่มที่ดีมากๆเล่มนึงแต่ก็มีหลายจุดที่ผมไม่ได้เห็นด้วยทั้งหมดก็ตาม เช่น
🚨 ส่วนตัวผมก็เคยเห็นกรณีโค้ดที่เขียนซ้ำแล้วมันใช้งานจริงได้ดีกว่าในแง่ Maintenance โค้ดที่โลจิกเหมือนกันแต่เขียนแยกตามไฟล์ที่ใช้แก้ก็กระทบจุดเดียวไม่วุ่นวาย หรือการใช้ Duplicate Class มากกว่าใช้ Inheritance แล้วผมคิดว่าดีกว่า ก็มีให้เห็นเหมือนกัน คหสต.ค้าบผม
หรือในบางมุมการเป็น “Craftman” ที่ไม่ยอมขายวิญญาณ เวลาอ่านต้องคิดตามว่าในสังคมวัฒนธรรมของอเมริกันที่เขาเอาแนวคิด Agile Manifesto ไปใช้แบบถูกต้องเต็มประสิทธิภาพ
ทุกคนในทีมมีเสียงเท่ากัน จนสามารถพูดคุยกันได้อย่างเสมอภาค ไม่ใช่ทุกที่จะเป็นแบบนั้น 100% 🙇🏻♂️ ถ้าไม่ใช่ที่อเมริกาลุงอาจจะเกิดยากหน่อย 55
📈 เรื่องนี้น่าคิดต่อว่าจริงๆแล้ว Agile Manifesto มันถูกคิดขึ้นมาจากแนวคิดความเป็น American, Western Culture รึเปล่า? มีส่วนไหนที่ดีหรือส่วนไหนที่ขัดกับวัฒนธรรมการทำงานแบบชาวเอเชียมั้ย ไว้จะมาเขียนต่อเรื่องนี้วันหลังในบทความถัดไป
Mindset
นอกจากเทคนิคที่ลุงคิดขึ้นมาผ่านประสบการณ์ ลุงยังสอนแนวคิด, การปกป้องคุณภาพโค้ด และการเขียนโค้ดคุณภาพที่ช่วยทีมได้
ทุกอย่างในหนังสือมันละเอียดยิบย่อยมาก แสดงให้เห็นว่าลุงเป็นคนเข้มงวดตั้งใจให้งานมีคุณภาพขนาดไหน
ถ้าอ่านเพลินๆเล่มนี้จะมีความ How To อยู่เยอะมาก เราจึงเห็นได้บ่อยๆว่ามีคนเอาสิ่งที่ลุงสอนไปเข้มงวดกับโค้ดคนอื่นเอาไปใช้ชี้ถูกผิดกับคนที่ยังไม่รู้จนเป็นดราม่าเกิดขึ้นเต็มไปหมด
ซึ่งอาจจะไม่ถูกทางนัก เพราะจริงๆแล้วถ้าตีความจากทั้งเล่มจริงๆแล้วลุงสื่อว่า คนที่เราควรเข้มงวดที่สุดคือตัวเราเอง คือหมายสูงสุดของเล่มนี้ที่แท้จริงครับ
แนวคิดนี้แหละที่ต่อยอดไปเป็น Craftsmanship Mindset ที่ผมกำลังจะเขียนในเรื่องต่อไป รอติดตามได้เลยครับ ❤️
บทความนี้ยาวนิดนึงเนื้อหาแน่นๆเลย ไม่ต้องจำได้ทั้งหมดก็ได้ เชื่อว่าถ้าเพื่อนๆเอาไปใช้แค่บางส่วนและเข้าใจแก่นของ Clean Code แค่นี้ก็น่าจะช่วยได้เยอะแล้วครับ
ละเผื่อใครสนใจประเด็นอื่นๆ ผมแปะลิ้งค์ไว้ให้ไปดูต่อกันนะ
- อ่านต่อเรื่อง Code Smell
- Youtube: คลิปลุงบรรยาย Clean Code 10 กว่าชั่วโมง ไม่อยากอ่านไปฟังแทนได้ 😂 ฟังจบบรรลุธรรม
- Reddit: ถกเถียงกันว่าควรเลิกแนะนำหนังสือ Clean Code ได้แล้ว (ด้านที่ไม่เห็นด้วย)
- Youtube: เลิกแนะนำ Clean Code (ด้านที่ไม่เห็นด้วย)
Clean Code is not written by following a set of rules. You don’t become a software craftman by learning a list of heuristics.
Professionalism and craftsmanship come from values that drive discipllines.
— Robert C. Martin (Clean Code, 2008)


Leave a Reply to A Philosophy of Software Design ปรัชญาการออกแบบโปรแกรมให้เรียบง่าย – Beamtan's BlogCancel reply