- ชี้ให้เห็นปัญหาการใช้สถาปัตยกรรมระบบที่ซับซ้อนเกินความจำเป็น ทั้งที่มีผู้ใช้จริงน้อย
- ยกตัวอย่างการนำเทคโนโลยีหลายอย่างอย่าง Redis, MongoDB, Kubernetes, ไมโครเซอร์วิส มาผสมกันแบบไร้ทิศทาง
- เน้นว่าสถานการณ์แบบนี้ใช้เพียง Postgres อินสแตนซ์เดียวและเซิร์ฟเวอร์ที่เรียบง่าย ก็เพียงพอ
- ย้ำว่า ความซับซ้อนไม่ใช่คุณธรรม และควรขยายก็ต่อเมื่อพิสูจน์แล้วว่าจำเป็น
- เป็นเนื้อหาที่เตือนสตาร์ทอัพและทีมพัฒนาให้ยึดหลัก การออกแบบที่เรียบง่ายให้เหมาะกับขนาด
การใช้เทคโนโลยีสแตกเกินจำเป็น
- วิจารณ์ การจับคู่เทคโนโลยีที่ถูกเลือกแบบสุ่ม เช่น ใช้ Redis และ MongoDB พร้อมกัน
- ตั้งคำถามว่า “ทำไม Redis ถึงคุยกับ MongoDB?” และ “ทำไมถึงใช้ MongoDB?”
- ชี้ว่ามีการนำ ระบบกระจายศูนย์ มาใช้ ทั้งที่จำนวนผู้ใช้จริงมีเพียง 12 คน (ในนั้น 6 คนเป็นบัญชีทดสอบ)
- กล่าวถึงกรณีที่เลือกขยายระบบเกินความจำเป็น ทั้งที่ Postgres อินสแตนซ์เดียว ก็เพียงพอ
ความฟุ่มเฟือยของโครงสร้างการดีพลอยและอินฟรา
- โครงสร้างปัจจุบัน: ไมโครเซอร์วิส 15 ตัว, ฐานข้อมูล 8 ตัว, Kubernetes คลัสเตอร์ 3 สภาพแวดล้อม, เมสเสจคิว 4 ตัว, service mesh, CI/CD pipeline ที่ใช้เวลา 2 ชั่วโมง
- ส่วนโครงสร้างที่จำเป็นจริงมีเพียง เซิร์ฟเวอร์เครื่องเดียว, Postgres, และ Redis สำหรับแคช เท่านั้น
- เปรียบเทียบให้เห็นชัดเจนถึง ความซับซ้อนของอินฟราที่มากเกินไป เมื่อเทียบกับความต้องการจริง
ความซับซ้อนของระบบและความสามารถในการอธิบาย
- ชี้ว่าถ้าต้องใช้ ไดอะแกรมที่พันกันเหมือนสปาเกตตี เพื่ออธิบายระบบให้ junior developer เข้าใจ แสดงว่ามีปัญหา
- สรุปสารหลักด้วยประโยคว่า ความซับซ้อนไม่ใช่คุณธรรม
- เน้นว่าควรเริ่มต้นอย่างเรียบง่าย และ เพิ่มความซับซ้อนเฉพาะเมื่อพิสูจน์แล้วว่าจำเป็น
บทเรียนสำคัญ
- การเลือกเทคโนโลยีและออกแบบระบบควร ทำให้เรียบง่ายตามขนาดและปริมาณการใช้งานจริง
- การขยายระบบแบบไร้ทิศทางและการเพิ่มเทคโนโลยีสแตกมากเกินไป ทำลายทั้งการบำรุงรักษาและประสิทธิภาพ
- สำหรับสตาร์ทอัพหรือทีมขนาดเล็ก การรักษา “ความเรียบง่ายที่เหมาะกับขนาด” เป็นสิ่งสำคัญ
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
บางครั้งพฤติกรรมแบบนี้ก็ดูเหมือนแค่ การผัดวันประกันพรุ่ง (procrastination)
เพราะไม่อยากคุยกับคนอย่างลูกค้า นักลงทุน หรือทีมกฎหมาย ก็เลยไปทำ “งานสนุกๆ” ในมุมของวิศวกรแทน
ภายนอกอาจดูเหมือนกำลังทำงานอย่างมีประสิทธิภาพ แต่จริงๆ คือกำลังย่ำอยู่กับที่
ถ้าไม่ฝืนทำงานที่น่าอึดอัดบ้างทุกวัน คนเราจะค่อยๆ ถดถอย แล้วสุดท้ายก็เจอปัญหาใหญ่ที่เดิมทีหลีกเลี่ยงได้
เรื่องนี้ไม่ใช่แค่กับซอฟต์แวร์ แต่ใช้ได้กับชีวิตโดยรวมด้วย
ได้เรียนรู้ว่าถ้าอยากหาเงิน ต้องทำ งานที่ลำดับความสำคัญถูกต้อง ไม่ใช่งานที่สนุกกว่า
หรือจริงๆ แล้วเป็นการตอบสนอง ความอยากได้สถาปัตยกรรมในอุดมคติ ของ CTO หรือ VPE ที่ห่างไกลจากความเป็นจริงกันแน่
ฉันจำได้ว่าเคยมีเกมหยั่งเชิงเรื่อง monolithic vs microservices ในการสัมภาษณ์ system design
สุดท้ายแล้วคนที่มีอำนาจก็มักชัดเจนว่าต้องการไปทางไหน และการขัดทางนั้นก็คือการเผาทุนทางการเมือง
ประมาณว่า “ฉันเคยเชื่อม ABC กับ XYZ มาแล้วนะ” แล้วก็ไล่โชว์ tech stack ของตัวเอง
มันยังมีแรงจูงใจแบบอยากทำให้เรซูเม่ดูเท่ด้วย
ที่จริงโปรเจกต์ส่วนใหญ่ใช้เทคโนโลยียุค 1990 ก็ทำได้สบาย แต่พอมีคำอย่าง distributed systems มันจะดู ‘คูล’ กว่ามาก
เป็นผลจากวัฒนธรรมการจ้างงานที่ผิดเพี้ยนของวงการ จนกลายเป็นว่าเชฟไม่ได้ถูกยอมรับจากการใช้มีดเก่ง แต่ต้องใช้ ‘มีดแบรนด์เฉพาะ’ ถึงจะได้รับการยอมรับ
เช่น DuckDuckGo ขอแค่ algorithm กับ data structure แล้วแค่บอกว่า “อนึ่ง เราใช้ Perl”
Stream มีคอร์ส 10 สัปดาห์ให้ผู้สมัครที่ยังไม่รู้ Go และ Jane Street ก็ไม่ได้บังคับว่าต้องมีประสบการณ์ OCaml
ส่วน bevuta IT GmbH ที่ฉันทำงานอยู่ก็เปิดโอกาสให้เรียน Clojure หลังเข้าทำงาน
แนวทางแบบนี้ต่างจากประกาศรับสมัครงานล้าสมัยประเภท “ต้องมีประสบการณ์ Ruby on Rails 10 ปี” อย่างสิ้นเชิง
เพราะอยากลองของใหม่และอยากเปรียบเทียบมันดู
สุดท้ายความจริงของวงการก็คือ ต้องมานั่งพูดถึง ความซับซ้อนที่ไม่จำเป็น เพื่อรองรับผู้ใช้แค่ไม่กี่ร้อยคน
คำว่า “แคชด้วย Redis” ถูกพูดกันบ่อยมาก ทั้งที่จริงๆ Postgres ก็พอแล้ว
การยืนยันว่าจะต้องใช้ Redis ให้ได้ ดูเหมือนแม้แต่ผู้เขียนเองก็ยังต้าน แรงกระตุ้นในการ overengineering ไม่ไหว
ถ้าระบบยังเล็กก็ยังไม่จำเป็นต้องมีแคช และการเก็บ ข้อมูลชั่วคราว ไว้ในระบบแยกก็ดูน่าสนใจกว่า
abstraction เรื่องแคชของ framework ส่วนใหญ่ก็ออกแบบมาโดยนึกถึง Redis อยู่แล้ว
เริ่มจาก in-memory cache ก่อน แล้วค่อยเพิ่ม Redis ตอนจำเป็นจะดีกว่า
แต่ Redis/Valkey ก็ยังเกินไปหน่อย memcached เรียบง่ายและใช้งานได้จริงกว่ามาก
มันไม่เก็บสถานะแบบ Redis จึงช่วยหลีกเลี่ยง รูปแบบการออกแบบที่ไม่ดี ที่ไปพึ่งพาความสอดคล้องของแคช
แคชใน DB ช้ากว่าเพราะต้องผ่าน file system
การเขียน query ให้มีประสิทธิภาพเป็นอีกปัญหาหนึ่งโดยสิ้นเชิง
ถ้าทำให้ Postgres กลับมาเร็วได้ ก็อาจถอด Redis ออกได้ แต่ส่วนใหญ่ก็มักปล่อยทิ้งไว้แบบนั้น
ตอนทำเว็บแอปบน AWS Lambda ที่ต้องรับ 1,000 requests ต่อวินาที Postgres เอาไม่อยู่ แต่ Redis รับได้สบาย
เพราะความ เรียบง่าย ของมัน จึงมีคุณค่าให้ใช้ในบางกรณีพิเศษ
น่าสนใจที่ผู้เขียนพูดเรื่อง “ความเรียบง่าย” แต่กลับทำหน้าเว็บด้วย Tailwind + JS framework + bundler
ทั้งที่จริงจะทำด้วย HTML แบบง่ายๆ ก็ได้
เลยขอแนะนำเว็บเฟรมเวิร์กเรียบง่ายที่สร้างเองได้อย่าง MastroJS
ในทางปฏิบัติมันจะ generate CSS ตาม utility class ที่ใช้จริงเท่านั้น
ทวีตต้นฉบับมาจาก ทวีตของ Jeff Atwood ในปี 2013
ตอนนี้ฉันเองก็กำลังชั่งใจเรื่องการตัดสินใจคล้ายๆ กันอยู่
ถ้าเป็นผลิตภัณฑ์ที่ยังไม่ผ่านการพิสูจน์ตลาด ก็ควรเริ่มเล็ก เริ่มเร็ว และทำ MVP ที่ pivot ได้
ในทางกลับกัน ถ้าต้องแสดงให้นักลงทุนหรือองค์กรใหญ่เห็นถึง ความสามารถในการออกแบบที่รองรับการขยายตัว ก็ควรเลือกโครงสร้างที่ scale ได้ตั้งแต่แรก
ถ้าโมเดลธุรกิจจะเวิร์กได้ก็ต่อเมื่อมันโตเกินกว่าที่ DB เดียวจะรับไหว การไปทาง สถาปัตยกรรมที่ขยายได้ตั้งแต่ต้น ก็อาจคุ้มกว่าในระยะยาว
สำหรับคนที่ทำงานอยู่ท่ามกลางฝันร้ายด้านอินฟรา ความเรียบง่ายอาจฟังดูเพ้อฝัน
แต่ความซับซ้อนจำนวนมากไม่ได้มีไว้แก้ปัญหาเชิงเทคนิคเท่านั้น มันมีไว้แก้ ปัญหาเชิงองค์กร ด้วย
deployment automation, redundancy เพื่อรับมือเหตุขัดข้อง, CI pipeline, การจัดการ secrets, การทำแคช ล้วนเป็นอุปกรณ์ป้องกันเพื่อคุ้มครองทีม
การมองข้ามสิ่งเหล่านี้ถือว่าเสี่ยงเมื่อทำงานกันเป็นทีม
แม้แต่ SQLite ก็ยังใช้ใน production ได้สบาย แต่มีอคติแพร่หลายว่ามัน “เหมาะแค่สำหรับทดสอบ”
บางครั้งค่าเริ่มต้นของ PaaS ก็ซับซ้อนเกินจำเป็น
เริ่มจาก build process แบบอิง checklist ก่อน แล้วพอมันนิ่งค่อยขยายไปสู่ automation ก็ได้
ฉันอยากเห็นกรณีใช้งานจริงที่ microservices จำเป็นจริงๆ สำหรับบริการที่ไม่ใช่ระดับ FAANG
หลายคนเอาแต่เดินตาม tech stack มาตรฐาน เพราะกลัวการคิดเอง
การใช้ Kafka, Mongo, Redis แบบไม่ตั้งคำถามนี่แหละคือปัญหา
ที่จริงการลงมือทำเฉพาะฟีเจอร์ที่ต้องใช้เองอาจดีกว่า และ ความสามารถในการเลือกองค์ประกอบให้น้อยที่สุด คือหัวใจของวิศวกร
ของอย่าง Kafka นี่บางทีก็แค่เผาเงินเปล่าๆ
คำว่า “เพิ่มความซับซ้อนเมื่อจำเป็นเท่านั้น” นั้นถูกต้อง แต่บางอย่างก็เพิ่มทีหลังได้ยาก
ถ้าออกแบบช่วงแรกพลาด อาจต้อง เขียนใหม่ทั้งระบบ
เพราะงั้นในโลกจริงจึงอาจต้องมีทางสายกลางอย่าง การตั้งค่า k8s แบบเรียบง่าย
ไม่นานมานี้ฉันก็เพิ่งเจอประสบการณ์คล้ายกันในบทสัมภาษณ์
ตลอด 10 ปีที่ผ่านมา ฉันโฟกัสกับการหา PMF (Product-Market Fit) ไม่ใช่หมกมุ่นกับการ scale ตั้งแต่ต้น
ถ้าผลิตภัณฑ์เข้ากับตลาด ปัญหาเรื่องการขยายระบบก็แก้ได้ด้วยเงิน
แต่พอถูกถามในการสัมภาษณ์ว่า “จะ scale บริการ Django ให้รองรับ 10 ล้าน requests ต่อวันได้อย่างไร”
ฉันตอบไปว่า “ก็เพิ่มสเปกเซิร์ฟเวอร์สิ” แล้วดูเหมือนพวกเขาจะไม่ค่อยพอใจนัก