Topic 11 Reversibility (ความสามารถในการย้อนกลับ)

ไม่มีอะไรอันตรายไปกว่าความคิดที่คุณมีอยู่เพียงความคิดเดียวอีกแล้ว

Emil-Auguste Chartier (Alain) , Propos sur la religion , 1938

วิศวกรมักชอบคำตอบเดียวที่เรียบง่ายสำหรับแก้ปัญหา ข้อสอบเลขที่ให้คุณประกาศได้อย่างมั่นใจเต็มร้อยว่า x equals 2 นั้นสบายใจกว่าการเขียนเรียงความคลุมเครือ ๆ อุ่น ๆ เกี่ยวกับสาเหตุมากมายของการปฏิวัติฝรั่งเศสเยอะ ฝ่ายบริหารก็มักเห็นด้วย กับวิศวกร คำตอบเดียวที่เข้าใจง่ายมันเหมาะกับ spreadsheet และแผนโปรเจกต์มากกว่า

ถ้าโลกแห่งความเป็นจริงยอมให้ความร่วมมือแบบนั้นได้ก็คงดี! แต่น่าเสียดายที่แม้วันนี้ x จะเท่ากับ 2 วันพรุ่งนี้มันอาจต้องกลายเป็น 5 และสัปดาห์หน้าอาจเป็น 3 ก็ได้ ไม่มีอะไรที่คงอยู่ตลอดไป—และถ้าคุณพึ่งพา fact อะไรสักอย่างหนักเกินไป คุณแทบจะการันตีได้เลยว่ามัน จะ เปลี่ยนแปลงแน่นอน

มีวิธี implement สิ่งหนึ่ง ๆ ได้มากกว่าหนึ่งแบบเสมอ และมักจะมี vendor ที่ให้บริการ third-party product มากกว่าหนึ่งเจ้าด้วย ถ้าคุณเข้าโปรเจกต์ไปพร้อมกับความคิดแคบ ๆ ว่ามีวิธีทำได้แค่ วิธีเดียว คุณอาจจะเจอเซอร์ไพรส์ที่ไม่น่าอภิรมย์เข้าให้ก็ได้ หลายทีมโปรเจกต์ต้องถูกบังคับให้ตาสว่างเมื่ออนาคตค่อย ๆ เผยตัวออกมา:

“แต่คุณบอกว่าเราจะใช้ database XYZ นี่! เราเขียนโค้ดโปรเจกต์นี้ไปแล้ว 85% จะเปลี่ยนตอนนี้ไม่ได้แล้วนะ!” programmer คนหนึ่งประท้วง “ขอโทษด้วย แต่บริษัทเราตัดสินใจจะ standardize ไปใช้ database PDQ แทน—สำหรับทุกโปรเจกต์เลย มันไม่ได้อยู่ในมือผมแล้วละ พวกเราต้องเขียนโค้ดใหม่กันหมด ทุกคนต้องมาทำงานวันเสาร์อาทิตย์จนกว่าจะแจ้งเปลี่ยนแปลง”

การเปลี่ยนแปลงไม่จำเป็นต้องโหดร้ายหรือฉับพลันขนาดนั้นเสมอไป แต่เมื่อเวลาผ่านไปและโปรเจกต์ของคุณคืบหน้าขึ้นเรื่อย ๆ คุณอาจพบว่าตัวเองติดอยู่ในสถานการณ์ที่ไปต่อไม่ได้ ทุกครั้งที่มีการตัดสินใจสำคัญ ทีมโปรเจกต์ก็จะยิ่งผูกมัดตัวเองเข้ากับ เป้าหมายที่แคบลงเรื่อย ๆ—เป็นความจริงเวอร์ชันที่แคบลงและมีตัวเลือกน้อยลง

พอถึงเวลาที่การตัดสินใจสำคัญ ๆ ถูกตัดสินใจไปหมดแล้ว เป้าหมายก็จะเล็กลงจนแค่มันขยับนิดเดียว หรือลมเปลี่ยนทิศ หรือ ผีเสื้อที่โตเกียวกระพือปีก คุณก็พลาดเป้าแล้ว [18] แถมอาจจะพลาดไปไกลมากด้วยซ้ำ

ปัญหาคือการตัดสินใจสำคัญ ๆ มักย้อนกลับได้ยาก

เมื่อคุณตัดสินใจใช้ database ของ vendor เจ้านี้ หรือ architectural pattern แบบนั้น หรือ deployment model แบบหนึ่ง คุณก็ผูกมัดตัวเองเข้ากับแนวทางที่ย้อนกลับไม่ได้แล้ว เว้นแต่จะยอมจ่ายราคาที่แพงมาก

