จัดการกับโค้ดเก่าสักที !

Tae Keerati Jearjindarat
3 min readSep 1, 2020

สวัสดีครับ กลับมาอีกแล้วหลังจากที่เคยเขียนถึง เรื่องการ Maintain ซอฟต์แวร์ และ สิ่งที่ได้จากโค้ดเก่า ๆ ในมุมของ First Jobbers ไปแล้ว วันนี้ผมอยากจะเขียนถึงสิ่งที่จะทำให้เราพัฒนาตัวเองมากขึ้นไปอีกขั้นสำหรับคนที่จบใหม่ และผมได้โอกาสแตะโค้ดเก่า ๆ จากงานที่ได้รับมอบหมายเลยคิดถึงเรื่องนี้ขึ้น ที่น่าสนใจคือ ในเรื่องนี้เรามักจะไม่ค่อยได้ยินหรือได้ยินผ่าน ๆ ในช่วงเวลา 4 ปี ที่เราเรียนมา ซึ่งความเข้าใจของแต่ละคนอาจจะไม่เหมือนกัน วันนี้ผมจะมาเขียนในมุมมองของผมที่ได้เรียนมาจาก Course Online และประสบการณ์ต่าง ๆ ให้อ่านกัน โดยเป้าหมายคืออยากให้เปิด keywords นี้สำหรับคนจบใหม่ให้ชัดเจนมากขึ้นครับ

Refactoring คืออะไร

ก่อนอื่นเรามาปรับความเข้าใจกันก่อน โดยคำว่า Refactoring ในบล็อคนี้มีความหมาย 2 รูปแบบคือ

Refactoring (noun)

Refactoring (noun) หมายถึงการเปลี่ยนโครงสร้างภายในซอฟต์แวร์ที่ทำให้ง่ายต่อการทำความเข้าใจและง่ายต่อการแก้ไขปรับเปลี่ยน โดยไม่เปลี่ยนพฤติกรรมเดิมจากที่เคยทำได้

Refactoring (verb)

Refactoring (verb) เป็นการจัดโครงสร้างของซอฟต์แวร์ใหม่ โดยทำการ Refactoring (noun) อย่างน้อย 1 วิธีมาจัดการ

แปลง่าย ๆ คือเป็นแตกต่างเหมือนกัน เพราะเป็นการแก้ไขโค้ดของเราโดยยังคงการทำงานเหมือนเดิม โดยอาจจะแก้มากกว่า 1 ส่วนประกอบก็ได้

เช่น เราอาจเขียนโปรแกรม Lift ขนของขึ้นมา 1 ชิ้น และใช้วิธี If — else ในการเช็คว่า Lift ตัวนี้อยู่ชั้นอะไร ซึ่งเราได้เขียนชุดทดสอบไว้ แต่เราอาจทำการ Refactoring โดยใช้ state design pattern เนื่องจาก Lift มี State ชัดเจนในการใช้งานเป็นต้น ซึ่ง Lift ยังคงขึ้นลงได้เหมือนเดิม แต่โครงสร้างภายในเปลี่ยนโดยใช้ Design Pattern ครับ

แล้วทำไมเราต้องทำล่ะ ?

ผมเคยพูดถึงเรื่องความสามารถในการ Maintainability ไว้ใน เรื่องการ Maintain ซอฟต์แวร์ ครับ ทั้งหมดเป็นเหตุผลที่เราควรจะ Refactoring โค้ดที่เป็น Product ของเราเพราะหากเรายิ่งพัฒนาซอฟต์แวร์มากขึ้น จำนวนโค้ดของเรายิ่งเพิ่มตาม ซึ่งหากเราไม่ดูแลให้ง่ายต่อการเข้าใจ จะทำให้คุณภาพแย่ลงตามความซับซ้อนที่มากขึ้น ทำให้สุดท้าย การแก้ไขจะยากมากขึ้นและทำให้ต้องทำใหม่ทั้งหมด ซึ่งสามารถอ่านเพิ่มเติมได้ในลิ้งที่ใส่ไว้ครับ

ลอง Role Play เล่น ๆ แล้วนึกภาพตามนะครับ

