โค้ดที่ตายแล้วคือส่วนของโปรแกรมที่ไม่เคยถูกดำเนินการและไม่ส่งผลต่อผลลัพธ์ แต่ยังคงอยู่ในไฟล์ต้นฉบับของโปรเจกต์ทางกายภาพ ซึ่งแตกต่างจากส่วนที่ถูกคอมเมนต์ โค้ดที่ตายแล้วถูกคอมไพล์และรวมอยู่ในไบนารี ทำให้ขนาดเพิ่มขึ้นและทำให้การนำทางซับซ้อน จากการวิจัยของ TIOBE Index (2025) โปรเจกต์เชิงพาณิชย์โดยเฉลี่ยมีโค้ดที่ไม่เคยถูกเรียกใช้ระหว่าง 10 ถึง 25 เปอร์เซ็นต์ โค้ดซอมบี้ เป็นประเภทย่อยของโค้ดที่ตายแล้วซึ่งเคยทำงานในอดีต แต่หลังจากการรีแฟคเตอร์ได้สูญเสียความเกี่ยวข้องและตอนนี้แค่กินพื้นที่ การทำความสะอาดส่วนเหล่านี้เป็นประจำช่วยลดภาระทางปัญญาของนักพัฒนาและลดความเสี่ยงของข้อผิดพลาดเมื่อทำการเปลี่ยนแปลง
ประเด็นสำคัญ
โค้ดที่ตายแล้ว (dead code) คือซอร์สโค้ดที่รวมอยู่ในโปรแกรมแต่ไม่เคยถูกดำเนินการในสถานการณ์การใช้งานใด ๆ คอมไพเลอร์หรืออินเทอร์พรีเตอร์ประมวลผลมัน แต่ในรันไทม์การควบคุมไม่เคยไปถึงส่วนเหล่านี้
ตัวอย่างคลาสสิกของโค้ดที่ตายแล้ว: ตัวแปรที่ถูกกำหนดค่าแต่ไม่เคยถูกอ่าน ฟังก์ชันหรือเมธอดที่ไม่ถูกเรียกที่ใด สาขาเงื่อนไขที่ไม่เคยเป็นจริง (if(false)) ลูปที่เนื้อหาไม่ถูกดำเนินการแม้แต่ครั้งเดียว
ตามรายงาน SonarQube State of Code Quality (2025) ประมาณ 15 เปอร์เซ็นต์ของคำเตือนทั้งหมดในโปรเจกต์ Java เชิงพาณิชย์เกี่ยวข้องกับเมธอดและฟิลด์ private ที่ไม่ได้ใช้งาน ในโปรเจกต์ JavaScript สัดส่วนของโค้ดที่ไม่ได้ใช้งานอาจสูงถึง 30 เปอร์เซ็นต์เนื่องจากลักษณะไดนามิกของภาษาและความอุดมสมบูรณ์ของไลบรารีของบุคคลที่สาม
ตรวจสอบโปรเจกต์ของคุณเป็นประจำเพื่อหาโค้ดที่ตายแล้ว โดยเฉพาะหลังจากการรีแฟคเตอร์ครั้งใหญ่และการลบฟีเจอร์ import ที่ถูกลืมหรือฟังก์ชันที่ไม่ได้ใช้งานในวันนี้พรุ่งนี้อาจกลายเป็นโค้ดซอมบี้ที่ทำให้สมาชิกทีมใหม่เข้าใจผิด
โค้ดซอมบี้ (zombie code) เป็นกรณีพิเศษของโค้ดที่ตายแล้วซึ่งแตกต่างด้วยบริบททางประวัติศาสตร์ โค้ดซอมบี้เคยทำงาน หลังจากเปลี่ยนแปลงในระบบกลับไม่สามารถเข้าถึงได้ แต่มันไม่ถูกลบ แต่ถูกเก็บไว้ «เผื่อไว้»
ความแตกต่างระหว่างโค้ดที่ตายแล้วและโค้ดซอมบี้อยู่ที่ต้นกำเนิด โค้ดที่ตายแล้วอาจถูกเขียนผิดพลาด (ไม่เคยทำงาน) ในขณะที่โค้ดซอมบี้คือโค้ดที่มีชีวิตมาก่อนซึ่งสูญเสียความเกี่ยวข้องหลังจากการรีแฟคเตอร์ ตัวอย่างเช่น ฟังก์ชันคำนวณส่วนลดตามตรรกะธุรกิจเก่าที่ถูกแทนที่ด้วยอันใหม่ แต่เมธอดเก่าไม่ถูกลบ — เผื่อในกรณีที่ต้องกลับมาใช้
อันตรายหลัก ของโค้ดซอมบี้คือภาพลวงตาของฟังก์ชันที่ทำงาน นักพัฒนาใหม่เห็นฟังก์ชัน อ่านเอกสาร สันนิษฐานว่ามันถูกเรียกที่ใดที่หนึ่ง — และเสียเวลาศึกษาสิ่งประดิษฐ์ เมื่อพยายามเรียกมันโดยตรงอาจพบว่ามันขึ้นอยู่กับเอนทิตีที่ถูกลบหรือ API ที่ล้าสมัย
ติดตามโค้ดซอมบี้ผ่านประวัติ git: หากฟังก์ชันไม่ถูกเปลี่ยนแปลงเป็นเวลาสองปีและไม่ได้ใช้งาน — มันคือซอมบี้ ลบมันโดยไม่ลังเล เพราะ git เก็บประวัติไว้ และโค้ดสามารถกู้คืนได้เสมอหากจำเป็น
สาเหตุแรกและที่พบบ่อยที่สุด — การพัฒนาแบบวนซ้ำ ที่มีการรีแฟคเตอร์ไม่สมบูรณ์ ทีมเพิ่มฟังก์ชันใหม่แทนที่ฟังก์ชันเก่า แต่ไม่ลบโมดูลที่ถูกแทนที่ สปรินต์สะสม «หาง» เหล่านี้ และหลังจากหนึ่งปีโปรเจกต์ถูกปกคลุมด้วยชั้นของโค้ดที่ตายแล้ว
สาเหตุที่สอง — การทดสอบ A/B และฟีเจอร์ toggle เงื่อนไขการเปิดใช้งานฟีเจอร์ใหม่อาจคงที่เมื่อเวลาผ่านไป (ตัวอย่างเช่น true เสมอ) แต่สาขา else ที่มีตรรกะทางเลือกยังคงอยู่ในโค้ด นักพัฒนากลัวที่จะลบมันเพราะเกรงว่าจะทำลายระบบโดยไม่ได้ตั้งใจหาก toggle ถูกเปิดกลับ
สาเหตุที่สาม — การสร้างอัตโนมัติและการคัดลอกวาง ตัวสร้างโค้ด (IDE, เอ็นจินเทมเพลต) สร้างเทมเพลตพร้อมเมธอดที่นักพัฒนาไม่กรอกหรือไม่ใช้ โค้ดที่คัดลอกจากโปรเจกต์อื่นมักมีบล็อกทั้งหมดที่ไม่เกี่ยวข้องกับบริบทใหม่
สาเหตุที่สี่ — ความกลัวในการลบ ในโปรเจกต์ขนาดใหญ่ นักพัฒนากลัวที่จะลบโค้ดเพราะพวกเขาไม่แน่ใจว่ามันไม่ได้ถูกใช้ที่ใดจริง ๆ ความกลัวนี้รุนแรงขึ้นจากระบบทดสอบที่อ่อนแอ: หากไม่มีการตรวจสอบอัตโนมัติ การลบอาจนำไปสู่ข้อบกพร่องที่ถูกค้นพบในสภาพแวดล้อมการผลิตเท่านั้น
โค้ดที่ตายแล้ว ส่งผลโดยตรงต่อคุณภาพของโปรเจกต์สี่ด้าน: ประสิทธิภาพการบิวด์ ขนาดสิ่งประดิษฐ์ ภาระทางปัญญาของทีม และความน่าเชื่อถือของการรีแฟคเตอร์
เวลาในการคอมไพล์เพิ่มขึ้น: คอมไพเลอร์ประมวลผลไฟล์ที่ไม่ได้ใช้ วิเคราะห์ dependencies และสร้างไบต์โค้ดหรือโค้ดเครื่องสำหรับส่วนที่จะไม่ถูกเรียกใช้ ในโปรเจกต์ขนาดใหญ่ สิ่งนี้เพิ่มนาทีให้กับการบิวด์แต่ละครั้ง สำหรับภาษาที่ถูกแปลความ (JavaScript, Python) เวลาโหลดโมดูลและการใช้หน่วยความจำเพิ่มขึ้น
ความเสี่ยงของข้อผิดพลาดเมื่อแก้ไข: นักพัฒนาเปลี่ยนแปลงโค้ดและไม่สงสัยว่าฟังก์ชันถูกใช้เฉพาะในสาขาที่ตายแล้ว หลังจากการรีแฟคเตอร์ โค้ดที่ตายแล้วหยุดคอมไพล์หรือสร้างข้อผิดพลาด — ทีมเสียเวลาในการวินิจฉัยปัญหาที่ไม่ส่งผลต่อการทำงานของแอปพลิเคชัน
ภาระทางปัญญา เป็นปัจจัยที่แพงที่สุด แต่ละฟังก์ชันที่ไม่ได้ใช้ต้องการความสนใจเมื่ออ่านโค้ด นักพัฒนาใช้พลังงานทางจิตเพื่อทำความเข้าใจว่าโค้ดนี้มีอยู่ทำไมและถูกเรียกที่ไหน การวิจัยของ Developer Productivity Lab (2025) แสดงให้เห็น: การลบ 20 เปอร์เซ็นต์ของโค้ดที่ตายแล้วช่วยลดเวลาในการเริ่มงาน (onboarding time) โดยเฉลี่ย 18 เปอร์เซ็นต์
ลบโค้ดที่ตายแล้วทันทีที่ค้นพบ แต่ละวันที่ล่าช้าเพิ่มโอกาสที่ใครบางคนในทีมจะเสียเวลาหลายชั่วโมงศึกษาสิ่งประดิษฐ์ที่ควรถูกลบตั้งแต่เมื่อวาน
การค้นหาโค้ดที่ตายแล้วทำด้วยสองวิธีหลัก: การวิเคราะห์แบบสแตติก (โดยไม่เรียกใช้โปรแกรม) และการวิเคราะห์แบบไดนามิก (การทำโปรไฟล์ความครอบคลุมในรันไทม์) แต่ละวิธีมีประสิทธิภาพสำหรับโค้ดที่ตายแล้วประเภทต่างกัน
ตัววิเคราะห์แบบสแตติกรองรับภาษาการเขียนโปรแกรมยอดนิยมทั้งหมด สำหรับ Java และ Kotlin — SonarQube, IntelliJ IDEA Inspections, SpotBugs สำหรับ JavaScript และ TypeScript — ESLint พร้อมกฎ no-unused-vars และ no-unused-modules สำหรับ Swift — SwiftLint พร้อมกฎ unused_declaration สำหรับ Python — pylint พร้อมตัวเลือก unused-import และ vulture สำหรับการค้นหาเชิงลึก
// build.gradle.kts - การกำหนดค่า ProGuard สำหรับ Android
android {
buildTypes {
release {
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
// proguard-rules.pro - เก็บเฉพาะคลาสที่จำเป็น
-keep class com.example.app.** { *; }
-assumenosideeffects class Timber {
static <methods>;
}
ProGuard ไม่เพียงลบคลาสและเมธอดที่ไม่ได้ใช้ แต่ยังย่อชื่อในบิวด์ที่เผยแพร่ บิวด์ที่เปิดใช้ ProGuard จะแสดงโดยอัตโนมัติว่าคลาสและเมธอดใดถูกพิจารณาว่าไม่ได้ใช้ — ในรายงาน usage.txt จะแสดงโค้ดที่ถูกลบทั้งหมด
เครื่องมือความครอบคลุมของโค้ด (JaCoCo สำหรับ Java, XCTest coverage สำหรับ Swift, Istanbul สำหรับ JavaScript) แสดงว่าบรรทัดและสาขาใดถูกดำเนินการระหว่างการทดสอบ เมธอดที่มีความครอบคลุมเป็นศูนย์ — ผู้สมัครสำหรับโค้ดที่ตายแล้ว อย่างไรก็ตามการขาดความครอบคลุมไม่รับประกันว่าโค้ดจะไม่ถูกเรียกในสภาพแวดล้อมการผลิต — เพื่อความแน่ใจสมบูรณ์ให้ใช้การวิเคราะห์ทั้งแบบสแตติกและไดนามิกร่วมกัน
กำหนดค่าไปป์ไลน์ CI ของคุณให้บิวด์ล้มเหลวเมื่อเกินเกณฑ์ของการประกาศที่ไม่ได้ใช้ Quality Gate ของ SonarQube ด้วยกฎ «สัดส่วนของโค้ด private ที่ไม่ได้ใช้ไม่เกิน 3%» ป้องกันการสะสมของโค้ดที่ตายแล้วในระดับกระบวนการพัฒนา
กระบวนการลบโค้ดที่ตายแล้ว ประกอบด้วยสี่ขั้นตอน: ค้นหา ตรวจสอบ ลบ ตรวจสอบอีกครั้ง การข้ามขั้นตอนใด ๆ เพิ่มความเสี่ยงของการถดถอย
ขั้นตอนแรก — ค้นหาผู้สมัคร ผ่านตัววิเคราะห์แบบสแตติก รับรายงานของการประกาศที่ไม่ได้ใช้: ฟังก์ชัน คลาส ตัวแปร import กรองผลบวกปลอม — ตัววิเคราะห์บางครั้งผิดพลาดในกรณีของ reflection การโหลดคลาสแบบไดนามิก หรือการเรียกที่ซ่อนผ่าน serialization
ขั้นตอนที่สอง — ตรวจสอบผ่าน git blame และประวัติการเปลี่ยนแปลง ดูว่าโค้ดถูกเขียนเมื่อไหร่และทำไม หากโค้ดเป็นส่วนหนึ่งของฟีเจอร์ที่ถูกปิดใช้งานโดยฟีเจอร์ toggle — ตรวจสอบให้แน่ใจว่า toggle คงที่และจะไม่ถูกเปิดอีก คอมเมนต์โค้ดที่คุณลังเลที่จะลบและทิ้ง TODO พร้อม ticket สำหรับการตรวจสอบอีกครั้งในหนึ่งเดือน
ขั้นตอนที่สาม — ลบในสาขาแยกต่างหาก พร้อมเรียกใช้ชุดทดสอบทั้งหมด หากการทดสอบผ่าน — ความน่าจะเป็นของการถดถอยต่ำ หากการทดสอบล้มเหลว — หมายความว่าโค้ดยังคงถูกใช้ และต้องเข้าใจในสถานการณ์ใด
// before - โค้ดที่ตายแล้วและโค้ดซอมบี้ในไฟล์เดียวกัน
int calculateV1(int price) { // ไม่ได้ถูกเรียกที่ใด
int tax = price * 0.18;
return price + tax;
}
int calculateV2(int price, double rate) {
return static_cast<int>(price * (1 + rate));
}
// after - โค้ดที่ตายแล้วถูกลบ โค้ดซอมบี้ถูกทำความสะอาด
int calculatePrice(int price, double rate) {
return static_cast<int>(price * (1 + rate));
}
ขั้นตอนที่สี่ — ตรวจสอบโค้ดของการเปลี่ยนแปลง ผู้ตรวจสอบควรยืนยันว่าโค้ดตายจริง หากผู้ตรวจสอบไม่แน่ใจ — ทิ้งคอมเมนต์ในโค้ดและเลื่อนการลบออกไปจนกว่าการวิเคราะห์จะสมบูรณ์ หลังจากรวมสาขา — ลบสาขาเพื่อไม่ให้โค้ดซอมบี้เพิ่มจำนวนในที่เก็บ git
ใช้กฎ: ไม่มี pull request ใดควรมีโค้ดที่ตายแล้วใหม่ เพิ่ม linter ใน hooks pre-commit ที่บล็อกการคอมมิตเมื่อมีตัวแปรหรือ import ที่ไม่ได้ใช้ การป้องกันถูกกว่าการทำความสะอาดเสมอ
คำถามที่พบบ่อย
ใช่ หากโค้ดที่ตายแล้วมีข้อผิดพลาดทางไวยากรณ์หรืออ้างอิงถึงประเภทที่ถูกลบ คอมไพเลอร์สมัยใหม่ยังคงตรวจสอบสาขาที่ตายแล้ว ดังนั้นข้อผิดพลาดในบล็อก if(false) จะทำให้การบิวด์ล้มเหลว นี่คือการป้องกัน: โค้ดไม่ควรตายมากจนคอมไพเลอร์ไม่ตรวจสอบ
โค้ดซอมบี้ ทำให้เข้าใจผิด: นักพัฒนาใหม่เห็นฟังก์ชันพร้อมเอกสารและสันนิษฐานว่ามันถูกใช้ เขาเสียเวลาศึกษาโค้ดที่ไม่ทำงานและอาจบังเอิญผูกตรรกะใหม่กับเอนทิตีที่ล้าสมัย ทำให้เกิดข้อบกพร่องที่ติดตามได้ยาก
ใช้ ESLint พร้อมกฎ no-unused-vars และ no-unused-modules รวมถึงยูทิลิตี้ knip — มันวิเคราะห์ exports และ imports ทั่วทั้งโปรเจกต์ ค้นหาไฟล์ ฟังก์ชัน และ dependencies ที่ไม่ได้ใช้ สำหรับโมโนรีโพขนาดใหญ่ knip แสดงภาพที่สมบูรณ์ที่สุด
ควรลบก่อนการเผยแพร่ แต่ไม่ใช่ในนาทีสุดท้าย การลบโค้ดที่ตายแล้วเป็นงานเทคนิคที่วางแผนแยกต่างหากในสปรินต์ ก่อนการเผยแพร่ทันที การลบอาจทำให้เกิดความไม่เสถียรหากโค้ดไม่ตายอย่างที่คิด
ใช่ คอมไพเลอร์สมัยใหม่และมินิไฟเออร์ (ProGuard, R8, Terser, Closure Compiler) ลบโค้ดที่ไม่สามารถเข้าถึงได้ในระดับ Dead Code Elimination อย่างไรก็ตามสิ่งนี้ไม่ได้แทนที่ความจำเป็นในการทำความสะอาดซอร์ส: คอมไพเลอร์ลบโค้ดออกจากไบนารี แต่ไม่ได้ลบออกจากที่เก็บ — นักพัฒนายังคงสะดุดกับมันเมื่ออ่าน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม