Copy-paste là thói quen sao chép các đoạn mã từ nơi này sang nơi khác mà không điều chỉnh cho phù hợp với ngữ cảnh mới. Thông thường, lập trình viên sao chép một khối từ mô-đun hiện có, chỉnh sửa tối thiểu và dán vào mô-đun mới — cùng với lỗi, bình luận cũ và các phụ thuộc không cần thiết. Theo TIOBE Code Quality Survey (2025), các dự án có mức độ copy-paste cao chứa nhiều lỗi gấp ba lần trên mỗi nghìn dòng mã so với các dự án có sự trừu tượng hóa thống nhất. Sự trùng lặp mã là nguồn gốc chính của nợ kỹ thuật: mỗi bản sao đòi hỏi bảo trì riêng biệt và sửa lỗi ở một nơi không đảm bảo lỗi được sửa ở những nơi khác.
Những điểm chính
Copy-paste (lập trình sao chép-dán) là việc di chuyển mã hiện có đến một nơi mới với ít hoặc không thay đổi. Thuật ngữ này được sử dụng với nghĩa miệt thị: nó ngụ ý rằng lập trình viên không thiết kế giải pháp mà đang sao chép một cách máy móc một khối có sẵn, thường không hiểu đầy đủ cách nó hoạt động.
Copy-paste có hai loại: cố ý (intentional) và vô tình (accidental). Cố ý — khi lập trình viên cố tình sao chép mã với kế hoạch tái cấu trúc sau này (nhưng kế hoạch thường bị bỏ dở). Vô tình — khi sự trùng lặp phát sinh mà không được chú ý, ví dụ, hai lập trình viên độc lập viết cùng một logic cho các màn hình khác nhau.
Theo báo cáo SonarQube State of Clean Code (2025), mã trùng lặp chiếm trung bình 12–18 phần trăm tổng khối lượng mã trong các dự án thương mại. Đồng thời, chi phí sửa lỗi trong mã trùng lặp cao gấp 2.5 lần so với mã có triển khai đơn nhất, vì lập trình viên phải tìm và sửa tất cả các bản sao.
Công cụ chính chống lại copy-paste là nguyên tắc DRY (Don't Repeat Yourself). Tuy nhiên, tuyệt đối hóa DRY cũng nguy hiểm: đôi khi sao chép là hợp lý khi hai bản sao cần phát triển độc lập với nhau. Điều quan trọng là phân biệt giữa “trùng lặp vô tình” (cần loại bỏ) và “trùng lặp cần thiết” (cần ghi chép lại).
Nguy hiểm đầu tiên và quan trọng nhất là sự lây lan lỗi. Nếu mã gốc có khiếm khuyết, nó sẽ được sao chép đến tất cả các vị trí mới cùng với mã. Khi khiếm khuyết được phát hiện và sửa trong mô-đun gốc, các bản sao vẫn không được sửa. Lập trình viên thậm chí có thể không biết rằng lỗi tồn tại trong năm tệp khác nhau.
Nguy hiểm thứ hai là sự tiến hóa không đồng đều. Hai bản sao của cùng một thuật toán tích lũy các sửa đổi khác nhau theo thời gian. Một bản sao thêm xác thực giá trị biên, bản khác thay đổi định dạng đầu ra. Sau vài tháng, không thể biết phiên bản nào là “đúng” và dự án mất đi tính nhất quán về hành vi.
Nguy hiểm thứ ba là khối lượng kiểm thử tăng lên. Mỗi trường hợp copy-paste đòi hỏi kiểm thử riêng. Nếu logic chung được trích xuất vào một hàm duy nhất, nó có thể được bao phủ bởi một bộ kiểm thử và tái sử dụng. Với sự trùng lặp, mỗi bản sao phải được kiểm thử riêng biệt — điều này nhân lên thời gian chạy CI và kích thước của cơ sở kiểm thử cần duy trì.
Nguy hiểm thứ tư là ảo tưởng về năng suất. Copy-paste tạo ra cảm giác sai lầm về tốc độ: lập trình viên nhanh chóng dán mã và thấy màn hình hoạt động. Nhưng “tốc độ” này biến thành nợ kỹ thuật phải trả cả vốn lẫn lãi khi phát hiện lỗi trong khối trùng lặp hoặc cần thay đổi logic kinh doanh.
Hiểu được nguyên nhân của copy-paste giúp thiết lập biện pháp phòng ngừa phù hợp. Hầu hết, lập trình viên sao chép mã không phải vì lười biếng, mà vì áp lực thời hạn, thiếu kiến thức hoặc kiến trúc bất tiện.
Nguyên nhân đầu tiên là thời hạn. Khi cần tạo một màn hình trong hai ngày và một màn hình tương tự đã tồn tại, lập trình viên sao chép toàn bộ và chỉ thay đổi những gì người dùng thấy. Không có thời gian để tái cấu trúc và trích xuất thành phần dùng chung — khách hàng yêu cầu kết quả. Kết quả là một màn hình thứ hai xuất hiện với 80 phần trăm mã dùng chung nhưng lịch sử thay đổi độc lập.
Nguyên nhân thứ hai là thiếu sự trừu tượng hóa thống nhất. Nếu dự án không có thành phần dùng chung cho một tác vụ điển hình (ví dụ: màn hình danh sách với pull-to-refresh), mỗi lập trình viên sẽ viết triển khai riêng hoặc sao chép của đồng nghiệp. Các quyết định kiến trúc được đưa ra khi bắt đầu dự án ảnh hưởng trực tiếp đến lượng copy-paste trong tương lai.
Nguyên nhân thứ ba là sợ làm hỏng mã đang chạy. Lập trình viên biết mô-đun hiện tại đang hoạt động. Tái cấu trúc để trích xuất mã dùng chung có thể ảnh hưởng đến chức năng hiện có. Nếu mức độ bao phủ kiểm thử thấp, rủi ro hỏng hóc vượt quá lợi ích cảm nhận của việc tái cấu trúc, và lập trình viên chọn con đường an toàn — sao chép.
Hãy giải quyết nguyên nhân, không phải triệu chứng. Giảm thời hạn và đưa vào đánh giá mã sẽ không giải quyết được vấn đề nếu dự án thiếu nền tảng kiến trúc vững chắc. Hãy đầu tư thời gian vào việc tạo các thành phần có thể tái sử dụng ở giai đoạn đầu — đó là cách duy nhất để giảm cám dỗ copy-paste trong tương lai.
Việc phát hiện copy-paste được thực hiện bởi các trình phân tích tự động so sánh các đoạn mã và xác định các điểm trùng khớp trên một ngưỡng nhất định. Các công cụ tốt nhất hoạt động ở cấp độ AST (Cây Cú pháp Trừu tượng) và bỏ qua định dạng, tên biến và bình luận.
PMD CPD (Copy-Paste Detector) là công cụ phổ biến nhất cho Java, Kotlin, Swift, JavaScript, Python và C++. CPD phân tích các token của mã nguồn và tìm các bản sao dài hơn số token tối thiểu được chỉ định (mặc định 100). Việc cấu hình ngưỡng là chìa khóa để có kết quả chất lượng: ngưỡng quá thấp tạo ra nhiều dương tính giả (các mẫu phổ biến như import), ngưỡng quá cao bỏ sót các bản sao thực sự.
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 nhúng trình phát hiện trùng lặp trực tiếp vào Quality Gate. Quy tắc Duplicated Blocks (%) hiển thị tỷ lệ phần trăm mã trùng lặp. Ngưỡng 5 phần trăm được coi là lành mạnh cho các dự án thương mại. Vượt quá sẽ chặn việc thăng cấp lên nhánh phát hành. SonarQube cũng nhóm các bản sao theo loại: khớp chính xác và sao chép cấu trúc (với các định danh được đổi tên).
Đối với JavaScript và TypeScript, các bản sao được phát hiện bằng ESLint với plugin eslint-plugin-sonarjs (quy tắc no-duplicate-string) và tiện ích jscpd, hỗ trợ hơn 150 ngôn ngữ. jscpd đặc biệt thuận tiện cho monorepo: nó tìm các bản sao giữa các gói, không chỉ trong một mô-đun duy nhất.
Tái cấu trúc copy-paste được tóm gọn trong một nguyên tắc: trích xuất phần chung và tham số hóa sự khác biệt. Kỹ thuật cụ thể phụ thuộc vào phạm vi trùng lặp và ngữ cảnh.
Trường hợp đơn giản nhất là trùng lặp trong cùng một lớp (ví dụ: hai phương thức có cùng logic nhưng khác kiểu). Giải pháp là tổng quát hóa với generics hoặc tái sử dụng phương thức với tham số kiểu. Nếu trùng lặp trải rộng trên nhiều lớp — hãy trích xuất mã chung vào một lớp tiện ích hoặc hàm mở rộng.
Trường hợp phức tạp hơn là trùng lặp ở cấp độ màn hình hoặc mô-đun. Ở đây, chỉ trích xuất một hàm không giúp ích gì, vì cấu trúc UI, logic vòng đời và ràng buộc dữ liệu đều bị trùng lặp. Giải pháp là tạo một lớp cơ sở màn hình chung hoặc một thành phần View tổng hợp, và truyền sự khác biệt qua tham số hoặc giao thức.
// 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)
}
}
Trường hợp phức tạp nhất là trùng lặp giữa các microservice hoặc thư viện. Trích xuất mã dùng chung có thể dẫn đến phụ thuộc vòng tròn hoặc liên kết không chính đáng. Trong những trường hợp như vậy, copy-paste có thể là một quyết định có ý thức: hai nhóm duy trì các dịch vụ độc lập và một thư viện dùng chung tạo ra nhiều vấn đề hơn là giải quyết. Điều quan trọng là ghi chép lại quyết định đó và thường xuyên kiểm tra xem các bản sao đã phân kỳ đủ để hợp nhất hay chưa.
Ngăn chặn copy-paste hiệu quả hơn là tái cấu trúc mã đã bị trùng lặp. Các biện pháp phòng ngừa chính nằm ở việc tổ chức quy trình phát triển, không phải ở công nghệ.
Biện pháp đầu tiên là đánh giá mã tập trung vào trùng lặp. Danh sách kiểm tra đánh giá nên bao gồm mục: “PR này có chứa mã đã tồn tại trong dự án không?” Nếu người đánh giá thấy copy-paste — họ chặn việc hợp nhất cho đến khi thành phần dùng chung được trích xuất. Yêu cầu này phải là một phần của Định nghĩa Hoàn thành của nhóm.
Biện pháp thứ hai là thư viện thành phần dùng chung. Mọi mẫu UI xuất hiện trên hai màn hình trở lên nên được trích xuất vào một mô-đun chung. Tạo một mô-đun dùng chung trong dự án và biến nó thành điểm đầu vào bắt buộc cho tất cả các thành phần UI. Nếu một thành phần chưa tồn tại — nó được tạo trước, sau đó mới sử dụng trên màn hình.
Biện pháp thứ ba là tự động hóa trong CI/CD. Thêm bước kiểm tra mã trùng lặp vào pipeline (PMD CPD, jscpd, SonarQube). Vượt quá ngưỡng sẽ dẫn đến lỗi xây dựng. Lập trình viên không thể hợp nhất PR làm tăng tỷ lệ copy-paste trên mức cho phép. Điều này chuyển trách nhiệm từ đánh giá mã sang tự động hóa và đảm bảo không có bản sao nào bị bỏ sót.
Hãy nuôi dưỡng văn hóa “một triển khai — một nơi.” Nếu bạn thấy cơ hội tái sử dụng — đừng trì hoãn việc tái cấu trúc. Mỗi copy-paste bị bỏ lại “để sau” sẽ nhân lên và biến thành nợ kỹ thuật không thể kiểm soát.
Các câu hỏi thường gặp
Không, có những kịch bản trùng lặp có ý thức: các microservice khác nhau cần phát triển độc lập; mã được sao chép cho một thử nghiệm với kế hoạch xóa; DTO mẫu cho các phiên bản API khác nhau. Điều quan trọng là ghi chép lại lý do và đặt thời hạn kiểm tra để tái cấu trúc.
Copy-paste là khi hai phần mã làm cùng một việc nhưng không có sự trừu tượng hóa chung. Tái sử dụng lành mạnh là khi mã chung được trích xuất vào một hàm, lớp hoặc mô-đun và sự khác biệt được tham số hóa. Nếu thay đổi logic yêu cầu chỉnh sửa ở ba nơi trở lên — đó là copy-paste.
PMD CPD hỗ trợ Swift và Objective-C. Cho Xcode, có các plugin như SwiftCop và trình phát hiện trùng lặp tích hợp trong AppCode. SonarQube cũng phân tích các dự án Swift, hiển thị các khối trùng lặp trực tiếp trong pull request.
Hãy tạo một ticket kỹ thuật để tái cấu trúc từng bản sao lớn. Đặt ưu tiên: màn hình thường xuyên thay đổi trước, màn hình ổn định sau. Cho mỗi PR mới chạm vào mã trùng lặp, hãy dành 15–20 phần trăm thời gian để hợp nhất dần dần.
Có, các trợ lý AI hiện đại (GitHub Copilot, Codeium) có thể phân tích ngữ cảnh và đề xuất trích xuất mã dùng chung khi phát hiện các mẫu lặp lại. Tuy nhiên, chúng không thay thế các trình phân tích tự động — hãy sử dụng Copilot để phòng ngừa và CPD / SonarQube để phát hiện.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm