Google แนะนำแนวทาง “Functional Core, Imperative Shell” เพื่อเขียนโค้ดให้สะอาด ทดสอบง่าย และปรับขยายได้

บทความจาก Google Testing Blog เสนอแนวคิดการแยกโค้ดออกเป็นสองส่วน: Functional Core ที่เป็นฟังก์ชันบริสุทธิ์ (pure functions) และ Imperative Shell ที่จัดการ side effects เช่น I/O, database, หรือ network — เพื่อให้โค้ดมีความสามารถในการทดสอบและดูแลรักษาได้ดีขึ้น.

แนวคิดหลักจากบทความ

Functional Core คือส่วนที่ไม่มี side effects
ประกอบด้วยฟังก์ชันที่รับข้อมูลเข้าและคืนผลลัพธ์โดยไม่เปลี่ยนแปลงสถานะภายนอก
สามารถทดสอบได้ง่ายและนำกลับมาใช้ซ้ำได้

Imperative Shell คือส่วนที่จัดการกับโลกภายนอก
เช่น การส่งอีเมล, อ่านข้อมูลจาก database, หรือเขียนไฟล์
ใช้ผลลัพธ์จาก Functional Core เพื่อดำเนินการจริง

ตัวอย่างจากบทความ: การส่งอีเมลแจ้งเตือนผู้ใช้หมดอายุ
โค้ดเดิมผสม logic กับ side effects ทำให้ทดสอบยาก
โค้ดใหม่แยก logic ออกมาเป็นฟังก์ชัน getExpiredUsers() และ generateExpiryEmails()
Shell ใช้ฟังก์ชันเหล่านี้เพื่อส่งอีเมลจริงผ่าน email.bulkSend(...)

ข้อดีของแนวทางนี้
ทดสอบง่าย: ไม่ต้อง mock database หรือ network
ปรับขยายง่าย: เพิ่มฟีเจอร์ใหม่โดยเขียนฟังก์ชันใหม่ใน core
ดูแลรักษาง่าย

ตัวอย่างโค้ดเปรียบเทียบ

ก่อน (ผสม logic กับ side effects):

function sendUserExpiryEmail() {
for (const user of db.getUsers()) {
if (user.subscriptionEndDate > Date.now()) continue;
if (user.isFreeTrial) continue;
email.send(user.email, "Your account has expired " + user.name + ".");
}
}

หลัง (แยก core และ shell):

function getExpiredUsers(users, cutoff) {
return users.filter(user => user.subscriptionEndDate <= cutoff && !user.isFreeTrial);
}

function generateExpiryEmails(users) {
return users.map(user => [user.email, "Your account has expired " + user.name + "."]);
}

email.bulkSend(generateExpiryEmails(getExpiredUsers(db.getUsers(), Date.now())));

การใช้แนวทาง Functional Core, Imperative Shell ช่วยให้โค้ดมีโครงสร้างที่ชัดเจนและยืดหยุ่น — เหมาะสำหรับทีมที่ต้องการลดความซับซ้อนและเพิ่มคุณภาพในการทดสอบและดูแลระบบในระยะยาว

สาระเพิ่มเติมจากแนวทาง functional programming
แนวคิดนี้คล้ายกับ “Hexagonal Architecture” หรือ “Clean Architecture” ที่แยก domain logic ออกจาก infrastructure
ภาษา functional เช่น Haskell, F#, หรือ Elm ใช้แนวทางนี้เป็นหลัก
ใน JavaScript/TypeScript ก็สามารถนำไปใช้ได้ โดยใช้ pure functions ร่วมกับ async shell
ช่วยลดการใช้ mock ใน unit test เพราะ core ไม่ต้องพึ่ง database หรือ network

