6 คะแนน โดย GN⁺ 2025-01-01 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • ข้อเสนอด้านการออกแบบระบบที่ขึ้นต้นด้วย “let’s just” มักซับซ้อนกว่าที่คาดไว้มาก และการตัดสินใจด้านสถาปัตยกรรมที่ดีต้องพึ่งพา กฎคร่าว ๆ จากประสบการณ์ และความเข้าใจบริบทอย่างมาก
  • โครงสร้างแบบปลั๊กอิน การเพิ่ม API และชั้นนามธรรม แม้จะดูสมเหตุสมผล แต่ในทางปฏิบัติต้องรองรับทั้ง ความเข้ากันได้ของพฤติกรรมการทำงาน การบำรุงรักษา ความปลอดภัย ประสิทธิภาพ และความต้องการของระบบนิเวศไปพร้อมกัน
  • การประมวลผลแบบอะซิงโครนัส การควบคุมการเข้าถึง และการซิงก์ข้อมูล อาจดูเป็นหัวข้อคุ้นเคย แต่ในสภาพแวดล้อมของผลิตภัณฑ์จริงมักนำไปสู่บั๊กที่ทำซ้ำได้ยาก การออกแบบโมเดลความปลอดภัยใหม่ และ ปัญหาการซิงก์ที่ยากมาก
  • การทำข้ามแพลตฟอร์มและช่องทางหนีไปใช้เนทีฟ (native escape) อาจใช้ได้กับผลิตภัณฑ์ระยะแรกที่เรียบง่าย แต่เมื่อฟีเจอร์ของแพลตฟอร์มและสถานะภายในเริ่มแยกจากกัน ก็ยากจะรักษา คุณภาพและความสม่ำเสมอ ไว้ได้
  • รูปแบบเหล่านี้ไม่ได้ผิดเสมอไป แต่ส่วนใหญ่มักไม่จำเป็นหรือมีทางเลือกอื่น และควรกลับไปแก้ปัญหาจาก หลักการพื้นฐาน (first principles) มากกว่าจะเลือกแนวทางที่ล้มเหลวได้ง่าย

ทำไม “let’s just” ถึงอันตราย

  • ข้อเสนอที่ตามหลังคำว่า “let’s just” นั้น 9 ใน 10 ครั้งจะกลายเป็น งานที่ซับซ้อนกว่ามาก เมื่อเทียบกับที่ประเมินกันไว้ในห้องประชุม
  • วิศวกรรมมีลักษณะเป็นสังคมศาสตร์อยู่ด้วย ว่าอะไรใช้ได้ผลนั้นจึง ขึ้นกับบริบท
  • เมื่อบอกว่าวิธีใดวิธีหนึ่งใช้ไม่ได้ สำหรับวิศวกรมันมักถูกตีความเหมือนเป็นคำท้าให้พิสูจน์ตัวอย่างโต้แย้ง
  • งานด้านการจัดการวิศวกรรมและสถาปัตยกรรมซอฟต์แวร์จำนวนมากอาศัยการผสมกันระหว่าง กฎคร่าว ๆ จากประสบการณ์ (rule of thumb) และบทเรียนที่ได้มาด้วยความยากลำบาก

“แค่ทำให้รองรับปลั๊กอินก็พอ”

  • เมื่อการมีเพียง implementation เดียวดูไม่พอ การเสียบ implementation ใหม่เข้าไปในสถาปัตยกรรมเดิมอาจดูเหมือนจะช่วยเพิ่มประสิทธิภาพหรือฟีเจอร์ใหม่ได้ โดยที่ผู้เรียกใช้ API ไม่ต้องเปลี่ยนอะไร
  • แต่เพราะ “API ไม่ใช่แค่ไฟล์ header หรือเอกสาร แต่คือ พฤติกรรมการทำงานจริง” ปลั๊กอินที่แค่ทำงานได้เลยจึงแทบไม่มีอยู่จริง
  • องค์ประกอบในซอฟต์แวร์สมัยใหม่ที่ใกล้เคียงปลั๊กอินมากที่สุดคือ ไดรเวอร์อุปกรณ์
    • ในอดีตไดรเวอร์มีคุณภาพการทำงานไม่ดีนักจนไม่เป็นที่ยอมรับอีกต่อไป หรือไม่ก็ระบบปฏิบัติการสมัยใหม่เปลี่ยนไปสู่แนวทางที่สร้างไดรเวอร์เอง
  • หากจะสร้างโครงสร้างที่รองรับปลั๊กอินได้จริง ต้องออกแบบ implementation ตัวที่สองพร้อมกับตัวหลักตั้งแต่แรก จึงจะพอเป็นหลักฐานได้อย่างน้อยหนึ่งครั้งว่ามันใช้งานได้จริง

“แค่เพิ่ม API เข้าไป”

  • หลังจากผลิตภัณฑ์หรือบริษัทประสบความสำเร็จในระดับหนึ่ง มักจะมีการเพิ่ม API ด้วยเหตุผลว่า “ต้องกลายเป็นแพลตฟอร์มเพื่อดึงดูดนักพัฒนา”
  • ผู้ให้บริการ API ต้องประนีประนอมอยู่เสมอระหว่างการเพิ่มฟีเจอร์กับ ความเข้ากันได้และการทำงานร่วมกันได้ และพฤติกรรมเดิมรวมถึงคุณลักษณะด้านประสิทธิภาพก็ทำให้ความอิสระในการเปลี่ยนแปลงลดลงมาก
  • การมี API ไม่ได้แปลว่าจะมีคนอยากใช้อย่างแน่นอน
    • API ใหม่มักเกิดขึ้นเมื่อผลิตภัณฑ์ต้องการบางความสามารถ แต่ยังไม่ให้ความสำคัญภายในมากพอจะทำเอง
    • ตลาดเป้าหมายอาจเล็ก เจาะจงแนวตั้ง หรือจำกัดอยู่ในโดเมนเฉพาะ จนคาดหวังให้พาร์ตเนอร์ภายนอกมาเติมช่องว่างผ่าน API
    • แต่พาร์ตเนอร์เหล่านั้นก็มีธุรกิจและลูกค้าของตัวเอง จึงอาจไม่อยากนำผลิตภัณฑ์อีกตัวเข้ามาเพิ่มเพื่อแก้ปัญหา
  • การเป็นแพลตฟอร์มเป็นธุรกิจที่ต้องมีความต้องการจริงขนาดใหญ่ และการเปิด API เพียงไม่กี่ตัวก็มักไม่เพียงพอจะสร้างฐานเศรษฐกิจให้บุคคลที่สามได้

