“การแก้ปัญหาเฉพาะหน้า” หรือ “การใช้ไม้ค้ำยัน” หมายถึงการสร้างวิธีแก้ปัญหาชั่วคราวที่แก้ไขบั๊กหรือเพิ่มฟังก์ชันการทำงาน แต่ไม่ได้กำจัดสาเหตุที่แท้จริงและไม่สอดคล้องกับมาตรฐานทางสถาปัตยกรรมของโครงการ ไม้ค้ำยันเป็นสิ่งที่หลีกเลี่ยงไม่ได้ในการพัฒนาทุกประเภท: กำหนดส่งมอบ ความเข้าใจระบบที่ไม่สมบูรณ์ และข้อจำกัดภายนอกบังคับให้ต้องตัดสินใจแบบประนีประนอม ตามข้อมูลของ Refactoring Guru ความแตกต่างสำคัญระหว่างไม้ค้ำยันเชิงปฏิบัติกับหนี้ทางเทคนิคอยู่ที่การตระหนักรู้ถึงการตัดสินใจและการมีแผนที่จะกำจัดมัน การใช้อย่างชำนาญวิธีแก้ปัญหาชั่วคราวต้องมีวินัยและการจัดทำเอกสาร
ประเด็นสำคัญ
ไม้ค้ำยัน (crutch) คือวิธีแก้ปัญหาซอฟต์แวร์ที่ทำงานได้แต่ทำ “อย่างเร่งรีบ”: มันแก้ปัญหาเฉพาะอย่างแต่ไม่ได้กำจัดสาเหตุของมัน ไม่เป็นไปตามสถาปัตยกรรมของโครงการ และสามารถพังได้เมื่อมีการเปลี่ยนแปลงเพียงเล็กน้อยในสภาพแวดล้อม การเปรียบเทียบนั้นแม่นยำ — เช่นเดียวกับไม้ค้ำยันจริง โค้ดแบบนี้ช่วยให้ “เดิน” ได้แต่ไม่ได้รักษา “ขา”
นักพัฒนา “ใช้ไม้ค้ำยันพยุง” บั๊ก ความไม่เข้ากันของเวอร์ชัน คุณลักษณะเฉพาะของแพลตฟอร์ม และข้อกำหนดเร่งด่วนของลูกค้า ไม้ค้ำยันทั่วไปคือ ไม้ค้ำยันแบบมีเงื่อนไข: ถ้าเป็น iOS 15 ให้เพิ่มระยะห่าง ถ้าเป็น Huawei ให้ซ่อนปุ่ม การตรวจสอบเหล่านี้ทวีคูณและเปลี่ยนโค้ดให้เป็น “เค้กชั้น” ของสาขาแพลตฟอร์มและเวอร์ชัน
ไม้ค้ำยันมี หลายขนาด: ตั้งแต่บรรทัดเดียวที่มีเงื่อนไขไม้ค้ำยันไปจนถึงโมดูล wrapper ทั้งหมดที่ “แก้ไข” พฤติกรรมของไลบรารี สิ่งสำคัญคือต้องเข้าใจว่าไม้ค้ำยันไม่ใช่สิ่งเลวร้ายเสมอไป: ในมือที่ถูกต้อง มันเป็นเครื่องมือที่ช่วยให้สามารถปล่อยผลิตภัณฑ์ได้ทันเวลา ปัญหาเริ่มต้นเมื่อไม้ค้ำยันอยู่ในโค้ดตลอดไป
สาเหตุหลัก ที่ไม้ค้ำยันปรากฏคือความขัดแย้งระหว่างวิธีแก้ปัญหาที่เหมาะกับข้อจำกัดที่แท้จริงของโครงการ นักพัฒนารู้วิธีทำให้ถูกต้อง แต่เวลา เงิน หรือข้อจำกัดทางเทคนิคขัดขวางไว้ ผลลัพธ์คือวิธีแก้ปัญหาแบบประนีประนอมที่ “แค่ทำงาน”
มาดู สาเหตุหลักสี่ประการ ที่นักพัฒนาใช้ไม้ค้ำยันอย่างมีสติ การเข้าใจสาเหตุเหล่านี้ช่วยให้มองไม้ค้ำยันไม่ใช่ความผิดพลาด แต่เป็นเครื่องมือเชิงปฏิบัติที่ต้องได้รับการจัดการ
สาเหตุที่พบบ่อยที่สุด การเปิดตัวคือ พรุ่งนี้ บั๊กเกิดขึ้นเฉพาะบนรุ่นเฉพาะ และการแก้ไขทางสถาปัตยกรรมจะใช้เวลาสองสัปดาห์ ไม้ค้ำยันแบบมีเงื่อนไขใช้เวลาหนึ่งชั่วโมงและแก้ปัญหา หลังจากการเปิดตัว ทีมสัญญาว่าจะกลับมาและเขียนใหม่ให้ถูกต้อง “ไม่มีอะไรถาวรไปกว่าวิธีแก้ปัญหาชั่วคราว” — นี่คือสิ่งที่เกิดขึ้นกับไม้ค้ำยันแบบนี้
ไลบรารี A ต้องการ Android 12 แต่แอปของคุณรองรับ Android 10 วิธีแก้คือ เขียน wrapper ที่ตรวจสอบเวอร์ชัน OS และเลือกเส้นทางการทำงาน นี่คือไม้ค้ำยันเพราะเมื่ออัปเดตไลบรารี จะต้องเขียน wrapper ใหม่ แต่ทางเลือก — การละทิ้งไลบรารีหรือการสนับสนุนอุปกรณ์เก่า — อาจแย่กว่า
// ไม้ค้ำยันสำหรับความเข้ากันได้กับ API 29
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
useModernApi()
} else {
useLegacyFallback()
}
ไลบรารีที่โครงการพึ่งพามีบั๊ก แต่การอัปเดตอาจใช้เวลาหลายสัปดาห์ (ต้อง PR, การตรวจสอบโค้ด, การเผยแพร่) แทนที่จะรอ ทีมเขียน wrapper ที่แก้ไขพฤติกรรมของไลบรารีทันที เมื่อไลบรารีเวอร์ชันที่แก้ไขแล้วถูกปล่อยออกมา wrapper จะถูกลบออก ถ้าไม่ลบออก นั่นก็เป็นปัญหาทางสถาปัตยกรรมแล้ว
นักพัฒนาใหม่ในโครงการเดิมไม่เข้าใจว่าเหตุใดโค้ดจึงทำงานแบบนั้น แทนที่จะหาคำตอบ พวกเขาเพิ่มเงื่อนไขใหม่ทับเงื่อนไขที่มีอยู่ นี่คือ ไม้ค้ำยันประเภทที่อันตรายที่สุด เพราะผู้เขียนไม่รู้ตัวว่ามันเป็นไม้ค้ำยัน วิธีแก้ไขเดียวคือการตรวจสอบโค้ดและการเขียนโปรแกรมคู่สำหรับสมาชิกทีมใหม่
ไม้ค้ำยันทุกอันไม่ใช่สิ่งเลวร้าย ในการพัฒนาจริง ความบริสุทธิ์ของโค้ดอย่างสมบูรณ์ นั้นไม่สามารถบรรลุได้และมักไม่เหมาะสม แนวทางเชิงปฏิบัติยอมรับว่าวิธีแก้ปัญหาชั่วคราวเป็นส่วนหนึ่งของกระบวนการ แต่ต้องการการตระหนักรู้ การจัดทำเอกสาร และแผนการกำจัด ไม้ค้ำยันสมเหตุสมผลเมื่อมันแก้ปัญหาทางธุรกิจได้เร็วกว่าวิธีแก้ปัญหาทางสถาปัตยกรรมที่สะอาด
เกณฑ์สำหรับ ไม้ค้ำยันที่สมเหตุสมผล: มันแก้ปัญหาเฉพาะ มีเจ้าของ (ผู้รับผิดชอบในการกำจัดมัน) และมีแผนการปรับโครงสร้างอยู่ หากขาดอย่างน้อยหนึ่งในสามเงื่อนไขนี้ ไม้ค้ำยันจะกลายเป็นหนี้ทางเทคนิค เครื่องมือเช่น ความคิดเห็น TODO พร้อมตั๋วในระบบติดตามเป็นวิธีการจัดทำเอกสารขั้นต่ำ
บั๊กร้ายแรงในสาขาการเปิดตัวที่ต้องแก้ไขก่อนการปรับใช้พรุ่งนี้ วิธีแก้ที่สะอาดต้องการการปรับโครงสร้างทางสถาปัตยกรรมและจะใช้เวลาสองสัปดาห์ ไม้ค้ำยัน: เพิ่มการตรวจสอบ nil และส่งการแก้ไขเป็น hotfix เงื่อนไขของความสมเหตุสมผล: มีการสร้างตั๋วปรับโครงสร้างในระบบติดตาม มีการกำหนดเจ้าของ และไม้ค้ำยันถูกทำเครื่องหมายด้วยความคิดเห็น สองสัปดาห์ต่อมา ทีมกลับมาทำงาน
// TODO: IT-1234 — ลบไม้ค้ำยันนี้หลังจากการปรับโครงสร้าง AuthService
guard let userId = session.user?.id else {
return Result.failure(.notAuthenticated)
}
เส้นแบ่งระหว่าง ไม้ค้ำยันที่มีสติ และปัญหาทางสถาปัตยกรรม (หนี้ทางเทคนิค) ผ่านสองพารามิเตอร์: การตระหนักรู้ถึงการตัดสินใจและการมีแผนที่จะกำจัดมัน ไม้ค้ำยันเป็นวิธีแก้ปัญหาชั่วคราวที่มีอายุการใช้งานที่ทราบเสมอ หนี้ทางเทคนิคเป็นผลมาจากไม้ค้ำยันจำนวนมากที่ถูกปล่อยปละละเลย
| พารามิเตอร์ | ไม้ค้ำยันที่มีสติ | หนี้ทางเทคนิค |
|---|---|---|
| การตระหนักรู้ | ทีมรู้ว่านี่เป็นวิธีแก้ปัญหาชั่วคราว | ไม่มีใครจำได้ว่าเหตุใดโค้ดถึงเป็นแบบนี้ |
| การจัดทำเอกสาร | มี TODO, ตั๋วในระบบติดตาม | ไม่มีความคิดเห็น การอ้างอิง หรือคำอธิบาย |
| แผนการกำจัด | มีการกำหนด Sprint สำหรับการปรับโครงสร้าง | “สักวันจะเขียนใหม่” |
| ผลกระทบ | เฉพาะที่ ไม่รบกวนฟังก์ชันใหม่ | ปิดกั้นการเปลี่ยนแปลง ทำให้การพัฒนาช้าลง |
สถานการณ์แย่ลงเมื่อจำนวนไม้ค้ำยันเกิน “มวลวิกฤต” ไม้ค้ำยันใหม่แต่ละอันเพิ่ม “ความเปราะบาง” ของระบบ: การเปลี่ยนแปลงในที่หนึ่งทำให้อีกที่หนึ่งพัง ในที่สุด การพัฒนาช้าลง บั๊กเพิ่มขึ้น และนักพัฒนาใหม่ไม่สามารถเข้าใจโค้ดโดยไม่ได้รับความช่วยเหลือจากผู้เขียน ณ จุดนี้ ไม้ค้ำยันหยุดเป็นวิธีแก้ปัญหาชั่วคราวและกลายเป็นปัญหาทางสถาปัตยกรรม
หากโค้ดมีการตรวจสอบซ้อนกันห้ารายการสำหรับเวอร์ชัน OS ผู้ผลิตอุปกรณ์ และการมีอยู่ของไลบรารีเฉพาะ — นี่ไม่ใช่ไม้ค้ำยัน แต่เป็นปัญหาทางสถาปัตยกรรม หากการเพิ่มการแก้ไขหนึ่งครั้งทำให้เกิดการถดถอยสามครั้งในโมดูลที่เกี่ยวข้อง — ไม้ค้ำยันไม่ได้เป็นเฉพาะที่อีกต่อไป หากการตรวจสอบโค้ดถูกปฏิเสธเป็นประจำเนื่องจาก “ไม้ค้ำยันอีกอัน” — ถึงเวลาวางแผนการปรับโครงสร้าง
การปรับโครงสร้างไม้ค้ำยัน คือกระบวนการแทนที่วิธีแก้ปัญหาชั่วคราวด้วยวิธีที่ถูกต้องทางสถาปัตยกรรม สิ่งนี้ต้องใช้เวลา ดังนั้นจึงจำเป็นต้องมีกลยุทธ์การจัดลำดับความสำคัญ: ไม่ใช่ทุกไม้ค้ำยันที่ต้องกำจัดทันที กลยุทธ์ที่ดีคือการประเมินไม้ค้ำยันแต่ละอันตามสองพารามิเตอร์: ความถี่ของการเปลี่ยนแปลงในพื้นที่โค้ดนั้นและผลกระทบต่อผู้ใช้
ลำดับความสำคัญสูง — ไม้ค้ำยันในโมดูลที่เปลี่ยนแปลงบ่อย (ตรรกะทางธุรกิจ, UI ทั่วไป) ที่ทำให้การพัฒนาช้าลงและทำให้เกิดการถดถอย ลำดับความสำคัญปานกลาง — ไม้ค้ำยันในโมดูลที่ไม่ค่อยเปลี่ยนแปลงแต่มีผลกระทบที่อาจเกิดขึ้นต่อผู้ใช้ (การประมวลผลการชำระเงิน, การอนุญาต) ลำดับความสำคัญต่ำ — ไม้ค้ำยันในโค้ดเดิมที่ทำงานอย่างเสถียรและไม่ได้วางแผนจะปรับเปลี่ยน
ขั้นตอนที่ 1: สินค้าคงคลัง — ค้นหา TODO และ FIXME ทั้งหมดที่เกี่ยวข้องกับไม้ค้ำยัน ขั้นตอนที่ 2: การประเมิน — กำหนดว่าอันไหนยังเกี่ยวข้อง ขั้นตอนที่ 3: การวางแผน — กำหนดการปรับโครงสร้างไม้ค้ำยันใน Sprint โดยเริ่มจากลำดับความสำคัญสูง ขั้นตอนที่ 4: การแทนที่ — ใช้วิธีแก้ที่สะอาด ลบไม้ค้ำยันและความคิดเห็น TODO ของมัน ขั้นตอนที่ 5: การตรวจสอบ — ตรวจสอบให้แน่ใจว่าการทดสอบผ่านและไม่มีการถดถอย
# ค้นหาไม้ค้ำยัน TODO ทั้งหมดในโครงการ
grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.kt" --include="*.swift" .
วิธีที่ดีที่สุดในการต่อสู้กับไม้ค้ำยันคือ “ไม่สร้างมันโดยไม่จำเป็น” ก่อนที่จะเขียนไม้ค้ำยัน ให้ถามตัวเองสามคำถาม: ฉันสามารถใช้วิธีแก้ที่สะอาดในเวลาที่เหมาะสมได้หรือไม่? มีทางเลือกอื่นที่ไม่ใช่ไม้ค้ำยันหรือไม่? ทีมจะมีเวลากลับมาและเขียนใหม่นี้หรือไม่? หากคำตอบของอย่างน้อยหนึ่งคำถามคือ “ไม่” — คิดอีกครั้งก่อนที่จะ “พยุง” โค้ด
คำถามที่พบบ่อย
การแก้ปัญหาเฉพาะหน้า คือการเขียนวิธีแก้ปัญหาชั่วคราวที่แก้ไขปัญหาแต่ไม่ได้กำจัดสาเหตุของมัน โค้ดทำงานได้แต่ไม่สอดคล้องกับสถาปัตยกรรมของโครงการและสามารถพังได้เมื่อมีการเปลี่ยนแปลง
ไม้ค้ำยัน เป็นวิธีแก้ปัญหาชั่วคราวที่มีสติพร้อมแผนการกำจัด หนี้ทางเทคนิคเป็นผลมาจากไม้ค้ำยันจำนวนมากที่ถูกลืม ไม้ค้ำยันเป็นเฉพาะที่ หนี้เป็นระบบและขัดขวางการพัฒนา
เมื่อกำหนดส่งมอบสำคัญ วิธีแก้ที่สะอาดต้องใช้เวลา และไม้ค้ำยันถูก จัดทำเอกสาร ด้วยความคิดเห็น TODO และตั๋วในระบบติดตาม เงื่อนไข: ไม้ค้ำยันมีแผนการกำจัดในอนาคตอันใกล้
เพิ่ม TODO หรือ FIXME พร้อมหมายเลขตั๋วและคำอธิบายสั้น ๆ ของวิธีแก้ที่ถูกต้อง ตัวอย่าง: // TODO: IT-567 — rewrite using Factory pattern หากไม่มีตั๋ว ไม้ค้ำยันจะถูกลืม
ทำ สินค้าคงคลัง ของ TODO ทั้งหมด จัดลำดับความสำคัญ เริ่มต้นด้วยโมดูลที่เปลี่ยนแปลงบ่อย แทนที่ไม้ค้ำยันด้วยวิธีแก้ที่สะอาด ลบความคิดเห็นและตรวจสอบด้วยการทดสอบ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม