Topic 32 Configuration

Let all your things have their places; let each part of your business have its time.

Benjamin Franklin, Thirteen Virtues, autobiography

เมื่อ Code ต้องพึ่งพาค่าที่อาจเปลี่ยนได้หลังจาก Application ออนไลน์ไปแล้ว ให้เก็บค่าพวกนั้นไว้ข้างนอก App และถ้า App ของคุณต้องรันบนหลาย Environment หรือสำหรับลูกค้าหลายเจ้า ให้แยกค่าเฉพาะของแต่ละ Environment และลูกค้าออกมาไว้ข้างนอกด้วย วิธีนี้คือการทำ Parameterize ให้กับ Application ของคุณ เพื่อให้ Code ปรับตัวเข้ากับที่ที่มันรันอยู่ได้

Tip 55 Parameterize Your App Using External Configuration

สิ่งทั่วไปที่น่าจะอยากเอาไปไว้ใน Configuration Data ก็อย่างเช่น:

  • Credentials สำหรับ External Service ต่างๆ (Database, Third-party API และอื่นๆ)
  • ระดับการ Log และที่เก็บ Log
  • Port, IP address, ชื่อเครื่อง และชื่อ Cluster ที่ App ใช้
  • ค่าการตรวจสอบความถูกต้องสำหรับแต่ละ Environment
  • ค่าที่กำหนดจากข้างนอก อย่างเช่น อัตราภาษี
  • รายละเอียดการแสดงผลของแต่ละ Site
  • License key

โดยพื้นฐานแล้ว อะไรก็ตามที่คุณรู้อยู่แล้วว่าจะต้องเปลี่ยน และสามารถเอาออกมาไว้ข้างนอก Code ได้ ก็เอามาใส่ไว้ใน Configuration Bucket ให้หมด

Static Configuration

Framework ส่วนใหญ่และ Application ทั่วไปมักจะเก็บ Configuration ไว้ในไฟล์แบบธรรมดา (Flat file) หรือไม่ก็ใน Database Table ถ้าเป็นไฟล์แบบธรรมดาส่วนใหญ่ก็จะนิยมใช้ Format แบบ Plain-text เช่น YAML และ JSON สำหรับ Application ที่เขียนด้วยพวก Scripting Language ก็อาจจะใช้ไฟล์ Source Code พิเศษที่เขียนขึ้นมาเพื่อเก็บ Configuration โดยเฉพาะ ถ้าข้อมูลมีโครงสร้างที่ชัดเจนและลูกค้าอาจจะขอแก้ไขเอง (เช่น อัตราภาษีขาย) การเก็บไว้ใน Database Table ก็น่าจะดีกว่า และแน่นอนว่าคุณสามารถใช้ได้ทั้งสองแบบเพื่อแบ่งเก็บ Configuration ตามลักษณะการใช้งาน

ไม่ว่าจะใช้แบบไหน Configuration ก็จะถูกอ่านเข้ามาเป็น Data Structure ใน Application ตอนที่เริ่มรัน ปกติแล้วมักจะทำให้มันเป็น Global เพื่อที่จะได้เข้าถึงค่าต่างๆ จากตรงไหนของ Code ก็ได้

เราแนะนำว่าอย่าทำแบบนั้น ให้เอา Configuration มาครอบไว้ด้วย (Thin) API แทน วิธีนี้จะช่วย Decouple ตัว Code ออกจากรายละเอียดการจัดเก็บของ Configuration

Configuration-As-A-Service

ถึงแม้ว่า Static Configuration จะได้รับความนิยม แต่ปัจจุบันเราชอบอีกวิธีมากกว่า เรายังอยากให้ Configuration เก็บไว้นอก Application อยู่ดี แต่แทนที่จะเป็นไฟล์หรือ Database เราชอบให้มันอยู่หลัง Service API มากกว่า ซึ่งมีข้อดีหลายอย่าง:

  • หลาย Application สามารถแชร์ Configuration ร่วมกันได้ โดยมี Authentication และ Access Control คอยคุมสิทธิ์
  • แก้ไข Configuration ได้จากที่เดียวแต่ส่งผลทั่วโลก (Globally)
  • สามารถจัดการข้อมูล Configuration ผ่าน UI เฉพาะทางได้
  • ทำให้ Configuration Data มีความเป็น Dynamic มากขึ้น

ประเด็นสุดท้ายเรื่องที่ Configuration ควรจะเป็นแบบ Dynamic นั้นสำคัญมาก เมื่อเราก้าวไปสู่การทำ Highly Available Application ความคิดที่ว่าต้องสั่งหยุดและรัน App ใหม่เพื่อแก้ Parameter แค่ตัวเดียวนั้นล้าหลังมากเมื่อเทียบกับโลกสมัยนี้ การใช้ Configuration Service จะช่วยให้แต่ละส่วนของ App สามารถ Register เพื่อขอ Notification เมื่อมีการอัปเดตค่า Parameter ที่ใช้งานอยู่ได้ และ Service ก็จะส่งข้อความแจ้งค่าใหม่ให้เมื่อมีการเปลี่ยนแปลง

