Keywords: tooling, config, logging, concurrency
อ่านแบบคน Python:
- ถ้าอยากเอา “ภาพรวม” ก่อน: คิดเป็น 4 แกน: deps / secrets / config / observability
- ถ้าอยาก “ลงมือทำ”: หยิบ checklist ท้ายบท แล้วไล่ทีละข้อกับโปรเจกต์ของคุณ
- ถ้าติด: เปิด 12-learning-playbook.md
บทนี้เป็น “บทเสริม” ที่เอาหลักการจากบทก่อน ๆ มารวมเป็นชุด เพื่อให้โปรแกรมของคุณ รันซ้ำแล้วเหมือนเดิม, พังแล้ว debug ได้, และ ไม่ต้องแก้ไฟดับทุกครั้งที่ย้ายเครื่อง
ขอบเขตของบทนี้ (ตั้งใจให้ปลอดภัย):
- เน้นแนวทางเชิงป้องกัน (defensive) และความทนทานของระบบ
- ไม่สอนเทคนิคโจมตี/หลบการตรวจจับ/ทำลายระบบ
กรณีที่เจอบ่อยใน Python คือโค้ดไปเรียก pip install ... ตอนรันจริง
- ปัญหา: พังยาก, ทำซ้ำไม่ได้, และกลายเป็นพื้นที่เสี่ยงด้าน supply chain
- ตรึง dependency ไว้ล่วงหน้า (เช่น
requirements.txtหรือ lock file ของเครื่องมือที่ใช้) - แยกขั้นตอน install ออกจากการรันโปรแกรม
- dependencies อยู่ใน
Cargo.toml Cargo.lockช่วยล็อกเวอร์ชันให้ reproducible (โดยเฉพาะ binary)
แนวคิด:
- “รันโปรแกรม” กับ “ติดตั้ง dependency” เป็นคนละเฟส
หลักง่าย ๆ:
- secret ไม่ควรอยู่ใน git
- secret ไม่ควรไปโผล่ใน query string/URL
- secret ไม่ควรหลุดไปใน logs หรือ error messages
- เผลอ
print(config)แล้วมี token/secret ติดไปด้วย
- ปล่อยให้ config loader คืน error ที่ “มีบริบทพอ” แต่ไม่พ่น secret
- log เป็น event/fields แล้วเลือก field ที่จะ render (ช่วยกันหลุด)
แนวทาง:
- ใส่
request_id/job_idเพื่อ debug แต่หลีกเลี่ยงการใส่ secret ลงใน fields
- I/O → JSON syntax → schema → semantics
ถ้าต้องเขียนไฟล์ config/state ระหว่างรัน:
- ห้ามเขียนทับไฟล์เดิมตรง ๆ เพราะ crash กลางทาง = ไฟล์ครึ่งก้อน
แนวทาง (platform-agnostic):
- เขียนไฟล์ใหม่ (
.tmp) - replace ด้วย rename/replace (atomic ในระดับ filesystem ส่วนใหญ่)
ตัวอย่าง pseudo-flow (ไม่ลงรายละเอียด OS-specific):
write endpoint.json.tmp
fsync (optional)
rename endpoint.json.tmp -> endpoint.json
- ตอนแรกไม่จำเป็น
- แต่พอเริ่มเพิ่ม/เปลี่ยน field บ่อย ๆ ให้ใส่
versionเพื่อ migrate อย่างมีจังหวะ
ถ้า log เป็น “ประโยคยาว ๆ” อย่างเดียว:
- search ยาก
- สรุปสถิติยาก
- correlation ยาก (เหตุการณ์ชุดเดียวกันกระจัดกระจาย)
แนวคิดที่คุ้มที่สุด:
- structured log (event + fields)
- ใส่
request_id/trace_id/job_id
ตัวอย่าง fields ที่พอสำหรับโปรเจกต์เล็ก:
level: info/warn/errorcategory: auth/config/apijob_id: UUIDaction: load_config / start_worker / shutdown
ถ้าคุณมีงาน background:
- อย่าปล่อยให้มัน “เกิดเอง” หลายจุดจนคุณไม่รู้ว่ามีกี่ตัวรันอยู่
แนวคิดที่ใช้ได้จริง:
- มี owner ชัด (สร้าง/หยุด)
- สั่งหยุดได้ (cancellation)
- รอให้จบได้ (join) เพื่อ shutdown อย่างสะอาด
หมายเหตุ:
- ในบทนี้ไม่ลงรายละเอียด ops/process control; แนะนำให้พึ่ง supervisor ของระบบ (เช่น container/system service) มากกว่าการไป
pkillจากในแอป
- deps
- ไม่มี
pip install/ดาวน์โหลด dependency ตอน runtime - มี lock/วิธีทำซ้ำได้
- config
- โหลด config ที่จุดเดียว (เช่น
main) - error บอก path/ชั้นที่พัง
- ถ้าต้องเขียนไฟล์: ใช้ atomic write
- logs
- log เป็น event-first
- มี
request_id/job_id - ไม่ log secrets
- concurrency
- background jobs มี start/stop/join
- lock scope แคบ และไม่ถือ lock ตอนทำ I/O หนัก