Microservices architecture
หลายปีที่ผมต่อสู้กับปัญหายอดนิยมในวงการซอฟต์แวร์ นั่นคือป้องกันไม่ให้ซอฟต์แวร์ไปถึงจุดที่เกินเยียวยาจนไม่สามารถดูแลต่อไปได้แล้ว ต้องทุบทิ้งแล้วทำใหม่ ซึ่งจังหวะนั้นมันยากสำหรับองค์กรมาก ๆ ไม่มีใครอยากหยุดเพิ่มฟีเจอร์เป็นเวลานาน ๆ ลงเงินมหาศาลเพื่อ replace ซอฟต์แวร์เดิม ที่เสร็จแล้วได้ฟีเจอร์เท่าเดิม ทำงานได้เหมือนเดิม
ทั้ง ๆ ที่ซอฟต์แวร์ไม่มีวันบูดไม่มีวันเน่าแท้ ๆ การลงทุนสร้างมันลงไปแล้ว ควรจะใช้มันได้เรื่อยไปสิ แล้วเรามาถึงจุดที่ต้อง replace ซอฟต์แวร์ได้อย่างไรนะ
ซอฟต์แวร์บูดเมื่อความรู้หายไป
เหตุผลที่ผมเจอบ่อยที่สุดที่ทำให้ไม่มีใครดูแลซอฟต์แวร์ต่อไปได้คือ เพราะความรู้ที่จำเป็นในการบำรุงรักษาซอฟต์แวร์นั้นหายไป ทั้งความรู้เกี่ยวกับปัญหาที่ซอฟต์แวร์นั้นต้องแก้ (requirement) และความรู้เกี่ยวกับวิธีแก้ปัญหา (design)
พอความรู้หายไป คนที่ต้องมาแก้ต่อ ก็ปะปุไปเรื่อยจนของมันซับซ้อนพันกันไปหมด สุดท้ายก็ไม่มีใครอยากดูแลซอฟต์แวร์นี้ หาใครมาทำแป๊บเดียวก็ออก จนค่าใช้จ่ายในการหาคน มันสูงเกินกว่า จะรับไหว ถึงตอนนั้นถ้าซอฟต์แวร์นี้ไม่สำคัญก็ต้องเลิกใช้ แต่ถ้ามันสำคัญก็ต้องจำใจ replace มันใหม่
ซอฟต์แวร์บูดเมื่อเทคโนโลยี ที่เลือกใช้ obsolete
ถ้าผ่านขั้นถนอมความรู้มาได้ ส่วนใหญ่ซอฟต์แวร์ ก็จะอยู่ได้ 5 ถึง 10 ปี แล้วเราจะเจอกำแพงถัดไป นั่นคือภาษาหรือเฟรมเวิร์คที่ใช้มันเก่า จนหมดความนิยมไปจากท้องตลาด ในขั้นนี้จะหาใครมาดูแลก็มีคนทำเป็นน้อยเหลือเกิน พอ supply น้อย ราคาก็สูงเกินกว่าจะรับไหว หรือถ้าปล่อยนานไปอาจจะหาคนมาดูแลไม่ได้เลยเพราะไม่มีใครรู้จักมันแล้ว
การจะต่ออายุขัยให้ซอฟต์แวร์ในสถานะนี้ การใช้กลยุทธ์แบ่งปัญหาใหญ่ ๆ เป็นปัญหาย่อย ๆ แล้วใช้สถาปัตยกรรม micrservices architecture เป็นคำตอบเดียวที่ผมเห็นทุกคนทำกัน นั่นคือการแบ่งบางส่วนเล็ก ๆจากซอฟต์แวร์ชิ้นใหญ่ออกมา แล้วให้มันคุยกับซอฟต์แวร์ชิ้นใหญ่ด้วย protocol ที่เป็นมาตรฐาน เสร็จแล้วค่อย replace ส่วนเล็ก ๆ นี้ด้วยเทคโนโลยีใหม่ ที่หาคนมาดูแลง่ายกว่า ค่อย ๆ ทำแบบนี้ไปเรื่อย ๆ จนซอฟต์แวร์ทั้งหมดอยู่บนเทคโนโลยีที่ต้องการ
เทคนิคการ ใช้ microservices architecture ค่อย ๆ แบ่งส่วน replace ไปนี้ ต่างกับการ replace ทั้งก้อนทีเดียวตรงที่ เราสามารถค่อย ๆ แบ่ง release ให้มีทั้งฟีเจอร์ใหม่เพื่อยังแข่งขันในธุรกิจได้อยู่ และมีส่วนเล็ก ๆ บางส่วน ที่เรา replace ไปด้วยได้ ทำให้การวางแผนเรายืดหยุ่นขึ้นมาก พร้อมทั้งบริหารความเสี่ยงได้ง่ายขึ้นด้วย
ความเห็นที่ผมมีต่อ microservices architecture
ผมเห็นว่ามันเป็นสถาปัตยกรรมที่ช่วยลดความเสี่ยง ได้ดี แทนที่เราจะลงทุนกับซอฟต์แวร์ทั้งหมดบนเทคโนโลยีเดียว การที่เราแบ่งมันเป็นส่วน ๆ เปิดโอกาสให้เราเลือกเทคโนโลยีที่หลากหลายที่เหมาะกับงานได้ เมื่อถึงคราวจำเป็นที่จะต้อง replace ก็ค่อย ๆ ทำทีละส่วนได้ด้วย แต่แลกมาด้วยความซับซ้อนที่ต้องบริหารจัดการ distributed system
สิ่งที่ผมกลืนไม่ลงมาตลอด คือการแบ่งทีมพัฒนา ไปดูแลแต่ละส่วนของระบบนี่แหละ
แล้วเราจะทำ end-to-end test ตอนไหนล่ะ? แล้ว เราจะ ป้องกันไม่ให้ผู้ใช้งานรู้สึกถึงรอยตะเข็บระหว่าง แต่ละ microservice ได้อย่างไร?
คำถามสองข้อด้านบน ทำให้ผมใช้ microservices architecture แค่ในมุมสถาปัตยกรรมเพียงอย่างเดียวมาตลอด ไม่เคย ปรับโครงสร้างทีมให้ดูเป็นส่วน ๆ มาก่อนเลย ผมยังพยามให้แต่ละทีม share ownership ในทุกส่วนของระบบ ทุกทีมจะได้สามารถสร้างคุณค่าให้กับผู้ใช้งานได้ด้วยตัวเอง เวลาผู้ใช้งานมีปัญหา ทีมไหนก็สามารถช่วยผู้ใช้งานแก้ปัญหาได้ ไม่ต้องมาไล่หาว่าส่วนที่มีปัญหาเป็นของใคร
หลังจากได้โอกาสเรียน microservices architecture กับ Sam Newman มุมมองผมก็ เปลี่ยนไป
มุมมองที่เปลี่ยนไป
ผมได้เรียนรู้ความสำคัญของ information hiding คือการแยกส่วนที่ public กับ private ออกจากกัน
สิ่งที่เราเก็บไว้เป็น private คือสิ่งที่เราสามารถเปลี่ยนแปลงได้ โดยไม่ต้องกลัวว่ามันจะส่งผลกระทบกับใคร ไม่ว่าจะเป็น API, หรือโครงสร้างข้อมูลก็ตาม ถ้าเราแยกออกว่าส่วนไหนที่เราจะใช้ภายใน component นี้ และส่วนไหนที่เราจะเปิดเป็น public ให้ภายนอกทราบ ทีมอาจจะสามารถแบ่งส่วนของระบบดูแล โดยไม่ต้องพึ่งแต่ end-to-end test เพื่อจะได้มั่นใจว่าทุกอย่างยังทำงานถูกได้
อีกปัจจัยที่สำคัญที่จะทำให้เราได้ประโยชน์จาก independently deployable ของ microservices architecture ได้ก็คือ การทำ backward compatibility ให้กับ public information
ส่วนไหนของระบบที่เราเปิดเป็น public ให้คนอื่นเข้าถึงได้ เราจะไปทุบสะพานที่ทุกคนกำลังข้ามอยู่ไม่ได้ ไม่งั้นทุกคนก็จะเดือดร้อนกันหมด
เราต้องเพิ่มสะพานใหม่ก่อน และประกาศให้ทุกคนรู้ว่าเราจะเลิกใช้สะพานเก่าแล้ว เมื่อทุกคนย้ายมาสัญจรบนสะพานใหม่หมดแล้ว เราถึงจะสามารถทุบสะพานเก่าทิ้งได้
ถ้าทำสองสิ่งนี้ได้ดี แล้วจะช่วยเพิ่มความมั่นใจในการ release ซอฟต์แวร์เวอร์ชั่นใหม่ โดย ไม่ต้องพึ่ง end-to-end test เพียงอย่างเดียวได้
มุมมองที่ระดับการยืนยัน
Sam เป็นคนพูดเอง ว่าอยากให้ microservices architecture เป็นทางเลือกสุดท้าย เพราะสิ่งที่เราต้องจ่ายสำหรับ distributed system คือความซับซ้อนที่มากับมัน
อีกอย่างที่ได้รับการยืนยันคือ การ แบ่ง monolithic architecture ออกเป็นส่วน ๆ อาจจะไม่ได้ microservices architecture เสมอไปก็ได้
ถ้าเราแบ่งไม่ดี เราจะพบว่าเราไม่สามารถแยก deploy เป็นส่วน ๆ ได้ แต่ต้องจ่ายความซับซ้อน ของ distributed system เหมือนเดิม Sam เรียกสถาปัตยกรรมนี้ว่า distributed monolith หครือชื่อแซว ๆ คือ distributed big ball of mud สรุปว่าความคล่องตัวก็ไม่ได้ แต่ต้องจ่ายความซับซ้อนอีก
มุมมองที่ได้เพิ่มเติม
ก่อนจะแบ่งระบบใหญ่ ๆ ออกเป็น distributed system ย่อย ๆ เราควรจะลองทำ modular monolith ก่อน เพื่อพิสูจน์ว่า วิธีแบ่งของเราเราสามารถ แยกกัน deploy ได้จริงๆ
ดูวิธีการสื่อสารระหว่างทีมที่ดูแลคนละส่วนของระบบ เมื่อเราพอใจกับการแบ่งแล้ว ค่อยแยกออกมาเป็น distributed system ตามสถาปัตยกรรม microservices architecture ภายหลัง วิธีนี้ช่วยลดค่าใช้จ่ายเวลาที่เราแบ่งแล้วไม่ถูกใจได้หลายเท่า
บทส่งท้าย
ถ้าการ release ซอฟต์แวร์เวอร์ชั่นใหม่ในองค์กรของคุณยังเจ็บปวดวุ่นวายอยู่ ทั้ง ๆ ที่คุณเข้าใจว่าเราได้ลงทุน ทำ microservices architecture แล้ว คำถามที่น่าสนใจคือ ในการ release ซอฟต์แวร์ที่ผ่านมา เราต้อง deploy กี่ microservice นะ แล้วจะทำยังไงได้บ้างให้จำนวนมันน้อยลง
ผมเลือกที่จะนำความรู้นี้มาแบ่งปันด้วยความหวังว่าจะช่วยให้การพัฒนาซอฟต์แวร์ของคุณสนุกขึ้นอีกนิดนะครับ ขอบคุณที่สละเวลาอ่านจนจบครับ