Junk trong phát triển — nó là gì, mã rác có hại thế nào và cách loại bỏ

Tác giả: IT Sectr Đã đăng: 2026-07-27 Thời gian đọc: 10 phút

Mã rác (junk code) là mã và các phụ thuộc không mang lại lợi ích cho dự án nhưng làm tăng kích thước, thời gian xây dựng và tải nhận thức của nhóm. Không giống như mã chết không bao giờ được thực thi, mã rác có thể hoạt động nhưng làm việc không hiệu quả hoặc dư thừa: thư viện trùng lặp, import không dùng, khối bị comment, polyfill cũ và các abstraction trang trí. Theo CodeScene Code Health Report (2025), trung bình 15 phần trăm phụ thuộc trong các dự án di động không được sử dụng trực tiếp mà chỉ kéo theo các gói transitive. Mã rác là “trọng lượng thừa” của dự án: nó làm cho mã nguồn dày hơn nhưng không mạnh hơn. Kiểm toán phụ thuộc thường xuyên và loại bỏ các abstraction dư thừa trực tiếp cải thiện tốc độ xây dựng và chất lượng mã.

Những điểm chính

  • Junk là mã hoặc phụ thuộc vô dụng hoặc dư thừa làm tăng kích thước dự án mà không có lợi ích.
  • Các loại junk: phụ thuộc chết, thư viện trùng lặp, mã bị comment, abstraction rỗng.
  • Phụ thuộc rác làm tăng bề mặt tấn công và làm chậm đường ống CI.
  • Công cụ kiểm toán: Gradle dependencies (Android), SwiftPM audit (iOS), depcheck (Node.js).
  • Dọn dẹp rác thường xuyên là một phần của bảo trì dự án như viết mã mới.

Mã rác là gì?

Junk (mã rác) là thuật ngữ chung cho mã, cấu hình và phụ thuộc tồn tại trong dự án nhưng không mang lại giá trị chức năng. Rác không nhất thiết bị hỏng hoặc không được sử dụng — vấn đề là sự hiện diện của nó làm xấu các chỉ số dự án mà không có sự biện minh thích đáng.

Rác được chia thành bốn loại. Thứ nhất — phụ thuộc dư thừa: thư viện được thêm vào cho một tính năng duy nhất có thể được triển khai bằng công cụ tiêu chuẩn. Thứ hai — trọng lượng chết: khối bị comment, TODO không có ticket, phương thức rỗng và lớp giả. Thứ ba — giải pháp trùng lặp: hai thư viện làm cùng một việc (ví dụ: Gson và Kotlin Serialization trong cùng một dự án). Thứ tư — kỹ thuật quá mức: các lớp kiến trúc không được sử dụng nhưng được duy trì “để phòng trường hợp.”

Theo nghiên cứu của Stripe Engineering Productivity (2025), loại bỏ 10 phần trăm rác khỏi một dự án điển hình giảm thời gian xây dựng hoàn chỉnh trung bình 22 phần trăm. Lý do: mỗi phụ thuộc thừa làm tăng đồ thị xây dựng, mỗi abstraction rỗng cần thời gian để hiểu, mỗi khối bị comment làm phân tán sự chú ý.

Khó khăn chính trong cuộc chiến chống rác là thiếu hậu quả tức thời. Một dự án có mã rác vẫn biên dịch và hoạt động. Vấn đề tích tụ dần dần: xây dựng chậm lại, số lượng phụ thuộc transitive tăng lên, và sau một năm thêm tính năng mới mất gấp đôi thời gian cần thiết.

Phụ thuộc rác và cách nhận diện

Phụ thuộc rác là các thư viện và gói được thêm vào dự án nhưng không được sử dụng trực tiếp trong mã, hoặc chỉ được sử dụng cho một tính năng duy nhất mà việc triển khai bằng API tiêu chuẩn sẽ đơn giản hơn.

Ví dụ điển hình: thư viện xử lý JSON khi dự án đã sử dụng Kotlin Serialization (hai trình phân tích là rác); thư viện Apache Commons Lang cho một lời gọi StringUtils.isEmpty duy nhất có thể thay thế bằng extension isNullOrBlank của Kotlin; thư viện DI được sử dụng trong một mô-đun trên mười trong khi các mô-đun khác nhận phụ thuộc thủ công qua constructor.

