Preventing Regression with IntelliJ IDEA Safe Delete
Stop guessing if a method is unused. Learn how to use IntelliJ IDEA's Safe Delete to perform whole-project usage analysis and prune dead code without breaking your build.
01 Jun 2026, 02:15 UTC

The Risk of the 'Delete' Key
Deleting a method or class in a large codebase often feels like a gamble. You might check the local references, but in a project with hundreds of modules, a single overlooked call site in a distant utility class can lead to a compilation error or, worse, a runtime crash. The goal is to prune dead code without introducing regressions.
The Safe Delete refactoring in IntelliJ IDEA solves this by shifting the burden of discovery from the developer to the IDE's static analysis engine. Instead of a blind deletion, it performs a whole-project usage scan to ensure the element is truly orphaned before it is removed.
How Safe Delete Analyzes Your Code
When you trigger a Safe Delete, the IDE doesn't just look for the name of the method; it uses the project's index to find every actual reference to that specific symbol. This is more accurate than a global text search because it understands scope and type inheritance.
If the analysis finds references, the IDE presents a Usages dialog. This window allows you to review every single line of code that depends on the element you are trying to remove. From here, you can choose to proceed with the deletion (which will remove those references), cancel the operation, or navigate to the usage to refactor it manually first.
Practical Example: Pruning a Legacy Utility Method
Consider a scenario where you have a DateUtils.formatLegacy() method that you believe is no longer needed after a migration to java.time.
- Trigger: Right-click the method name in the editor and select
Refactor > Safe Delete...(or use the shortcutAlt + Deleteon Windows/Linux,Cmd + Deleteon macOS). - Analysis: The IDE scans the project. If
formatLegacy()is used in three different modules, a dialog appears listing those files. - Decision: You review the list. You realize one usage is in a critical reporting module you forgot about. You can double-click that entry to jump to the code and update it to the new API before attempting the delete again.
- Execution: Once the usage list is empty (or you've decided the remaining usages are also obsolete), click OK. The IDE removes the method and any remaining references across the project.
The Blind Spots: Reflection and Dynamic Dispatch
Safe Delete is powerful, but it is not infallible. It relies on static analysis, meaning it can only find references that the compiler can see. There are two primary scenarios where Safe Delete might report "no usages" even when the code is still required:
- Reflection: If a method is called via
java.lang.reflector a framework that uses string-based method lookups (like some older Spring or Hibernate configurations), the IDE cannot track that connection. - Dynamic Proxying: Code invoked through complex dynamic proxies or external API calls via reflection may not be flagged.
To verify the result, always run your test suite or perform a project-wide build (Ctrl+F9 / Cmd+F9) immediately after a Safe Delete to catch any missing symbols that static analysis missed.
Managing Large-Scale Deletions
In projects with millions of lines of code, the initial scan for Safe Delete can be resource-intensive and may cause a temporary freeze in the UI. To mitigate this, ensure your project indexes are up to date and consider performing deletions in smaller batches rather than attempting to wipe out entire packages at once.
Rollback Strategy: If a Safe Delete removes more than intended, the standard Undo command works for immediate changes. For larger refactorings that spanned multiple files, use Local History (Right-click Project Root > Local History > Show History) to revert the project state to a point before the refactoring began.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.