นึกภาพว่าเราเป็นเชฟร้านอาหารแห่งหนึ่ง เราเปิดร้านมาได้ 8 เดือน และมีลูกค้าอยู่ประมาณหนึ่ง

แต่ร้านเราเริ่มสกปรกและในฐานะเชฟ เราเห็นว่าเราควรจะปิดร้านประมาณ 1 อาทิตย์เพื่อทำความสะอาดให้ร้านมีคุณภาพดี มีบรรยากาศน่าเข้ามาทานอาหาร และดูสะอาด แต่เราจะบอกผู้จัดการยังไงหากเราต้องปิดร้านถึง 1 อาทิตย์เพื่อทำความสะอาดโดยขาดรายได้ ? แถมส่งผลให้ลูกค้าผิดหวังหากต้องการจะมาทานที่ร้านเรา โดยอาจจะคิดว่า เฮ้ย กระบวนการของร้านห่วยถึงต้องปิดร้านตั้ง 1 อาทิตย์เพื่อทำความสะอาดเลยหรอ ?

ในเมื่อความเป็นจริงเราทำแบบนี้ไม่ได้ แสดงว่าคำว่าทำไปก่อนเดี๋ยวค่อยมา Refactoring ให้ดีทีหลังโดยขอเวลาประมาณ 2 Sprint มันแทบจะเป็นไปไม่ได้เลย
แต่ก็อาจจะเกิดขึ้นได้ในบางกรณี

แล้วเมื่อไหร่ที่ควรจะ Refactor

  1. คำที่ดังมากๆ ในช่วงนี้นั่นคือ Test Driven Development (TDD) ครับ เนื่องจากตัว TDD มีกระบวนการ 3 แบบอยู่นั่นคือ Red — Green — Refactor ซึ่งถ้าเราเขียนด้วยหลัก TDD เราจะมีช่วงเวลาให้ทำโดยปกติอยู่แล้วครับ นั่นคือเขียน Test ให้ Fail จากนั้นเขียนโค้ดให้ผ่าน Test และทำการแก้ไขให้ดีทั้ง โค้ดที่เป็น Logic และ โค้ดสำหรับ Test
  2. อีกคำที่ผมไม่คุ้นหูคือ Pain Driven Development (PDD) หลักการคร่าว ๆ คือเราจะ Refactor เมื่อ Design ปัจจุบันไม่ตอบโจทย์เราอีกต่อไป เช่น เมื่อเราต้องทำระบบ User Management โดยเริ่มต้นเราอาจจะทำแค่ Login ด้วย Email และ Password แต่วันหนึ่งมีความต้องการเข้าสู่ระบบด้วย Facebook ทำให้เราอาจจะต้อง Refactoring ระบบ User เก่าให้รองรับข้อมูลที่มาจาก Facebook ด้วย
  3. เมื่อเราได้รับงานแก้บัค แล้วเราเห็นว่าโค้ดจุดนี้มันไม่ดี อ่านยากและเข้าใจยาก นั่นแปลว่าถึงเวลาที่คุณจะต้องจัดการครับ เมื่อเราเห็นปัญหาไม่ควรจะนิ่งเฉย เราต้องทำอะไรกับมัน หากไม่เคยมี Test อาจเริ่มต้นด้วยการเขียน Test จาก Behavior เดิม และค่อย ๆ Refactoring ไปครับ แต่ถ้ามี Test อยู่แล้ว เราก็เพียงเพิ่ม Testcase ที่เป็นบัคของเรา และไล่ Refactoring โค้ดเดิมให้ดูแลง่ายขึ้นครับ
  4. ช่วงเวลาแห่งการรีวิวโค้ด ไม่ว่าเราจะเป็นผู้รีวิวแล้วเห็นปัญหา เราอาจจะฝากให้คนที่ทำอยู่แก้ไขไปได้เพื่อให้โค้ดคุณภาพดีมากขึ้นครับ หรือถ้าเราเป็นคนที่ทำอยู่ เราก็รับคำแนะนำและทำการแก้ไขให้ดี หรือเราอาจจะแก้เองและให้คนที่เคยทำช่วยดูก็ได้ครับว่าผิดพลาดตรงไหนหรือไม่