Mỗi phụ thuộc thừa không chỉ là mã thừa trong tệp nhị phân. Nó làm tăng bề mặt tấn công cho các lỗ hổng: theo GitHub Advisory Database (2025), 40 phần trăm CVE nghiêm trọng trong các dự án di động đến từ phụ thuộc transitive mà nhà phát triển không kiểm soát. Càng ít phụ thuộc, bề mặt tấn công càng nhỏ.

Phân tích phụ thuộc dự án Android

groovy
// View Gradle dependency tree
./gradlew app:dependencies --configuration releaseRuntimeClasspath

// Find unused dependencies (Gradle plugin)
plugins {
    id "com.autonomousapps.dependency-analysis" version "2.0.0"
}

// Generate unused library report
./gradlew buildHealth

Đối với iOS, sử dụng lệnh swift package show-dependencies để hiển thị cây phụ thuộc đầy đủ. Công cụ Xcode Build Timeline cho thấy mỗi thư viện thêm bao nhiêu thời gian vào quá trình xây dựng. Nếu một thư viện chiếm 30 phần trăm thời gian biên dịch nhưng chỉ được sử dụng trên một màn hình, đó là ứng viên để loại bỏ hoặc thay thế.

Đối với Node.js (React Native), sử dụng depcheck — tiện ích tìm phụ thuộc không dùng trong package.json, và npm-check — tiện ích hiển thị thêm các phiên bản cũ. Đưa ra quy tắc: mỗi phụ thuộc mới phải qua đánh giá mã với lý do “tại sao không thể sử dụng công cụ tiêu chuẩn.”

Import chết và mã bị comment

Import chết là loại rác phổ biến nhất. Chúng không ảnh hưởng đến thời gian chạy nhưng làm tăng thời gian biên dịch: trình biên dịch xử lý mọi import, kể cả những cái không dùng. Trong các dự án lớn, loại bỏ import không dùng giảm thời gian xây dựng 5–10 phần trăm.

IDE hiện đại tự động làm nổi bật import không dùng bằng màu xám. Thiết lập tự động dọn dẹp khi lưu tệp: trong IntelliJ IDEA — Optimize Imports on the fly, trong Xcode — Editor > Remove Unused Imports. Thêm kiểm tra trong CI: trình lint phải chặn các commit có import không dùng.

Mã bị comment là một loại rác khác. Nhà phát triển comment các khối để “không mất” chức năng trong quá trình tái cấu trúc. Tuy nhiên, git lưu trữ toàn bộ lịch sử thay đổi: bất kỳ mã nào đã xóa đều có thể khôi phục bằng một lệnh git revert hoặc git log -S . Mã bị comment trong master là thiếu tôn trọng nhóm: mỗi nhà phát triển dành năng lượng tinh thần cho câu hỏi “tại sao cái này bị comment và khi nào nên bỏ comment?”

Quy tắc: không có mã bị comment trong kho lưu trữ. Nếu mã không cần thiết, hãy xóa vĩnh viễn. Nếu mã cần thiết nhưng tạm thời bị vô hiệu hóa, sử dụng feature toggle với ticket và ngày hết hạn. Các comment như // TODO: remove after migration — đừng để lại mà không có thời hạn. Đặt ngày và nhắc nhở bản thân bằng lịch.

Abstraction dư thừa và kỹ thuật quá mức

Kỹ thuật quá mức là tạo ra các lớp kiến trúc không giải quyết vấn đề hiện tại nhưng đòi hỏi bảo trì. Đây là một trong những loại rác khó nhất vì về mặt hình thức mã “đúng”: nó tuân theo SOLID, được bao phủ bởi kiểm thử và phù hợp với kiến trúc. Vấn đề là nó không cần thiết.