“เพิ่มชั้นนามธรรมอีกชั้นหนึ่ง”

  • คำกล่าวของ Butler Lampson ที่ว่า “ปัญหาทุกอย่างในวิทยาการคอมพิวเตอร์แก้ได้ด้วยชั้นของการอ้อมผ่านอีกชั้นหนึ่ง” นั้นมีความจริงอยู่ไม่น้อย
  • ความล้มเหลวมักเกิดขึ้นได้สองแบบ
    • นามธรรมที่ใส่เข้าไปเร็วเกินไปจะกลายเป็น นามธรรมเกินจำเป็น และค้างอยู่ในสถาปัตยกรรมโดยไม่มีแผนใช้งานจริง
    • นามธรรมที่เพิ่มเข้ามาทีหลังอาจทำให้การบำรุงรักษา ความปลอดภัย และการปรับแต่งประสิทธิภาพซับซ้อนขึ้นมาก
  • Windows NT มีนามธรรมเกินจำเป็นหลายอย่างที่ใส่มาตั้งแต่ต้นแต่แทบไม่ได้ใช้งานจริง
  • ในวิวัฒนาการของ Mac OS ก็มีกรณีที่นามธรรมซึ่งตอนแรกดูแปลกกลับมีประโยชน์ในอีกสองรุ่นถัดมา โดยความต่างอยู่ที่มีการวางแผนรองรับไว้
  • หากนามธรรมที่เพิ่มทีหลังถูกใช้เพียงบางส่วนของโค้ด ก็จะมีโค้ดจำนวนมากที่ไม่ใช้นามธรรมใหม่นั้น ทำให้ ภาระการบำรุงรักษา สูงขึ้น

“ทำให้เป็นอะซิงโครนัส”

  • ช่วง 25 ปีแรกของวิทยาการคอมพิวเตอร์จำนวนไม่น้อยถูกใช้ไปกับการทำความเข้าใจและนำพฤติกรรมแบบอะซิงโครนัสไปใช้งานจริง
  • ในหลักสูตรบัณฑิตศึกษาช่วงทศวรรษ 1980 มีการใช้เวลามากกับหัวข้ออย่างนักปรัชญานั่งกินข้าว ผู้ผลิต-ผู้บริโภค และช่างตัดผมหลับ
  • ปัจจุบันวิศวกรจำนวนมากรับมือกับปัญหาอะซิงโครนัสในรูปแบบที่ถูกนามธรรมไว้พอสมควรแล้ว ด้วยกฎของ data layer และเว็บเฟรมเวิร์ก
  • หากต้องจัดการอะซิงโครนัสเองโดยตรงนอกเฟรมเวิร์กหรือ data layer ตอนแรกมันอาจดูเหมือนทำงานได้ดี แต่หนึ่งปีให้หลังอาจมี บั๊กที่ทำซ้ำได้ยาก ปรากฏขึ้น
  • และได้แต่หวังว่าบั๊กนั้นจะไม่ใช่ปัญหาข้อมูลเสียหาย

“เรื่องควบคุมการเข้าถึงค่อยเพิ่มทีหลัง”

  • ตำแหน่งที่จะวางการควบคุมการเข้าถึงเคยเป็นหัวข้อถกเถียงเชิงทฤษฎี แต่ระบบปัจจุบันอยู่ในสภาพแวดล้อมที่ซับซ้อนกว่ามากเพราะต้องเผชิญการโจมตีอย่างต่อเนื่อง
  • ทุกคนรู้ว่าความปลอดภัยต้องมีตั้งแต่ต้น แต่ด้วยแรงกดดันเรื่องความเร็วในการออกสู่ตลาด จึงแทบไม่มีระบบใดที่ออกแบบการควบคุมการเข้าถึงและโมเดลความปลอดภัยไว้อย่างครบถ้วนตั้งแต่แรก
  • หากไม่เริ่มจากมุมมองของลูกค้าและผู้โจมตี ก็ยากจะสร้าง การออกแบบการควบคุมการเข้าถึง ที่เหมาะกับผลิตภัณฑ์
  • วิธีที่ค่อยมาแปะการควบคุมการเข้าถึงทีหลังอาจล้มเหลว หรือไม่ก็นำไปสู่สถานการณ์ที่ต้องเขียนผลิตภัณฑ์ใหม่ในภายหลัง
  • และการเขียนใหม่นั้นก็เป็นประสบการณ์ที่ไม่ดีสำหรับทุกฝ่ายรวมถึงลูกค้าด้วย

“มาซิงก์ข้อมูลกันเถอะ”

  • ในสภาพแวดล้อมที่มีหลายอุปกรณ์ หลายแอป SaaS และหลายแหล่งเก็บข้อมูล ข้อเสนอว่า “แค่ซิงก์ข้อมูลกัน” มักเกิดขึ้นบ่อย
  • อย่างที่ Ray Ozzie ผู้บุกเบิกด้าน client/server และการซิงก์ข้อมูลย้ำไว้ว่า การซิงก์เป็นปัญหาที่ยาก
  • ในวิทยาการคอมพิวเตอร์ ปัญหาที่ยากหมายถึงความท้าทายที่มักเรียนรู้ได้จากประสบการณ์เท่านั้นและรับมือได้ลำบากมาก
  • แม้แต่ใน data store ที่มี semantics และ transaction ครบถ้วน การซิงก์ก็ยังยาก และเมื่อมี blob, ข้อมูลไม่มีโครงสร้าง และการแปลงข้อมูลเข้ามา ความยากก็พุ่งสูงขึ้นอย่างรวดเร็ว
  • การวางรากฐานของวิธีแก้ปัญหาไว้บนการซิงก์ข้อมูลแทบไม่ใช่ตัวเลือกที่อยากเลือก และนี่ก็เป็นเหตุผลว่าทำไมจึงมีบริษัทมูลค่าหลายพันล้านดอลลาร์ที่อยู่ได้ด้วยการแก้ปัญหาการซิงก์เพียงอย่างเดียว