Reversibility (ความสามารถในการย้อนกลับ)

หัวข้อหลายเรื่องในหนังสือเล่มนี้มุ่งช่วยให้คุณสร้างซอฟต์แวร์ที่ flexible และปรับตัวได้ ถ้าเรายึดตามคำแนะนำเหล่านั้น— โดยเฉพาะ DRY principle , decoupling และการใช้ external configuration —เราก็ไม่ต้องตัดสินใจแบบที่ย้อนกลับไม่ได้บ่อยเท่าไหร่ นี่เป็นเรื่องดี เพราะเราไม่ได้ตัดสินใจถูกที่สุดตั้งแต่ครั้งแรกเสมอไป บางทีเราก็ผูกมัดตัวเองกับเทคโนโลยีหนึ่งแล้วมาพบทีหลังว่าหาคนที่มีสกิลที่จำเป็นมาทำงานไม่พอ บางทีเราก็ล็อกกับ third-party vendor เจ้าหนึ่งแล้วเขาก็ดันถูกคู่แข่งซื้อกิจการไปพอดี requirement, ผู้ใช้ และฮาร์ดแวร์เปลี่ยนแปลงเร็วกว่าที่เราจะพัฒนา ซอฟต์แวร์ตามทันเสียอีก

สมมติว่าตอนต้นโปรเจกต์คุณตัดสินใจใช้ relational database จาก vendor A พอมาถึงช่วง performance testing ในภายหลัง คุณกลับพบว่า database ตัวนี้ช้าเกินไป แต่ document database จาก vendor B เร็วกว่า สำหรับโปรเจกต์แบบดั้งเดิมส่วนใหญ่ คุณคงซวยแน่ ๆ เพราะส่วนใหญ่แล้วการเรียกใช้ third-party product มักฝังปนอยู่ทั่วทั้งโค้ด แต่ถ้าคุณ abstract แนวคิดเรื่อง database ออกมาจริง ๆ—จนถึงจุดที่มันแค่ให้บริการ persistence เป็น service เท่านั้น—คุณก็จะมีความยืดหยุ่น ที่จะเปลี่ยนม้ากลางศึกได้

ในทำนองเดียวกัน สมมติว่าโปรเจกต์เริ่มต้นเป็นแอปแบบ browser-based แต่พอใกล้จบเกม ฝ่าย marketing ก็ตัดสินใจว่าจริง ๆ แล้ว อยากได้เป็น mobile app มากกว่า มันจะยากแค่ไหนสำหรับคุณ? ในโลกที่สมบูรณ์แบบ มันไม่ควรกระทบคุณมากนัก อย่างน้อยก็ในฝั่ง server คุณก็แค่ดึง HTML rendering ออกแล้วแทนที่ด้วย API เท่านั้นเอง

ความผิดพลาดอยู่ที่การสมมติว่าการตัดสินใจใด ๆ นั้นแน่นอนตายตัวเหมือนสลักลงบนหิน—และไม่เตรียมพร้อมรับมือกับเหตุการณ์ ไม่คาดฝันที่อาจเกิดขึ้น แทนที่จะสลักการตัดสินใจลงบนหิน ให้คิดว่ามันเหมือนตัวหนังสือที่เขียนลงบนทรายชายหาดมากกว่า คลื่นลูกใหญ่สามารถซัดเข้ามาลบมันทิ้งได้ทุกเมื่อ

Tip 18 ไม่มีการตัดสินใจใดที่เป็นที่สิ้นสุด

Flexible Architecture (สถาปัตยกรรมที่ยืดหยุ่น)

ในขณะที่หลายคนพยายามรักษาให้ โค้ด ของตัวเองยืดหยุ่น คุณก็ต้องคิดถึงการรักษาความยืดหยุ่นในด้าน architecture, deployment และการผูกกับ vendor ด้วยเช่นกัน

เรากำลังเขียนสิ่งนี้อยู่ในปี 2019 นับตั้งแต่ต้นศตวรรษที่ผ่านมา เราเห็น server-side architecture ที่ถูกเรียกว่า “best practice” มาแล้วดังนี้:

  • เครื่องเหล็กก้อนใหญ่ (mainframe)
  • กลุ่มสหพันธ์ของเครื่องเหล็กก้อนใหญ่หลายเครื่อง
  • คลัสเตอร์ที่ทำ load balancing บนฮาร์ดแวร์ commodity
  • virtual machine บน cloud ที่รันแอปพลิเคชัน
  • virtual machine บน cloud ที่รัน service
  • เวอร์ชัน containerized ของสิ่งที่กล่าวมาข้างต้น
  • แอปพลิเคชันแบบ serverless ที่รองรับด้วย cloud
  • และสุดท้ายก็หนีไม่พ้นที่จะย้อนกลับไปใช้เครื่องเหล็กก้อนใหญ่สำหรับงานบางอย่างอีกครั้ง

