Using IntelliJ IDEA’s PSI‑Based On‑the‑Fly Analysis to Prevent Refactoring Mistakes
Learn how IntelliJ IDEA’s persistent PSI tree powers real‑time inspections and quick‑fixes, and see a concrete example of safely converting string concatenation to StringBuilder with the IDE’s intention action.
25 Jan 2026, 04:10 UTC

The problem: refactoring without seeing the full impact
When you rename a variable, extract a method, or apply a quick‑fix, you want the IDE to warn you if the change would break something that isn’t obvious in the current editor view. In large projects, relying only on manual inspection or a full rebuild is slow and error‑prone.
Thesis
IntelliJ IDEA’s persistent Program Structure Interface (PSI) tree lets the IDE perform on‑the‑fly code analysis and generate intention actions instantly, giving you immediate feedback while keeping the UI responsive. Understanding how this works helps you trust the suggestions and know when to double‑check them.
How the PSI‑based analysis works
IntelliJ builds a PSI representation of each source file as soon as it is opened. The PSI tree mirrors the Java AST but is kept in memory and updated incrementally as you type. Background indexing threads parse changed files and refresh the affected PSI nodes without blocking the editor.
Inspections and intention actions are implemented as classes that register patterns against PSI node types. When the tree changes, the IDE matches those patterns on a background thread and surfaces results (warnings, errors, or light‑bulb hints) in the editor gutter.
Because the PSI is persistent, the same analysis can be reused by the build tool windows (Gradle/Maven) to show static‑analysis warnings alongside compilation output.
Worked example: converting string concatenation to StringBuilder
Consider the following Java snippet:
String msg = "Hello, " + user.getName() + ". Today is " + date;
Place the caret anywhere inside the string expression. IntelliJ shows a yellow bulb (intention action) because its PSI‑based inspection matches the pattern of a String concatenation with more than two operands.
Press Alt+Enter (or Option+Enter on macOS) and select “Replace with StringBuilder”. The IDE generates the following code:
StringBuilder sb = new StringBuilder();
sb.append("Hello, ");
sb.append(user.getName());
sb.append(". Today is ");
sb.append(date);
String msg = sb.toString();
The transformation is performed by rewriting the PSI nodes that represent the original + expressions into a series of append calls on a newly created StringBuilder. Because the change is made directly on the PSI tree, the editor updates instantly, and any dependent inspections (e.g., unused variable checks) are re‑evaluated on the same thread.
Trade‑off and limitation
The PSI tree is updated incrementally, but during a rapid series of edits (e.g., pasting a large block or running a code‑generation plugin) the background indexing threads can lag. In that window, inspections may report stale errors or quick‑fixes that no longer apply. Additionally, intention actions that rewrite control flow (like the StringBuilder conversion) can interfere with annotation processors that expect the original source shape, potentially causing generated code to diverge from expectations.
To mitigate this, wait for the “Indexing” indicator in the status bar to disappear after bulk changes before relying on inspection results. If you suspect stale data, you can manually trigger a full re‑analysis:
- Open
Analyze → Inspect Code… - Select “Whole Project” scope.
- Compare the generated report with a baseline taken from a known‑good commit (e.g., using
git diffon the report XML).
If the report shows new warnings that disappear after a restart or after waiting for indexing to finish, the issue was likely a temporary PSI lag.
Actionable closing
Leverage IntelliJ IDEA’s on‑the‑fly PSI analysis by:
- Keeping the inspection profile up to date (enable the “String concatenation can be replaced with StringBuilder” inspection under
Editor → Inspections → Java → Performance issues). - Using intention actions only after the indexing indicator is idle, especially after large refactors or code‑generation steps.
- Running a project‑wide inspection before committing to catch any lingering PSI‑stale warnings.
By understanding the underlying PSI mechanism, you can trust the IDE’s real‑time feedback while knowing when to verify it manually.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.