https://testing.googleblog.com/2025/10/simplify-your-code-functional-core.html
🧼🧠 Google แนะนำแนวทาง “Functional Core, Imperative Shell” เพื่อเขียนโค้ดให้สะอาด ทดสอบง่าย และปรับขยายได้ บทความจาก Google Testing Blog เสนอแนวคิดการแยกโค้ดออกเป็นสองส่วน: Functional Core ที่เป็นฟังก์ชันบริสุทธิ์ (pure functions) และ Imperative Shell ที่จัดการ side effects เช่น I/O, database, หรือ network — เพื่อให้โค้ดมีความสามารถในการทดสอบและดูแลรักษาได้ดีขึ้น. ✅ แนวคิดหลักจากบทความ ✅ Functional Core คือส่วนที่ไม่มี side effects ➡️ ประกอบด้วยฟังก์ชันที่รับข้อมูลเข้าและคืนผลลัพธ์โดยไม่เปลี่ยนแปลงสถานะภายนอก ➡️ สามารถทดสอบได้ง่ายและนำกลับมาใช้ซ้ำได้ ✅ Imperative Shell คือส่วนที่จัดการกับโลกภายนอก ➡️ เช่น การส่งอีเมล, อ่านข้อมูลจาก database, หรือเขียนไฟล์ ➡️ ใช้ผลลัพธ์จาก Functional Core เพื่อดำเนินการจริง ✅ ตัวอย่างจากบทความ: การส่งอีเมลแจ้งเตือนผู้ใช้หมดอายุ ➡️ โค้ดเดิมผสม logic กับ side effects ทำให้ทดสอบยาก ➡️ โค้ดใหม่แยก logic ออกมาเป็นฟังก์ชัน getExpiredUsers() และ generateExpiryEmails() ➡️ Shell ใช้ฟังก์ชันเหล่านี้เพื่อส่งอีเมลจริงผ่าน email.bulkSend(...) ✅ ข้อดีของแนวทางนี้ ➡️ ทดสอบง่าย: ไม่ต้อง mock database หรือ network ➡️ ปรับขยายง่าย: เพิ่มฟีเจอร์ใหม่โดยเขียนฟังก์ชันใหม่ใน core ➡️ ดูแลรักษาง่าย 🛠️ ตัวอย่างโค้ดเปรียบเทียบ 🔖 ก่อน (ผสม logic กับ side effects): function sendUserExpiryEmail() { for (const user of db.getUsers()) { if (user.subscriptionEndDate > Date.now()) continue; if (user.isFreeTrial) continue; email.send(user.email, "Your account has expired " + user.name + "."); } } 🔖 หลัง (แยก core และ shell): function getExpiredUsers(users, cutoff) { return users.filter(user => user.subscriptionEndDate <= cutoff && !user.isFreeTrial); } function generateExpiryEmails(users) { return users.map(user => [user.email, "Your account has expired " + user.name + "."]); } email.bulkSend(generateExpiryEmails(getExpiredUsers(db.getUsers(), Date.now()))); การใช้แนวทาง Functional Core, Imperative Shell ช่วยให้โค้ดมีโครงสร้างที่ชัดเจนและยืดหยุ่น — เหมาะสำหรับทีมที่ต้องการลดความซับซ้อนและเพิ่มคุณภาพในการทดสอบและดูแลระบบในระยะยาว ✅ สาระเพิ่มเติมจากแนวทาง functional programming ➡️ แนวคิดนี้คล้ายกับ “Hexagonal Architecture” หรือ “Clean Architecture” ที่แยก domain logic ออกจาก infrastructure ➡️ ภาษา functional เช่น Haskell, F#, หรือ Elm ใช้แนวทางนี้เป็นหลัก ➡️ ใน JavaScript/TypeScript ก็สามารถนำไปใช้ได้ โดยใช้ pure functions ร่วมกับ async shell ➡️ ช่วยลดการใช้ mock ใน unit test เพราะ core ไม่ต้องพึ่ง database หรือ network https://testing.googleblog.com/2025/10/simplify-your-code-functional-core.html
TESTING.GOOGLEBLOG.COM
Simplify Your Code: Functional Core, Imperative Shell
This article was adapted from a Google Tech on the Toilet (TotT) episode. You can download a printer-friendly version of this TotT epis...
0 Comments 0 Shares 54 Views 0 Reviews