เหตุผลที่ควรเขียน ADR
(github.blog)ADR = Architecture Decision Records
การบันทึกไว้ในโค้ดเบสว่าเหตุใดจึงตัดสินใจเชิงสถาปัตยกรรมแบบนั้น
GitHub ก็กำลังนำสิ่งนี้ไปใช้กับทีมมือถือ iOS/Android และบทความนี้อธิบายว่าทำไมมันจึงจำเป็น
ไม่ใช่เพื่อคุณ แต่เพื่อคุณในอนาคต
ADR ไม่ใช่กระบวนการทบทวนการตัดสินใจที่ฉันเคยทำ แต่เป็นสิ่งที่ช่วยให้จำ mindset ในตอนที่ตัดสินใจสถาปัตยกรรมนี้ได้อีกครั้งในอีก 6~12 เดือนข้างหน้า
ADR จับภาพช่วงเวลาที่มีการตัดสินใจไว้ รวมถึง PoC ที่ถูกพูดถึงในที่ประชุม/Zoom meeting/Slack/โค้ดทั้งหมด
คือการดึง context ที่อยู่ในหัวออกมาเป็นคำพูด เพื่อให้เมื่อกลับมาทบทวนสถาปัตยกรรมนี้ภายหลัง จะสามารถใส่ context นั้นกลับเข้าไปในหัวได้อีกครั้ง
โบนัสที่แท้จริงจะปรากฏเมื่ออีกหลายเดือนต่อมา มีคนถามคุณแบบกล่าวโทษว่าทำไมโมดูล GitHubAPIClient ถึงทำงานแบบนี้
แทนที่จะต้อง pair กัน 30 นาทีเพื่ออธิบายโค้ด คุณสามารถโยน ADR นี้ให้ แล้วอธิบายการตัดสินใจที่เกิดขึ้นระหว่างการสร้างโมดูลนั้นได้
ไม่ใช่เพื่อคุณ แต่เพื่อเพื่อนร่วมงานของคุณ
ADR ควรเขียนให้ยาวกว่าคำอธิบายบรรทัดเดียวอย่าง "ฟีเจอร์นี้คือการทำงานตาม feature-#3128"
มันเป็นรูปแบบคำอธิบายที่ยาวขึ้น ซึ่งช่วยให้เพื่อนร่วมงานเข้าใจได้ว่าทำไมฟีเจอร์นี้จึงถูกสร้างด้วยวิธีนี้แทนที่จะเป็นอีกวิธีหนึ่ง
(เช่น แสดงใน ADR ด้วยหัวข้ออย่าง "ทางเลือกที่พิจารณา" , "ข้อดีและข้อเสีย" เป็นต้น)
สิ่งที่ง่ายสำหรับคุณ อาจซับซ้อนสำหรับเพื่อนร่วมงานก็ได้
ลองสละเวลาเล็กน้อย เขียนกระบวนการคิดตอนที่ตัดสินใจไว้ จะเป็นการเปิดโอกาสให้สมาชิกทีมได้เข้ามาอยู่ในหัวของคุณ
เมื่อเขียน ADR แล้ว ก็จะทำให้เกิด "Decision Socialization (การทำให้การตัดสินใจเป็นเรื่องร่วมกัน)" ได้
เมื่อเป็นแบบนี้ แทนที่แต่ละคนจะตัดสินใจแยกกัน ทีมจะเป็นผู้ตัดสินใจโดยรับผิดชอบต่อการดูแลรักษาร่วมกัน
หากเขียน ADR ก่อนเปิด pull request คุณจะได้รับ PR review ที่ดีกว่าจากทีมที่เข้ามาตรวจทาน
ไม่ใช่เพื่อคุณ แต่เพื่อเพื่อนร่วมงานในอนาคต
ADR ไม่ได้มีไว้เพื่อแสดงว่าคุณฉลาดแค่ไหน หรือทำให้คนอื่นงงกับสถาปัตยกรรมที่คุณสร้าง
ADR ช่วยให้เมื่อมีสมาชิกทีมใหม่เข้ามา พวกเขาจะเข้าใจทั้งโค้ดเบสปัจจุบันและวิธีที่โค้ดเบสพัฒนามาตลอดช่วงเวลาที่ผ่านมาได้
เมื่อทีมขยายตัวและเติบโตขึ้น ช่องทางการสื่อสารระหว่างสมาชิกทีมก็เพิ่มขึ้น
การเขียนการตัดสินใจเหล่านี้ไว้จะช่วยให้ทีมสื่อสารกับคนที่เพิ่งเข้าร่วมใหม่ได้ดีขึ้นเมื่อทีมเติบโต
สถานการณ์ที่ดีที่สุดคือ สมาชิกทีมของคุณเขียน ADR ใหม่ขึ้นมาเพื่อแทนที่การตัดสินใจที่คุณเคยทำไว้ และในอนาคตคุณก็จะได้เรียนรู้จากเพื่อนร่วมงานของคุณ
"เขียน ADR ให้มากขึ้น ทีมของเราเติบโตขึ้นและโค้ดเบสซับซ้อนขึ้นเมื่อไร ADR ก็ยิ่งเป็นวิธีที่ดีในการช่วยเหลือตัวเราในอนาคต สมาชิกทีมปัจจุบัน และสมาชิกทีมในอนาคต"
3 ความคิดเห็น
ในบรรดากรณีศึกษารัฐบาลอิเล็กทรอนิกส์ มี Gov.UK ที่มีชื่อเสียง ซึ่งยังมี Repository ที่รวบรวม ADR ที่เกี่ยวข้องกับสถาปัตยกรรม AWS cloud ของตนเองด้วย
https://github.com/alphagov/govuk-aws/…
เป็นข้อมูลอ้างอิงที่ดีมากครับ
ตัวอย่าง ADR
การตัดสินใจเลือก CSS framework
https://github.com/joelparkerhenderson/architecture_decision_record/…
สมมติฐาน: เพราะต้องการสร้างเว็บแอปที่ modern, เร็ว และ responsive จึงไม่อยากใช้ jQuery
ข้อจำกัด: ต้องเป็น framework ที่ไม่จำเป็นต้องใช้ jQuery
สิ่งที่พิจารณา: ไม่ใช้อะไรเลย หรือเลือกจาก Bootstrap/Bulma/Foundation/Materialize/Semantic UI โดยพิจารณา Bulma และ Semantic UI อย่างจริงจังเป็นพิเศษ
Monorepo vs Multirepo
https://github.com/joelparkerhenderson/architecture_decision_record/…
→ กำลังใช้ git เป็น SCM อยู่ จึงต้องตัดสินใจระหว่าง monorepo vs polyrepo vs hybrid
→ ถ้าองค์กร/ทีม/โปรเจกต์มีขนาดเล็กและต้องทำ iteration อย่างรวดเร็ว ให้ใช้ Monorepo
→ ถ้าองค์กร/ทีม/โปรเจกต์มีขนาดใหญ่และความเสถียรสำคัญกว่า ให้ใช้ Polyrepo
การตัดสินใจเลือกภาษาโปรแกรม
https://github.com/joelparkerhenderson/architecture_decision_record/…
→ frontend ใช้ TypeScript
→ backend ใช้ Rust
→ ฝั่ง frontend เป็นงานทั่วไป แต่ต้องพัฒนา, deploy และทำซ้ำได้อย่างรวดเร็ว ไม่จำเป็นต้องรองรับ legacy
→ ฝั่ง backend มีความต้องการสูงกว่างานทั่วไปเล็กน้อย ต้องคำนึงถึงคุณภาพ, ความเสถียร และความปลอดภัย ต้องการระดับเกือบ real-time (ต้องไม่มีการหยุดจาก GC) functional programming / parallel processing และ multi-core processing รวมถึง memory safety ก็สำคัญ
ข้อจำกัด: ต้องสามารถรันได้แน่นอนบน serverless ของ major cloud service (Amazon Lamba)
สิ่งที่พิจารณา: C/C++/Clojure/Elixir/Erlang/Elm/Flow/Go/Haskell/Java/Javascript/Kotlin/Python/Ruby/Rust/TypeScript
ข้อถกเถียง:
→ C: ตัดออกเพราะความปลอดภัยต่ำ; Rust ทำได้ดีกว่าในแทบทุกด้าน
→ C++: ตัดออกเพราะยุ่งเหยิง (mess); Rust ทำได้ดีกว่าในแทบทุกด้าน
→ Clojure: การทำ modeling ยอดเยี่ยม; ใกล้ Lisp มากที่สุด; runtime บน JVM ที่ยอดเยี่ยม
→ Elixir: runtime ที่ยอดเยี่ยมด้าน deployment และ concurrency; developer experience ยอดเยี่ยม; ecosystem ค่อนข้างเล็ก
→ Erlang: runtime ที่ยอดเยี่ยมด้าน deployment และ concurrency; developer experience ค่อนข้างท้าทาย; ecosystem ค่อนข้างเล็ก
→ Elm: มีอนาคตสดใส; IBM กำลังแชร์กรณีศึกษาที่ดี; ecosystem เล็ก
→ Flow: เป็นการพัฒนา JS ที่น่าสนใจ; แต่เหล่านักพัฒนากำลังถอยห่างออกไป
→ Go: developer experience ยอดเยี่ยม; concurrency ยอดเยี่ยม; เคยมีการตัดสินใจที่ไม่ดีบางอย่างจนทำให้ภาษาดูแปลก
→ Haskell: ภาษา functional ที่ดีที่สุด; ชุมชนนักพัฒนาขนาดเล็ก; ยังมีกรณีสำเร็จใน production ไม่มากนัก
→ Java: runtime ดีที่สุด; ecosystem ยอดเยี่ยม; developer experience ธรรมดา
→ JavaScript: ภาษาที่ได้รับความนิยมมากที่สุด; ecosystem กว้างที่สุด
→ Kotlin: ปรับปรุงหลายอย่างของ Java; ได้รับการสนับสนุนอย่างยอดเยี่ยมจาก JetBrains; มีกรณีความสำเร็จจาก Java to Kotlin มากมาย
→ Python: ภาษาที่ได้รับความนิยมสูงสุดทางฝั่ง system administration; มีเครื่องมือวิเคราะห์ที่ดี; มี web framework ที่ดี; ถูกทิ้งเมื่อ Google เลือก Go
→ Ruby: developer experience ดีที่สุด; web framework ดีที่สุด; ชุมชนยอดเยี่ยม; ช้ามาก; ทำ packaging ได้ยาก
→ Rust: ภาษาใหม่ที่ดีที่สุด; เน้น Zero-cost abstractions (แม้ทำ abstraction ก็ไม่ทำให้ช้าลง); เน้น concurrency; ecosystem ค่อนข้างเล็ก; มีข้อจำกัดใน optimization ของ compiler บางอย่าง (เช่น การเข้าถึงหน่วยความจำโดยตรงต้องเป็น unsafe)
→ TypeScript: เพิ่ม type ให้กับ JavaScript; transpiler ยอดเยี่ยม; นักพัฒนากำลังย้ายจาก JS ไป TS มากขึ้นเรื่อยๆ; ได้รับการสนับสนุนอย่างแข็งแกร่งจาก Microsoft
ตัดสินใจว่าจะไม่เลือกแบบ VM-based (เพราะเพิ่มความซับซ้อน)
หากต้องการ runtime ที่เร็วที่สุด ให้เลือก JS และ C
หากต้องการ runtime ที่เร็วเกือบที่สุด ให้เลือก TypeScript และ Rust
หากเลือก VM และ web framework แล้ว
→ Closer และ Luminous
→ Java และ Spring
→ Elixir และ Phoenix