ลองเพิ่มกระแสฮิตล่าสุดเข้าไปในลิสต์นี้ดูสิ แล้วมองมันด้วยความทึ่ง—มันเป็นปาฏิหาริย์จริง ๆ ที่มีอะไรทำงานได้เลยด้วยซ้ำ

แล้วคุณจะวางแผนรับมือกับความผันผวนของ architecture แบบนี้ได้ยังไง? คุณทำไม่ได้หรอกครับ

สิ่งที่คุณทำได้คือทำให้มันเปลี่ยนง่าย ซ่อน third-party API ไว้หลัง abstraction layer ของคุณเอง แยกโค้ดออกเป็น component ต่าง ๆ แม้สุดท้ายคุณจะ deploy มันรวมกันบน server เครื่องเดียวก็ตาม วิธีนี้ก็ยังง่ายกว่าการเอาแอปแบบ monolithic มาแยกทีหลัง เยอะ (พวกเรามีแผลเป็นเป็นหลักฐานยืนยันเรื่องนี้)

และแม้ว่านี่จะไม่ใช่ประเด็นเรื่อง reversibility โดยตรงนัก แต่ก็ขอฝากคำแนะนำสุดท้ายไว้อีกอย่างหนึ่งครับ

Tip 19 อย่าไล่ตามกระแสฮิต

ไม่มีใครรู้หรอกว่าอนาคตจะเป็นยังไง โดยเฉพาะพวกเราเองก็ไม่รู้เหมือนกัน! ดังนั้นจงทำให้โค้ดของคุณ “rock-n-roll” ได้ คือ “rock on” เมื่อมันทำได้ และ “roll with the punches” เมื่อมันจำเป็นต้องทำ

Related Sections Include (หัวข้อที่เกี่ยวข้อง)

  • Topic 8, ​ The Essence of Good Design ​
  • Topic 10, ​ Orthogonality ​
  • Topic 19, ​ Version Control ​
  • Topic 28, ​ Decoupling ​
  • Topic 45, ​ The Requirements Pit ​
  • Topic 51, ​ Pragmatic Starter Kit ​

Challenges (โจทย์ท้าทาย)

  • ได้เวลามาเล่นกับกลศาสตร์ควอนตัมเล็กน้อยผ่านแมวของ Schrödinger กันครับ

    สมมติว่าคุณมีแมวอยู่ในกล่องปิด พร้อมกับอนุภาคกัมมันตรังสีหนึ่งตัว อนุภาคนี้มีโอกาสพอดี 50% ที่จะแตกตัวออกเป็นสอง ถ้ามันแตก แมวจะตาย ถ้ามันไม่แตก แมวก็จะปลอดภัย ดังนั้นแมวตัวนี้ตายหรือเป็นกันแน่? ตามทฤษฎีของ Schrödinger คำตอบที่ ถูกต้องคือ ทั้งสองอย่าง (อย่างน้อยก็ตราบใดที่กล่องยังปิดอยู่) ทุกครั้งที่ปฏิกิริยาระดับ subnuclear เกิดขึ้นโดยมีผลลัพธ์ที่เป็นไปได้สองแบบ จักรวาลจะถูกโคลนออกมา ในจักรวาลหนึ่งเหตุการณ์นั้นเกิดขึ้น ในอีกจักรวาลหนึ่งมันไม่เกิด แมวมีชีวิตอยู่ในจักรวาลหนึ่ง และตายในอีกจักรวาลหนึ่ง คุณจะรู้ว่า คุณ อยู่ในจักรวาลไหนก็ต่อเมื่อเปิดกล่องเท่านั้น

    ไม่แปลกเลยที่การเขียนโค้ดเผื่ออนาคตนั้นยากเย็นแสนเข็ญ

    แต่ลองคิดถึงวิวัฒนาการของโค้ดในแนวทางเดียวกับกล่องที่เต็มไปด้วยแมวของ Schrödinger ดูสิ ทุกการตัดสินใจจะนำไปสู่ อนาคตอีกเวอร์ชันหนึ่ง โค้ดของคุณรองรับอนาคตที่เป็นไปได้กี่แบบ? แบบไหนมีโอกาสเกิดขึ้นมากกว่ากัน? แล้วมันจะยากแค่ไหน ที่จะรองรับมันเมื่อถึงเวลานั้นจริง ๆ?

    คุณกล้าเปิดกล่องนั้นไหมล่ะครับ?