คัดลอกวางในการพัฒนาแอปพลิเคชัน — มันคืออะไร อันตรายอย่างไร และวิธีหลีกเลี่ยง

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-27 เวลาอ่าน: 10 นาที

คัดลอกวาง (copy-paste) คือการปฏิบัติในการคัดลอกชิ้นส่วนโค้ดจากที่หนึ่งไปยังอีกที่หนึ่งโดยไม่ปรับให้เข้ากับบริบทใหม่ ส่วนใหญ่แล้ว นักพัฒนาจะคัดลอกบล็อกจากโมดูลที่มีอยู่ แก้ไขเพียงเล็กน้อย แล้ววางลงในโมดูลใหม่ — พร้อมด้วยบั๊ก ความคิดเห็นที่ล้าสมัย และการพึ่งพาที่ไม่จำเป็น ตาม TIOBE Code Quality Survey (2025) โปรเจกต์ที่มีระดับการคัดลอกวางสูงมีข้อบกพร่องมากกว่าถึงสามเท่าต่อพันบรรทัดของโค้ดเมื่อเทียบกับโปรเจกต์ที่มีนามธรรมแบบรวมศูนย์ การซ้ำซ้อนของโค้ด เป็นแหล่งหลักของหนี้ทางเทคนิค: แต่ละสำเนาต้องการการบำรุงรักษาแยกต่างหาก และการแก้ไขบั๊กในที่เดียวไม่รับประกันว่าบั๊กอื่นจะได้รับการแก้ไข

