ทำไมและเมื่อไหร่ควร refactor
ทำไมต้องเสียเวลาด้วย?
หัวข้อที่มีชื่อว่า “ทำไมต้องเสียเวลาด้วย?”Refactoring ไม่ได้เพิ่มฟีเจอร์และไม่ได้ทำให้ผู้ใช้พึงพอใจโดยตรง จึงเป็นธรรมที่จะถามว่าคุณได้อะไรกลับมาจากความพยายามนี้ สามอย่าง ซึ่งทั้งหมดทบต้นขึ้นตลอดอายุของโปรเจกต์
- อ่านง่าย เวลาส่วนใหญ่ของ dev หมดไปกับการอ่าน code ไม่ใช่การเขียน code ที่จัดรูปทรงจนเจตนาโผล่ออกมาเอง อ่านได้เร็วกว่าและเข้าใจผิดน้อยกว่า ทุกนาทีที่ลงไปกับการทำให้ code ชัดเจน จะได้คืนกลับมาหลายเท่าจากทุกคนที่มาอ่านทีหลัง รวมถึงตัวคุณเองในอีกไม่กี่เดือน
- บั๊กน้อยลง โครงสร้างที่ชัดเจนเปิดโปงข้อบกพร่องที่ code ยุ่ง ๆ ซ่อนไว้ พอแต่ละ function ทำงานเดียวที่ตั้งชื่อได้ สมมติฐานผิด ๆ ก็ไม่เหลือที่ให้ซุก Refactoring ไม่ได้แก้บั๊กด้วยตัวเอง แต่ทำให้บั๊กมองเห็นได้ และทำให้ตอนแก้จริงปลอดภัยขึ้น
- เปลี่ยนแปลงถูกลง ผลตอบแทนที่แท้จริง code ที่มีโครงสร้างดีต่อยอดได้ถูก ส่วน code ที่ยุ่งเหยิงทำให้ทุกฟีเจอร์ใหม่กลายเป็นโครงการขุดค้นทางโบราณคดี Refactoring คือการลงทุนที่ทำให้ต้นทุนของการเปลี่ยนแปลงในอนาคตยังคงต่ำ
Technical debt
หัวข้อที่มีชื่อว่า “Technical debt”วิธีคิดที่ใช้ได้ดีกับ code รก ๆ คือมองเป็น technical debt ทุกครั้งที่คุณใช้ทางลัดเพื่อส่งงานให้ทัน เท่ากับกู้ยืมจากอนาคต code ใช้งานได้ตอนนี้ แต่แก้ยากขึ้น และเหมือนหนี้จริง ๆ ตรงที่ คิดดอกเบี้ย คือการแก้ทุกครั้งในบริเวณนั้นจะแพงกว่าที่ควรอยู่นิดหนึ่งเสมอ
หนี้ก้อนเล็กที่ตั้งใจกู้เป็นเรื่องฉลาดได้ เพราะช่วยให้ฟีเจอร์ออกสู่ตลาดทัน อันตรายอยู่ที่การไม่เคยจ่ายคืนเลย ปล่อยไว้นานดอกเบี้ยจะทบต้นจนแก้ code บรรทัดเดียวกินเวลาเป็นสัปดาห์ Refactoring คือวิธีจ่ายเงินต้นและหยุดดอกเบี้ย
flowchart LR
A["Ship a shortcut"] --> B["Debt accrues<br/>(harder to change)"]
B --> C{"Pay it down?"}
C -->|"Refactor"| D["Interest cleared,<br/>code stays cheap"]
C -->|"Ignore"| E["Interest compounds,<br/>every change slows"]
E --> B กฎลูกเสือ
หัวข้อที่มีชื่อว่า “กฎลูกเสือ”ทิ้ง code ไว้ให้สะอาดกว่าตอนที่คุณเจอเสมอ แม้จะแค่นิดเดียวก็ตาม
คุณไม่จำเป็นต้องมี “refactoring sprint” โดยเฉพาะ การปรับปรุงที่ยั่งยืนที่สุดมาจากการเก็บกวาดเล็ก ๆ ที่สอดแทรกอยู่ในงานปกติ เปลี่ยนชื่อตัวแปรที่สับสนหนึ่งตัว extract บล็อกที่ยุ่งเหยิงหนึ่งบล็อก ลบ branch ที่ตายแล้วหนึ่งอันทุกครั้งที่คุณผ่านมา code เบสจะค่อย ๆ มีแนวโน้มสะอาดขึ้นโดยไม่มีใครต้องจัดตารางเวลาเลย
สามโอกาสที่ควร refactor
หัวข้อที่มีชื่อว่า “สามโอกาสที่ควร refactor”ในทางปฏิบัติ refactoring ที่ดีเป็นแบบ ฉวยโอกาส คือแล่นไปพร้อมกับงานที่คุณทำอยู่แล้ว มีสามจังหวะที่เป็นธรรมชาติ
- Preparatory refactoring — “ทำให้การเปลี่ยนแปลงง่ายก่อน แล้วค่อยเปลี่ยนแบบง่าย ๆ นั้น” ก่อนเพิ่มฟีเจอร์ ให้จัดรูปทรง code รอบ ๆ ให้ฟีเจอร์ลงตัวสะอาด ๆ ยังไงคุณก็ต้องอ่าน code ตรงนี้อยู่แล้ว ลงแรงอีกสองสามนาทีเตรียมทางไว้เลย
- Comprehension refactoring — พอคุณแกะ code ที่งงงวยออกได้สำเร็จ ให้เขียนความเข้าใจนั้นกลับลงไปใน code เลย เปลี่ยนชื่อให้ตรง extract ส่วนที่เพิ่งไขได้ออกมา ผู้อ่านคนถัดไปจะได้ไม่ต้องดิ้นซ้ำรอยคุณ
- Litter-pickup refactoring — ระหว่างทางคุณเห็นความรกที่ไม่เกี่ยวกับงานตรงหน้า ถ้าเล็กก็เก็บตรงนั้นเลย ถ้าใหญ่ก็จดไว้แล้วค่อยกลับมา อย่าปล่อยให้การเก็บกวาดบานปลายจนกลายเป็น diff ที่รีวิวไม่ไหว
เมื่อไหร่ที่ ไม่ ควร refactor
หัวข้อที่มีชื่อว่า “เมื่อไหร่ที่ ไม่ ควร refactor”Refactoring ไม่ได้ฟรี และมีบางเวลาที่ควรวางหมวกลง
| สถานการณ์ | ทำไมจึงควรยับยั้งไว้ |
|---|---|
| ไม่มี test | หากไม่มีตาข่ายความปลอดภัย คุณก็พิสูจน์ไม่ได้ว่า behavior ถูกรักษาไว้ จงเพิ่ม characterization test ก่อน แล้วจึง refactor |
| code ใช้แล้วทิ้ง | สคริปต์ครั้งเดียวจบหรือ spike ที่คุณจะลบทิ้งพรุ่งนี้ ไม่คุ้มกับการลงทุน |
| ช่วงคับขันก่อนเส้นตาย | ถ้าต้องส่งการแก้ให้ทันในชั่วโมงหน้า ก็ส่งไปก่อน จดหนี้ไว้แล้วค่อยจ่ายคืนอย่างตั้งใจทีหลัง ไม่ใช่ตอนที่โดนกดดัน |
| code ที่คุณกำลังจะลบ | จัดโครงสร้าง code ที่ยังไงก็จะถอดออกใน ticket ถัดไป คือลงแรงทิ้งเปล่า |
กฎลูกเสือฉบับซื่อสัตย์ย่อมรวมถึงการรู้ว่าเมื่อไหร่ที่ที่ตั้งแคมป์กำลังไฟไหม้และตอนนี้ไม่ใช่ช่วงเวลาที่จะเก็บกวาด
เนื้อหาที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “เนื้อหาที่เกี่ยวข้อง”| Refactor เมื่อ | หลีกเลี่ยงเมื่อ |
|---|---|
| กำลังจะ add feature และ code ที่อยู่ขัดขวาง | ไม่มีแผนแตะ code นั้นในระยะอันใกล้ |
| code review พบว่า code อ่านยากและถามซ้ำ | release freeze — ความเสี่ยง regression สูงกว่าประโยชน์ |
| bug พบในพื้นที่ที่ code ซับซ้อนโดยไม่จำเป็น | team ยังไม่เข้าใจ domain นั้นดีพอ |
⚠️ ไม่ควร Refactor เมื่อ:
- ไม่มี test — ต้องเขียน test ก่อน
- deadline กดดัน — refactor หลัง release แล้วค่อย ship feature
- code ที่จะ refactor เป็น “dead code” ที่ยังไงก็กำลังจะลบทิ้ง