ทำไมและเมื่อไหร่ควร refactor
ทำไมต้องเสียเวลาด้วย?
หัวข้อที่มีชื่อว่า “ทำไมต้องเสียเวลาด้วย?”Refactoring ไม่ได้เพิ่มฟีเจอร์และไม่ได้ทำให้ผู้ใช้พึงพอใจโดยตรง จึงเป็นธรรมที่จะถามว่าคุณได้อะไรกลับมาจากความพยายามนี้ สามอย่าง ซึ่งทั้งหมดทบต้นขึ้นตลอดอายุของโปรเจกต์
- อ่านง่าย เวลาส่วนใหญ่ของ dev หมดไปกับการอ่าน code ไม่ใช่การเขียน code ที่จัดรูปทรงจนเจตนาโผล่ออกมาเอง อ่านได้เร็วกว่าและเข้าใจผิดน้อยกว่า ทุกนาทีที่ลงไปกับการทำให้ code ชัดเจน จะได้คืนกลับมาหลายเท่าจากทุกคนที่มาอ่านทีหลัง รวมถึงตัวคุณเองในอีกไม่กี่เดือน
- บั๊กน้อยลง โครงสร้างที่ชัดเจนเปิดโปงข้อบกพร่องที่ code ยุ่ง ๆ ซ่อนไว้ พอแต่ละ function ทำงานเดียวที่ตั้งชื่อได้ สมมติฐานผิด ๆ ก็ไม่เหลือที่ให้ซุก Refactoring ไม่ได้แก้บั๊กด้วยตัวเอง แต่ทำให้บั๊กมองเห็นได้ และทำให้ตอนแก้จริงปลอดภัยขึ้น
- เปลี่ยนแปลงถูกลง ผลตอบแทนที่แท้จริง code ที่มีโครงสร้างดีต่อยอดได้ถูก ส่วน code ที่ยุ่งเหยิงทำให้ทุกฟีเจอร์ใหม่กลายเป็นโครงการขุดค้นทางโบราณคดี Refactoring คือการลงทุนที่ทำให้ต้นทุนของการเปลี่ยนแปลงในอนาคตยังคงต่ำ
Technical debt
หัวข้อที่มีชื่อว่า “Technical debt”วิธีคิดที่ใช้ได้ดีกับ code รก ๆ คือมองเป็น technical debt อุปมาที่ Ward Cunningham ตั้งขึ้นในรายงานประสบการณ์ OOPSLA ปี 1992 ทุกครั้งที่คุณใช้ทางลัดเพื่อส่งงานให้ทัน เท่ากับกู้ยืมจากอนาคต 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 ไว้ให้สะอาดกว่าตอนที่คุณเจอเสมอ แม้จะแค่นิดเดียวก็ตาม
Robert C. Martin ทำให้กฎนี้แพร่หลายในชื่อ Boy Scout Rule ผ่านหนังสือ Clean Code (2008) และ 97 Things Every Programmer Should Know (2010)
คุณไม่จำเป็นต้องมี “refactoring sprint” โดยเฉพาะ การปรับปรุงที่ยั่งยืนที่สุดมาจากการเก็บกวาดเล็ก ๆ ที่สอดแทรกอยู่ในงานปกติ เปลี่ยนชื่อตัวแปรที่สับสนหนึ่งตัว extract บล็อกที่ยุ่งเหยิงหนึ่งบล็อก ลบ branch ที่ตายแล้วหนึ่งอันทุกครั้งที่คุณผ่านมา code เบสจะค่อย ๆ มีแนวโน้มสะอาดขึ้นโดยไม่มีใครต้องจัดตารางเวลาเลย
สามโอกาสที่ควร refactor
หัวข้อที่มีชื่อว่า “สามโอกาสที่ควร refactor”ในทางปฏิบัติ refactoring ที่ดีเป็นแบบ ฉวยโอกาส คือแล่นไปพร้อมกับงานที่คุณทำอยู่แล้ว มีสามจังหวะที่เป็นธรรมชาติ (ตาม Fowler, Refactoring พิมพ์ครั้งที่ 2 บทที่ 2 “When Should We Refactor?”)
- Preparatory refactoring — “ทำให้การเปลี่ยนแปลงง่ายก่อน แล้วค่อยเปลี่ยนแบบง่าย ๆ นั้น” (คำของ Kent Beck ปี 2012) ก่อนเพิ่มฟีเจอร์ ให้จัดรูปทรง code รอบ ๆ ให้ฟีเจอร์ลงตัวสะอาด ๆ ยังไงคุณก็ต้องอ่าน code ตรงนี้อยู่แล้ว ลงแรงอีกสองสามนาทีเตรียมทางไว้เลย
- Comprehension refactoring — พอคุณแกะ code ที่งงงวยออกได้สำเร็จ ให้เขียนความเข้าใจนั้นกลับลงไปใน code เลย เปลี่ยนชื่อให้ตรง extract ส่วนที่เพิ่งไขได้ออกมา ผู้อ่านคนถัดไปจะได้ไม่ต้องดิ้นซ้ำรอยคุณ
- Litter-pickup refactoring — ระหว่างทางคุณเห็นความรกที่ไม่เกี่ยวกับงานตรงหน้า ถ้าเล็กก็เก็บตรงนั้นเลย ถ้าใหญ่ก็จดไว้แล้วค่อยกลับมา อย่าปล่อยให้การเก็บกวาดบานปลายจนกลายเป็น diff ที่รีวิวไม่ไหว
เมื่อไหร่ที่ ไม่ ควร refactor
หัวข้อที่มีชื่อว่า “เมื่อไหร่ที่ ไม่ ควร refactor”Refactoring ไม่ได้ฟรี และมีบางเวลาที่ควรวางหมวกลง
| สถานการณ์ | ทำไมจึงควรยับยั้งไว้ |
|---|---|
| ไม่มี test | หากไม่มีตาข่ายความปลอดภัย คุณก็พิสูจน์ไม่ได้ว่า behavior ถูกรักษาไว้ จงเพิ่ม characterization test ก่อน แล้วจึง refactor |
| code ใช้แล้วทิ้ง | สคริปต์ครั้งเดียวจบหรือ spike ที่คุณจะลบทิ้งพรุ่งนี้ ไม่คุ้มกับการลงทุน |
| ช่วงคับขันก่อนเส้นตาย | ถ้าต้องส่งการแก้ให้ทันในชั่วโมงหน้า ก็ส่งไปก่อน จดหนี้ไว้แล้วค่อยจ่ายคืนอย่างตั้งใจทีหลัง ไม่ใช่ตอนที่โดนกดดัน |
| code ที่คุณกำลังจะลบ | จัดโครงสร้าง code ที่ยังไงก็จะถอดออกใน ticket ถัดไป คือลงแรงทิ้งเปล่า |
กฎลูกเสือฉบับซื่อสัตย์ย่อมรวมถึงการรู้ว่าเมื่อไหร่ที่ที่ตั้งแคมป์กำลังไฟไหม้และตอนนี้ไม่ใช่ช่วงเวลาที่จะเก็บกวาด