Ví dụ kinh điển là một lớp UseCase trừu tượng với một phương thức invoke duy nhất chỉ đơn giản gọi một kho lưu trữ. Nếu UseCase không thêm logic (lưu cache, thử lại, chuyển đổi) mà chỉ chuyển tiếp lời gọi, đó là một thực thể thừa. Nó làm tăng điều hướng trong dự án: nhà phát triển mở UseCase, thấy invoke → kho lưu trữ, và đóng nó lại. Lãng phí thời gian, lợi ích bằng không.

Một ví dụ khác là tham số hóa quá mức. Một interface generic với sáu tham số kiểu được sử dụng ở một nơi duy nhất. Mỗi tham số kiểu là tải nhận thức: khi đọc mã, bạn phải ghi nhớ sáu kiểu trong khi chỉ hai kiểu thực sự được sử dụng. Nếu một abstraction không được tái sử dụng, nó là dư thừa.

Tiêu chí cắt: nếu một abstraction không được tái sử dụng trong ba ngữ cảnh khác nhau, hãy loại bỏ nó. Một abstraction được biện minh khi nó thực sự giải quyết vấn đề trùng lặp, không phải khi nó dự đoán các kịch bản tương lai giả định. YAGNI (You Ain’t Gonna Need It) là nguyên tắc tốt nhất để ngăn ngừa kỹ thuật quá mức.

Công cụ kiểm toán rác

Kiểm toán rác đòi hỏi sự kết hợp của phân tích tĩnh, phân tích phụ thuộc và đánh giá thủ công. Không thể tự động hóa hoàn toàn việc phát hiện các abstraction dư thừa, nhưng rác kỹ thuật (import chết, thư viện không dùng, mã bị comment) có thể được tìm thấy bằng công cụ.

LoạiCông cụKiểm tra gì
Phụ thuộc không dùngdependency-analysis (Gradle)Thư viện không dùng trong mã
Phụ thuộc không dùngdepcheck (Node.js)Gói từ package.json không có import
Phụ thuộc không dùngswift package --show-dependenciesCây phụ thuộc SwiftPM
Import chếtIDE (Optimize Imports)Câu lệnh import không dùng
Mã bị commentgrep -r “//” / rg “^\s*//”Khối comment chứa mã
Phương thức/lớp rỗngSonarQube / CodeClimatePhương thức không có thân hoặc thân rỗng
Thư viện trùng lặpGradle lint (duplicate classes)Xung đột lớp từ các thư viện khác nhau

Để kiểm toán đầy đủ, chạy buildHealth (Android) hoặc depcheck (Node.js) mỗi sprint một lần. Tạo bảng điều khiển trong CI hiển thị xu hướng số lượng phụ thuộc qua các sprint. Nếu số lượng tăng nhưng chức năng không tăng tương ứng, nhóm đang tích tụ rác.

Chú ý đến lớp trùng lặp — lỗi xảy ra khi hai thư viện chứa cùng một lớp. Đây không chỉ là rác mà còn là nguồn trực tiếp của xung đột xây dựng. Trong Gradle, các xung đột này được giải quyết qua force hoặc exclude, nhưng mỗi giải pháp như vậy là tín hiệu rằng một trong các thư viện là thừa.

Quy trình dọn dẹp dự án thường xuyên

Dọn rác không phải là hành động một lần mà là một quy trình thường xuyên. Không có quy trình, rác sẽ quay lại trong vòng hai đến ba sprint. Thực hành tốt nhất là phân bổ 10–15 phần trăm năng lực của mỗi sprint cho việc dọn dẹp kỹ thuật, bao gồm kiểm toán rác.

Quy trình gồm bốn bước. Đầu tiên — chẩn đoán: chạy công cụ, nhận báo cáo, ưu tiên. Ưu tiên cao: phụ thuộc có CVE đã biết và thư viện trùng lặp. Ưu tiên trung bình: import chết và mã bị comment. Ưu tiên thấp: abstraction dư thừa (cần phân tích thủ công).

Thứ hai — dọn dẹp: loại bỏ phụ thuộc chết, thay thế thư viện trùng lặp bằng một, xóa mã bị comment. Mỗi thay đổi phải là một commit riêng với thông điệp rõ ràng: “remove unused dependency: gson (replaced by kotlinx.serialization)”, “delete commented code in LoginViewModel.”