Boy Scout Rule

เป็นกฏเล็ก ๆ น้อย ๆ ที่ควรจำไว้ในการแก้โค้ดเก่าหรือเมื่อ Refactoring ครับ สรุปคร่าว ๆ คือ เราต้องทำให้โค้ดสะอาดกว่าตอนที่เราเจอมันครั้งแรก ให้เหมือนกับลูกเสือที่รักษาพื้นที่ตั้งแคมป์ให้สะอาดกว่าเดิม หลังจากตั้งแคมป์ไปแล้ว

ไม่ใช่ทุกเวลาที่เราควรจะ Refactoring

แน่นอนครับ เมื่อมีเวลาที่เหมาะ ก็ย่อมมีเวลาที่ไม่เหมาะเช่นกัน เบื้องต้นมีดังนี้ครับ

  1. เมื่อโค้ดปัจจุบันยังทำงานไม่ได้ พูดง่าย ๆ ก็คือของที่กำลังทำอยู่ยังใช้ไม่ได้เลย ยิ่งแก้ไปเรื่อย ๆ จะยิ่งพังครับ ฉะนั้นเราควรทำให้โค้ดใช้ได้อย่างน้อยก็ขอแค่หนึ่งส่วนก่อนก็ได้ครับ แล้วหลังจากนั้นค่อย ๆ ทำให้ฟังก์ชันเก่าใช้งานได้เหมือนเดิม และค่อย Refactoring ครับ — Made it work, Made it right, Made it good
  2. เมื่อมี Technical Debt เยอะเกินไป อาจจะเหมาะกับการ Rewrite มากกว่า Refactoring ครับ ยิ่งถ้าโค้ดเดิมไม่สามารถเขียน Test ได้หรือซับซ้อนเกินกว่าจะแก้ เราก็ไม่ควรเสียเวลาไป Refactoring ครับ เพราะถ้าทำใหม่อาจจะใช้เวลาที่เร็วกว่า แต่สิ่งที่ทำใหม่ก็ควรจะออกแบบให้ดีครับ เจ็บแล้วต้องจำ และสามารถอ่านเรื่อง Technical Debt ได้ในบล็อคสิ่งที่ได้จากโค้ดเก่าครับ
  3. เมื่อใกล้ Deadline ยิ่งไม่ควรอย่างยิ่ง จากที่บอกไปคือ Made it work ให้ได้ก่อนครับ อาจจะย้อนแย้งจากที่บอกไปคือไม่ควรทำๆ ไปก่อนแล้วค่อยมา Refactoring แต่งานต้องเสร็จครับ และเราไม่มีเวลาแล้ว ซึ่งต้องอย่าลืมว่าคุณค่าของการ Refactoring มีค่ากับ Developer เพียงแต่กระทบต่อความเร็วของการพัฒนาในอนาคต กรณีนี้ให้นึกถึง Technical Debt ที่เราออกแบบแล้วแต่ยอมรับกับสิ่งที่ทำลงไปว่ามันไม่ใช่สิ่งที่ดีที่สุดและต้องหาเวลากลับมาแก้ต่อไปครับ

“Other than when you are very close to a deadline… you should not put off refactoring because you haven’t got time.”

Martin Fowler, Refactoring

กระบวนการ Refactoring

สำคัญมากก่อนจะ Refactoring อยากแนะนำว่าเราควรใช้ Version Control นะครับ เพราะหากเราทำผิดพลาด เราสามารถย้อนเวอร์ชั่นไป 1 Step เพื่อดึงโค้ดที่ใช้งานได้กลับมา ไม่งั้นงานเราอาจจะหายทั้งหมด !