ประเด็นสำคัญ

  • คัดลอกวาง — การคัดลอกโค้ดโดยไม่เข้าใจและปรับเปลี่ยน แหล่งหลักของหนี้ทางเทคนิค
  • อันตรายของการคัดลอกวาง: บั๊กแพร่กระจายไปทั่วโปรเจกต์ การแก้ไขสำเนาหนึ่งไม่แก้ไขสำเนาอื่น
  • DRY (Don't Repeat Yourself) — หลักการพื้นฐานที่ป้องกันการคัดลอกวาง
  • เครื่องมือตรวจจับการซ้ำซ้อน: PMD CPD, SonarQube, ESLint พร้อมกฎการซ้ำซ้อน
  • การปรับโครงสร้างการคัดลอกวาง — การแยกโค้ดทั่วไปออกเป็นฟังก์ชัน คลาส หรือไลบรารี

การคัดลอกวางคืออะไร?

การคัดลอกวาง (การเขียนโปรแกรมแบบคัดลอกวาง) คือการย้ายโค้ดที่มีอยู่ไปยังที่ใหม่โดยมีการเปลี่ยนแปลงเล็กน้อยหรือไม่มีเลย คำนี้ใช้ในความหมายเชิงดูถูก: มันบอกเป็นนัยว่านักพัฒนาไม่ได้ออกแบบวิธีแก้ปัญหา แต่กำลังคัดลอกบล็อกสำเร็จรูปโดยอัตโนมัติ ซึ่งมักจะไม่เข้าใจอย่างถ่องแท้ว่ามันทำงานอย่างไร

การคัดลอกวางมีสองประเภท: โดยเจตนา (intentional) และ โดยบังเอิญ (accidental) โดยเจตนา — เมื่อนักพัฒนาจงใจคัดลอกโค้ดโดยมีแผนจะปรับโครงสร้างในภายหลัง (แต่แผนมักถูกละทิ้ง) โดยบังเอิญ — เมื่อการซ้ำซ้อนเกิดขึ้นโดยไม่มีใครสังเกต เช่น นักพัฒนาสองคนเขียนตรรกะเดียวกันอย่างอิสระสำหรับหน้าจอที่แตกต่างกัน

ตามรายงาน SonarQube State of Clean Code (2025) โค้ดที่ซ้ำซ้อนคิดเป็นเฉลี่ย 12–18 เปอร์เซ็นต์ของปริมาณโค้ดทั้งหมดในโปรเจกต์เชิงพาณิชย์ ในขณะเดียวกัน ค่าใช้จ่ายในการแก้ไขบั๊กในโค้ดที่ซ้ำซ้อนสูงกว่าโค้ดที่มีการนำไปใช้เดี่ยวถึง 2.5 เท่า เนื่องจากนักพัฒนาต้องค้นหาและแก้ไขสำเนาทั้งหมด

เครื่องมือหลักในการต่อต้านการคัดลอกวางคือหลักการ DRY (Don't Repeat Yourself) อย่างไรก็ตาม การทำให้ DRY เป็นสิ่งสัมบูรณ์ก็อันตรายเช่นกัน บางครั้งการคัดลอกก็เป็นสิ่งที่สมเหตุสมผลเมื่อสำเนาสองชุดต้องวิวัฒนาการอย่างเป็นอิสระต่อกัน สิ่งสำคัญคือต้องแยกแยะระหว่าง “การซ้ำซ้อนโดยบังเอิญ” (ซึ่งต้องกำจัด) และ “การซ้ำซ้อนที่จำเป็น” (ซึ่งต้องบันทึกเป็นเอกสาร)

เหตุใดการคัดลอกวางจึงอันตราย

อันตรายแรกและสำคัญที่สุดคือ การแพร่กระจายของบั๊ก หากโค้ดต้นฉบับมีข้อบกพร่อง มันจะถูกคัดลอกไปยังตำแหน่งใหม่ทั้งหมดพร้อมกับโค้ด เมื่อพบข้อบกพร่องและแก้ไขในโมดูลต้นฉบับ สำเนาจะยังคงไม่ได้รับการแก้ไข นักพัฒนาอาจไม่รู้ด้วยซ้ำว่าบั๊กนั้นมีอยู่ในห้าไฟล์ที่แตกต่างกัน

อันตรายที่สองคือ วิวัฒนาการที่ไม่เท่าเทียม สำเนาสองชุดของอัลกอริทึมเดียวกันสะสมการแก้ไขที่แตกต่างกันเมื่อเวลาผ่านไป สำเนาหนึ่งเพิ่มการตรวจสอบค่าขอบเขต อีกสำเนาหนึ่งเปลี่ยนรูปแบบเอาต์พุต หลังจากนั้นไม่กี่เดือน ก็ไม่สามารถบอกได้ว่ารุ่นใด “ถูกต้อง” และโปรเจกต์สูญเสียความสอดคล้องของพฤติกรรม

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

อันตรายที่สี่คือ ภาพลวงตาของประสิทธิภาพการทำงาน การคัดลอกวางสร้างความรู้สึกเท็จเกี่ยวกับความรวดเร็ว: นักพัฒนาวางโค้ดอย่างรวดเร็วและเห็นว่าหน้าจอทำงาน แต่ “ความรวดเร็ว” นี้กลายเป็นหนี้ทางเทคนิคที่ต้องชำระคืนพร้อมดอกเบี้ยเมื่อพบข้อบกพร่องในบล็อกที่ซ้ำซ้อนหรือจำเป็นต้องเปลี่ยนแปลงตรรกะทางธุรกิจ

เหตุใดนักพัฒนาจึงคัดลอกโค้ด

การเข้าใจสาเหตุของการคัดลอกวางช่วยกำหนดมาตรการป้องกันที่เหมาะสม ส่วนใหญ่นักพัฒนาคัดลอกโค้ดไม่ใช่เพราะความเกียจคร้าน แต่เป็นเพราะแรงกดดันจากกำหนดเวลา การขาดความรู้ หรือสถาปัตยกรรมที่ไม่สะดวก

สาเหตุแรกคือ กำหนดเวลา เมื่อต้องสร้างหน้าจอภายในสองวันและมีหน้าจอที่คล้ายกันอยู่แล้ว นักพัฒนาจะคัดลอกหน้าจอนั้นทั้งหมดและเปลี่ยนแปลงเฉพาะสิ่งที่ผู้ใช้เห็น ไม่มีเวลาสำหรับการปรับโครงสร้างและแยกคอมโพเนนต์ที่ใช้ร่วมกัน — ลูกค้าต้องการผลลัพธ์ ผลลัพธ์คือหน้าจอที่สองปรากฏขึ้นพร้อมโค้ดที่ใช้ร่วมกัน 80 เปอร์เซ็นต์ แต่มีประวัติการเปลี่ยนแปลงที่เป็นอิสระ

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

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

จัดการที่สาเหตุ ไม่ใช่อาการ การลดกำหนดเวลาและการนำการตรวจสอบโค้ดมาใช้จะไม่แก้ปัญหาหากโปรเจกต์ขาดฐานสถาปัตยกรรมที่แข็งแกร่ง ลงทุนเวลาในการสร้างคอมโพเนนต์ที่นำกลับมาใช้ใหม่ได้ตั้งแต่ระยะแรก — นั่นเป็นวิธีเดียวที่จะลดสิ่งล่อใจของการคัดลอกวางในอนาคต

เครื่องมือตรวจจับการซ้ำซ้อน

การตรวจจับการคัดลอกวางดำเนินการโดยโปรแกรมวิเคราะห์อัตโนมัติที่เปรียบเทียบชิ้นส่วนโค้ดและระบุการตรงกันที่สูงกว่าเกณฑ์ที่กำหนด เครื่องมือที่ดีที่สุดทำงานในระดับ AST (แผนภูมิไวยากรณ์นามธรรม) และละเว้นการจัดรูปแบบ ชื่อตัวแปร และความคิดเห็น

PMD CPD (Copy-Paste Detector) เป็นเครื่องมือที่พบมากที่สุดสำหรับ Java, Kotlin, Swift, JavaScript, Python และ C++ CPD วิเคราะห์โทเค็นของโค้ดต้นฉบับและค้นหาการซ้ำซ้อนที่ยาวกว่าจำนวนโทเค็นขั้นต่ำที่กำหนด (ค่าเริ่มต้น 100) การกำหนดค่าเกณฑ์เป็นกุญแจสำคัญในการได้ผลลัพธ์ที่มีคุณภาพ: เกณฑ์ต่ำเกินไปจะให้ผลบวกปลอมจำนวนมาก (รูปแบบทั่วไปเช่น import) เกณฑ์สูงเกินไปจะพลาดการซ้ำซ้อนจริง

การเรียกใช้ PMD CPD ผ่าน Gradle

groovy
plugins {
    id 'pmd'
}

pmd {
    toolVersion = '7.0.0'
    ruleSetFiles = files("pmd-rules.xml")
}

tasks.register('cpd') {
    doLast {
        exec {
            workingDir = projectDir
            commandLine 'cpd',
                '--minimum-tokens', '75',
                '--language', 'kotlin',
                '--files', 'src/main/kotlin',
                '--format', 'xml',
                '--failOnViolation', 'true'
        }
    }
}

SonarQube ฝังตัวตรวจจับการซ้ำซ้อนโดยตรงใน Quality Gate กฎ Duplicated Blocks (%) แสดงเปอร์เซ็นต์ของโค้ดที่ซ้ำซ้อน เกณฑ์ 5 เปอร์เซ็นต์ถือว่าสุขภาพดีสำหรับโปรเจกต์เชิงพาณิชย์ การเกินจะบล็อกการเลื่อนไปยังสาขาที่เผยแพร่ SonarQube ยังจัดกลุ่มการซ้ำซ้อนตามประเภท: การตรงกันแบบแน่นอนและการคัดลอกเชิงโครงสร้าง (พร้อมตัวระบุที่เปลี่ยนชื่อ)

สำหรับ JavaScript และ TypeScript การซ้ำซ้อนจะถูกตรวจจับโดยใช้ ESLint กับปลั๊กอิน eslint-plugin-sonarjs (กฎ no-duplicate-string) และยูทิลิตี้ jscpd ซึ่งรองรับภาษา 150+ ภาษา jscpd สะดวกเป็นพิเศษสำหรับ monorepo: มันค้นหาการซ้ำซ้อนระหว่างแพ็คเกจ ไม่ใช่แค่ภายในโมดูลเดียว

กลยุทธ์การปรับโครงสร้างโค้ดที่ซ้ำซ้อน

การปรับโครงสร้างการคัดลอกวางสรุปได้เป็นหลักการเดียว: แยกส่วนที่เหมือนกัน และกำหนดพารามิเตอร์ให้กับความแตกต่าง เทคนิคเฉพาะขึ้นอยู่กับขอบเขตของการซ้ำซ้อนและบริบท

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

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

swift
// before - two copies of the same UITableViewController
class UserListController: UITableViewController {
    private let viewModel = UserListViewModel()
    // 40 lines of code
}

class ProductListController: UITableViewController {
    private let viewModel = ProductListViewModel()
    // same 40 lines but with Product instead of User
}

// after - generic base class shared
class ListViewController<T: ListViewModel>: UITableViewController {
    let viewModel: T
    // 40 lines of code - once only

    init(viewModel: T) {
        self.viewModel = viewModel
        super.init(style: .plain)
    }
}

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

การป้องกันการคัดลอกวางในระดับทีม

การป้องกันการคัดลอกวางมีประสิทธิภาพมากกว่าการปรับโครงสร้างโค้ดที่ซ้ำซ้อนอยู่แล้ว มาตรการป้องกันหลักอยู่ที่การจัดระเบียบกระบวนการพัฒนา ไม่ใช่ในเทคโนโลยี

มาตรการแรกคือ การตรวจสอบโค้ดที่เน้นการซ้ำซ้อน รายการตรวจสอบการตรวจสอบควรรวมรายการ: “PR นี้มีโค้ดที่มีอยู่แล้วในโปรเจกต์หรือไม่?” หากผู้ตรวจสอบเห็นการคัดลอกวาง — เขาจะบล็อกการรวมจนกว่าจะแยกคอมโพเนนต์ที่ใช้ร่วมกัน ข้อกำหนดนี้ควรเป็นส่วนหนึ่งของ Definition of Done ของทีม

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

มาตรการที่สามคือ ระบบอัตโนมัติใน CI/CD เพิ่มขั้นตอนการตรวจสอบโค้ดที่ซ้ำซ้อนในไปป์ไลน์ (PMD CPD, jscpd, SonarQube) การเกินเกณฑ์จะทำให้การสร้างล้มเหลว นักพัฒนาไม่สามารถรวม PR ที่เพิ่มอัตราส่วนการคัดลอกวางเกินระดับที่อนุญาตได้ ซึ่งจะย้ายความรับผิดชอบจากการตรวจสอบโค้ดไปยังระบบอัตโนมัติและรับประกันว่าจะไม่มีการซ้ำซ้อนใดถูกมองข้าม

ส่งเสริมวัฒนธรรม “การนำไปใช้หนึ่งครั้ง — หนึ่งที่” หากคุณเห็นโอกาสในการนำกลับมาใช้ใหม่ — อย่าเลื่อนการปรับโครงสร้างออกไป การคัดลอกวางที่ถูกทิ้งไว้ “ทีหลัง” จะทวีคูณและกลายเป็นหนี้ทางเทคนิคที่ควบคุมไม่ได้

คำถามที่พบบ่อย

การคัดลอกวางเป็นสิ่งที่ไม่ดีเสมอไปหรือไม่?

ไม่ มีสถานการณ์ของการซ้ำซ้อนอย่างมีสติ: ไมโครเซอร์วิสที่แตกต่างกันซึ่งต้องวิวัฒนาการอย่างอิสระ โค้ดที่คัดลอกสำหรับการทดลองที่มีแผนลบ; DTO แม่แบบสำหรับ API เวอร์ชันต่างๆ สิ่งสำคัญคือการบันทึกเหตุผลเป็นเอกสารและกำหนด กำหนดเวลาตรวจสอบ สำหรับการปรับโครงสร้าง

จะแยกแยะการคัดลอกวางจากการนำกลับมาใช้ใหม่อย่างมีสุขภาพดีได้อย่างไร?

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

เครื่องมือใดที่ตรวจจับการคัดลอกวางในโปรเจกต์ iOS?

PMD CPD รองรับ Swift และ Objective-C สำหรับ Xcode มีปลั๊กอินเช่น SwiftCop และตัวตรวจจับการซ้ำซ้อนในตัวของ AppCode SonarQube ยังวิเคราะห์โปรเจกต์ Swift โดยแสดงบล็อกที่ซ้ำซ้อนโดยตรงในคำขอดึง

จะทำอย่างไรหากมีการคัดลอกวางอยู่แล้วและไม่มีเวลาปรับโครงสร้าง?

สร้างตั๋วทางเทคนิคสำหรับการปรับโครงสร้างสำเนาขนาดใหญ่แต่ละชุด กำหนดลำดับความสำคัญ: หน้าจอที่เปลี่ยนแปลงบ่อยก่อน หน้าจอที่เสถียรทีหลัง สำหรับ PR ใหม่แต่ละรายการที่แตะโค้ดที่ซ้ำซ้อน ให้จัดสรร 15–20 เปอร์เซ็นต์ ของเวลาสำหรับการรวมอย่างค่อยเป็นค่อยไป

เครื่องมือ AI ช่วยตรวจจับการคัดลอกวางได้หรือไม่?

ใช่ ผู้ช่วย AI สมัยใหม่ (GitHub Copilot, Codeium) สามารถวิเคราะห์บริบทและแนะนำการแยกโค้ดที่ใช้ร่วมกันเมื่อตรวจพบรูปแบบที่ซ้ำซ้อน อย่างไรก็ตาม พวกมันไม่ได้แทนที่โปรแกรมวิเคราะห์อัตโนมัติ — ใช้ Copilot สำหรับ การป้องกัน และ CPD / SonarQube สำหรับการตรวจจับ

สรุป

  • การคัดลอกวาง — การทำซ้ำโค้ดโดยการคัดลอกโดยไม่ปรับเปลี่ยน แหล่งหลักของหนี้ทางเทคนิค
  • การแพร่กระจายของบั๊ก: การแก้ไขสำเนาหนึ่งไม่แก้ไขสำเนาอื่น ข้อบกพร่องกระจายไปทั่วโปรเจกต์
  • สาเหตุหลัก: กำหนดเวลา ขาดนามธรรมที่ใช้ร่วมกัน กลัวการทำลายโค้ดที่ทำงานได้ระหว่างการปรับโครงสร้าง
  • เครื่องมือตรวจจับ: PMD CPD, SonarQube, jscpd, ESLint sonarjs/no-duplicate-string, SwiftCop
  • การปรับโครงสร้าง: การแยกโค้ดทั่วไปออกเป็นฟังก์ชัน คลาสเจเนอริก หรือคอมโพเนนต์ที่ใช้ร่วมกันพร้อมการกำหนดพารามิเตอร์ความแตกต่าง
  • การป้องกัน: การตรวจสอบโค้ดพร้อมตรวจสอบการซ้ำซ้อน ไลบรารีคอมโพเนนต์ที่ใช้ร่วมกัน การตรวจสอบ CI สำหรับการซ้ำซ้อน
  • กฎทางวัฒนธรรม: หนึ่งการนำไปใช้ — หนึ่งที่ บันทึกการซ้ำซ้อนอย่างมีสติเป็นเอกสารและตรวจสอบตามกำหนดเวลา

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม