10 Signs of code problem
อยากเขียน Code ให้ดีดูแลรักษาง่ายๆ ไม่โดนคนบ่นด่า ไม่มีปัญหาเวลา Release บทความนี้ผมมี 10 สัญญาณที่บอกว่า Code ของเรากำลังมีปัญหา ลองดูหัวข้อเหล่านี้กันดูครับว่ามีหัวข้อไหนที่ตรงบ้าง
1. ไม่มี Unit/Integration Test
ถ้าไม่เขียน Test แล้วคนอื่นที่มาแก้อาจจะทำให้ Logic ของเราที่เคยทำงานได้ปกติกลับกลายเป็นพังได้เลย ดังนั้นใส่ Test ไปหน่อยยังดีกว่าไม่ใส่เลย โดยเฉพาะ Edge case ต่างๆ ครับ ถ้าเขียน Unit Test ไม่ได้ อย่างน้อยก็เขียนเป็น Integration Test นะครับ
2. ต้องทดสอบผ่าน UI เท่านั้น
ถ้าโดนข้อนี้แสดงว่า Architecture ของระบบน่าจะมีปัญหาแล้วหละครับ ระบบที่ดีจะต้องถูกออกแบบให้มีความ Flexible ประมาณนึง เราจะใช้จุดนี้แหละในการทดสอบด้วย Unit/Integration Test ดังนั้นถ้าต้องทดสอบผ่าน UI เท่านั้น เราควร Refactor หรือปรับ Architecture ของระบบให้ดีขึ้นครับ
3. ไม่มี Metrics หรือขาด Health check ที่ดีสำหรับ Monitoring ระบบ
ข้อนี้มีปัญหาพื้นฐานตัวเดียวกับข้อ 2 ครับ แต่ถ้าทำข้อ 2 ได้แล้วยังขาดข้อนี้อยู่ เราควรจะเพิ่มส่วนนี้เข้าไปด้วย เพื่อให้เราสามารถดูสุขภาพของระบบได้ละเอียด และแก้ปัญหาได้ตรงจุดมากขึ้นครับ
บางระบบแค่ดู Metrics ไม่พอครับ ต้องทำ Health check ด้วย เช่น เราพัฒนาระบบส่งอีเมล์เอง ก็ควรจะ check ทุกๆ 5–10 นาทีโดยทดลองส่งอีเมล์จริงๆ บน Production ดูว่าเข้า Inbox ที่ตั้งไว้หรือไม่ ถ้าอีเมล์ไม่เข้าก็ Alert และก็ซ่อมโดยทันทีครับ
4. ใน Pull Request/Merge Request มีจำนวนไฟล์ที่ถูกแก้ไขเยอะ
ลองสังเกตดูว่า ใน PR/MR ของเราแต่ละอัน จำนวนไฟล์ที่ถูกแก้ไขเยอะขนาดไหน ยิ่งไฟล์เยอะและอยู่คนละ Folder หมายถึงปัญหารุนแรง ถ้าเจอแบบนี้ควรจะ Refactor ก่อน 1 รอบเพื่อให้โครงสร้างของ Code ถูกต้อง ลดจำนวนไฟล์ที่ต้องแก้ไขใน PR/MR ของเราครับ
5. มี Code Pattern อยู่หลากหลายใน Project เดียวกัน
ยกตัวอย่างเช่น หาก Project ของเรามีทั้ง MVC, MVP, MVVM คงจะแปลกน่าดู เวลาคนอื่นมาเขียนต่อจะเลือกใช้ Pattern ไหนดี สับสนไปหมด ทั้งนี้ไม่จำกัดแค่ Pattern ใหญ่ๆ แต่ยังรวมถึงรายละเอียดอื่นๆ ด้วย เช่น ใช้ for loop ปนกับ while เขียน Functional ปนกับ OOP หรือใช้ lib version เก่าปนกับ version ใหม่ เป็นต้น
ปัญหาเกิดจากตอน Refactor แล้วตามแก้ไม่หมดทุกจุด สะสมไว้นาน ดังนั้นก็ตาม Refactor ให้เป็น Pattern เดียวกันให้หมดครับ เพื่อให้คนอื่นๆ ไม่งงและใช้อ้างอิงเป็นตัวอย่างได้
6. Style การเขียน Code ไม่เหมือนกัน
ทำงานเป็นทีมต้องเขียน Code ให้ตรงกับ Style ของทีมครับ อย่า Indy เด็ดขาดแม้ว่าของใหม่จะดีแค่ไหนก็ตาม เช่น ใน Java 8 ทีมเขียนไว้เป็น For loop แต่เราดันไปใช้ Stream ก็จะทำให้ Style ไม่สอดคล้อง จริงๆ รวมถึง spacing, ปีกกา, การเว้นบรรทัดอีกด้วย ถ้าจะเปลี่ยน Style ให้ตามแก้ให้หมดทุกจุดครับ ไม่เช่นนั้นก็จะเกิดปัญหาข้อ 5 ตามมา
7. Breaking Changes
ของบางอย่างเปลี่ยนแล้วพังทันที ทำงานร่วมกันไม่ได้ ตัวอย่างเช่น
- เปลี่ยนชนิด Field ในฐานข้อมูลจาก Integer เป็น String ทำให้ Code ทำงานไม่ได้
- Server ลบ Required Field ออกจาก API ที่คืนให้ Client ทำให้ Client Crash/Error
- Client Version ใหม่ส่งค่ามาเป็น String แต่ Server Version เก่ารับแค่ Integer
เรื่องนี้ต้องระวังให้มากเพราะทำระบบพังไปหลายต่อหลายครั้งแล้ว ต้องมั่นใจว่าสิ่งที่เปลี่ยนแปลงนั้น Backward Compatible และ/หรือ Forward Compatible เสมอ และก็อย่าลืมกลับมา clean up code เก่าๆ เหล่านี้ด้วยนะครับ
8. Rollback ไม่ได้
ต่อจากข้อ 7 ถ้าเราเปลี่ยนอะไรบางอย่างแล้ว Rollback กลับไม่ได้จะลำบากมากๆ ครับ เพราะถ้าหากเกิดปัญหาต้องเดินไปข้างหน้าแก้ปัญหาให้ได้อย่างเดียว บางครั้งอาจใช้เวลานานมากๆ จึงจะแก้ได้สำเร็จ
ตัวอย่างเช่น Code ใหม่เปลี่ยนจาก Integer เป็น String และก็ได้ Handle เรื่อง Compatible ไว้แล้ว จากนั้นเรา Migrate database จาก Integer เป็น String เรียบร้อย แต่ว่า Code ใหม่ดันมี Bug Critical มากๆ ในกรณีนี้จะถอยกลับก็ไม่ได้ เพราะ Code เก่าไม่รองรับ String ดังนั้นต้องแก้ Bug ให้ได้อย่างเดียว
9. Roll forward ซ้ำๆ ไม่ได้
ข้อนี้เป็นเคสกลับกันของข้อ 8 ตัวอย่างเช่น เราจะ Migrate database โดยย้ายข้อมูล User ID ที่เป็นตัวเลข เปลี่ยนให้กลายเป็น String ใน Column ใหม่ ระหว่าง Migrate ดันเจอปัญหาทำให้ Process หยุดไป พอรัน Script Migrate ซ้ำทำให้ Data ที่รันไปแล้วเสียหาย หรือข้อมูลที่ทำสำเร็จแล้วมีการเปลี่ยนแปลง ดังนั้นเราต้องออกแบบการ Release ให้สามารถ Rollback ได้อย่างปลอดภัยและสามารถ Roll forward ซ้ำๆ หลายรอบได้อย่างปลอดภัยเช่นเดียวกัน
10. กลัวที่จะ Deploy/Release งาน
ถ้าคุณรู้สึกกลัวหรือไม่มั่นใจว่างานที่ทำจะมีปัญหาหรือไม่ ก็อาจจะเป็นสัญญาณของปัญหาได้ เช่น ภาษาที่ใช้เจอ Null Exception บ่อยๆ ทำให้ระบบ Crash แทนที่จะมัวแต่กลัวเราควรจะหาวิธีป้องกันมากกว่า เช่น ใช้ Static Analysis Tool ใช้ Design Pattern มาช่วย หรือเปลี่ยนภาษาไปเลย เป็นต้น
ทั้งหมด 10 ข้อที่เขียนมาให้ เป็นสัญญาณที่ผมพบบ่อยๆ ซึ่งบางครั้งเราก็ไม่ได้เอะใจจนมันเกิดปัญหาขึ้น บางปัญหาไม่ได้ทำให้ระบบล่มแต่ก็เป็นเหมือนภัยเงียบใน Code ได้เหมือนกัน กว่าจะเห็นปัญหาชัดๆ ก็สายเกินไปและยากเกินจะแก้ไข ลองสังเกต 10 ข้อเหล่านี้ดูนะครับ