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 (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 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ỏ.
// 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 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
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.
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.
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ại | Công cụ | Kiểm tra gì |
|---|---|---|
| Phụ thuộc không dùng | dependency-analysis (Gradle) | Thư viện không dùng trong mã |
| Phụ thuộc không dùng | depcheck (Node.js) | Gói từ package.json không có import |
| Phụ thuộc không dùng | swift package --show-dependencies | Cây phụ thuộc SwiftPM |
| Import chết | IDE (Optimize Imports) | Câu lệnh import không dùng |
| Mã bị comment | grep -r “//” / rg “^\s*//” | Khối comment chứa mã |
| Phương thức/lớp rỗng | SonarQube / CodeClimate | Phương thức không có thân hoặc thân rỗng |
| Thư viện trùng lặp | Gradle 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.
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
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ì.
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.
Đ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ó, đặ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.
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
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