ถ้าผู้ที่หลงเข้ามาอ่านคนใดติดตามเจ้าของบล็อกมากนานมากพอ ก็อาจจะคุ้นเคยกับชื่อบทความนี้ก็เป็นได้ เพราะเจ้าของบล็อกเคยเขียนไว้นานนนนนนมากแล้ว (ราวๆ 3 ปีที่แล้ว) และในวันนี้ก็จะขอกลับมาเขียนบทความในซีรีย์นี้กันอีกครั้ง
เปิดประเด็น
"ฟีเจอร์นี้เขียนมาตั้งสัปดาห์นึงแล้ว ยังไม่เสร็จเลยครับ""บั๊กนี้หนูนั่งแก้มาสองวันแล้ว ยังแก้ไม่ได้เลยค่ะ"
ผู้ที่หลงเข้ามาอ่านหลายๆคนน่าจะคุ้นเคยความรู้สึกนี้กันมาไม่น้อย ไม่ว่าจะเป็นการแก้บั้กหรือทำฟีเจอร์ซักตัวที่ใช้เวลาหลายวันหลายคืนแต่ว่าจนแล้วจนเล่าก็ยังไม่เสร็จเสียที จนสุดท้ายก็ต้องไปขอความช่วยเหลือจากคนอื่นเพื่อช่วยแก้ปัญหา
เจ้าของบล็อกก็เป็นคนหนึ่งที่มีผู้คนเข้ามาถามหรือเข้ามาขอความช่วยเหลือกันมากมาย ไม่ว่าจะเป็นนักพัฒนาในวัยทำงานหรือวัยเรียนก็ตาม แต่หนึ่งในบทสนทนาที่เจ้าของบล็อกเจออยู่บ่อยๆก็คือ
"โห ผมนั่งงมมาเป็นสัปดาห์ แต่พี่แก้ไขได้ในไม่กี่วัน/กี่ชั่วโมง"
ซึ่งเหตุการณ์แบบนี้ทำให้เจ้าของบล็อกรู้สึกว่ามันกลายเป็นหนึ่งปัญหายอดนิยมที่นักพัฒนาหลายๆคนประสบปัญหาอยู่แฮะ
...ระยะเวลายอดฮิตในการเสียเวลาไปกับปัญหาเหล่านี้คือ 2 วัน หรือไม่ก็ 1 สัปดาห์ ซึ่งเจ้าของบล็อกก็ไม่รู้เหมือนกันว่าเพราะอะไร...
เมื่อนักพัฒนาใช้เวลาแก้ปัญหาใดๆนานเกินไป
ในการทำงานเป็นนักพัฒนาของเจ้าของบล็อกนั้น ระยะเวลาที่ใช้ในการทำงานนั้นเป็นสิ่งที่สำคัญมาก การใช้เวลาไปกับสิ่งใดก็ตามนานเกินที่ควรจะเป็นจึงถือว่าเป็นความผิดพลาดที่ส่งผลกระทบต่อการทำงานต่อไปเป็นอย่างมาก (ถึงแม้ว่าบางคนมองว่ามันเป็นเรื่องปกติก็ตาม)ยกตัวอย่างเช่น ใน 1 Sprint ของเจ้าของบล็อกมีสิ่งที่ต้องทำอยู่ 3 ฟีเจอร์
แต่ดันติดปัญหาบางอย่างในฟีเจอร์แรกที่ต้องทำ และเสียเวลาไปเป็นสัปดาห์ถึงจะทำเสร็จ ก็เลยทำให้ฟีเจอร์อื่นๆเลื่อนตามไปด้วย
กลายเป็นว่าใน Sprint นั้นทำเสร็จแค่ฟีเจอร์เดียว ฟีเจอร์ถัดไปก็ต้องทำต่อใน Sprint หน้า แถมฟีเจอร์ต่างๆที่รออยู่ใน Sprint ถัดไปก็ต้องเลื่อนตามๆกันไป และถ้าเป็นงานที่ต้องทำต่อเนื่อง มีการปล่อยฟีเจอร์ในทุกๆ Sprint ก็คงไม่แฮปปี้ซักเท่าไรถ้าแผนที่วางไว้ทั้งหมดเลื่อน เพียงเพราะว่ามีฟีเจอร์หนึ่งที่ใช้เวลาทำนานกว่าที่คาดไว้
แล้วจะแก้ปัญหานี้ยังไงดีล่ะ?
เพราะอะไรถึงใช้เวลานานเกินไป?
• ไม่เข้าใจในสิ่งที่ทำ• ไม่รู้วิธีที่ถูกต้อง
• ไม่เข้าใจว่าปัญหาเกิดขึ้นเพราะอะไร
ซึ่งสาเหตุเหล่านี้เป็นเรื่องปกติที่เกิดขึ้น เพราะเป็นเรื่องธรรมดาที่นักพัฒนาคนหนึ่งต้องเจอกับภารกิจที่ไม่อาจจะคาดเดาได้
ถ้าถามว่าจะแก้ไขได้ยังไงก็คงตอบได้ไม่ยากว่า "ต้องเรียนรู้ให้พร้อมอยู่เสมอ" แต่ในความเป็นจริงมันก็ไม่ได้สวยหรูขนาดนั้นน่ะสิ เพราะส่วนมากงานที่ได้รับมอบหมายนั้นมักจะเป็นสิ่งที่ไม่สามารถคาดเดาได้ ยกตัวอย่างเช่น เจ้าของบล็อกเป็นนักพัฒนาในบริษัท Software House ที่รับงานจากลูกค้า ดังนั้นเจ้าของบล็อกจะคาดเดาไม่ได้เลยว่างานที่ลูกค้าต้องการนั้นจะมาในรูปแบบไหน
จากที่เจ้าของบล็อกได้พบเจอ ปัญหาเกิดขึ้นเพราะการเอาโค้ดจากที่ใดก็ตาม (ไม่ว่าจะหาเจอด้วย Google หรือที่มีคนตอบไว้ใน StackOverflow) ไปใช้งานโดยที่ไม่ได้เข้าใจในโค้ดนั้นๆจริงๆ
ถ้าโชคดี โค้ดก็ทำงานได้ตามปกติ แต่ถ้าโชคร้าย โค้ดนั้นทำงานไม่ได้หรือทำงานได้แค่บางเงื่อนไขเท่านั้น (ถ้ารู้ตัวไวก็ดีไป แต่ถ้ารู้ตัวทีหลังก็อาจจะซวยได้) และพอพบว่าโค้ดที่เอามาใช้มันทำงานไม่ได้ ก็เลยไปหาโค้ดอื่นๆมาใช้แทน ทำแบบนี้ไปเรื่อยๆจนกว่าจะเจอโค้ดที่สามารถทำงานได้ตามที่ต้องการ
พอมารู้ตัวอีกทีก็เสียเวลาไปหลายวันแล้วนั่นเอง...
แล้วต้องทำอย่างไรล่ะ?
มาจำลองสถานการณ์กันเถอะ
ภารกิจฟีเจอร์ 101
เช้าวันหนึ่งเจ้าของบล็อกได้รับมอบหมายว่าจะต้องทำฟีเจอร์หนึ่งที่ไม่เคยทำมาก่อน สิ่งแรกที่เจ้าของบล็อกจะทำคือฟีเจอร์นั้นๆจะต้องมีโค้ดอะไรบ้าง
เริ่มจากทำความเข้าใจก่อนว่าฟีเจอร์ที่ต้องทำนั้นมันเป็นยังไง ทำงานยังไง และมีอะไรที่ต้องรู้บ้าง แล้วแบ่งการทำงานออกเป็นหลายๆส่วนเพื่อที่จะได้รู้ว่าต้องเขียนโค้ดอะไรบ้างวิเคราะห์ Use Case ที่เป็นไปได้ทั้งหมด
อันนี้ค่อนข้างสำคัญ ถึงแม้ว่าจะได้ Requirement และฟังบรีฟมาแล้วก็ตาม แต่ในความเป็นจริงมักจะพบว่า Use Case ที่ได้รับมามันไม่ครอบคลุมซักเท่าไรนัก (เป็นเรื่องปกติ) ดังนั้นเจ้าของบล็อกจึงต้องมานั่งคิดก่อนว่ามี Use Case อะไรบ้างที่สามารถเป็นไปในการทำงานนั้นๆหาข้อมูล หาโค้ด หรือหาตัวอย่างการทำงานที่จะเอามาใช้ในฟีเจอร์
อ้าว งี้มันก็เหมือนเดิมนี่นา?ใจเย็นๆก่อน เจ้าของบล็อกไม่ได้บอกว่าการทำแบบนี้มันผิด จริงๆแล้วก็ควรทำแบบนั้นแหละ เพราะหลายๆอย่างก็ไม่จำเป็นต้องทำเองทั้งหมด
แต่หัวใจสำคัญคือขั้นตอนต่อไปครับ
อย่ายึดติดกับวิธีคิดเดิมๆถ้ามันไม่สามารถแก้ปัญหาได้
บ่อยครั้งที่เจ้าของบล็อกได้เจอนักพัฒนาที่มัวแต่คิดวิธีเดิมๆทั้งๆที่มันไม่เวิร์ก (ยกตัวอย่างเช่น พยายามแก้โค้ดเดิมๆให้มันทำงานได้ ทั้งๆที่เขียนผิดวิธีตั้งแต่แรก) ดังนั้นอย่าปล่อยให้หัวสมองมัวแต่ยึดติดกับวิธีคิดแบบเดิมๆถ้าวิธีคิดนั้นไม่ถูกต้องตั้งแต่แรก คิดต่อไปก็มีแต่เสียเวลาเปล่า เพราะสุดท้ายมันก็ใช้ไม่ได้อยู่ดีดังนั้นถ้ารู้สึกว่าติดปัญหาที่ทำให้ไปต่อไม่ได้ และใช้เวลานานเกินไป ให้ล้มเลิกความคิดนั้นทันทีแล้วมองหาวิธีอื่นซะ จะได้ไม่เสียเวลา
ทั้งนี้ทั้งนั้นวิธีคิดนั้นจะต้องถูกพิสูจน์ก่อนว่าเวิร์กหรือไม่ด้วยการทำ Proof of Concept
ทำ Proof of Concept (POC) ทุกครั้งเพื่อดูความเป็นไปได้
นี่คือขั้นตอนสำคัญที่นักพัฒนาหลายๆคนมองข้ามไปและไม่ได้ทำเลย ทั้งๆที่การทำ Proof of Concept นั้นจะช่วยให้นักพัฒนามั่นใจได้ว่าสิ่งที่จะเอามาใช้งานนั้นตอบโจทย์จริงๆหรือไม่ โดยที่ใช้เวลาไม่นานมากเมื่อไม่ได้ทำ Proof of Concept สิ่งที่เกิดขึ้นก็จะเป็นแบบนี้
ก็คือการเอาโค้ดตัวอย่างไปใส่ลงในโปรเจคโดยตรง และโค้ดนั้นอาจจะต้องเสียเวลาอีกนิดหน่อยเพื่อปรับโค้ดให้ทำงานร่วมกับโค้ดส่วนอื่นๆในโปรเจคได้
แล้วก็มาเจอทีหลังว่า "มันไม่เวิร์กแฮะ..."
พอมันไม่เวิร์ก จึงต้องเสียเวลามานั่งเอาโค้ดออก แล้วก็ไปหาโค้ดใหม่มาใส่ในโปรเจคอีกครั้ง และเนื่องจากโค้ดนั้นๆไม่สามารถทำงานได้ทันที ก็เลยต้องเสียเวลาอีกเล็กน้อยเหมือนเดิมเพื่อปรับโค้ดให้ทำงานร่วมกับโค้ดอื่นๆในโปรเจคได้
จะดีกว่ามั้ย ถ้าทำ Proof of Concept ตั้งแต่แรก?
สำหรับผู้ที่หลงเข้ามาอ่านคนใดที่นึกไม่ออกว่า Proof of Concept เป็นยังไง ปกติแล้วเจ้าของบล็อกจะเรียกขั้นตอนนี้แบบบ้านๆว่า "สร้างโปรเจคแบบโง่ๆเพื่อทดสอบ" นั่นเอง
สร้างโปรเจคแบบโง่ๆเพื่อทดสอบ
ตามชื่อเรียกเลยครับ มันก็คือการที่เจ้าของบล็อกสร้างโปรเจคขึ้นมาใหม่ตัวหนึ่ง ในนั้นไม่มีโค้ดอื่นใดนอกเหนือไปจากโค้ดที่เกี่ยวข้องกับสิ่งที่เราจะทำเท่านั้น เพื่อไม่ให้มีโค้ดอื่นๆที่ไม่เกี่ยวข้องมีผลกระทบไปด้วย ดังนั้นมันก็จะกึ่งๆบังคับให้เจ้าของบล็อกต้องเขียนโค้ดที่เป็นอิสระต่อกันให้มากที่สุดเท่าที่เป็นไปได้และในการทดสอบนั้นไม่ใช่แค่ว่าเอาโค้ดมาแปะ นอกจากการทดสอบเพื่อดูว่าโค้ดทำงานได้มั้ย ก็จะต้องทดสอบจาก Use Case ที่ได้คิดไว้ในขั้นตอนก่อนหน้าด้วย โดยโค้ดที่ทดสอบนั้นจะต้องสามารถทำงานได้ถูกต้องตาม Use Case ทั้งหมดที่สามารถเกิดขึ้นได้ อาจจะต้องมีการเขียนโค้ดเพิ่มเติมเล็กน้อยเพื่อจำลองการทำงานบางอย่างเพื่อทดสอบด้วย
ยกตัวอย่างเช่น เจ้าของบล็อกสร้าง Custom View ตัวหนึ่งขึ้นมา (เขียนเองหรือว่าเอาโค้ดมาจากที่อื่นก็ได้) เจ้าของบล็อกก็จะต้องทดสอบด้วยการสร้างโปรเจคเปล่าๆขึ้นมา แล้วเรียกใช้งาน Custom View ในนั้น โดยจำลองการทำงานที่สามารถเกิดขึ้นได้ทั้งหมดดังนี้
• เรียกใช้งานใน Layout XML โดยตรง
• เรียกใช้งานโดย Inflate ผ่านโค้ด Java
• เรียกใช้งานใน Fragment
• เรียกใช้งานใน View Pager
• เรียกใช้งานใน List View
• เรียกใช้งานใน Recycler View
• ทั้งหมดนี้จะต้องสามารถหมุนหน้าจอ แล้วยังทำงานได้ถูกต้อง
ซึ่งทั้งหมดนี้คือเงื่อนไขที่เป็นไปได้ในการเรียกใช้งาน Custom View ดังนั้นถ้าโค้ดดังกล่าวทำงานได้ในทุกๆเงื่อนไข นั่นก็หมายความว่า Custom View ตัวนี้สามารถนำไปใช้งานได้แบบสบายหายห่วงนั่นเอง
โค้ดพร้อมแล้ว เอาไปใช้งานได้เลย
เพราะทดสอบตามเงื่อนไขที่น่าจะเกิดขึ้นได้ทั้งหมดแล้ว ก็นำโค้ดไปใช้งานในโปรเจคได้เลย จบภารกิจแก้บั๊ก 101
นอกเหนือจากการพัฒนาฟีเจอร์บางอย่างแล้ว การแก้บั๊กก็เป็นหนี่งในปัญหาหลักของการใช้เวลานานเกินที่คาดไว้เช่นกัน เพราะบ่อยครั้งที่แก้บั๊กบางอย่างไป แล้วดันเกิดบั๊กอีกตัวขึ้นมา (นี่บั๊กหรือไฮร้า)บั๊กเกิดขึ้นที่ไหน และเพราะอะไร
น่าจะเป็นสิ่งที่ต้องทำเป็นอย่างแรกสุด เพราะถ้าไม่รู้ว่าเกิดขึ้นที่ไหนและเกิดขึ้นได้ยังไง ก็เขียนโค้ดใหม่ทั้งหมดเถอะนะทดสอบว่าบั๊กเกิดจากสิ่งที่คิดไว้จริงๆหรือไม่
ควรพิสูจน์ทุกครั้งว่าสาเหตุของบั๊กนั้นเกิดเพราะอะไร อย่าเชื่อมั่นโดยการมโนว่ามันน่าจะเป็นอย่างนั้น เพราะนั่นคือสาเหตุหลักที่ทำให้นักพัฒนาส่วนใหญ่เสียเวลาไปกับการมโนโดยใช่เหตุ ดังนั้นการหาสาเหตุของบั๊กนั้นจะต้องแสดงผลให้เห็นได้จริงๆ ไม่ว่าจะเป็นการใช้ Debugger หรือว่าเขียนโค้ดบางอย่างเพิ่มเข้าไปเพื่อทดสอบการเกิดบั๊กจริงๆและสาเหตุในการเกิดบั๊กดังกล่าวก็เป็นเสมือน Use Case หนึ่งที่ถูกมองข้ามไป ดังนั้นจงอย่าลืมว่าโค้ดที่เกิดบั๊กนั้นมี Use Case เดิมคืออะไรบ้าง แล้วเพิ่มสาเหตุของบั๊กเข้าไปใน Use Case ดังกล่าวด้วย เพื่อทดสอบให้ครบทุก Use Case ในภายหลัง จะได้ไม่เผลอแก้บั๊กแล้วผุดบั๊กตัวอื่นขึ้นมาโดยไม่ได้ตั้งใจ
สำหรับการหาสาเหตุก็เหมือนการหาเบาะแสหลักฐานน่ะแหละ ให้เริ่มจากสิ่งที่ใกล้ตัวบั๊กนั้นมากที่สุดก่อน แล้วค่อยไล่ทดสอบไปเรื่อยๆเพื่อหาว่าจริงๆแล้วเกิดจากตรงไหนกันแน่
ยกตัวอย่างเช่น เจ้าของบล็อกเขียนแอปฯตัวหนึ่งที่มีการส่งข้อมูลไปที่ Server แล้วอยู่มาวันหนึ่งมีการแจ้งมาว่ามีบั๊กเกิดขึ้นที่ฝั่ง Server เพราะข้อมูลที่ได้รับนั้นไม่ถูกต้องตามที่กำหนดไว้
ดังนั้นเจ้าของบล็อกจะต้องพิสูจน์ก่อนว่าบั๊กนั้นเกิดจาก Server เอาค่าไปทำอะไรแล้วผิดเองหรือฝั่งแอปฯส่งข้อมูลไปผิดตั้งแต่แรกกันแน่ เพื่อที่จะได้ไม่เสียเวลาไล่หาบั๊กผิดที่ ดังนั้นก็จะเริ่มจากเช็คก่อนเลยว่าฝั่ง Server ตอนรับค่าไปนั้นถูกต้องหรือไม่ จากนั้นก็เช็คต่อที่ฝั่งแอปฯในตอนที่ส่งค่าไปให้ Server นั้นมีค่าถูกต้องหรือไม่ จากนั้นก็จะไล่ไปเรื่อยๆตั้งแต่คำสั่งส่งค่าไปให้ Server, คำสั่งต่างๆที่มีผลต่อค่าดังกล่าว ไปจนถึงการ Input ข้อมูลจาก User ในตอนใช้งาน
และที่สำคัญ ให้นึกถึงสาเหตุของบั๊กให้ที่เกิดขึ้นได้ให้ครบทุกกรณีเสมอ แล้วไล่ทดสอบให้หมดซะ ถึงแม้ว่าบางกรณีจะฟังดูไม่น่าจะเกิดขึ้นได้ก็ตาม เพราะเจ้าของบล็อกเคยมีความคิดประมาณว่า "มันปัญญาอ่อนมาก มันไม่น่าจะเกิดขึ้นหรอก" แต่ก็มาพบทีหลังว่าบั๊กนั้นเกิดขึ้นเพราะสาเหตุปัญญาอ่อนนั้นจริงๆ
แก้ไขโค้ดเพื่อแก้บั๊กซะ
จะเขียนโค้ดเพื่อแก้ไขเองหรือจะค้นหาตาม Google ก็แล้วแต่เถอะทำ Proof of Concept กับโค้ดที่มีการแก้ไขด้วย (ถ้าเป็นไปได้)
เพื่อให้มั่นใจว่าการแก้ไขโค้ดของเจ้าของบล็อกนั้นถูกต้อง ไม่กระทบต่อการทำงานใน Use Case อื่นๆ (ที่มาของแก้บั๊กแล้วงอกเป็นบั๊กตัวอื่น)แก้บั๊กเสร็จเรียบร้อย
ปิดงานได้! กลับบ้านนอนกันเถอะสรุป
และนี่ก็คือที่มาว่าทำไมเจ้าของบล็อกถึงไม่ค่อยติดปัญหาในโค้ดหรือบั๊กใดๆนานเกินเหตุ ไม่ต้องมานั่งมืดแปดด้านเพราะหาทางออกไม่เจอ เพราะเวลาคิดอะไรขึ้นมาก็จะมีการพิสูจน์เพื่อให้แน่ใจทุกครั้ง ดังนั้นไม่ต้องกลัวว่าจะเผลอหลงทางให้เสียเวลาโดยไม่จำเป็น ดังนั้นถ้าผู้ที่หลงเข้ามาอ่านคนใดที่ยังประสบปัญหาซ้ำๆซากๆแบบนี้อยู่ ก็เลิกวิธีแบบเดิมๆซะ แล้วลองใช้วิธีแบบเจ้าของบล็อกดูครับหัวใจสำคัญของบทความนี้ก็คือ อย่ามัวแต่ยึดติดกับวิธีแบบเดิมๆให้เสียเวลา และเมื่อคิดวิธีหรือโค้ดเพื่อแก้ปัญหาบางอย่างได้ ควรทดสอบก่อนเป็นอย่างแรกเพื่อดูว่ามันเป็นวิธีที่ใช่จริงๆหรือไม่ทุกๆครั้ง
ถ้าสังเกตขั้นตอนและวิธีคิดในบทความนี้ดีๆก็จะพบว่ามันสอดคล้องกับ "การเขียนโปรแกรมที่ดีนั้น ควรมีการเขียนเทสด้วย" เพราะการเขียนเทสก็เสมือน Proof of Concept ของโค้ดนั่นเอง
เจ้าของบล็อกจะถือว่าเมื่อใดก็ตามที่คิดอะไรบางอย่างแล้วไปต่อไม่ได้หรือแก้ไขปัญหาไม่ได้ซักที และใช้เวลานานเกิน 6 ชั่วโมง ก็จะถือว่าวิธีนั้นใช้ไม่ได้ทันที (เพราะถ้าเจ้าของบล็อกมาถูกทาง มันไม่ควรจะเสียเวลานานขนาดนั้น) ก็จะล้มเลิกความคิดนั้นทันทีแล้วมองหาวิธีอื่นที่แตกต่างกันออกไป
สุดท้ายนี้ ก็หวังว่าจะได้ยินคำพูดที่ว่า "นั่งแก้บั๊กมาสองวันแล้ว ยังแก้ไม่ได้เลย" ลดน้อยลงนะครับ