ไม่ว่าจะใช้แบบไหน Configuration Data จะเป็นตัวกำหนด Runtime Behavior ของ Application และเมื่อค่า Configuration เปลี่ยน เราก็ไม่จำเป็นต้องมา Rebuild Code ใหม่

Don't Write Dodo-Code

ถ้าไม่มี External Configuration ตัว Code ของคุณก็จะไม่ยืดหยุ่นและปรับตามสถานการณ์ได้ยาก ถามว่ามันแย่ขนาดนั้นเลยเหรอ? ก็ถ้ามองในโลกความเป็นจริง เผ่าพันธุ์ที่ไม่ยอมปรับตัวก็ต้องสูญพันธุ์ไปนั่นแหละ

นก Dodo ไม่ได้ปรับตัวตามการมาของมนุษย์และสัตว์เลี้ยงบนเกาะ Mauritius จนมันต้องสูญพันธุ์ไปอย่างรวดเร็ว[45] ถือเป็นการสูญพันธุ์ครั้งแรกที่มีบันทึกว่าเกิดจากน้ำมือมนุษย์

อย่าปล่อยให้โปรเจกต์ของคุณ (หรือชีวิตการทำงานของคุณ) ต้องลงเอยแบบนก Dodo

Sketch of a dodo.

หัวข้อที่เกี่ยวข้อง

  • Topic 9, ​_DRY—The Evils of Duplication_​
  • Topic 14, ​_Domain Languages_​
  • Topic 16, ​_The Power of Plain Text_​
  • Topic 28, ​_Decoupling_​

อย่าทำเยอะจนเกินไป (Don't Overdo It)

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

อย่าทำเยอะเกินไป มีลูกค้าเจ้ายุคแรกๆ ของเราพยายามจะทำให้ทุก Field ใน Application นั้นปรับแก้ได้ผ่าน Configuration ผลก็คือมันใช้เวลาเป็นสัปดาห์ๆ ในการแก้เรื่องเล็กๆ น้อยๆ เพราะคุณต้อง Implement ทั้งตัว Field นั้นและ Code ส่วนหลังบ้าน (Admin) เพื่อจะเซฟหรือแก้ไขมัน ลูกค้ารายนี้มีตัวแปร Configuration มากถึง 4 หมื่นตัวเลยทีเดียว และมันเป็นฝันร้ายของการเขียนโปรแกรมสุดๆ

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

เชิงอรรถ (Footnotes)

[37]

มันไม่ใช่กฎหรอก มันน่าจะเป็น "ไอเดียดีๆ ของ Demeter" มากกว่า

[38]

https://media.pragprog.com/articles/jan_03_enbug.pdf

[39]

เรารู้น่าว่า Ruby มีความสามารถนี้อยู่แล้วใน function at_exit

[40]

https://media.pragprog.com/titles/tpp20/code/event/rxcommon/logger.js

[41]

ดูเหมือนว่าการใช้ตัวอักษร |> เป็น Pipe ครั้งแรกต้องย้อนไปปี 1994 ในการคุยกันเรื่องภาษา Isobelle/ML ซึ่งถูกเก็บไว้ที่ https://blogs.msdn.microsoft.com/dsyme/2011/05/17/archeological-semiotics-the-birth-of-the-pipeline-symbol-1994/

[42]

เราถือวิสาสะตรงนี้หน่อย คือในทางเทคนิคเรายังคงเรียกใช้ฟังก์ชันต่อไป แต่แค่ไม่ได้รัน Code ข้างในนั้น

[43]

จริงๆ แล้วคุณจะเพิ่ม Operator แบบนี้เข้าไปใน Elixir โดยใช้ความสามารถของ Macro ก็ได้ ตัวอย่างหนึ่งคือ Monad library ใน hex หรือจะใช้ construct with ของ Elixir ก็ได้ แต่มันจะเสียอรรถรสในการเขียน Transformation แบบ Pipeline ไป

[44]

https://www.quora.com/What-does-Alan-Kay-think-about-inheritance-in-object-oriented-programming

[45]

มันก็ช่วยไม่ได้อะนะ เพราะพวกที่มาตั้งรกรากเขาชอบเอากระบองไล่ตีไอ้นกที่มันนิ่งๆ (หรือก็คือ "โง่") พวกนี้จนตายเป็นเรื่องสนุก

Copyright © 2020 Pearson Education, Inc.