Step มีดังนี้ครับ

  1. Commit โค้ดของเราไปเก็บไว้สักที่ อาจใช้ Version Control ตามบริษัทตัวเองอยู่แล้วหรือใช้ยี่ห้อดัง ๆ เช่น Github, Gitlab หรืออื่น ๆ แล้วแต่สะดวก อาจจะเริ่มทำโดยการแตก Branch แยกออกมาก็ได้หากทำงานร่วมกันหลายคน
  2. Verify Existing ให้ตรวจสอบว่าการทำงานเดิมยังใช้ได้อยู่หรือไม่ โดยอาจจะทำ Manual Test หรือ Automated tests ก็ได้ครับเพื่อความรวดเร็ว เนื่องจากสิ่งสำคัญคือของเดิมจะต้องใช้งานได้ ทำยังไงก็ได้ให้มั่นใจว่าของเดิมใช้งานได้
  3. Apply Refactoring เมื่อเราตรวจสอบแล้ว เราจึง Apply ให้สิ่งที่เรา Refactoring ไปสู่ Code ชุดหลัก โดยอาจใช้การ Merge Branch ในฟีเจอร์ของ Version Control ช่วยครับ
  4. Confirm คือการนำสิ่งที่ Apply ไปแล้วมาทดสอบอีกรอบเพื่อความมั่นใจว่าสิ่งที่เราทำไปจะต้องใช้งานได้เหมือนเดิมโดยผ่านการ Run Tests อีกรอบครับ หากไม่มีปัญหาก็ถือว่าเสร็จกระบวนการ

หากเริ่มทำใหม่ เราอาจจะแบ่งแบบง่าย ๆ ก็ได้ครับคือให้มี 3 version ประกอบไปด้วย

  1. First Draft คือเวอร์ชั่นที่ทดลองทำแบบมั่ว ๆ โค้ดแย่ ๆ ทดลอง solution
  2. First Revision คือโค้ดที่เริ่มปรับแก้ให้ดีแล้ว มีการ Apply Refactoring
  3. Final Version เป็นเวอร์ชั่นที่ใช้งานได้จริงผ่านการ Refactoring แล้ว

โดยแต่ละ Version เราจะรันกระบวนการซ้ำ ๆ ข้างต้นไปเรื่อย ๆ ร่วมกันด้วยก็ได้จนถึง Final Version ที่เรามั่นใจจากการ Run Tests ครับ โดยที่สำคัญคือต้องมี Test โดย Test จะเน้นที่ Behavior ของระบบครับ

เราจะเริ่มจัดการโค้ดเก่ากันได้หรือยัง ?

จะเห็นว่าสิ่งสำคัญคือเราต้องมีชุดทดสอบ ส่วนตัวผมพยายามเขียนเทสเข้าไว้ เพื่อให้มั่นใจว่าเมื่อเราแก้อะไรไป มันจะต้องทำงานได้อย่างที่คาดหวังไว้ และทำงานให้เสร็จไวๆ เพื่อให้มีเวลา Refactoring โค้ดที่ทำไว้ เพื่อให้อนาคต คนที่มารับช่วงต่อจะมีความสุขเวลาอ่านโค้ดของเรา และยังส่งผลต่อองค์กรด้วยเนื่องจากเราเขียนโค้ดที่ดีมีคุณภาพที่ทำให้พัฒนาซอฟต์แวร์ได้อย่างรวดเร็วและดูแลต่อได้ครับ

งั้นวันนี้ ทุกคนลองเริ่มต้นเขียน Test และลอง Refactoring โค้ดอย่างมั่นใจกันนะครับ

ถึงเวลาจัดการโค้ดเก่ากันสักที !

ขอบคุณ

  • ขอบคุณข้อมูลความรู้จาก Course ใน Pluralsight
    ชื่อว่า Microsoft Azure Developer: Refactoring Code
    สามารถตามไปเรียนเพิ่มเติมกันได้ครับ
  • ขอบคุณทาง depa และ คณะเทคโนโลยีสารสนเทศ มจธ.
    ที่มอบทุนการศึกษาเพชรพระจอมเกล้าเพื่อพัฒนาเทคโนโลยีและนวัตกรรมดิจิทัล และทำให้ได้ความรู้เพิ่มสำหรับใช้ทำงานจริงครับ

เรื่องนี้อาจจะเป็นเรื่องสุดท้ายเกี่ยวกับการดูแล Software แล้ว
เรื่องต่อไปจะเป็นเรื่องอะไร ขอให้ติดตามตอนต่อไป ขอบคุณครับ 🙏

--

--

Tae Keerati Jearjindarat

Hi, I'm Tae, Associate Engineering Manager at LINEMAN Wongnai. Thanks for following <3