“ทำให้เป็นข้ามแพลตฟอร์ม”

  • เรื่องข้ามแพลตฟอร์มเป็นข้อถกเถียงที่วนซ้ำมานาน และมักมีคนบอกว่าโค้ดที่ตนสร้างทำงานได้ดี หรือยก Unity กับเกมมาเป็นตัวอย่าง
  • เมื่อบอกว่าจะทำอะไรบางอย่างให้เป็นข้ามแพลตฟอร์ม แท้จริงแล้วก็แทบเท่ากับให้คำมั่นว่าจะสร้างระบบปฏิบัติการ ผู้ให้บริการคลาวด์ หรือเบราว์เซอร์อย่างใดอย่างหนึ่ง
  • การทำข้ามแพลตฟอร์มใช้ได้ดีในสองกรณี
    • เมื่อแพลตฟอร์มยังใหม่และเรียบง่าย เช่น ตอนที่คลาวด์ยังอยู่ในระดับ compute และ storage แบบง่าย ๆ
    • เมื่อแอปพลิเคชันหรือผลิตภัณฑ์ยังใหม่และเรียบง่าย
  • แต่เมื่อเริ่มแยกตัวออกจากแพลตฟอร์มฐาน หรือเริ่มสร้างฟีเจอร์ที่ถูกแสดงออกแตกต่างกันโดยสิ้นเชิงในแต่ละแพลตฟอร์มเป้าหมาย เงื่อนไขเหล่านี้ก็พังลง
  • Microsoft พบว่าการทำ Office for Mac และ Office for Windows ด้วยโค้ดชุดเดียวกันเป็นเรื่องยากมาก จึง fork โค้ดของ Office ในปี 1998 และไม่ย้อนกลับไปอีก
  • เดิมที Microsoft ดำรงอยู่ในฐานะธุรกิจที่สร้างแอปข้ามแพลตฟอร์ม แต่แนวทางนั้นใช้ได้ดีในยุคที่เอกสาร OS API หนาเพียงราว 100 หน้า และแต่ละ OS ยังสืบสายมาจาก CP/M
  • บทความที่เกี่ยวข้อง: Divergent Thoughts on Cross Platform

“ถ้าจำเป็นค่อยหนีไปใช้เนทีฟ”

  • เพราะแนวทางข้ามแพลตฟอร์มมักใช้ได้ดีเพียงช่วงสั้น ๆ เฟรมเวิร์กและ API abstraction จึงมักมี ช่องทางหนีไปใช้เนทีฟ (native escape) ให้ด้วย
  • แนวคิดนี้คือเมื่อแพลตฟอร์มพัฒนาไปจนมีฟีเจอร์ที่เฟรมเวิร์กยังไม่เปิดเผยออกมา ก็ให้เรียกใช้จากแพลตฟอร์มเนทีฟโดยตรง
  • แต่เฟรมเวิร์กหรือ API ที่ให้นามธรรมไว้นั้นมักเก็บสถานะภายในหรือ cache เอาไว้ด้วย
  • เมื่อเรียกแพลตฟอร์มเนทีฟโดยตรง โครงสร้างข้อมูลและสถานะที่เฟรมเวิร์กไม่รับรู้ก็จะถูกเปลี่ยนไป
  • บางเฟรมเวิร์กมีวิธีแลกเปลี่ยนข้อมูลหรือสถานะระหว่างโค้ดฝั่ง escape กับตัวเฟรมเวิร์ก แต่ก็เป็นทางแก้ที่คล้ายกับการนำสถาปัตยกรรมแบบ malloc/free กลับมาอีกครั้งในยุคของการจัดการหน่วยความจำอัตโนมัติ

เลือกใช้ได้ แต่ไม่ควรเป็นค่าเริ่มต้น

  • ไม่ได้หมายความว่าต้องตอบ “ไม่” กับแนวทางเหล่านี้เสมอไป
  • ในบางบริบท วิธีเหล่านี้อาจใช้ได้ผล
  • แต่ในกรณีส่วนใหญ่ รูปแบบเหล่านี้มักไม่จำเป็นหรือมีวิธีที่ดีกว่า
  • แทนที่จะหยิบรูปแบบซอฟต์แวร์ที่มีโอกาสล้มเหลวสูงขึ้นมาใช้ก่อน ควรแก้ปัญหาจาก หลักการพื้นฐาน