Thứ ba — xác minh: xây dựng dự án, chạy kiểm thử, kiểm tra giao diện. Nếu kiểm thử vượt qua sau khi loại bỏ phụ thuộc, phụ thuộc đó thực sự không cần thiết. Nếu kiểm thử thất bại, có một tham chiếu ẩn mà trình phân tích tĩnh không phát hiện được.

Thứ tư — phòng ngừa: cập nhật danh sách kiểm tra đánh giá mã, thêm quy tắc “không có phụ thuộc mới nào mà không có lý do” vào Định nghĩa Hoàn thành, thiết lập kiểm tra tự động trong CI. Phòng ngừa là cách duy nhất để ngăn rác tích tụ trở lại.

Câu hỏi thường gặp

Rác khác với nợ kỹ thuật thế nào?

Nợ kỹ thuật là một sự thỏa hiệp có ý thức (nhanh nhưng chất lượng thấp) được lên kế hoạch sửa chữa. Rác không phải là quyết định có ý thức mà là rác tích tụ: phụ thuộc thừa, mã bị comment, abstraction rỗng mà không ai lên kế hoạch hoặc muốn bảo trì.

Bao lâu nên dọn rác một lần?

Nhịp độ tối ưu là dành 10 phần trăm mỗi sprint cho việc dọn dẹp kỹ thuật. Điều này giữ rác trong tầm kiểm soát mà không tích tụ khối lượng tới hạn. Nếu dự án có nhiều rác, hãy bắt đầu bằng một sprint dọn dẹp lớn, sau đó chuyển sang nhịp độ thường xuyên.

Làm thế nào để thuyết phục nhóm loại bỏ rác?

Đo lường và đưa ra con số: đo thời gian xây dựng trước và sau khi loại bỏ 3–5 phụ thuộc thừa. Giảm 15–30 giây mỗi lần xây dựng nhân với số lần xây dựng mỗi ngày cho ra hàng giờ thời gian tiết kiệm của nhóm. Con số thuyết phục hơn những lời kêu gọi trừu tượng về sự sạch sẽ.

Có nên loại bỏ rác khỏi phụ thuộc nếu dự án ổn định?

Có, đặc biệt nếu phụ thuộc có CVE. Ngay cả khi dự án ổn định, lỗ hổng trong phụ thuộc transitive là một rủi ro bảo mật. Hơn nữa, khi cập nhật SDK hoặc ngôn ngữ, phụ thuộc cũ có thể không tương thích, và việc loại bỏ nó trước khi nâng cấp sẽ tiết kiệm hàng giờ di chuyển.

Làm gì với TODO trong mã?

Mọi TODO không có ticket đều là rác. Đặt quy tắc: TODO chỉ được viết dưới định dạng // TODO(PROJECT-1234): fix liên kết với một nhiệm vụ trong trình theo dõi. Thường xuyên kiểm tra TODO và đóng những cái đã mất tính liên quan. Xóa TODO hết hạn — nếu vấn đề không xuất hiện trong sáu tháng, nó không nghiêm trọng.

Tổng kết

  • Rác là mã vô dụng, phụ thuộc không dùng và abstraction dư thừa làm tăng dự án mà không có lợi ích.
  • Bốn loại: phụ thuộc dư thừa, trọng lượng chết, thư viện trùng lặp và kỹ thuật quá mức.
  • Mỗi phụ thuộc thừa làm tăng thời gian xây dựng, bề mặt tấn công và tải nhận thức.
  • Công cụ kiểm toán: dependency-analysis (Gradle), depcheck (Node.js), SonarQube, grep cho mã bị comment.
  • Dọn dẹp thường xuyên: 10–15 phần trăm sprint cho công việc kỹ thuật, kiểm toán phụ thuộc mỗi sprint một lần.
  • Phòng ngừa: đánh giá mã với kiểm tra phụ thuộc mới, YAGNI trong thiết kế, tự động dọn import.
  • Quy tắc: không phụ thuộc mới nào không có lý do, không TODO nào không có ticket, không dòng mã bị comment nào trong master.

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.

Thảo luận dự án

Đọc thêm