2 ความคิดเห็น

 
ndrgrd 2025-01-02

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

 
GN⁺ 2025-01-01
ความคิดเห็นจาก Hacker News
  • ปัญหาของไอเดียแบบนี้ไม่ได้อยู่ที่ตัวไอเดียเองเท่าไร แต่อยู่ที่ แนวทางแบบ “ก็แค่ลองทำดู” หรือความคาดหวังที่มาก่อนมัน
    เช่น ถ้ามอง API ว่าเป็น “แค่ฟีเจอร์หนึ่ง” ของผลิตภัณฑ์ ด้วยท่าทีแบบ “ก็แค่เพิ่ม API เข้าไป” ผมคิดว่าอัตราความสำเร็จน่าจะพอ ๆ กับ “ก็แค่เพิ่ม UI เข้าไป”
    การจะทำ UI ที่ดีต้องคิดอย่างรอบคอบและทำอย่างละเอียดถี่ถ้วน และยังต้องมีผู้เชี่ยวชาญในด้านนั้นด้วย
    อินเทอร์เฟซแบบอื่นของผลิตภัณฑ์ก็ไม่มีเหตุผลอะไรที่จะแตกต่างกัน ประเด็นสำคัญไม่ใช่ว่าเป็นไอเดียแย่หรือไอเดียดี แต่คือ มันไม่ใช่สิ่งที่ทำแบบ “ก็แค่” ได้

    • ที่ทำงานเก่าของผมมีกฎอยู่ข้อหนึ่ง
      คนเดียวที่มีสิทธิ์พูดว่า “ก็แค่” คือ developer ที่ต้องรับผิดชอบทำให้สิ่งนั้นใช้งานได้จริง และถ้า developer คนอื่นพูดคำนั้น ก็เท่ากับอาสารับงานนั้นเอง
      สำหรับพวกเรา กฎนี้ใช้ได้ดี
    • ใช่เลย เวลาเพิ่ม API ไม่มีคำว่า “ก็แค่”
      การทำ API ให้ดีต้องใช้ การออกแบบและความซับซ้อน มากมาย
      ถ้าออกแบบไม่ดี client อาจต้องเรียกหลายครั้งทั้งที่ควรจบในการเรียกครั้งเดียว หรือ API อาจชวนสับสนจนเรียกผิด หรือใช้งานไม่ได้เลย
      ต้องมีทั้ง authentication และ authorization ด้วย จึงต้องตั้งค่า OAuth2 หรืออย่างน้อยต้องมีการสร้าง จัดเก็บ และตรวจสอบ API token ที่ปลอดภัย
      ข้อมูลก็ต้องถูกจัดการอย่างปลอดภัย และถ้าประสิทธิภาพแย่ ฐานข้อมูลอาจถูกโหลดถล่มจนรับไม่ไหว
      ถ้าใส่ caching เข้ามา ก็จะมีความซับซ้อนเพิ่มขึ้นจาก cache invalidation และ server/process สำหรับรองรับ cache
      ถ้าไม่มี rate limiting client ที่เขียนมาไม่รอบคอบก็อาจยิง API รัว ๆ ได้ และถ้าเอกสารแย่ API ก็แทบไร้ประโยชน์
      ในบางกรณีอาจต้องมี SDK สำหรับหลายภาษาอีกด้วย และถ้าไม่มีข้อความ error ที่ดี ผู้ใช้ครั้งแรกก็จะไม่รู้ว่าทำไมการเรียกถึงล้มเหลว
    • คำแนะนำจากผู้เชี่ยวชาญส่วนใหญ่คล้ายคำแนะนำเรื่องความรัก
      มักเอาสิ่งที่เคยผิดพลาดกับตัวเองไปสรุปเป็นหลักทั่วไป แต่แทบจะเอาไปใช้ตรง ๆ ไม่ได้ เว้นแต่คุณจะเป็นคนเดิมในสถานการณ์ที่คล้ายกันมาก ๆ
      คำแนะนำที่ถูกอยู่เสมอ เช่น “คิดอย่างรอบคอบ พยายามทำสิ่งที่ถูกต้อง และย้อนดูผลลัพธ์” ก็กว้างเกินไปจนแทบไม่มีประโยชน์
      คำพูดแบบนั้นขายบทความบล็อกหรือหนังสือได้ไม่ค่อยดี
    • อาชีพ Presales ดูเหมือนจะมีอยู่แทบทั้งหมดเพราะคำว่า “ก็แค่”
      เพราะต้องคอยทำให้แน่ใจว่าลูกค้าจะไม่ตกใจตอนเริ่มยกส่วนที่เหลือของภูเขาน้ำแข็งขึ้นมาเหนือผิวน้ำ
    • ใช่เลย ถ้าการวางตำแหน่งของบทความเป็น “จริง ๆ แล้วต้องใช้อะไรบ้างเพื่อทำให้ไอเดียระบบ ทำงานได้จริงแบบที่คนคิดว่า ‘ก็แค่ทำ’” คงจะดีกว่ามาก
      แบบนั้นบทความจะไม่ใช่เรื่องเป็นไปได้/เป็นไปไม่ได้ แต่เป็นเรื่องรูปแบบของความสำเร็จและความล้มเหลว
      พอรู้รายละเอียดที่จำเป็นแล้ว ก็จะพูดว่า “ก็แค่” ได้ยากขึ้น ทั้งที่ยังไม่รู้ด้วยซ้ำว่าตัวเองไม่รู้อะไร
      แต่สำหรับ DSL ผมเห็นด้วยแทบ 100%
      มันใกล้เคียงกับการเพิ่มความซับซ้อนที่ไม่จำเป็นและพยายามทำตัวน่ารัก และคนที่มีคุณสมบัติพอจะสร้างมันควรเป็นคนระดับที่เคยสร้างภาษาโปรแกรมมิงที่ประสบความสำเร็จ แล้วนำข้อผิดพลาดมาปรับปรุงเป็นฉบับที่ 2 หรือภาษาแห่งที่สอง แต่ถึงอย่างนั้นก็ยังผิดพลาดได้อีกมาก
  • (1) บางครั้ง DSL ก็ทำงานได้ดีมาก ดู https://www.jooq.org/
    (2) Elastic Load Balancer คือ control loop ที่ตอบสนองต่อ workload และเทคโนโลยีประเภทนี้ก็กลายเป็นของใช้งานทั่วไปไปแล้ว
    (3) ในอุตสาหกรรมส่วนใหญ่ การ provision น้อยเกินไปเป็นเรื่องแพร่หลาย ดู https://erikbern.com/2018/03/27/waiting-time-load-factor-and... และ https://www.amazon.com/Goal-Process-Ongoing-Improvement/dp/0...
    (4) anomaly detection ไม่ใช่ปัญหา distributed system โดยเนื้อแท้เหมือนรายการอื่น ๆ แต่คนที่เคยเจ็บหนักสักครั้งอาจรู้สึกว่าจำเป็นต้องมี
    และยังเป็นสาขาที่ยากในเชิงปัญญาด้วย
    อัลกอริทึมแรกที่ทำให้รู้สึกว่ามันฉลาดพอสมควรคือ https://scikit-learn.org/1.5/modules/outlier_detection.html#... และบางครั้งมันก็ทำงานได้เหมือนปาฏิหาริย์
    ตอนนำไปใช้กับข้อความด้วย embedding ที่ใช้ CNN เมื่อปี 2018 มันเข้ากันได้ดี แต่กับ SBERT กลับไม่โชคดีเลย

    • เคยเขียน DSL มาสองตัว ตัวหนึ่งทำร่วมกับทีม และมองว่าทั้งคู่ประสบความสำเร็จ
      มันแก้ปัญหาได้ และไม่มีใครบ่นด่า
      ปัจจัยสำคัญที่สุดน่าจะเป็นเพราะทั้งคู่ เล็ก
      ทั้งสองคล้ายกันมาก และ reuse โค้ดด้วย
      ตัวหนึ่งใช้สำหรับเขียน rule เพื่อตรวจสอบฟอร์มขนาดใหญ่ อีกตัวใช้สำหรับเขียน rule การตัดสินใจตามคำตอบในฟอร์ม
      DSL ที่ดีทำให้คนที่ทำไม่ได้ กลายเป็นทำได้
      DSL ที่สร้างขึ้นเพื่อประหยัดเวลามีโอกาสมีประโยชน์น้อยกว่ามาก เพราะมีแนวโน้มสูงว่าจะไม่ได้ประหยัดเวลาจริง
      ในทั้งสองกรณี เราต้องนำพฤติกรรมของโดเมนที่ซับซ้อนเข้ามาไว้ในโปรแกรม
      ดังนั้นจึงต้องสอนโดเมนให้โปรแกรมเมอร์ หรือจับโปรแกรมเมอร์คู่กับ domain expert หรือสอนการเขียนโปรแกรมให้ domain expert
      ถ้ามีงานเยอะ การมอบอำนาจให้ domain expert ทำเองก็ดูน่าสนใจ
      โปรแกรมเมอร์จะไปทำอย่างอื่นได้ และ feedback loop ก็สั้นลง
      ถ้าเป็นโดเมนที่ลึก คงไม่อยากส่งโปรแกรมเมอร์ไปเรียนเหมือนเข้าโรงเรียนเพื่อทำความเข้าใจ และถ้าเป็นโดเมนตื้น ๆ คนที่ค่าตัวถูกกว่าก็อาจจัดการได้
      DSL มาพร้อม ภาระทางความคิด สูง
      แต่ถ้าทางเลือกคือการเรียนภาษาโปรแกรมเต็มรูปแบบ มันก็สมเหตุสมผลขึ้น
      DSL เพื่อประหยัดเวลาคือกรณีที่คนซึ่งเขียนโค้ดเป็นอยู่แล้วอยากเขียนโค้ดให้น้อยลง แต่ปริมาณที่ลดได้มีน้อย จึงมักไม่ค่อยคุ้ม
      แล้วเมื่อโปรแกรมเมอร์ต้องเปลี่ยนอะไรสักอย่าง ก็ต้องเรียนรู้หรือจำ DSL ทั้งตัวนี้แทนที่จะเป็นโค้ดที่อ่านเข้าใจได้โดยสัญชาตญาณ
      กฎง่าย ๆ อีกอย่างคือ DSL สำหรับโปรแกรมเมอร์มีโอกาสเป็นไอเดียที่ดีน้อยกว่า DSL สำหรับคนที่ไม่ใช่โปรแกรมเมอร์
    • jOOQ เป็นหายนะ และไม่แนะนำให้ใครใช้
      ต้องเสียเวลาหลายชั่วโมงเขียน SQL query แล้วทดสอบในเครื่องมืออย่าง DataGrip จากนั้นค่อยหาวิธีแปลงมันเป็น DSL
      ถ้าใช้ฟีเจอร์ SQL ที่ “แปลกถิ่น” อย่าง JSON expression ปัญหาจะยิ่งหนักขึ้น
      การดีบักจะกลายเป็น flow แบบ “พิมพ์ SQL ที่ generate ออกมา คัดลอกไปใส่ที่อย่าง DataGrip ปรับแต่ง query แล้วหาวิธียัดกลับเข้าไปใน DSL”
      เป็นการเสียเวลาอย่างมหาศาล
      จุดขายหลักของ jOOQ คือ type-safe query แต่เมื่อ IntelliJ เริ่มตรวจสอบ SQL ที่เป็น string ในโค้ดให้ตรงกับข้อมูลจริงได้ ความสำคัญของมันก็หายไป
      flow ที่แก้ SQL โดยตรงและทดสอบกับฐานข้อมูลทันทีนั้นดีกว่าอย่างชัดเจน
      jOOQ ช่วยตอกย้ำประเด็นของต้นฉบับเกี่ยวกับ DSL
    • นอกจากสิ่งอย่าง regular expression แล้ว ยังไม่เคยเห็น DSL ที่ดี และแม้แต่อันนั้นก็ได้ยินว่าหลายคนไม่พอใจกับตัวภาษาเอง
      ตัวอย่าง DSL ยอดนิยมที่ถือว่าแย่หรือเกือบล้มเหลวได้ ได้แก่ HCL, E4X, XUL, ภาษาจัดรูปแบบสตริงของ Common Lisp เป็นต้น
      HCL คือภาษาคอนฟิกของ Terraform ซึ่งเห็นได้ชัดตั้งแต่แรกว่าไม่ได้จัดการปัญหาที่พบบ่อยมากอย่างการ provision อุปกรณ์ที่คล้ายกันตามจำนวนตัวแปร
      ความพยายามเพิ่มฟีเจอร์ภายหลังก็ดูฝืน ๆ และยังแก้ปัญหาได้ไม่หมด
      E4X เป็น JavaScript DSL สำหรับทำงานกับ XML ในกรณีง่าย ๆ มันทำให้การทำงานกับ XML เขียนได้กระชับขึ้น แต่ก็สามารถกลายเป็นกำแพงเครื่องหมายวรรคตอนที่อ่านยากได้อย่างรวดเร็ว
      คล้ายกับ LINQ ของ Microsoft มันไม่ได้บอกผู้เขียนเลยว่า computational complexity ของโค้ดภายในอยู่ระดับไหน
      สุดท้ายโค้ดที่ใช้ DSL นี้มักถูกเขียนใหม่ในรูปแบบที่กระชับน้อยกว่าแต่วิเคราะห์ได้ง่ายกว่า
      XUL เป็นภาษา UI สำหรับส่วนขยาย browser chrome ของ Firefox และสำหรับจุดประสงค์การสร้างส่วนขยาย Firefox ก็ถือว่าโอเค
      แต่ Firefox ยังอยากขายมันเป็นเทคโนโลยีฐานสำหรับแอปภายในองค์กรด้วย และในพื้นที่นั้นมันยังขาดไปมาก
      การทำเรื่องง่าย ๆ ต้องใช้ทริกและทางอ้อมจำนวนมาก
      ภาษาจัดรูปแบบสตริงของ Common Lisp ก็คล้ายกัน คือใช้กับปัญหาเล็ก ๆ ได้ดีแต่ขยายไม่ไหว
      ปัญหาการจัดรูปแบบบางอย่างต้องใช้วิธีแก้ที่ประหลาดมาก หรือไม่มีคำตอบตั้งแต่แรก และพอเห็นโค้ดที่เรียก format แบบ recursive แล้วรู้สึกเกลียดจริง ๆ
      โดยรวมแล้ว ปัญหาที่พบบ่อยที่สุดของแนวทางนี้คือมันเป็นการแก้เฉพาะหน้าและขยายต่อได้ไม่ดี
      ไม่นานก็จะชนกับปัญหาที่แก้ให้ถูกต้องไม่ได้ และโปรแกรมขนาดใหญ่ที่เขียนด้วย DSL มักเป็นฝันร้ายในการดูแล
    • ทุกครั้งที่เห็นคนเกลียด DSL ก็รู้สึกสงสัย แต่แล้วก็มักตระหนักว่าคนที่วิจารณ์ไม่ได้วิจารณ์ DSL โดยทั่วไป แต่เป็น DSL ที่ต้องเขียนเองตั้งแต่ศูนย์
      ถ้าวาง DSL ไว้บน Lisp ก็ต้องเขียนแค่ domain logic ไม่ใช่ภาษาพื้นฐาน
      งานส่วนใหญ่เสร็จไปแล้ว และภาษาก็มีประโยชน์ตั้งแต่วันแรก
      ถ้าทำเป็น hosted DSL บน Lisp ก็ใช้งานจริงได้ แล้วไม่เข้าใจว่าทำไมต้องสร้างภาษาใหม่ตั้งแต่ศูนย์แล้วนั่งดูมันเหี่ยวตายไป
    • DSL จะทำงานได้ดีเมื่อมี IDE ที่ autocomplete ได้ และ feedback loop ที่เร็วหรือแทบจะทันที
  • รายการเหล่านี้ทั้งหมดมีกรณีความสำเร็จอยู่มากมาย
    คำว่า “แทบจะ” ใช้เป็นทางหนีทีไล่ได้ยาก
    นี่ดูเหมือนเป็นแค่มองโลกในแง่ร้ายกับการเสียดสีแบบเหนื่อยล้า
    ผมเข้าใจความรู้สึกนั้นและก็เคยรู้สึกเองเหมือนกัน และบางครั้งก็ยากที่จะทำให้วิศวกรที่มีไฟยอมเลิกไอเดียแย่ ๆ
    แต่บรรยากาศแบบนี้สำหรับผมแล้วรู้สึกว่า เป็นพิษ

    • เหมือนเป็นแค่การพูดว่า “ของพวกนี้ ยุ่งยากกว่าที่เห็น ในการทำให้ถูกต้องหรือ deploy ให้ได้ผล” ในแบบที่ดึง engagement ได้สูง หรือพูดอีกอย่างคือออกแนว clickbait
    • เบื้องหลังกรณี “ความสำเร็จ” จำนวนไม่น้อย มี ทีมวิศวกรผ่านศึก ที่คอยจัดการความล้มเหลวทั้งหมดของไอเดียนั้นอยู่
      เช่น control loop ที่หลุดไปหาอินฟินิตี้หรือขอบเขตสูงสุด/ต่ำสุด, cache ที่กู้คืนจาก distributed failure ไม่ได้, state ที่เสียหายระหว่าง live migration, burst ที่ทำให้ overload ในจังหวะไม่เหมาะสม, alert ตรวจจับ anomaly ปลอม ๆ ที่บอกวันหยุดราชการทั่วโลก
      ใต้ไอเดียทั้งหมดนั้นมีปมความซับซ้อนที่แทบทุกคนประเมินต่ำไป
    • โดยรวมเห็นด้วย
      นี่คือรายการไอเดียระบบที่ยากกว่าที่คิดตอนแรก และควรเข้าหาอย่างจริงจัง ไม่ใช่ทำเล่น ๆ
      ฟังดูคล้าย ๆ แบบเลือนรางกับ “ดูดีแต่แทบไม่มีวันทำงานได้” แต่ในรายละเอียดต่างกันโดยสิ้นเชิง
      ถ้าถือว่าเป็นปัญหายากและลงทุนให้เหมาะสม การทำให้มันทำงานได้ดีอย่างถูกต้องก็เป็นผลลัพธ์ที่ทำได้โดยปกติ
      ถ้าเป็นฟีเจอร์ที่เอามาแปะทีหลัง หรือคิดแบบไร้เดียงสาว่าง่าย ก็มักจะผิดพลาด
    • ไม่ได้อ่านแล้วรู้สึกว่าเป็น “มองโลกในแง่ร้ายกับการเสียดสีแบบเหนื่อยล้า”
      อ่านเหมือนเป็นเรื่องของวิศวกรที่หมกมุ่นกับ การ optimize ล่วงหน้า ทั้งที่ไม่มีประโยชน์ทางธุรกิจ
      เรื่องนี้แพร่หลายมากในวงการ เพราะมันสนุกกว่าที่จะออกแบบและสร้างยานอวกาศ auto-scaling แบบซ้ำซ้อน แทนที่จะเตรียม backup ที่ deploy ได้ภายในไม่กี่ชั่วโมงและ overprovision server ไว้ 200% ซึ่งอาจมีต้นทุนไม่ถึงหนึ่งในสิบด้วยซ้ำ
      ไอเดียแบบนี้ก็มีเวลาที่สมเหตุสมผล แต่ก็คือหลังจากที่มันจำเป็นแล้ว
      ไม่ใช่สิ่งที่ควรออกแบบใส่ไว้ล่วงหน้าในช่วงผลิตภัณฑ์ระยะแรก
      มีผลิตภัณฑ์น้อยมากที่ต้องการสเกล ความพร้อมใช้งาน และความซับซ้อนในระดับที่ implementation พวกนี้พยายามแก้จริง ๆ
    • ดูเหมือน Steven ไม่ได้บอกว่าสิ่งเหล่านี้เป็นไปไม่ได้ แต่บอกว่ามันยากและมักไม่ค่อยสำเร็จอย่างผิดปกติ
  • ดูเหมือนว่าหลายคนที่นี่กำลังพยายามหาฟังก์ชันตัดสินที่ละเอียดอ่อนเพื่อแยกข้อยกเว้น แต่จริง ๆ แล้วมันง่ายมาก
    ไอเดียเหล่านี้ ถ้าฉันทำก็ยอดเยี่ยม แต่ถ้าไอ้งั่งคนก่อนหน้าฉันทำ มันจะไม่มีวันทำงานได้ตามเจตนา

    • เหมือนจะใช่เลย
      และบางครั้งไอ้งั่งคนก่อนหน้านั้นก็คือตัวฉันเองเมื่อไม่กี่เดือนก่อน
  • อยากเพิ่ม Domain-Driven Design เข้าไปด้วย
    การพยายามทำให้แอปพลิเคชันสอดคล้องกับโครงสร้างธุรกิจ แล้วไปตรึงการออกแบบธุรกิจไว้ เป็นสูตรสำเร็จสู่หายนะ
    ถ้าเป็นธุรกิจเล็กหรือหยุดนิ่ง คุณอาจไม่รู้สึกว่ามีปัญหา
    แต่ถ้าธุรกิจประสบความสำเร็จหรือเติบโต คุณจะเสียใจทันทีที่พยายามสร้างโดเมนชื่ออธิบายยืดยาวอันน่ากลัว ซึ่งผูกติดกับแนวปฏิบัติงานที่ล้าสมัยไปแล้ว
    แทนที่จะทำแบบนั้น การออกแบบโดยเน้นชั้นฟังก์ชันตามวิธีที่พิสูจน์มาหลายสิบปี และวาง business logic ไว้ใน configuration, แถวฐานข้อมูล, user workflow ให้มากที่สุด จะยืดหยุ่นกว่ามาก

    • สุดท้ายก็จะเสียใจกับทั้งสองทางเลือก
      คุณยกกับดักของ Domain-Driven Design ว่าเป็นภาษาที่ล้าสมัยและความสามารถในการ reuse code/ระบบต่ำสำหรับการลองสิ่งใหม่ ๆ แต่กลับกัน การออกแบบที่ abstract สูงซึ่งใส่ business logic ทั้งหมดไว้ใน configuration และ workflow ฯลฯ ก็ยืดหยุ่นได้ก็ต่อเมื่อทั้งองค์กรเข้าใจ abstraction, configuration และชุดผสมจำนวนมหาศาลนั้นได้ค่อนข้างดี
      ชุดผสมนั้นจะระเบิดกลายเป็นเขาวงกตอย่างรวดเร็ว สร้างพฤติกรรมที่ไม่รู้ที่มาและคาดไม่ถึง ซึ่งผู้คนจะเริ่มพึ่งพา
      ค่าใช้จ่ายในการ onboarding นักพัฒนาใหม่และการเปลี่ยนทีมพัฒนาก็จะรับมือได้ยาก
      องค์กรจะเริ่มพูดกันคนละสองภาษา
      feature request ส่วนใหญ่ที่ดูเหมือนง่ายจากภายนอก ถ้าทำให้ abstraction แตก ก็จะกลายเป็นการออกแบบระบบขนาดใหญ่ใหม่ หรือไม่ก็กลายเป็น “ตอนนี้แค่ hack abstraction นี้ให้ดูเหมือนเป็นการเปลี่ยนแปลงที่ปลอดภัยกว่าและเล็กกว่าก็พอ”
      อย่างแรกนั้นยากมากเสมอ แม้จะมีวิศวกรชั้นยอดที่เข้าใจพฤติกรรมของทั้งระบบและ codebase อย่างสมบูรณ์ รวมถึงมีแนวปฏิบัติและกระบวนการด้านวิศวกรรมที่ยอดเยี่ยม และอาจกินเวลาหลายเดือนหรือหลายปี
      อย่างหลังเกิดขึ้นบ่อยกว่า และนั่นคือเหตุผลที่โปรเจกต์ที่ “abstract สูง แบ่งชั้นตามฟังก์ชัน ขับเคลื่อนด้วย configuration และมี business logic แบบ emergent” ตอนแรกดูสมบูรณ์แบบและยืดหยุ่น แต่สุดท้ายกลายเป็น “นี่มันอะไรกันวะ”
      หลังจากระบบถูก implement แล้ว business logic ที่เกิดขึ้นแบบ emergent นั้นก็จะกลายเป็นภาษาที่ทุกคนพูด
      ถ้าองค์กรพูดภาษาสองสามภาษาที่เข้ากันไม่ได้เลย มันจะเจ็บปวดมาก และถ้าไม่มีคนหลายคนที่แปลไปมาระหว่างภาษาพวกนั้นได้คล่องทั้งบนลงล่างและด้านข้าง ก็จะรู้สึกว่าน่าจะเลือกแสดงโดเมนให้ใกล้เคียงกว่านี้ตั้งแต่แรก
    • “ทำให้ state ที่เป็นไปไม่ได้ไม่สามารถ represent ได้” ก็รวมอยู่ในนี้ด้วย
      ถ้าคุณออกแบบให้ state บางอย่างไม่สามารถ represent ด้วย type ได้ คุณต้องมั่นใจได้ว่า state นั้นเป็น state ที่เป็นไปไม่ได้ จริง ๆ ตลอดอายุของการออกแบบนั้น
  • ไม่ค่อยเข้าใจหัวข้อ control loop ที่ตอบสนองต่อโหลด
    มันเป็นองค์ประกอบพื้นฐานและรากฐานของระบบจำนวนมาก
    centrifugal governor ของเครื่องจักรไอน้ำในยุค 1800 หรือเครื่องเล่นแผ่นเสียง Victrola ในยุค 1900 ก็เป็น control loop ที่ตอบสนองต่อโหลด
    อิเล็กทรอนิกส์ทั้งสาขาเป็นตาข่ายของ control loop ที่ตอบสนองต่อโหลด และเกียร์อัตโนมัติของรถยนต์ก็เช่นกัน

    • ปัญหาที่พบบ่อยคือการเพิ่ม control loop โดยที่ยังไม่เข้าใจสัญญาณดีพอ หรือเพิ่มโดยไม่คำนึงถึง control loop อื่น
      CPU utilization เป็นตัวอย่างที่น่าสนใจ
      เช่น คุณอาจเห็นสถานการณ์ที่ load balancer ข้าม region ต่อสู้กับ load shedding ภายใน process
      เพราะสัญญาณของ load balancer ไม่สะท้อน load shedding หรือสะท้อนอย่างไม่ถูกต้อง
      อีกปัญหาหนึ่งคือ control loop ที่พยายาม optimize ผลลัพธ์ local ของ service แต่กลับทำให้ผลลัพธ์โดยรวมแย่ลง
      โดยรวมแล้ว ผมคิดว่าควรวาง control loop จำนวนน้อย ไว้ในตำแหน่งที่มีผลกระทบสูง
    • ยังไม่ชัดเจนทั้งหมด แต่โดยเฉพาะอาจหมายถึงโหลด CPU
      โหลด CPU มีปัญหาบางอย่างตามที่อธิบายไว้ใน https://arxiv.org/abs/2312.10172
  • ปัญหาเหล่านี้มีรูปแบบร่วมกันอยู่
    ทั้งหมดเป็น cross-cutting concerns ที่เพิ่มข้อจำกัดให้กับโมเดลการเขียนโปรแกรมแบบประมวลผลข้อมูลตามลำดับ ซึ่งโปรแกรมเมอร์คุ้นเคย
    ทุกครั้งที่เพิ่มข้อจำกัด ก็เท่ากับเพิ่มสิ่งที่นักพัฒนาทุกคนหลังจากนั้นต้องคอยคิดถึงตลอดเวลาขณะพัฒนาต่อบนระบบนั้น
    ระบบจึงมีแนวโน้มจะถูกจำกัดมากเกินไป จนตกอยู่ในสถานการณ์ที่เดินหน้าต่อไม่ได้หากไม่คลายข้อจำกัดบางอย่าง
    ถึงไม่ใช่ว่าเป็นไปไม่ได้ ก็จะช้าลง
    เพราะนักพัฒนาต้องพิจารณาทุกครั้งว่าฟีเจอร์ใหม่จะโต้ตอบกับ API, ความปลอดภัย, การซิงก์, latency, แพลตฟอร์มอื่น ๆ และโค้ดเนทีฟ ที่ได้สัญญาว่าจะรองรับไว้แล้วอย่างไร
    ดังนั้นการรองรับคุณสมบัติทั้งหมดเหล่านี้ก็เป็นไปได้
    ตัวอย่างเช่น หากทำให้การซิงก์ข้อมูลแบบโปร่งใสเป็นคุณค่าหลักของแพลตฟอร์ม การพัฒนาทั้งหมดหลังจากนั้นก็จะให้ความสำคัญกับการรองรับสิ่งนั้นก่อน และค่อย ๆ พัฒนาชุดฟีเจอร์ที่เป็นไปได้ภายใต้ข้อจำกัดนั้น
    ชุดฟีเจอร์นั้นอาจไม่ตรงกับสิ่งที่ผู้ใช้ต้องการเป๊ะ ๆ แต่นั่นคือขอบเขตที่มันรองรับ
    ผลิตภัณฑ์จะดูน่าสนใจสำหรับลูกค้าที่ให้คุณสมบัตินั้นเป็นปัจจัยอันดับหนึ่งในการตัดสินใจซื้อ

  • เคยทำโปรเจกต์ที่ใช้ DSL หลายตัว, แคชแบบ P2P และ parallelism แบบผสมมาแล้ว และทั้งหมดก็ทำงานได้
    การสร้างมันก็สนุกมากจริง ๆ
    ยกเว้นกรณีหนึ่ง ถือว่าเป็นการลงทุนที่ดี
    แคชแบบ P2P สุดท้ายแล้วแทบไม่คุ้มค่า เพราะไม่ได้จำเป็น
    ดังนั้นการบอกว่าสิ่งเหล่านี้แทบไม่มีทางทำงานได้เลยจึงผิดอย่างชัดเจน
    มันซับซ้อนก็จริง แต่ความซับซ้อนนั้นนำมาซึ่งความสามารถที่หาได้ยากด้วยวิธีอื่น
    บทเรียนจากตัวอย่างแคชแบบ P2P คือ ต้องแน่ใจก่อนว่าความสามารถนั้น จำเป็นจริง ๆ หรือไม่

  • อ่านแล้วรู้สึกแปลกอยู่บ้าง เพราะผมเคยทำหลายไอเดียในนี้ให้สำเร็จมาแล้ว

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

    • ผมอ่าน “ก็แค่ซิงก์ข้อมูลสิ” ว่าเป็นความหวังแบบไร้เดียงสาว่า จะอ่านจากฝั่งหนึ่งและเขียนจากอีกฝั่งหนึ่ง แล้ว แหล่งความจริง สองแห่งจะไม่แยกทางกัน
      ถ้าจะทำให้ถูกต้อง คิวและการประมวลผลอีเวนต์เป็นสิ่งจำเป็น