<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog</title>
	<atom:link href="https://blog.jetbrains.com/idea/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.jetbrains.com</link>
	<description>Developer Tools for Professionals and Teams</description>
	<lastBuildDate>Wed, 09 Sep 2026 15:05:53 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.jetbrains.com/wp-content/uploads/2024/01/cropped-mstile-310x310-1-32x32.png</url>
	<title>IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog</title>
	<link>https://blog.jetbrains.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>The Evolution of WSL Support in JetBrains IDEs</title>
		<link>https://blog.jetbrains.com/platform/2026/09/the-evolution-of-wsl-support-in-jetbrains-ides/</link>
		
		<dc:creator><![CDATA[Kristina Pchelintseva]]></dc:creator>
		<pubDate>Wed, 09 Sep 2026 10:39:49 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/09/JB-social-BlogFeatured-1280x720-1.png</featuredImage>		<product ><![CDATA[idea]]></product>
		<product ><![CDATA[phpstorm]]></product>
		<product ><![CDATA[webstorm]]></product>
		<category><![CDATA[ecosystem]]></category>
		<category><![CDATA[engineering]]></category>
		<category><![CDATA[idea]]></category>
		<category><![CDATA[intellij-platform]]></category>
		<category><![CDATA[remote-development]]></category>
		<category><![CDATA[webstorm]]></category>
		<category><![CDATA[intellij-idea]]></category>
		<category><![CDATA[phpstorm]]></category>
		<category><![CDATA[wsl]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=platform&#038;p=738427</guid>

					<description><![CDATA[JetBrains IDEs have worked with WSL for many years, and over time, several ways of using it have emerged across our products. Depending on the entry point, the IDE could rely on a different underlying architecture, leading to a different experience. Starting with the 2026.2 release, there is one recommended entry point. In IntelliJ IDEA, [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>JetBrains IDEs have worked with WSL for many years, and over time, several ways of using it have emerged across our products. Depending on the entry point, the IDE could rely on a different underlying architecture, leading to a different experience.</p>



<p>Starting with the 2026.2 release, there is one recommended entry point. In IntelliJ IDEA, WebStorm, and PhpStorm, opening a project that lives in WSL runs the IDE in what we call <em>Native</em> mode. The IDE stays a Windows application, while a small agent inside WSL handles files and processes on its behalf.</p>



<p>Below is how <em>Native</em> mode works, how our approach to WSL integration evolved to get here, and – since we tried three other approaches first – why we&#8217;re confident it&#8217;s the right foundation for your work.</p>



<h2 class="wp-block-heading">What it takes to support WSL in an IDE</h2>



<p>We’ll start with an idealized picture in which the IDE functions seamlessly inside WSL while being operated from Windows. In practical terms, “seamlessly” means you should be able to use any of the IDE&#8217;s capabilities – terminal, run and debug configurations, profiling, etc. – against a project that resides in a Linux environment. Not only that, but this must incur as little latency as possible for the coding experience to feel quick and natural, as if the project files and the IDE itself resided in a single operating-system environment.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" fetchpriority="high" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image3.jpg" alt="" class="wp-image-738429"/></figure>



<p>We’ll trace how this challenge has been addressed, beginning with the earliest implementation, in which the IDE interacted with WSL through the 9P transport protocol. We will then cover Remote Development and WSLg before examining <em>Native</em> mode – the currently preferred mode for how IDEs work with WSL.</p>



<h2 class="wp-block-heading">Toward native IDE execution in WSL </h2>



<h3 class="wp-block-heading">9P filesystem access and GeneralCommandLine</h3>



<p>For the IDE to function, it needs access to the basics of a project, which are plain files. This is where the 9P filesystem protocol comes in. It provides the mechanism that allows Windows-side processes access files inside WSL. In practice, project-related file I/O performed by the IDE – scanning, indexing, archive access, and similar operations – is routed through the 9P-based filesystem layer to the Linux virtual machine.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image5.jpg" alt="" class="wp-image-738440"/></figure>



<p>Once file access is accounted for, the next challenge is process execution. While running on Windows, the IDE must be able to invoke tools inside WSL with the correct Linux paths, working directories, executables, environment variables, and arguments. The IDE-internal <a href="https://github.com/17712484466/intellij-community/blob/master/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java" target="_blank" rel="noopener"><code>GeneralCommandLine</code></a> class manages this by normalizing command execution in the WSL environment.</p>



<p>While this architecture enables the IDE to operate against WSL, it introduces significant trade-offs in both file access and process execution.</p>



<p>The 9P protocol is known to be problematic for several reasons:</p>



<ul class="wp-block-list">
<li>Limited symlink handling – 9P does not properly expose Linux symbolic links to Windows through <code>\\wsl$</code>. As a result, the IDE may detect an entry but be unable to resolve or index the linked directory tree. This affects pnpm workspaces, Python virtual environments, PHP Composer path repositories, and other environments that rely on symlinks.</li>



<li>Microsoft Defender scans – on-access scanning over 9P can stretch WSL file reads by tens of seconds.</li>



<li>Latency and degraded throughput – 9P-backed filesystem access adds latency, and throughput can degrade significantly, especially in workflows involving many small files. Core IDE operations such as indexing follow this pattern, generating numerous separate requests that cross the Windows/WSL VM boundary through 9P.</li>
</ul>



<p>The situation was no less challenging on the <code>GeneralCommandLine</code> side. The pipeline added a maintenance burden, as developers had to account for WSL-specific execution semantics across the codebase.</p>



<p>As a consequence, the approach became increasingly difficult to scale and maintain, and it surfaced persistent issues with symlink handling and performance.</p>



<h3 class="wp-block-heading">Running the IDE inside WSL with WSLg</h3>



<p>What if, instead, the IDE itself resided inside WSL, close to the project it works with? This is where WSLg comes in. It provides a way to run Linux GUI applications whose visual output is integrated directly into the Windows desktop.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image4.jpg" alt="" class="wp-image-738451"/></figure>



<p>Here’s why we do not recommend the WSLg approach:</p>



<ul class="wp-block-list">
<li>From a product perspective, if the IDE is displayed on Windows, it is preferable for it to behave as a Windows-native application rather than as a Linux GUI application projected into the Windows desktop. This preserves greater control over the IDE experience and avoids dependence on an additional GUI-remoting layer.</li>



<li>In a WSLg setup, the IDE may run on the native Wayland path, where JetBrains Runtime uses WLToolkit against WSLg&#8217;s Wayland compositor. This path carries known limitations in rendering, popups, window management, input methods, and desktop integration.</li>
</ul>



<p>No first-class product experience was ever built around this setup. Although running the IDE through WSLg is technically viable, it has never been considered the preferred direction, and no dedicated out-of-the-box installation or onboarding flow was provided. It remains an ad hoc workaround rather than a supported IDE workflow.</p>



<h3 class="wp-block-heading">The Remote Development approach</h3>



<p>Another approach was Remote Development, which addressed the same fundamental challenges of file access and process execution from a different angle. Rather than extending the Windows-based IDE across the WSL boundary, it moved the IDE backend into WSL and kept only the client on Windows. This architecture solved the underlying integration problems more directly, but introduced a different set of trade-offs.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image6.jpg" alt="" class="wp-image-738462"/></figure>



<p>The main technical challenge of this configuration is establishing and maintaining communication between them while ensuring that the UI accurately reflects user actions and the IDE continues to function as a coherent whole. At the same time, placing the backend inside WSL provides the clear advantage of direct access to Linux files and processes.</p>



<p>In this setup, the two IDE parts communicate over the JetBrains RD protocol. The protocol is a structured, bidirectional stream of IDE models and events that keeps the thin client and the backend IDE logically in sync, while all heavy work, such as indexing, analysis, builds, debugging, VCS operations, and so on, happens on the backend.</p>



<p>The client, in its turn:</p>



<ul class="wp-block-list">
<li>Renders all windows and editors, and handles user input.</li>



<li>Mirrors the project and editor state it receives from the backend over the RD protocol, and sends back edits, caret moves, refactoring commands, debug actions, and so on.</li>



<li>Loads UI-level plugins.</li>
</ul>



<p>The elephant in the room, however, is the cost of this split architecture. The IDE backend is a heavyweight component in its own right, requiring roughly 2 GB of additional disk space and time to download and install inside WSL. The split also introduces an inherent performance penalty, as UI-event state, user input, and other interaction data must travel continuously between the local client and the backend.</p>



<p>It also adds development overhead, as the code has to be split into client and server parts. Leaving specific modules undivided can cause delays and freezes in highly dynamic UIs. The associated engineering effort therefore becomes a constant and unavoidable cost.</p>



<h3 class="wp-block-heading"><em>Native</em> mode and the IJent agent</h3>



<p>The current design – now integrated into most of our IDEs – represents the culmination of this work and addresses many of the shortcomings of earlier approaches to WSL.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/WSL-Native-Mode.jpg" alt="" class="wp-image-738562"/></figure>



<p>To provide access to the Linux filesystem, processes, and other environment resources without relying on 9P, <code>GeneralCommandLine</code>, or other second-class communication mechanisms, we developed a small agent named IJent. Because it is designed specifically for IDE scenarios, we can shape its behavior, protocol, and capabilities according to our requirements.</p>



<p>Together, the IDE and IJent form a client–server pair. This may resemble the Remote Development model, but the server component is much thinner and, at the same time, more versatile. Installing IJent into a WSL environment is merely one of several possible configurations; the same model can also apply to Docker and Dev Containers.</p>



<p>By choosing this particular technology stack, we address a broad set of concerns:</p>



<ul class="wp-block-list">
<li>Rust implementation – Rust keeps the executable slim. By avoiding Java/Kotlin for the agent, we eliminate additional runtime dependencies inside containers or WSL.</li>



<li>Transport layer – Stdio gives us a portable, firewall-friendly transport across all environments. Hyper-V sockets on WSL offer a faster path, which is especially beneficial for large or numerous filesystem transfers.</li>



<li>Correct filesystem semantics – IJent executes filesystem operations on behalf of the IDE inside the target environment. As a result, path resolution, including symbolic links, follows correct Linux semantics rather than being mediated through 9P. Third-party plugins also benefit from this model, since their file operations are routed through IJent.</li>
</ul>



<p></p>



<h4 class="wp-block-heading">EelApi: one interface for any environment</h4>



<p>For the IDE to fully benefit from IJent, changes on the IDE side are also required. Enter EelAPI.</p>



<p>EelApi is an API designed to abstract away the distinction between local and remote environments for everyone writing IDE-related code – plugin authors and platform contributors alike. With EelApi, the underlying environment against which the IDE operates should no longer matter, removing the need to account for these concerns explicitly in code. In this sense, EelApi is platform-agnostic, and the IDE can remain unaware of whether it is working locally, in WSL, in Docker, or in a Dev Container.</p>



<p>In general, IJent implements the EelApi interface, providing the actual functionality that EelApi exposes to the IDE and plugins.</p>



<p>If you’re interested in a more detailed explanation of EelApi, together with a practical introduction to the topic, head over to the article <a href="https://blog.jetbrains.com/platform/2026/06/the-dev-containers-story-introducing-eelapi-for-plugin-authors">The Dev Containers Story: Introducing EelApi for Plugin Authors</a>.</p>



<h2 class="wp-block-heading">What this means in practice</h2>



<p>There are two ways to open a WSL project today:</p>



<ul class="wp-block-list">
<li>Opening the project directly gives you <em>Native</em> mode in IntelliJ IDEA, WebStorm, and PhpStorm, and more IDEs are adopting the IJent and EelApi architecture as we speak.</li>



<li>The Remote Development entry point on the Welcome screen still works, but it is no longer the recommended way to open a WSL project.</li>
</ul>



<p>We conducted performance tests in order to compare 9P mode and <em>Native</em> mode. The results show a clear performance gain in favor of the new approach:</p>



<section class="jb-wsl-perf-widget" data-jb-wsl-perf-widget aria-labelledby="jb-wsl-perf-title">
  <style>
    .jb-wsl-perf-widget {
      --jb-wsl-perf-text: #19191c;
      --jb-wsl-perf-muted: #5f5f66;
      --jb-wsl-perf-line: #d9d9e3;
      --jb-wsl-perf-soft: #f4f4f7;
      --jb-wsl-perf-track: #e9e9ee;
      --jb-wsl-perf-legacy: #7b7b84;
      --jb-wsl-perf-accent: #6b57ff;
      color: var(--jb-wsl-perf-text);
      font: inherit;
      margin: 32px 0;
    }
    .jb-wsl-perf-widget * { box-sizing: border-box; }
    .jb-wsl-perf-panel {
      background: #fff;
      border: 1px solid var(--jb-wsl-perf-line);
      border-radius: 8px;
      overflow: hidden;
    }
    .jb-wsl-perf-header {
      display: grid;
      gap: 18px;
      grid-template-columns: minmax(0, 1fr);
      padding: 26px 28px 24px;
    }
    .jb-wsl-perf-eyebrow {
      color: var(--jb-wsl-perf-accent);
      font-size: 12px;
      font-weight: 700;
      letter-spacing: .08em;
      line-height: 1.3;
      margin: 0 0 8px;
      text-transform: uppercase;
    }
    .jb-wsl-perf-title {
      color: var(--jb-wsl-perf-text);
      font-size: clamp(24px, 4vw, 34px);
      font-weight: 700;
      letter-spacing: -.02em;
      line-height: 1.15;
      margin: 0;
      max-width: 680px;
    }
    .jb-wsl-perf-chart {
      border-top: 1px solid var(--jb-wsl-perf-line);
      padding: 24px 28px 26px;
    }
    .jb-wsl-perf-legend {
      display: flex;
      flex-wrap: wrap;
      gap: 10px 20px;
      margin: 0 0 20px;
    }
    .jb-wsl-perf-legend-item {
      align-items: center;
      color: var(--jb-wsl-perf-muted);
      display: inline-flex;
      font-size: 13px;
      gap: 7px;
      line-height: 1.3;
    }
    .jb-wsl-perf-swatch {
      background: var(--jb-wsl-perf-legacy);
      border-radius: 2px;
      display: inline-block;
      height: 10px;
      width: 10px;
    }
    .jb-wsl-perf-swatch-ijent { background: var(--jb-wsl-perf-accent); }
    .jb-wsl-perf-rows { display: grid; gap: 22px; }
    .jb-wsl-perf-row {
      display: grid;
      gap: 12px;
      grid-template-columns: minmax(130px, .7fr) minmax(220px, 2fr);
      min-width: 0;
    }
    .jb-wsl-perf-metric {
      align-self: center;
      color: var(--jb-wsl-perf-text);
      font-size: 15px;
      font-weight: 700;
      line-height: 1.35;
    }
    .jb-wsl-perf-bars { display: grid; gap: 7px; min-width: 0; }
    .jb-wsl-perf-barline {
      align-items: center;
      display: grid;
      gap: 10px;
      grid-template-columns: minmax(0, 1fr) 52px;
    }
    .jb-wsl-perf-track {
      background: var(--jb-wsl-perf-track);
      border-radius: 3px;
      height: 13px;
      overflow: hidden;
    }
    .jb-wsl-perf-fill {
      background: var(--jb-wsl-perf-legacy);
      border-radius: 3px;
      display: block;
      height: 100%;
    }
    .jb-wsl-perf-fill-ijent { background: var(--jb-wsl-perf-accent); }
    .jb-wsl-perf-number {
      color: var(--jb-wsl-perf-muted);
      font-size: 13px;
      font-variant-numeric: tabular-nums;
      line-height: 1;
      white-space: nowrap;
    }
    .jb-wsl-perf-delta {
      color: #3a327d;
      font-size: 12px;
      font-weight: 700;
      line-height: 1.3;
      margin: 3px 0 0;
    }
    .jb-wsl-perf-notes {
      background: var(--jb-wsl-perf-soft);
      border-top: 1px solid var(--jb-wsl-perf-line);
      display: grid;
      gap: 10px;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      padding: 18px 28px 20px;
    }
    .jb-wsl-perf-note {
      color: var(--jb-wsl-perf-muted);
      font-size: 13px;
      line-height: 1.5;
      margin: 0;
    }
    .jb-wsl-perf-note strong { color: var(--jb-wsl-perf-text); }
    @media (max-width: 640px) {
      .jb-wsl-perf-header { grid-template-columns: 1fr; padding: 22px 18px 20px; }
      .jb-wsl-perf-chart { padding: 21px 18px 23px; }
      .jb-wsl-perf-row { gap: 8px; grid-template-columns: 1fr; }
      .jb-wsl-perf-notes { grid-template-columns: 1fr; padding: 17px 18px 19px; }
    }
    @media (prefers-color-scheme: dark) {
      .jb-wsl-perf-widget {
        --jb-wsl-perf-text: #f4f4f7;
        --jb-wsl-perf-muted: #b7b7bf;
        --jb-wsl-perf-line: #45454d;
        --jb-wsl-perf-soft: #29292e;
        --jb-wsl-perf-track: #414148;
        --jb-wsl-perf-legacy: #a4a4ac;
        --jb-wsl-perf-accent: #9d8cff;
      }
      .jb-wsl-perf-panel { background: #202024; }
      .jb-wsl-perf-delta { color: #c7bdff; }
    }
  </style>

  <div class="jb-wsl-perf-panel">
    <div class="jb-wsl-perf-header">
      <div>
        <p class="jb-wsl-perf-eyebrow">Cold-open benchmark</p>
        <p class="jb-wsl-perf-title" id="jb-wsl-perf-title">IJent cuts 38% off the wait for a large WSL project</p>
      </div>
    </div>

    <div class="jb-wsl-perf-chart" aria-label="Median duration in seconds: 9P compared with IJent">
      <div class="jb-wsl-perf-legend" aria-label="Chart legend">
        <span class="jb-wsl-perf-legend-item"><span class="jb-wsl-perf-swatch" aria-hidden="true"></span>9P</span>
        <span class="jb-wsl-perf-legend-item"><span class="jb-wsl-perf-swatch jb-wsl-perf-swatch-ijent" aria-hidden="true"></span>IJent</span>
      </div>
      <div class="jb-wsl-perf-rows">
        <div class="jb-wsl-perf-row">
          <div class="jb-wsl-perf-metric">Ready to work</div>
          <div class="jb-wsl-perf-bars">
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill" style="width:100%"></span></span><span class="jb-wsl-perf-number">18.5 s</span></div>
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill jb-wsl-perf-fill-ijent" style="width:62.2%"></span></span><span class="jb-wsl-perf-number">11.5 s</span></div>
            <p class="jb-wsl-perf-delta">38% less</p>
          </div>
        </div>
        <div class="jb-wsl-perf-row">
          <div class="jb-wsl-perf-metric">Scan project tree</div>
          <div class="jb-wsl-perf-bars">
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill" style="width:43.8%"></span></span><span class="jb-wsl-perf-number">8.1 s</span></div>
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill jb-wsl-perf-fill-ijent" style="width:20%"></span></span><span class="jb-wsl-perf-number">3.7 s</span></div>
            <p class="jb-wsl-perf-delta">54% less</p>
          </div>
        </div>
        <div class="jb-wsl-perf-row">
          <div class="jb-wsl-perf-metric">Index files</div>
          <div class="jb-wsl-perf-bars">
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill" style="width:58.4%"></span></span><span class="jb-wsl-perf-number">10.8 s</span></div>
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill jb-wsl-perf-fill-ijent" style="width:45.4%"></span></span><span class="jb-wsl-perf-number">8.4 s</span></div>
            <p class="jb-wsl-perf-delta">22% less</p>
          </div>
        </div>
        <div class="jb-wsl-perf-row">
          <div class="jb-wsl-perf-metric">Read file content</div>
          <div class="jb-wsl-perf-bars">
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill" style="width:65.9%"></span></span><span class="jb-wsl-perf-number">12.2 s</span></div>
            <div class="jb-wsl-perf-barline"><span class="jb-wsl-perf-track"><span class="jb-wsl-perf-fill jb-wsl-perf-fill-ijent" style="width:30.8%"></span></span><span class="jb-wsl-perf-number">5.7 s</span></div>
            <p class="jb-wsl-perf-delta">53% less</p>
          </div>
        </div>
      </div>
    </div>

    <div class="jb-wsl-perf-notes">
      <p class="jb-wsl-perf-note"><strong>Where this applies:</strong> A cold first open of <code>spring-framework</code>, with 23 subprojects and 8,191 source files. A small project with few source files showed no measurable difference.</p>
      <p class="jb-wsl-perf-note"><strong>Test setup:</strong> Median of five measured runs on Windows 11 with WSL 2, Ubuntu 24.04, and IntelliJ IDEA Ultimate 263.SNAPSHOT.</p>
    </div>
  </div>
</section>



<p>Beyond performance, this architecture places development in WSL within the broader context of development in non-local environments, allowing them to be approached through the same underlying model. The same IJent agent already backs our work on Docker and Dev Containers.</p>



<p>Thank you for reading and for all the feedback that helped us reach this point. Tell us how <em>Native</em> mode holds up against your projects in the comments below.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Debug Past an HTTP 403 Without Breaking Spring Security</title>
		<link>https://blog.jetbrains.com/idea/2026/09/debug-past-an-http-403-without-breaking-spring-security/</link>
		
		<dc:creator><![CDATA[Andrey Belyaev]]></dc:creator>
		<pubDate>Tue, 08 Sep 2026 12:34:18 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/IJ-social-BlogFeatured-1280x720-1-9.png</featuredImage>		<category><![CDATA[features]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[debug]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[spring-debugger]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=730371</guid>

					<description><![CDATA[You set a breakpoint inside a Spring MVC controller, or a service, send the request to an HTTP endpoint, and nothing happens. The response is back &#8211; and it is a 403 (or 401, or a redirect to a login page). At that point, the bug you were trying to catch is no longer the [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>You set a breakpoint inside a Spring MVC controller, or a service, send the request to an HTTP endpoint, and nothing happens. The response is back &#8211; and it is a 403 (or 401, or a redirect to a login page).</p>



<p>At that point, the bug you were trying to catch is no longer the only problem. Now, you need to answer different questions:</p>



<ul class="wp-block-list">
<li>Which roles or authorities does the endpoint require?</li>



<li>Should I just disable the security temporarily to debug the logic behind it, maybe?</li>
</ul>



<p>Security inlays in IntelliJ IDEA are designed for that moment. Instead of updating <code>SecurityConfig</code> temporarily and restarting the app, or fiddling with the authentication in the HTTP test tool, you can inspect and adjust the runtime security right inside the IDE.</p>



<h2 class="wp-block-heading">See What the Endpoint Requires</h2>



<p>When a secured endpoint blocks a request, the first question is &#8220;which roles does the running app require right now for this endpoint?&#8221;</p>



<p>The inlay displays the role-based requirement for an endpoint &#8211; the <code>hasRole</code> / <code>hasAuthority</code>-shaped matchers that most HTTP authorization is built from. However, not every security rule can be displayed with a roles list. For example, rules defined in code in a custom <code>AuthorizationManager</code> show up as unknown &#8211; a single lock icon, without roles.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image-25.png" alt="" class="wp-image-737792"/></figure>



<p>Seeing the actual endpoint security state is important in real Spring applications. Authorization can depend on multiple <code>SecurityFilterChain</code> beans, matcher order, active profiles, and conditionally registered configurations. Reading <code>SecurityConfig</code> classes you can find is not always the same as knowing what the running application requires.</p>



<p>If the roles list isn&#8217;t what you expected to see, you still need to read <code>SecurityConfig</code> classes anyway. So, the inlay provides a config link to the real security configuration code behind the endpoint.</p>



<h2 class="wp-block-heading">Unlock the Endpoint for the Current Debug Session</h2>



<p>When you figure out which roles the endpoint requires, the next question is: &#8220;Should I skip endpoint authorization, or resend the request with a specific authority set?&#8221; Both options are available within the unlock action in the inlay popup.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image-26.png" alt="" class="wp-image-737803"/></figure>



<p><strong>Read this before you click Unlock.</strong> Unlock acts on the running process itself, not just on requests sent from the IDE. Any client hitting that exact URI and method is affected: <code>curl</code>, Postman, a browser, etc. If the debugged app is reachable on your network, any request to that exact URI and method is treated as authenticated with the authorities you granted &#8211; for anyone. Unlock endpoints carefully, and re-lock or restart when you are done.</p>



<p>Since unlocking is a security-affecting action, it is worth mentioning what it opens and for how long:</p>



<ul class="wp-block-list">
<li><strong>Scope</strong> &#8211; applies to the exact request URI <em>and</em> HTTP method. The concrete URI comes from the request you&#8217;re working with when you unlock &#8211; the failing request in your <code data-enlighter-language="generic" class="EnlighterJSRAW">.http</code> file, browser, or curl command. Unlocking <code>GET /admin/users/42</code> in the IDEA HTTP Client leaves <code>POST /admin/users/42</code> and <code>GET /admin/users/99</code> blocked. Unlocking <code>@GetMapping("/admin/users/{id}")</code> in the controller code will unlock the endpoint for any <code>{id}</code> value.</li>



<li><strong>Layer</strong> &#8211; covers HTTP endpoint authorization only; method-level checks such as <code>@PreAuthorize</code> are evaluated separately (see &#8220;What Unlock Does Not Cover&#8221; below).</li>



<li><strong>Lifetime</strong> &#8211; lasts only for the running debug session, or until you lock the path again. Restarting the app clears every unlock. Both a full JVM restart and a Spring Boot DevTools in-process restart work. The unlocked paths and their authority sets live on the session and go with it.</li>



<li><strong>Persists</strong> &#8211; nothing in the app. The only thing kept is IDE-local: the username you last impersonated, per endpoint, to pre-fill on re-unlock.</li>
</ul>



<p>So, depending on your tasks, you can do the following using the security indicator inlay:</p>



<h3 class="wp-block-heading">Simple Unlock</h3>



<p>Sometimes security is not what you are testing. You already know the endpoint is protected, but you need to reach the logic behind it. Unlocking treats the request as authenticated with the full set of roles that the endpoint requires, for that URI and method. Spring Security just sees an identity that passes and you can continue the debugging task you were doing. This is the right mode when the real tasks are:</p>



<ul class="wp-block-list">
<li>validate request or mapping logic in the controller;</li>



<li>debug a service behind a protected route;</li>



<li>a smoke test that is blocked by a local authentication setup;</li>



<li>a bug reproduction you do not want to interrupt with config edits and restarts.</li>
</ul>



<p>If the IDE cannot evaluate the roles list for the endpoint, this functionality is not available. For this case, use&#8230;</p>



<h3 class="wp-block-heading">Unlock with custom authorities</h3>



<p>The inlay popup menu also allows you to specify a username and a custom role list. Treat this as a way to test a specific authority set. It lets you check authority-based behavior behind the endpoint without creating a real account, changing test data, or going through a full SSO or token flow.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image-27.png" alt="" class="wp-image-737815"/></figure>



<p>For example, you can enter the username <code>admin</code> with <code>ROLE_ADMIN</code>, <code>ROLE_MANAGER</code> authorities in the popup. Resend the request, and answer questions like:</p>



<ul class="wp-block-list">
<li>Does the controller return the expected data when the request has specified authorities?</li>



<li>Is the problem in the controller logic, or in the authority set used for the request?</li>



<li>How does downstream code &#8211; branching, data filtering, authority checks &#8211; behave for this exact authority set?</li>



<li>Does the audit engine record a proper username &#8211; <code>admin</code>?</li>
</ul>



<p>When you perform either of these actions, the request runs with an artificial <code>Authentication</code> in the <code>SecurityContext</code>, created inside the running application by the debugger with the username and roles you chose. Code that reads the authority set &#8211; via <code>hasRole / hasAuthority</code> checks, <code>SecurityContextHolder.getContext().getAuthentication()</code>, or a <code>Principal / Authentication</code><em> </em>method parameter &#8211; sees the authorities provided by IntelliJ IDEA.</p>



<p>One thing to watch for: a controller parameter annotated with <code>@AuthenticationPrincipal</code> does not resolve for inlay-unlocked requests &#8211; it comes back null. This is a known bug, <a href="https://youtrack.jetbrains.com/issue/IDEA-389767" target="_blank" rel="noopener">IDEA-389767</a>, which is open and affects 2026.2 as of this writing &#8211; check it for the current status. (See &#8220;How Unlocking Works Under the Hood&#8221; below for why.)</p>



<p>Compared with the usual local workarounds &#8211; a <code>permitAll</code> dev profile, a mock authenticator, a commented-out filter &#8211; nothing rebuilds or restarts the app, and nothing stays in configuration code that could accidentally get into a production environment.</p>



<h2 class="wp-block-heading">How Unlocking Works Under the Hood</h2>



<p>Is unlocking a backdoor, or a CSRF bypass? Neither &#8211; unlock doesn&#8217;t disable or suspend anything. It treats the request as authenticated with the authorities you granted, for that exact URI and method, and lets Spring Security evaluate it from there.</p>



<p>If your app enforces CSRF tokens on state-changing requests, <code>CsrfFilter</code> runs earlier in the chain and still rejects a <code>POST / PUT / DELETE</code> with a missing or invalid token. So, you still need a valid token, or a CSRF-exempt setup, separately.</p>



<p>Unlocking is a debug-time feature. Please note that it&#8217;s an internal mechanism, not a public IDE API.</p>



<p>On application startup, the debugger places a hidden, non-suspending breakpoint at Spring Security&#8217;s own <code>AuthorizationFilter</code> &#8211; the exact spot where Spring decides whether a request is allowed. After Spring application context initialization, the IDE reads the effective HTTP authorization rules. Instead of parsing your source, the debugger reads the live <code>SecurityFilterChain</code> out of the running JVM to figure out what each endpoint requires. That is why the inlay shows the actual list of authorities at the HTTP layer. Clicking Unlock adds the URL, username, and roles to a set the IDE keeps for the debug session. The application code and security config are untouched.</p>



<p>The rest of the work happens per request, without pausing anything. When a request hits an &#8220;unlocked&#8221; URL, the execution flow passes the non-suspending breakpoint in the <code>AuthorizationFilter</code>. If the URL is in the &#8220;unlocked&#8221; list, the debugger adds a <code>TestingAuthenticationToken</code> instance to Spring&#8217;s <code>SecurityContext</code>. This auth object is already marked as authenticated, with the chosen username as principal and the &#8220;unlock&#8221; roles as granted authorities. Then the request goes on. Later the authorization engine reads the security context, sees the supplied authenticated principal, and evaluates it normally. For any URL you did not unlock, the breakpoint evaluation returns immediately and normal security applies.</p>



<p>The principal used in the auth object is the plain username <code>String</code>, not your app&#8217;s <code>UserDetails</code> or other custom principal type. That&#8217;s why a controller parameter annotated with <code>@AuthenticationPrincipal</code> (or any custom principal class) does not resolve for inlay-unlocked requests, as mentioned in the &#8220;Unlock the Endpoint for the Current Debug Session&#8221; chapter.</p>



<p>You can inject the auth object as a controller method parameter, since the artificial auth object is available through the <code>SecurityContextHolder</code>. So the following code should work fine:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@GetMapping("/active")

    ResponseEntity&lt;List&lt;Member>> getAllActive(Principal principal) {

        log.info("Auth Principal Name: {}", principal.getName());

        //...

    }</pre>



<p>Please note that those parameters need to be declared as the base type for <code>TestingAuthenticationToken</code>: <code>Principal</code> or <code>Authentication</code>, not a concrete subtype. A parameter typed as <code>JwtAuthenticationToken</code>, for instance, throws an <code>IllegalStateException</code> at request time instead of resolving, because Spring MVC checks that the real principal &#8211; the injected <code>TestingAuthenticationToken</code> &#8211; is actually an instance of the declared type. That&#8217;s a more visible failure than <code>@AuthenticationPrincipal</code>&#8216;s silent null, though the root cause is the same type mismatch. Code that depends on a concrete principal object type, token subtype, claims, credentials, or session-backed identity may still behave differently or even fail for unlocked endpoints.</p>



<p>Unlock hooks <code>AuthorizationFilter</code>, the filter behind authorizeHttpRequests and the AuthorizationManager API. This API is available since Spring Security 5.x and the non-deprecated path in 6.x. If your configuration still uses the older <code>authorizeRequests / FilterSecurityInterceptor</code>, that filter never runs, so unlock does not apply.</p>



<p>Security inlays are turned on by default, and only work while the Spring Debugger is active. You can turn them off with the <code>spring.debugger.security.enabled</code> flag in IntelliJ&#8217;s Registry (open it with Find Action → <em>Registry…</em>). Security inlays also work with a remote JVM, including a shared or staging environment. Please note that there is no log message and no actuator indicator if someone has unlocked an endpoint on the remote application. The only way to check is to look at the inlay in the editor of the attached IDE.</p>



<h2 class="wp-block-heading">What Unlock Does Not Cover</h2>



<p>Unlock works with HTTP endpoint security: rules like <code>SecurityFilterChain</code> URL matching, and <code>requestMatchers()</code> method calls. It only works on the Servlet stack, where the breakpoint is added to Spring Security&#8217;s <code>AuthorizationFilter</code>. It means Spring WebFlux is not supported. It uses a different class instead, <code>AuthorizationWebFilter</code>, which unlock does not hook (yet).</p>



<p>Right now, there is also no support for method-security annotations, such as:</p>



<ul class="wp-block-list">
<li><code>@PreAuthorize</code>;</li>



<li><code>@PostAuthorize</code>;</li>



<li><code>@Secured</code>.</li>
</ul>



<p>There is no inlay for them, no way to see what they require, and no unlock action that targets them directly.</p>



<p>However, the same injected <code>Authentication</code> is what a method security interceptor reads too. If a service method is guarded by <code>@PreAuthorize</code> and the authorities you injected on an unlocked endpoint satisfy it, that check passes.</p>



<p>Let&#8217;s have a look at the small example:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@RestController
class UserController {

    @GetMapping("/admin/users/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findUser(id);
    }
}

@Service
class UserService {

    @PreAuthorize("hasRole('ADMIN')")
    public User findUser(Long id) {
        // ...
    }
}

@Configuration
@EnableMethodSecurity
class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated());
        return http.build();
    }
}
</pre>



<p>When you unlock <code>GET /admin/users/{id}</code> as <code>ROLE_ADMIN</code>, the call from <code>UserController</code> into <code>UserService.findUser </code>also passes the <code>@PreAuthorize</code> check – not because unlock targets it directly, but because the method-security interceptor reads the same <code>SecurityContext</code> the breakpoint already populated.</p>



<h2 class="wp-block-heading">One Unlock, Several Clients</h2>



<p>A request can come from several places: an IntelliJ IDEA <code>.http</code> file, a <code>curl</code> script, a Postman collection, or a test suite hitting your locally debugged app. If the request is rejected, don&#8217;t spend time switching to another HTTP testing tool.<br></p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image-28.png" alt="" class="wp-image-737853"/></figure>



<p>For requests sent from the built-in IntelliJ IDEA HTTP Client, the security inlay sits right near the request URL in an <code>.http</code> file, so you don&#8217;t even need to switch to the controller code. For external clients, you&#8217;ll need to open the controller code, unlock the endpoint, and re-run the same <code>curl</code> or Postman request.</p>



<h2 class="wp-block-heading">A Note for Automated and AI Workflows</h2>



<p>In IntelliJ IDEA 2026.2, unlock is available in the inlay in the editor or in the <code>.http</code> file &#8211; it is a manual, in-IDE action. There is no programmatic switch yet, so an external script or an AI agent cannot use it on its own. We&#8217;re planning an MCP tool and a related skill that would let an agent get the roles list and perform lock/unlock actions for a selected endpoint directly, without a human in the loop. That&#8217;s aimed at the next release, 2026.3.</p>



<p>Until then, unlock the endpoint once from the IDE to let an agent test it. It stays unlocked for the rest of the debug session, so the agent can keep replaying <code>curl</code>, <code>.http</code>, or test requests with no further clicks. The manual step is a one-time setup, so an agent iterating on downstream controller or service logic can run unattended after it.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Security inlays are designed to simplify your (developer&#8217;s) life. They show the real security rules for an endpoint, and let you pass them so you can debug more easily. You can see what the endpoint requires and unlock it if needed. The request is then treated as authenticated with the authorities you choose for the debug session. All of this happens without editing <code>SecurityConfig</code> or restarting the app.</p>



<p>By applying unlock, IDEA makes a temporary security change in the running app. It opens the exact URI and method you unlocked to every client that reaches it. If you unlocked a path pattern in the controller code, it opens all paths that match the pattern.</p>



<p>The path stays open until you lock it again or the debug session ends. Please use it carefully, especially on an app with network access.</p>



<p>A few limitations are worth mentioning:</p>



<ul class="wp-block-list">
<li><strong>Servlet only</strong> &#8211; Inlays and unlock hook Spring Security&#8217;s <code>AuthorizationFilter</code>. That filter only exists on the Servlet stack, so WebFlux isn&#8217;t supported yet.</li>



<li><strong>Debug mode only</strong> &#8211; This feature requires an active debugger session, local or on a remote JVM.</li>



<li><strong><code>Principal / Authentication</code>, not a concrete subtype</strong> &#8211; Declare a controller method parameter as <code>Principal </code>or <code>Authentication</code>. A parameter annotated with <code>@AuthenticationPrincipal</code> comes back <code>null</code>. A parameter typed as a concrete subtype, like <code>JwtAuthenticationToken</code>, throws an error instead, because Spring MVC checks that the injected object matches the declared type.</li>
</ul>



<p>The inlays are part of the <a href="https://plugins.jetbrains.com/plugin/25302-spring-debugger" target="_blank" rel="noopener">Spring Debugger</a> plugin for IntelliJ IDEA Ultimate, available since the 2026.2 release.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Java Annotated Monthly – September 2026 </title>
		<link>https://blog.jetbrains.com/idea/2026/09/java-annotated-monthly-september-2026/</link>
		
		<dc:creator><![CDATA[Irina Mariasova]]></dc:creator>
		<pubDate>Mon, 07 Sep 2026 11:27:14 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/09/IJ-social-BlogFeatured-1280x720-1.png</featuredImage>		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=737583</guid>

					<description><![CDATA[This month’s Java Annotated Monthly brings you the latest Java news, a generous dose of AI-focused articles, Kotlin updates, and highlights from a variety of technologies and frameworks, plus plenty more worth adding to your reading list. Our featured writer, Aicha Laafia, takes a closer look at upcoming JEPs and shares her key findings on [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>This month’s Java Annotated Monthly brings you the latest Java news, a generous dose of AI-focused articles, Kotlin updates, and highlights from a variety of technologies and frameworks, plus plenty more worth adding to your reading list.</p>



<p>Our featured writer, <a href="https://www.linkedin.com/in/aicha-laafia-0266a6126/?locale=fr" target="_blank" rel="noopener">Aicha Laafia</a>, takes a closer look at upcoming JEPs and shares her key findings on what may be next for Java. Let’s dive in!</p>



<h2 class="wp-block-heading">Featured Content&nbsp;</h2>


    <div class="about-author ">
        <div class="about-author__box">
            <div class="row">
                                                            <div class="about-author__box-img">
                            <img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/da3a-400o400o2-NhiB1dzTu4T26r3Fsu2sLt.jpg" alt="" loading="lazy">
                        </div>
                                        <div class="about-author__box-text">
                                                    <h4>Aicha Laafia</h4>
                                                <p>Aicha Laafia is a Java software engineer at SQLI with a love for coding, an interest in sustainability, and a commitment to empowering women in tech. She is a Women Techmakers ambassador, an Oracle ACE Associate, and part of the JetBrains Community Contributors program. When she is not coding, she enjoys exploring delicious food, phone photography, and watching Formula 1.</p>
                    </div>
                            </div>
        </div>
    </div>



<p>September is here, and so is <a href="https://openjdk.org/projects/jdk/27/" target="_blank" rel="noopener">JDK 27</a>. Nine JEPs if you count the previews, and nothing to rewrite unless you are using preview features. Honestly? That’s my favorite kind of release. You upgrade the version, and your app gets faster.</p>



<h3 class="wp-block-heading">The features that change your numbers</h3>



<p>Compact object headers are on by default (<a href="https://openjdk.org/jeps/534" target="_blank" rel="noopener">JEP 534</a>). Headers shrink from 96 bits to 64, cutting heap usage by 10% – 20% and pushing throughput up by 5% – 10%. G1 also becomes the default garbage collector everywhere (<a href="https://openjdk.org/jeps/523" target="_blank" rel="noopener">JEP 523</a>), even on the small machines that used to get Serial GC instead, so check your smallest containers before you upgrade. For the full tour, <a href="https://www.linkedin.com/in/sven-woltmann" target="_blank" rel="noopener">Sven Woltmann</a> wrote a solid <a href="https://www.happycoders.eu/java/java-27-features/" target="_blank" rel="noopener">Java 27 breakdown</a>, and if video is more your thing, <a href="https://www.linkedin.com/in/cat-edelveis/" target="_blank" rel="noopener">Catherine Edelveis</a> covers the whole release <a href="https://www.youtube.com/watch?v=VxN-jrjF_GA" target="_blank" rel="noopener">here</a>.</p>



<h3 class="wp-block-heading">Two security wins that protect you by default</h3>



<p>JFR recordings collect environment variables and system properties, which is exactly where your tokens and passwords live, so Java 27 hides them for you (<a href="https://openjdk.org/jeps/536" target="_blank" rel="noreferrer noopener">JEP 536</a>). And TLS 1.3 now gets post-quantum key exchange (<a href="https://openjdk.org/jeps/527" target="_blank" rel="noreferrer noopener">JEP 527</a>), because attackers already save encrypted traffic, waiting for quantum computers to break it later. Java 27 makes sure they can’t!</p>



<h3 class="wp-block-heading">Still on Java 8 or 11?</h3>



<p>Don’t jump, hop. Start now, LTS by LTS, and read my article <a href="https://foojay.io/today/jurassic-jdk-migrate-or-extinct/" target="_blank" rel="noopener">Jurassic JDK: Migrate or Extinct</a> first. I covered everything I wish someone had told me before my first migration.</p>



<p>Enjoy this month’s Java Annotated Monthly!</p>



<h2 class="wp-block-heading">Java News</h2>



<p>See what’s new, changing, and coming next in the world of Java:</p>



<ul class="wp-block-list">
<li>Java News Roundup <a href="https://www.infoq.com/news/2026/08/java-news-roundup-aug03-2026/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">1</a>, <a href="https://www.infoq.com/news/2026/08/java-news-roundup-aug10-2026/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">2</a>, <a href="https://www.infoq.com/news/2026/08/java-news-roundup-aug17-2026/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">3</a>, <a href="https://www.infoq.com/news/2026/08/java-news-roundup-aug24-2026/" target="_blank" rel="noopener">4</a>&nbsp;</li>



<li><a href="https://www.infoq.com/news/2026/08/jep401-value-objects-preview/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">Project Valhalla&#8217;s First Preview: JEP 401 Redefines == for Java Objects</a></li>



<li><a href="https://www.infoq.com/news/2026/08/java-native-json-api/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">JEP 540 Proposed to Target JDK 28 with a Simple JSON API</a></li>



<li><a href="https://nipafx.dev/inside-java-newscast-114" target="_blank" rel="noopener">JSON API, Valhalla Progress, LTS ❤️ PQC &#8211; Inside Java Newscast #114</a></li>



<li>Inside Java <a href="https://inside.java/2026/08/20/podcast-067/" target="_blank" rel="noopener">Episode 67 “Keeping Up With the Java Release Train” [I/O]</a></li>



<li><a href="https://www.infoq.com/news/2026/08/java-27-so-far/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">JDK 27 and JDK 28: What We Know So Far</a></li>



<li><a href="https://inside.java/2026/08/25/jipher-cryptography-for-java/" target="_blank" rel="noopener">Announcing Oracle Jipher 10.37: FIPS 140-3 Cryptography for Java</a></li>



<li><a href="https://inside.java/2026/08/12/java-mcp-migration/" target="_blank" rel="noopener">Evolving a Java MCP Server During MCP Specification Upgrades</a>&nbsp;</li>
</ul>



<h2 class="wp-block-heading">Java Tutorials and Tips</h2>



<p>Sharpen your Java skills with practical tutorials, clever techniques, and tips worth trying:&nbsp;</p>



<ul class="wp-block-list">
<li><a href="https://www.youtube.com/watch?v=1Jci3yQzKyk" target="_blank" rel="noopener">Persistence Workflows with IntelliJ IDEA – Andrey Belyaev | IntelliJ IDEA Tech Talks</a></li>



<li><a href="https://blog.gradle.org/resilient-sync" target="_blank" rel="noopener">Resilient Sync: Your IDE Keeps Helping even when your Build is Broken</a></li>



<li>Inside Java <a href="https://inside.java/2026/08/06/podcast-065/" target="_blank" rel="noopener">Episode 65 “Embracing Virtual Threads with Helidon” [I/O]</a></li>



<li><a href="https://foojay.io/today/introduction-to-retrieval-augmented-generation-with-java-and-mongodb/" target="_blank" rel="noopener">Introduction to Retrieval-Augmented Generation with Java and MongoDB</a></li>



<li><a href="https://inside.java/2026/08/11/efficient-java-apps-profiling-troubleshooting/" target="_blank" rel="noopener">The Power of JDK Flight Recorder: Efficient Profiling and Troubleshooting for Java Applications</a></li>



<li><a href="https://blog.frankel.ch/security-baked-into-jvm/4/" target="_blank" rel="noopener">Security Baked Into the JVM: sixteen Subjects on the wire</a>&nbsp;</li>



<li><a href="https://inside.java/2026/08/24/value-classes-compiler-sympathy/" target="_blank" rel="noopener">Value Classes Still Need Compiler Sympathy</a></li>



<li>Inside Java <a href="https://inside.java/2026/08/27/podcast-068/" target="_blank" rel="noopener">Episode 68 “Operator Overloading with Type Classes” [AtA]</a></li>



<li><a href="https://krzysztofslusarski.github.io/2026/08/19/g1barrier.html" target="_blank" rel="noopener">Why is Arrays.fill 265 times slower on G1GC?</a></li>



<li><a href="https://www.youtube.com/watch?v=2L7zLdHeyY0" target="_blank" rel="noopener">Beyond Virtual Threads: Structured Concurrency &#8211; Balkrishna Rawool | IntelliJ IDEA Tech Talks</a></li>
</ul>



<h2 class="wp-block-heading">Kotlin Corner</h2>



<p>Step into Kotlin Corner for fresh updates:</p>



<ul class="wp-block-list">
<li><a href="https://blog.jetbrains.com/research/2026/08/programming-language-migration/">When Escape Routes Become Toll Roads: Mapping How Developers Move Between Programming Languages</a>&nbsp;</li>



<li><a href="https://blog.jetbrains.com/research/2026/07/kotlinllm-open-source/">KotlinLLM is Going Open Source&nbsp;</a></li>



<li><a href="https://blog.jetbrains.com/kotlin/2026/07/introducing-the-kotlin-benchmark-evaluate-ai-coding-agents-on-real-world-kotlin-tasks/">Introducing the Kotlin Benchmark for AI Coding Agents&nbsp;</a></li>
</ul>



<h2 class="wp-block-heading">AI&nbsp;</h2>



<p>Catch up on the AI ideas, tools, and experiments shaping how developers build software:</p>



<ul class="wp-block-list">
<li><a href="https://blog.jetbrains.com/idea/2026/08/intellij-idea-goes-lsp/">IntelliJ IDEA Goes LSP: Java and Kotlin Intelligence Comes to VS Code, Cursor, and Agentic Flows</a></li>



<li><a href="https://www.infoq.com/news/2026/08/jetbrains-ai-spend/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">JetBrains Details its First Steps to Bring Rapidly Growing AI Spend under Control</a></li>



<li><a href="https://foojay.io/today/jakarta-agentic-ai-hits-its-first-milestone/" target="_blank" rel="noopener">Jakarta Agentic AI Hits Its First Milestone</a></li>



<li><a href="https://www.infoq.com/news/2026/08/mcp-stateless-gateway/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">MCP Goes Stateless, and Developers Ask Whether That Just Makes it an API Again</a></li>



<li><a href="https://blog.jetbrains.com/idea/2026/08/top-5-ai-features-in-intellij-idea/">Top 5 AI Features in IntelliJ IDEA</a></li>



<li><a href="https://blog.jetbrains.com/idea/2026/08/agent-skills-in-intellij-idea/">Agent Skills in IntelliJ IDEA</a></li>



<li><a href="https://henrikwarne.com/2026/08/15/lessons-learned-from-fixing-flaky-tests-with-claude/" target="_blank" rel="noopener">Lessons Learned from Fixing Flaky Tests with Claude</a>&nbsp;</li>



<li><a href="https://redoubleai.substack.com/p/the-reality-of-ai-in-the-enterprise" target="_blank" rel="noopener">The reality of AI in the enterprise</a></li>



<li><a href="https://blog.jetbrains.com/idea/2026/08/how-to-use-ai-agents-in-intellij-idea-with-acp/">How to Use AI Agents in IntelliJ IDEA With ACP</a></li>



<li><a href="https://dzone.com/articles/java-enterprise-is-already-ready-for-the-ai-era" target="_blank" rel="noopener">Java Enterprise Is Already Ready for the AI Era</a></li>



<li><a href="https://spring.io/blog/2026/08/17/spring-office-hours-podcast-S5E20" target="_blank" rel="noopener">Spring Office Hours Podcast: S5E20 – The Developer’s Guide to AI with Danny Thompson</a></li>



<li><a href="https://blog.scottlogic.com/2026/08/06/how-to-accelerate-agentic-adoption.html" target="_blank" rel="noopener">How to accelerate agentic adoption</a>&nbsp;</li>
</ul>



<h2 class="wp-block-heading">Languages, Frameworks, Libraries, and Technologies</h2>



<p>Explore what’s moving and shaking across languages, frameworks, libraries, and the wider tech landscape:</p>



<ul class="wp-block-list">
<li>This Week in Spring <a href="https://spring.io/blog/2026/08/04/this-week-in-spring-august-4-2026" target="_blank" rel="noopener">1</a>, <a href="https://spring.io/blog/2026/08/11/this-week-in-spring-august-11-2026" target="_blank" rel="noopener">2</a>, <a href="https://spring.io/blog/2026/08/18/this-week-in-spring-august-18-2026" target="_blank" rel="noopener">3</a>, <a href="https://spring.io/blog/2026/08/25/this-week-in-spring-august-25" target="_blank" rel="noopener">4</a></li>



<li><a href="https://www.infoq.com/news/2026/08/pod-deployment-unit-ai-agents/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">Pods as Workers, Not Agents: Rethinking the Deployment Unit for AI Agents on Kubernetes</a></li>



<li><a href="https://blog.gradle.org/gradle-is-going-agentic" target="_blank" rel="noopener">Gradle Is Going Agentic</a></li>



<li><a href="https://in.relation.to/2026/08/19/hibernate-immutable-2lc-9x-speedup/" target="_blank" rel="noopener">When @Immutable Made All the Difference: A 3.2x Speedup with Hibernate Second-Level Cache</a>&nbsp;</li>



<li><a href="https://foojay.io/today/vibe-coding-maven-and-the-dependencies-you-didnt-choose/" target="_blank" rel="noopener">Vibe Coding, Maven, and the Dependencies You Didn’t Choose</a></li>
</ul>



<h2 class="wp-block-heading">Conferences and Events</h2>



<p>Mark your calendar with the conferences, meetups, and events worth showing up for:</p>



<ul class="wp-block-list">
<li><a href="https://www.kcdc.info" target="_blank" rel="noopener">Kansas City Developer Conference</a> – Kansas City, USA, September 9–12</li>



<li><a href="https://jcconf.tw/2026/" target="_blank" rel="noopener">JCConfTaiwan</a> – Taipei, Taiwan, September 11</li>



<li><a href="https://javaforumnord.de/2026/" target="_blank" rel="noopener">Java Forum Nord</a> – Hannover, Germany, September 22</li>



<li><a href="https://jugsaxony.org/day/" target="_blank" rel="noopener">JUG Saxony Day</a> – Radebeul, Germany, September 25</li>



<li><a href="https://2026.confitura.pl" target="_blank" rel="noopener">Confitura</a> – Warsaw, Poland, September 25–26</li>



<li><a href="https://gotocph.com/2026" target="_blank" rel="noopener">GOTO Copenhagen</a> – Copenhagen, Denmark, September 28 – October 2</li>
</ul>



<figure class="wp-block-image size-full"><a href="https://lp.jetbrains.com/intellij-idea-conf-2026/?utm_source=newsletter&amp;utm_medium=jam&amp;utm_campaign=intellijideaconf" target="_blank" rel="noopener"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/09/image-24.png" alt="" class="wp-image-737606"/></a></figure>



<h2 class="wp-block-heading">Culture and Community</h2>



<p>Check out these stories about what makes the developer community tick.</p>



<ul class="wp-block-list">
<li><a href="https://www.infoq.com/presentations/ai-sdlc-maturity-framework-bottlenecks/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">The Five Stages of AI Maturity in Engineering Organizations &#8211; Where and Why Teams Get Stuck</a></li>



<li></li>



<li><a href="https://www.infoq.com/news/2026/08/platform-products-people-use/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">From Projects to Products: Turning Platforms into Products People Use</a></li>



<li><a href="https://blog.jetbrains.com/research/2026/08/programming-language-migration/">When Escape Routes Become Toll Roads: Mapping How Developers Move Between Programming Languages</a></li>



<li><a href="https://www.infoq.com/podcasts/infoq-culture-trends-2026/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">Culture &amp; Methods Trends 2026: The Human Side of AI Engineering</a></li>



<li><a href="https://www.infoq.com/news/2026/08/incidents-reliability-metrics/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">More Incidents Don&#8217;t Necessarily Mean Less Reliability</a></li>



<li><a href="https://lucumr.pocoo.org/2026/8/19/what-is-reasoning/" target="_blank" rel="noopener">What Is Reasoning</a></li>



<li><a href="https://www.infoq.com/news/2026/08/code-AI-write-only-disposable/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">How Code in the Age of Artificial Intelligence Becomes Write-Only and Disposable</a></li>



<li><a href="https://www.infoq.com/podcasts/brownfield-codebases-mob-programming/?utm_campaign=infoq_content&amp;utm_source=infoq&amp;utm_medium=feed&amp;utm_term=global" target="_blank" rel="noopener">The Human Edge: Why Brownfield Codebases Need Mob Programming, Not Just AI Vibes</a></li>



<li><a href="https://lucumr.pocoo.org/2026/8/22/fast-hard-code/" target="_blank" rel="noopener">Fast and Hard Code</a></li>
</ul>



<h2 class="wp-block-heading">And Finally…</h2>



<p>Check out the latest articles by the IntelliJ IDEA team:</p>



<ul class="wp-block-list">
<li><a href="https://blog.jetbrains.com/idea/2026/09/the-grails-plugin-has-a-new-home-apache-grails/">The Grails Plugin Has a New Home: Apache Grails</a></li>



<li><a href="https://blog.jetbrains.com/idea/2026/08/println-debugging-done-right/">Println Debugging Done Right</a></li>



<li><a href="https://blog.jetbrains.com/idea/2026/08/spring-boot-configuration-management-best-practices/">Spring Boot Configuration Management Best Practices</a></li>



<li><a href="https://blog.jetbrains.com/idea/2026/08/project-loom-in-intellij-idea-virtual-threads-scoped-values-and-structured-concurrency/">Project Loom in IntelliJ IDEA: Virtual Threads, Scoped Values, and Structured Concurrency</a></li>
</ul>



<p>That’s it for today! We’re always collecting ideas for the next Java Annotated Monthly – send us your suggestions via <a href="https://mail.google.com/mail/u/0/?fs=1&amp;tf=cm&amp;source=mailto&amp;to=JAM@jetbrains.com" target="_blank" rel="noopener">email</a> or <a href="https://x.com/intellijidea?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor">X</a> by September 20. Remember to check out our archive of <a href="https://www.jetbrains.com/lp/jam/" target="_blank" rel="noopener">past JAM issues</a> for any articles you might have missed!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>IntelliJ IDEA 2026.2.2 Is Out!</title>
		<link>https://blog.jetbrains.com/idea/2026/09/intellij-idea-2026-2-2/</link>
		
		<dc:creator><![CDATA[Maria Kosukhina]]></dc:creator>
		<pubDate>Wed, 02 Sep 2026 19:22:08 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/09/IntelliJ-IDEA-2026.2.2.png</featuredImage>		<category><![CDATA[releases]]></category>
		<category><![CDATA[bug-fix-update]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=736048</guid>

					<description><![CDATA[IntelliJ IDEA 2026.2.2 is now available. You can update to this version from inside the IDE, using the&#160;Toolbox App, or by using snaps if you are a Ubuntu user. You can also download it from our&#160;website. IntelliJ IDEA 2026.2.2 brings the following refinements: To see the full list of issues addressed in this version, please [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>IntelliJ IDEA 2026.2.2 is now available.</p>



<p>You can update to this version from inside the IDE, using the&nbsp;<a href="https://www.jetbrains.com/toolbox-app/" target="_blank" rel="noreferrer noopener">Toolbox App</a>, or by using snaps if you are a Ubuntu user. You can also download it from our&nbsp;<a href="https://www.jetbrains.com/idea/download/" target="_blank" rel="noreferrer noopener">website</a>.</p>



<p>IntelliJ IDEA 2026.2.2 brings the following refinements:</p>



<ul class="wp-block-list">
<li>Loading a remote OpenAPI specification no longer fails. [<a href="https://youtrack.jetbrains.com/issue/IJPL-63202/" target="_blank" rel="noopener">IJPL-63202</a>]</li>



<li>The order of terminal tabs is now preserved after reopening a project. <a href="https://youtrack.jetbrains.com/issue/IJPL-212254/Terminal-tabs-order-is-not-saved-in-IntelliJ" target="_blank" rel="noopener">[IJPL-212254</a>]</li>



<li>Checkboxes in Markdown now have improved contrast for better visibility. [<a href="https://youtrack.jetbrains.com/issue/IJPL-93391/Low-contrast-of-checkboxes-in-markdown" target="_blank" rel="noopener">IJPL-93391</a>]</li>



<li>The vertical scrollbar is once again displayed correctly in the Markdown preview pane. [<a href="https://youtrack.jetbrains.com/issue/IJPL-251259/2026.2-Markdown-Preview-Missing-vertical-scrollbar-JCEF-rendering-failure" target="_blank" rel="noopener">IJPL-251259</a>]</li>



<li>Opening directories containing a large number of .md files no longer causes the IDE to freeze. [<a href="https://youtrack.jetbrains.com/issue/IJPL-93385/InteilliJ-IDE-Keeps-freezing-when-opening-a-directory-with-large-number-of-files" target="_blank" rel="noopener">IJPL-93385</a>]</li>



<li>Spring Modulith no longer incorrectly reports accessibility violations for types exposed through <code>@NamedInterface</code> in nested modules. [<a href="https://youtrack.jetbrains.com/issue/IDEA-376940/Spring-Modulith-IntelliJ-incorrectly-flags-Spring-Modulith-NamedInterface-exposed-types-from-nested-modules-as-accessibility" target="_blank" rel="noopener">IDEA-376940</a>]</li>
</ul>



<p>To see the full list of issues addressed in this version, please refer to the <a href="https://youtrack.jetbrains.com/articles/IDEA-A-2100662739/IntelliJ-IDEA-2026.2.2-262.10315.125-build-Release-Notes" data-type="link" data-id="https://youtrack.jetbrains.com/articles/IDEA-A-2100662739/IntelliJ-IDEA-2026.2.2-262.10315.125-build-Release-Notes" target="_blank" rel="noopener">release notes</a>. </p>



<p>If you encounter any bugs, please report them using our&nbsp;<a href="https://youtrack.jetbrains.com/issues/IDEA" target="_blank" rel="noreferrer noopener">issue tracker</a>.&nbsp;</p>



<p>Happy developing!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>The Grails Plugin Has a New Home: Apache Grails</title>
		<link>https://blog.jetbrains.com/idea/2026/09/the-grails-plugin-has-a-new-home-apache-grails/</link>
		
		<dc:creator><![CDATA[Andrey Belyaev]]></dc:creator>
		<pubDate>Tue, 01 Sep 2026 12:02:33 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/IJ-social-BlogFeatured-1280x720-1-7.png</featuredImage>		<category><![CDATA[news]]></category>
		<category><![CDATA[apache-grails]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[plugin]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=733975</guid>

					<description><![CDATA[The Grails plugin for IntelliJ IDEA (which provides framework-aware coding assistance, GSP tooling, and GORM integration) has moved to the Apache Grails project. The Apache Software Foundation (ASF), which already stewards the Grails framework, now owns the plugin&#8217;s source code, which lives in its new GitHub repository. On JetBrains Marketplace, the plugin is listed as [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>The Grails plugin for IntelliJ IDEA (which provides framework-aware coding assistance, GSP tooling, and GORM integration) has moved to the Apache Grails project. The Apache Software Foundation (ASF), which already stewards the Grails framework, now owns the plugin&#8217;s source code, which lives <a href="http://github.com/apache/grails-intellij-plugin" target="_blank" rel="noopener">in its new GitHub repository</a>. On <a href="https://plugins.jetbrains.com/plugin/18504-apache-grails" target="_blank" rel="noopener">JetBrains Marketplace</a>, the plugin is listed as Apache Grails, with the ASF as the official vendor.</p>



<h3 class="wp-block-heading">Why Apache Grails</h3>



<p>The people closest to a framework are in the best position to build its tooling. Both teams agreed that the Apache Grails project was the right home for the plugin, with the Apache Grails Project Management Committee (PMC) now overseeing development. With development in the Grails team&#8217;s hands, the plugin can evolve in line with the framework&#8217;s roadmap (including GSP tooling, GORM integration, and build toolchain updates) and follow a release cadence that the team controls. JetBrains can stay focused on core IDE and platform work while continuing to feature the plugin on Marketplace.</p>



<h3 class="wp-block-heading">What&#8217;s changed</h3>



<p>The release cadence and IDE compatibility are set by Apache Grails PMCnow, not by IntelliJ IDEA releases. Updates ship on the framework&#8217;s roadmap, and the Grails team builds, signs, and publishes them straight to Marketplace under its own vendor credentials. The PMC and community contributors are already reviewing PRs, merging fixes, and shipping releases independently. Build 262.0.0, which supports IntelliJ IDEA 2026.2, is already available.</p>



<h3 class="wp-block-heading">What this means for you</h3>



<p>Already using the plugin from Marketplace? No migration is required: Updates will continue to come from the new maintainers, and the plugin ID stays the same for now. Starting with 2026.2, however, the plugin is no longer bundled with IntelliJ IDEA Ultimate by default. If you cannot find the plugin in your IDE, install it from the Marketplace under its new <a href="https://plugins.jetbrains.com/plugin/18504-apache-grails" target="_blank" rel="noopener">Apache Grails listing</a>. At JetBrains, we will continue to collaborate with the Apache Grails team and give them early notice of platform API changes.</p>



<h3 class="wp-block-heading">Get Involved</h3>



<p>Follow the project in the <a href="https://github.com/apache/grails-intellij-plugin" target="_blank" rel="noopener">repository </a>on GitHub, or visit the <a href="https://plugins.jetbrains.com/plugin/18504-apache-grails" target="_blank" rel="noopener">Apache Grails plugin</a> listing on JetBrains Marketplace. Got feedback? Open an issue in the new repository or leave feedback on the Marketplace page.</p>



<p>We&#8217;ll keep featuring the plugin on Marketplace, and we&#8217;re looking forward to seeing where the Apache Grails team takes it from here.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Project Loom in IntelliJ IDEA: Virtual Threads, Scoped Values, and Structured Concurrency</title>
		<link>https://blog.jetbrains.com/idea/2026/08/project-loom-in-intellij-idea-virtual-threads-scoped-values-and-structured-concurrency/</link>
		
		<dc:creator><![CDATA[Marit van Dijk]]></dc:creator>
		<pubDate>Fri, 28 Aug 2026 05:03:34 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/IJ-social-BlogFeatured-1280x720-1-6.png</featuredImage>		<category><![CDATA[java]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[project-loom]]></category>
		<category><![CDATA[scoped-values]]></category>
		<category><![CDATA[structured-concurrency]]></category>
		<category><![CDATA[virtual-threads]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=733843</guid>

					<description><![CDATA[Java concurrency is a powerful feature, but it can be difficult to get right. Writing correct multithreaded code requires a deep understanding of thread pools, synchronization, cancellation, and error propagation. Even experienced developers regularly introduce subtle bugs like thread leaks, swallowed exceptions, and race conditions that only surface under specific circumstances. Traditionally, Java concurrency has [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Java concurrency is a powerful feature, but it can be difficult to get right. Writing correct multithreaded code requires a deep understanding of thread pools, synchronization, cancellation, and error propagation. Even experienced developers regularly introduce subtle bugs like thread leaks, swallowed exceptions, and race conditions that only surface under specific circumstances.</p>



<p>Traditionally, Java concurrency has had several limitations:&nbsp;</p>



<ul class="wp-block-list">
<li>Scalability and resource costs from blocking threads.</li>



<li>Difficulty sharing contextual data between threads safely.</li>



<li>Problems managing threads such as leaks and cancellation delays.</li>



<li>Concurrent code that is hard to understand, debug, and maintain.</li>
</ul>



<p><a href="https://openjdk.org/projects/loom/" target="_blank" rel="noopener">Project Loom</a> aims to eliminate the tradeoff between simplicity and efficiency in concurrent Java code, making it easier to write, debug, profile, and maintain code that is correct, readable, and scalable. It does this through three features that work together:</p>



<ul class="wp-block-list">
<li><strong>Virtual Threads</strong> (<a href="https://openjdk.org/jeps/444" target="_blank" rel="noopener">JEP 444</a>, stable since Java 21) – Platform threads are expensive and limited in number, making highly concurrent applications resource-heavy and hard to scale. Virtual threads are lightweight and managed by the JVM, allowing many more threads to run concurrently without the same overhead.</li>



<li><strong>Scoped Values</strong> (<a href="https://openjdk.org/jeps/506" target="_blank" rel="noopener">JEP 506</a>, stable since Java 25) – ThreadLocal variables are mutable, hard to reason about, and prone to memory leaks. Scoped values provide immutable, automatically cleaned-up data sharing that scales efficiently with virtual threads.</li>



<li><strong>Structured Concurren</strong><strong>cy</strong> (<a href="https://openjdk.org/jeps/533" target="_blank" rel="noopener">JEP 533</a>, seventh preview in Java 27) – Unstructured concurrency leads to thread leaks, cancellation delays, and code that is difficult to debug and maintain. Structured concurrency treats a group of related threads as a single unit of work, making cancellation and error handling predictable and consistent. It also provides a clear parent-child thread hierarchy that improves observability and makes concurrent code easier to trace and inspect.</li>
</ul>



<p>In this post, we&#8217;ll give you an overview of these features, explain some of the problems they solve, show how they work together, and demonstrate how IntelliJ IDEA supports you along the way.</p>



<h2 class="wp-block-heading">Problems with Java concurrency before Project Loom</h2>



<p>To illustrate some of the problems with concurrency and how Project Loom can solve them, let’s look at an example of how we could write concurrent code without using any of the features of Project Loom. We’ll then rewrite this code to take advantage of these features and see how they compare.</p>



<p>As an example, we will use an application that loads a customer profile. It fetches order history and product recommendations for a customer in parallel. You can find the project’s source code <a href="https://github.com/mlvandijk/customer-profile-service" target="_blank" rel="noopener">here</a>.</p>



<p>The method <code>getProfile()</code> in the <code>CustomerProfileService</code> (which you can find <a href="https://github.com/mlvandijk/customer-profile-service/blob/036b2aab35652291b389d8cac726e2017604c220/profile-service/src/main/java/com/maritvandijk/profileservice/service/CustomerProfileService.java#L32" target="_blank" rel="noopener">here</a>) loads the customer profile using <code>CompletableFuture</code> to run the calls concurrently:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public CustomerProfile getProfile(String customerId) throws OrderServiceException, RecommendationServiceException {
        CompletableFuture&lt;List&lt;Order>> orderFuture =
                CompletableFuture.supplyAsync(() -> orderServiceClient.getOrders(customerId), executor);
        CompletableFuture&lt;List&lt;Recommendation>> recFuture =
                CompletableFuture.supplyAsync(() -> recommendationServiceClient.getRecommendations(customerId), executor);

        CompletableFuture&lt;Void> allFutures = CompletableFuture.allOf(orderFuture, recFuture);

        try {
            allFutures.get(2, TimeUnit.SECONDS); // single timeout covering both futures
            return new CustomerProfile(customerId, orderFuture.join(), recFuture.join());
        }

        // catch block 
</pre>



<p>The <code>catch</code> block handles any exceptions. This code does what it is supposed to do: It runs the independent calls in parallel, has a timeout, and makes an effort to cancel remaining tasks on failure. But there are still several potential problems:</p>



<ul class="wp-block-list">
<li><strong>Thread leaks.</strong> <code>cancel(true)</code> marks the future as cancelled, but for <code>CompletableFuture</code> the interrupt flag is ignored and the work already running in the pool continues to completion unless it was explicitly wired to a cancellation signal.</li>



<li><strong>Awkward error handling.</strong> <code>ExecutionException</code> wraps the real cause and must be unwrapped manually (shown in the code snippet below and also available <a href="https://github.com/mlvandijk/customer-profile-service/blob/036b2aab35652291b389d8cac726e2017604c220/profile-service/src/main/java/com/maritvandijk/profileservice/service/CustomerProfileService.java#L48" target="_blank" rel="noopener">here</a>). The unwrapping chain will need to be updated every time a new exception type is introduced.</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">        catch (ExecutionException e) {
            Throwable cause = e.getCause();
            orderFuture.cancel(true);
            recFuture.cancel(true);

            if (cause instanceof RestClientResponseException ex &amp;&amp; ex.getStatusCode().value() == 503) {
                if (orderFuture.isCompletedExceptionally()) {
                    throw new OrderServiceException("Order service unavailable", e.getCause());
                }
                if (recFuture.isCompletedExceptionally()) {
                    throw new RecommendationServiceException("Recommendation service unavailable", e.getCause());
                }
            }
            throw new RuntimeException("Unexpected error", e.getCause());
</pre>



<ul class="wp-block-list">
<li><strong>Duplicated cancellation logic.</strong> The <code>cancel()</code> calls are repeated across both the <code>TimeoutException</code> and <code>ExecutionException</code> catch blocks. If any additional parallel call is added later, it needs a <code>cancel()</code> call in both catch blocks, which is easy to forget. These blocks could drift out of sync as the code evolves.</li>



<li><strong>Fragile context propagation.</strong> Passing contextual data such as a logged-in user&#8217;s session or a trace ID across threads using <code>ThreadLocal</code> is fragile. <code>ThreadLocal </code>variables are mutable, their values persist for the lifetime of a thread unless explicitly removed, and child threads do not automatically inherit them unless you use <code>InheritableThreadLocal</code>, which has its own pitfalls.</li>



<li><strong>Poor observability.</strong> A thread dump shows a flat list of pool threads with no indication of which threads belong to which request, or which are still waiting on something that already failed. To see running threads, you can get a thread dump in IntelliJ IDEA when the program is <a href="https://www.jetbrains.com/help/idea/examining-suspended-program.html" target="_blank" rel="noopener">suspended</a> (either stopped at a breakpoint or paused). In the <em>Debug</em> tool window, click <em>More</em> and select <em>Get Thread Dump</em> while the service is handling a request.</li>
</ul>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ProjectLoom_GetThreadDump_Pause.mp4"></video><figcaption class="wp-element-caption"><em>Pause output and Get Thread Dump</em></figcaption></figure>



<p>Sidenote: To add the <em>Get Thread Dump</em> button to your <em>Debugger</em> tool window, right-click the <em>Debugger </em>tool window and select <em>Customize Toolbar</em>. In the popup, click <em>Add</em>, search for and select <em>Get Thread Dump</em>,<em> </em>and click <em>OK.</em></p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ProjectLoom_GetThreadDump_button.mp4"></video><figcaption class="wp-element-caption"><em>Customize Toolbar with the Get Thread Dump button</em></figcaption></figure>



<p>Let’s take a look at how Project Loom addresses these problems.</p>



<h2 class="wp-block-heading">Virtual Threads (JEP 444, stable since Java 21)</h2>



<p>The first feature of Project Loom is<a href="https://openjdk.org/jeps/444" target="_blank" rel="noopener"> Virtual Threads</a>, which drastically improve throughput in Java applications with blocking code.&nbsp;&nbsp;</p>



<p>Traditionally, the number of available threads in a Java application is limited because platform threads wrap operating system (OS) threads, and the number of OS threads is limited. Platform threads are also expensive; creating them can take milliseconds, each one consumes significant memory, and context switching between them has considerable overhead. To manage these costs, applications use thread pools – a fixed set of reusable threads managed by an <code>ExecutorService</code>.</p>



<p>In contrast, virtual threads are lightweight threads. They are cheap to create (taking microseconds instead of milliseconds), and since they are not tied to OS threads, they are not limited in number; you could run millions of them. When a virtual thread blocks, the underlying platform thread is released for other work and reassigned when the virtual thread is ready to continue. This means that virtual threads can significantly improve throughput for blocking workloads, such as I/O (anything that waits on databases, network calls, or file access), pauses, or synchronization.</p>



<p>In Java 24, an additional improvement was made to improve the scalability of Java code. With <a href="https://openjdk.org/jeps/491" target="_blank" rel="noopener">JEP 491: Synchronize Virtual Threads without Pinning</a>, virtual threads that block in synchronized methods and statements release their underlying platform threads. You can see this in action in the <a href="https://www.youtube.com/watch?v=_nt-z0FS3tM" target="_blank" rel="noopener">What’s New in IntelliJ IDEA 2025.2 livestream</a>.</p>



<p>We already briefly discussed <a href="https://blog.jetbrains.com/idea/2025/09/java-25-lts-and-intellij-idea/#virtual-threads-jep-444-and-synchronize-virtual-threads-without-pinning-jep-491">virtual threads</a> in <a href="https://blog.jetbrains.com/idea/2025/09/java-25-lts-and-intellij-idea">Java 25 LTS and IntelliJ IDEA</a>. For more information about using virtual thread dumps, have a look at <a href="https://blog.jetbrains.com/idea/2025/12/thread-dumps-and-project-loom-virtual-threads/">Thread Dumps and Project Loom (Virtual Threads)</a>.</p>



<p>To debug problems with concurrent threads, check out the new, improved logpoint functionality described in <a href="https://blog.jetbrains.com/idea/2026/08/println-debugging-done-right/">Println Debugging Done Right</a>.</p>



<h2 class="wp-block-heading">Scoped Values (JEP 506, stable since Java 25)</h2>



<p>The second feature of Project Loom is<a href="https://openjdk.org/jeps/506" target="_blank" rel="noopener"> Scoped Values</a> – a safer, more scalable alternative to <code>ThreadLocal</code> variables, designed with virtual threads in mind. They solve the problem of sharing contextual data across threads cleanly and safely.</p>



<p>To share data between components of an application, we can use thread-local variables, but these have several downsides. A <code>ThreadLocal</code> variable is mutable and, therefore, hard to reason about. Data persists for the thread&#8217;s lifetime unless manually removed (risking memory leaks and security issues), and child threads inherit copies that increase memory footprint.</p>



<p><code>ScopedValue</code> provides a better model: A value is bound once within a defined scope, automatically available to all code running within that scope, and cleaned up automatically when the scope ends. The binding cannot be changed from within the scope, eliminating the risk of accidental mutation and guaranteeing that any code reading the value will see the same one. When used with structured concurrency, scoped values require no explicit propagation to child threads, making context sharing both safer and simpler.</p>



<p>Note that even if your code does not explicitly use <code>ThreadLocal</code>, frameworks like Spring use it under the hood.</p>



<p>For more details, see the section on <a href="https://blog.jetbrains.com/idea/2025/09/java-25-lts-and-intellij-idea/#scoped-values-jep-506">Scoped Values</a> in <a href="https://blog.jetbrains.com/idea/2025/09/java-25-lts-and-intellij-idea">Java 25 LTS and IntelliJ IDEA</a>.</p>



<h2 class="wp-block-heading">Structured Concurrency (JEP 533, seventh preview in Java 27)</h2>



<p>The third feature of Project Loom is<a href="https://openjdk.org/jeps/533" target="_blank" rel="noopener"> Structured Concurrency</a>, which is currently still in preview. Java 27 again brings some changes to this preview feature. As we have already added some support for this feature in IntelliJ IDEA, now is the perfect time to try it out.</p>



<p>Structured concurrency is designed to promote a style of concurrent programming that reduces common problems such as thread leaks and cancellation delays, duplicated cancellation logic, and awkward error handling. The core idea is that a group of related concurrent tasks is treated as a single unit of work with a clear owner, a clearly defined lifetime, and clear rules. Subtasks cannot outlive their scope, failures propagate cleanly, and cancellation flows automatically from parent to children.</p>



<p>The <code>StructuredTaskScope</code> lets you break a task down into concurrent subtasks that are coordinated as a single unit. Subtasks are forked to run on their own thread and joined as a unit when the work completes.</p>



<p><code>StructuredTaskScope</code> has a factory method <code>StructuredTaskScope.open()</code>. This method has several overloads that allow you to provide a <code>Joiner</code> and/or a configuration callback. This lets you define the failure policy, a name for observability, and a timeout all in one place when opening the scope.</p>



<p>In our example, we want both methods (<code>fetchOrders()</code> and <code>fetchRecommendations()</code>) to succeed in order to correctly assemble the customer profile. We can provide a name for our scope and set a timeout for how long we are willing to wait on the results. If either of them fails, the other is cancelled. When a subtask fails or the timeout expires, <code>join()</code> throws an <code>ExecutionException</code> with the underlying cause. We switch on that cause to handle each case explicitly – including <code>CancelledByTimeoutException</code>, which is what the joiner uses to signal a timeout.</p>



<p>What if we don’t need results from all methods called in parallel? For example, imagine recommendations are available from two different caches and you only need the result of one of the calls to succeed. If one task succeeds, the other can be shut down. To accomplish this, we can use a different Joiner, <code>anySuccessfulOrThrow()</code>. As soon as one cache returns a result, the scope shuts down and the other task is cancelled automatically. If both fail, the <code>join()</code> method throws an <code>ExecutionException</code> with the exception of one of the failed subtasks as the cause.</p>



<p>To quickly scaffold a <code>StructuredTaskScope</code> in IntelliJ IDEA, use the built-in live template <code>sts</code>.&nbsp;</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ProjectLoom_STS_new.mp4"></video><figcaption class="wp-element-caption"><em>Use the live template </em>sts<em> to create and open a StructuredTaskScope.</em></figcaption></figure>



<p>Structured concurrency is still a preview feature in Java 27, so it is not yet recommended for production use. That said, the feature has been relatively stable in its broad shape for several preview rounds, with some changes to the API, and now is a great time to experiment with it. To identify where you could use structured concurrency in your code, look for places where the code performs multiple tasks in parallel and awaits the results. This code is a candidate to be rewritten using structured concurrency.</p>



<h2 class="wp-block-heading">Rewriting CustomerProfileService using Project Loom features</h2>



<p>The <a href="https://github.com/mlvandijk/customer-profile-service/tree/modern" target="_blank" rel="noopener">&#8220;modern&#8221; branch of our demo project</a> contains the same application rewritten using the features from Project Loom. The structure follows the same pattern as before: Orders and recommendations are fetched in parallel inside the scope.</p>



<p>The updated method <code>getProfile()</code> in the <code>CustomerProfileService</code> (which you can find <a href="https://github.com/mlvandijk/customer-profile-service/blob/b94e1fa22603324081bb7479bfb7b2f4b3535bda/profile-service/src/main/java/com/maritvandijk/profileservice/service/CustomerProfileService.java#L29" target="_blank" rel="noopener">here</a>) now uses a <code>StructuredTaskScope</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public CustomerProfile getProfile(String customerId) throws InterruptedException, TimeoutException {
        try {
            return ScopedValue.where(CUSTOMER_ID, customerId).call(() -> {
                try (var scope = StructuredTaskScope.open(
                        Joiner.awaitAllSuccessfulOrThrow(),
                        config -> config.withName("customer-profile").withTimeout(Duration.ofSeconds(2)))) {
                    var orderTask = scope.fork(() -> orderServiceClient.getOrders(CUSTOMER_ID.get()));
                    var recTask = scope.fork(() -> recommendationServiceClient.getRecommendations(CUSTOMER_ID.get()));
                    scope.join();
                    return new CustomerProfile(customerId, orderTask.get(), recTask.get());
                } catch (ExecutionException e) {
                    switch (e.getCause()) {
                        case StructuredTaskScope.CancelledByTimeoutException _ -> throw new TimeoutException("Request timed out");
                        case OrderServiceException ose -> throw ose;
                        case RuntimeException rte -> throw rte;
                        default -> throw new RuntimeException(e.getCause());
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Interrupted", e);
                }
            });
        } catch (InterruptedException | TimeoutException | RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }</pre>



<p>&nbsp;&nbsp;&nbsp;</p>



<p>Notice that we no longer need the duplicated <code>cancel()</code> calls in two catch blocks. If either task fails or the timeout is exceeded, all remaining subtasks are cancelled automatically. There is no longer any need for manual <code>cancel() </code>calls.</p>



<p>The code now clearly expresses its intent: Fetch orders and recommendations in parallel, wait up to two seconds, and fail cleanly if anything goes wrong. Because the pattern of what the code does is clearly captured in the code, this code is easier to read, understand, and reason about.</p>



<p>To see the difference structured concurrency makes, run the updated service in IntelliJ IDEA and take a thread dump while requests are being processed. You can create a thread dump, as described earlier. From <a href="https://youtu.be/FVsMsCFtlOs?si=LCKeVUKz6Fzki8MX&amp;t=1440" target="_blank" rel="noopener">IntelliJ IDEA 2026.1</a>, virtual threads forked within a <code>StructuredTaskScope</code> are grouped into containers representing their scopes. The IntelliJ IDEA debugger now shows you the structure in structured concurrency.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ProjectLoom_GetThreadDump.mp4"></video><figcaption class="wp-element-caption"><em>Get Thread Dump with StructuredTaskScope</em></figcaption></figure>



<h2 class="wp-block-heading">Using Java 27 (EA) in IntelliJ IDEA</h2>



<p>To try out the features described in this post, you will need <a href="https://openjdk.org/projects/jdk/27/" target="_blank" rel="noopener">Java 27</a>. You can download it from inside IntelliJ IDEA via <em>Project Structure | Project Settings | Project</em>, and then open the <em>SDK</em> dropdown and select <em>Download JDK</em>. Set <em>Version</em> to <em>27</em> and select the <em>Early-Access</em> version.&nbsp;</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ProjectLoom_DownloadJDK.mp4"></video><figcaption class="wp-element-caption"><em>Download the JDK from IntelliJ IDEA</em></figcaption></figure>



<p>If you are using a different way to download JDKs, you can point IntelliJ IDEA to your installation. Go to <em>Project Structure | Project Settings | Project</em>, open the <em>SDK</em> dropdown, select <em>Add JDK from disk</em>, and point IntelliJ IDEA to your installation of Java 27.</p>



<p>If you’re using command-line tools like <a href="https://sdkman.io/" target="_blank" rel="noopener">SDKMAN!</a> or <a href="https://asdf-vm.com/" target="_blank" rel="noopener">asdf</a>, you can use inlay hints to make version management easier. If your <em>.sdkmanrc</em> or <em>.tool-versions</em> file specifies a JDK version that is not yet installed, an inlay hint will appear that allows you to download it directly.&nbsp;</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ProjectLoom_SDKMAN_setJDK.mp4"></video><figcaption class="wp-element-caption"><em>Download the JDK via .sdkmanrc</em></figcaption></figure>



<p>If the JDK is already installed but not configured for the project, you can use the inlay hint to set it as the project JDK.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ProjectLoom_SDKMAN_setJDK-1.mp4"></video><figcaption class="wp-element-caption"><em>Set the JDK via .sdkmanrc</em></figcaption></figure>



<p>For more information, see the <a href="https://www.jetbrains.com/help/idea/sdk.html#configure-jdk-with-version-manager" target="_blank" rel="noopener">documentation</a>.</p>



<p>To get support for new language features, like structured concurrency, when using an early access version of the JDK, set the <em>Language level</em> to<em> X – Experimental features</em>.</p>



<p>If Java 27 has already been released when you’re reading this post, download the Java 27 distribution you want to use from IntelliJ IDEA or, if you already have Java 27 installed, point the IDE to your installation. To use structured concurrency, you also need to enable preview features. Set the <em>Language level</em> to <em>27 (Preview) – Primitive types in patterns, instanceof, and switch (5th preview)</em> in <em>Project Structure</em>. IntelliJ IDEA will flag usage of preview features in the editor, so you are always aware which features are not yet stable.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Virtual threads, scoped values, and structured concurrency are designed as a cohesive system, each addressing a different dimension of the problem:</p>



<ul class="wp-block-list">
<li><strong>Virtual Threads</strong> improve scalability. They remove the need to manage thread pool sizes and make it practical to run one thread per task, even at high concurrency.</li>



<li><strong>Scoped Values</strong> improve context propagation. This JEP solves some of the downsides of <code>ThreadLocal</code> (and framework workarounds), giving all tasks in a scope automatic, safe access to shared immutable context.</li>



<li><strong>Structured Concurrency</strong> solves the structural problems in concurrency by giving concurrent tasks a clear lifetime, a clear owner, and a clean failure model, thus eliminating thread leaks, duplicated cancellation logic, and <code>ExecutionException</code> unwrapping.</li>
</ul>



<p>Together, they let you write concurrent code that is much easier to read than traditional concurrent code, while being safe and scalable. The boilerplate that currently may take multiple steps to get right is replaced by code that is more concise and reads exactly like the problem it is solving.</p>



<p>You can use these features in IntelliJ IDEA. If you have questions or feedback, please let us know in the comments below.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Spring Boot Configuration Management Best Practices</title>
		<link>https://blog.jetbrains.com/idea/2026/08/spring-boot-configuration-management-best-practices/</link>
		
		<dc:creator><![CDATA[Siva Katamreddy]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 08:35:26 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/IJ-social-BlogFeatured-1280x720-1-2.png</featuredImage>		<category><![CDATA[idea]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[best-practices]]></category>
		<category><![CDATA[spring-boot]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=730538</guid>

					<description><![CDATA[Spring Boot provides comprehensive externalized application configuration support. It enables one application artifact to run in different environments by supplying values from various sources such as: In this article, we&#8217;ll explore the best practices for managing Spring Boot application configuration.A well-designed configuration strategy should ensure that: Configuration properties classification Typically, Spring Boot application configuration falls [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Spring Boot provides comprehensive <a href="https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config" target="_blank" rel="noopener">externalized application configuration</a> support. It enables one application artifact to run in different environments by supplying values from various sources such as:</p>



<ul class="wp-block-list">
<li>Property files</li>



<li>Environment variables</li>



<li>System properties</li>



<li>Command-line arguments</li>
</ul>



<p><br>In this article, we&#8217;ll explore the best practices for managing Spring Boot application configuration.<br>A well-designed configuration strategy should ensure that:</p>



<ul class="wp-block-list">
<li>Configuration remains separate from the application code. </li>
</ul>



<ul class="wp-block-list">
<li>The application fails to start when the required configuration is missing or invalid.</li>
</ul>



<ul class="wp-block-list">
<li>Default values can be overridden for each deployment environment.</li>
</ul>



<ul class="wp-block-list">
<li>Sensitive values are supplied by a dedicated secrets management system.</li>
</ul>



<h2 class="wp-block-heading">Configuration properties classification</h2>



<p>Typically, Spring Boot application configuration falls into three categories:</p>



<ul class="wp-block-list">
<li><strong>Application defaults:</strong> Safe, non-secret values such as third-party service URLs, timeouts, and retry limits. Store these with the application.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Deployment configuration:</strong> Values that identify an environment, such as database hosts, queue names, and external service URLs. Supply these through the deployment platform.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Secrets:</strong> Passwords, API keys, certificates, and private keys. Store these in a dedicated secrets system.</li>
</ul>



<p></p>



<p>For example, <code>application.properties</code> can provide application default configuration properties:<br></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">app.promotion-service.base-url=http://localhost:8181
app.promotion-service.timeout=3s
app.promotion-service.retries=3
logging.level.com.jetbrains=DEBUG
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.open-in-view=false</pre>



<p>A default value should be safe for every environment in which it may be used. Properties such as database URLs and credentials should never be hard-coded in the application code. If a required value has no safe default, validate its presence during startup.</p>



<h2 class="wp-block-heading">Use @ConfigurationProperties for binding application properties</h2>



<p>Spring applications can access configuration values through <code>Environment</code>, <code>@Value</code>, or <code>@ConfigurationProperties</code>.</p>



<p>Use <code>Environment</code> when property names must be resolved dynamically or infrastructure code needs direct access to property sources.</p>



<p>Use <code>@Value</code> for isolated values:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">PromotionService(
  @Value("${app.promotion-service.base-url}") String baseUrl,
  @Value("${app.promotion-service.timeout}") Duration timeout,
  @Value("${app.promotion-service.retries}") int retries) {
    this.baseUrl = baseUrl;
    this.timeout = timeout;
    this.retries = retries;
}</pre>



<p>Scattered <code>@Value</code> expressions make property names difficult to discover, validate, and refactor. A dedicated configuration type using <code>@ConfigurationProperties</code> supports all these features.</p>



<p>For related configuration properties, prefer <code>@ConfigurationProperties</code>. It provides:</p>



<ul class="wp-block-list">
<li>Type-safe binding and conversion</li>



<li>Relaxed binding between property names and Java members</li>



<li>Group-level validation</li>



<li>IDE completion and navigation through generated metadata</li>
</ul>



<p></p>



<p>For example, if we are integrating with a third-party REST API, we may want to configure the service base URL, timeout, and number of retries.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">app.promotion-service.base-url=${PROMOTION_SERVICE_URL}
app.promotion-service.timeout=${PROMOTION_SERVICE_TIMEOUT:3s}
app.promotion-service.retries=3</pre>



<p>In the above configuration, we are setting <code>base-url</code> value from the environment variable <code>PROMOTION_SERVICE_URL</code> and <code>timeout</code> value from the <code>PROMOTION_SERVICE_TIMEOUT</code> environment variable with a default value of 3 seconds.</p>



<p>Spring Boot supports setter-based binding. You can bind properties to a class that uses setters as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@ConfigurationProperties(prefix = "app.promotion-service")
public class PromotionSvcProperties {

    private String baseUrl;
    private Duration timeout;
    private int retries;

    // Setters and getters
}</pre>



<p>Register configuration types using <code>@ConfigurationPropertiesScan</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}</pre>



<p>The <code>@ConfigurationPropertiesScan</code> annotation scans for <code>@ConfigurationProperties</code> annotated components and registers them as Spring beans.</p>



<p>Now we can inject <code>PromotionSvcProperties</code> into other Spring beans and access property values.</p>



<h2 class="wp-block-heading">Prefer Records for @ConfigurationProperties binding</h2>



<p>Typically, configuration is normally established during startup and remains unchanged for the lifetime of the application.</p>



<p>For most application configurations, a Java record is the preferred option. It provides immutability out of the box so that their values won’t be modified even by mistake in contrast to class-based binding where you can accidentally invoke a setter:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@ConfigurationProperties(prefix = "app.promotion-service")
public record PromotionSvcProperties(
        String baseUrl,
        Duration timeout,
        int retries) {
}</pre>



<p>Spring Boot&#8217;s relaxed binding maps canonical kebab-case names such as <code>base-url</code> to the <code>baseUrl</code> field.</p>



<p>Sometimes we may want to bind properties to a bean provided by a third-party library, and we can’t change their source code to add <code>@ConfigurationProperties</code> annotation.</p>



<p>To bind configuration properties directly to a third-party class, declare it as a <code>@Bean</code> and annotate the bean method with <code>@ConfigurationProperties</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Configuration
public class ClientConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "third-party.client")
    public ThirdPartyClientProperties clientProperties() {
        return new ThirdPartyClientProperties();
    }
}</pre>



<p>You can configure the <code>third-party.client</code> properties as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">third-party.client.base-url=https://api.example.com
third-party.client.connect-timeout=5s
third-party.client.read-timeout=30s</pre>



<p>If the third-party class is immutable or not supports setter binding, create your own properties class and use it to construct the third-party object:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@ConfigurationProperties(prefix = "third-party.client")
public record ClientProperties(
    URI baseUrl,
    Duration connectTimeout,
    Duration readTimeout
) {}


@Configuration
@EnableConfigurationProperties(ClientProperties.class)
class ClientConfiguration {

    @Bean
    ThirdPartyClient thirdPartyClient(ClientProperties properties) {
        return new ThirdPartyClient(
                properties.baseUrl(),
                properties.connectTimeout(),
                properties.readTimeout()
        );
    }
}</pre>



<p>The wrapper approach is generally preferable because it avoids coupling your application configuration directly to the third-party library&#8217;s class structure.</p>



<h2 class="wp-block-heading">Fail fast, fail early: validate configuration during startup</h2>



<p>Configuration errors should be detected on application startup and fail fast if a configuration is missing or invalid. Add <code>@Validated</code> to a <code>@ConfigurationProperties</code> bean and apply <strong>Jakarta Bean Validation</strong> constraints to its properties.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Validated
@ConfigurationProperties(prefix = "app.promotion-service")
public record PromotionSvcProperties(
 @NotBlank String baseUrl,
        @NotNull Duration timeout,
        @Min(1) @Max(5) int retries,
        @NotNull @Valid SyncProperties sync) {

    public record SyncProperties(@NotEmpty String cron) {
    }
}</pre>



<p>With <code>spring-boot-starter-validation</code> on the classpath, binding or validation failures stop application startup. Validate required values, numeric ranges, nested groups, and other application-level constraints.</p>



<p>Use wrapper types when absence must be distinguished from a Java default. For example, an <code>Integer</code> annotated with <code>@NotNull</code> can identify a missing value, while an <code>int</code> defaults to 0.</p>



<p>You can also specify default values using <code>@DefaultValue</code> as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Validated
@ConfigurationProperties(prefix = "app.promotion-service")
public record PromotionSvcProperties(
 @NotBlank String baseUrl,
        @NotNull Duration timeout,
        @Min(1) @Max(5) @DefaultValue("3") Integer retries,
        @NotNull @Valid SyncProperties sync) {

    public record SyncProperties(@DefaultValue("0 0 * * * *") String cron) {
    }
}</pre>



<p>In the above example, we have specified the default values for <code>retries</code> and <code>cron</code> properties using <code>@DefaultValue</code> which will be used in case these property values are not configured.</p>



<h2 class="wp-block-heading">Understand property precedence</h2>



<p>Spring Boot combines multiple property sources. When the same property appears in more than one source, the source with higher precedence supplies the effective value.</p>



<p>The following simplified order shows the sources most commonly used in application deployments, from lowest to highest precedence:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">application.properties/yaml (low-precedence)
           ↓
profile-specific configuration files
           ↓
OS environment variables
           ↓
Java system properties
           ↓
command-line arguments    (high-precedence)</pre>



<p>Spring Boot configuration loading precedence matters when troubleshooting a value that differs from the expected configuration value.</p>



<p>Environment variables are widely supported by operating systems, container runtimes, and cloud platforms. Spring Boot derives environment variable names from canonical property names by replacing dots with underscores, removing dashes, and converting the result to uppercase:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">app.payment-timeout     -> APP_PAYMENT_TIMEOUT
spring.datasource.url   -> SPRING_DATASOURCE_URL</pre>



<p>Determining the effective value of a property can be challenging when it is defined in multiple configuration sources. IntelliJ IDEA can display resolved configuration values as editor inlay hints. Selecting a hint identifies the property source that supplies the value and indicates whether it is overridden by another source, such as an environment variable or a system property.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/effective-config-values.mp4"></video></figure>



<p>IntelliJ IDEA also provides navigation between property declarations, <code>@ConfigurationProperties</code> members, and property usages. For custom configuration properties, this support is enhanced by the metadata generated by <code>spring-boot-configuration-processor</code>.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/property-navigation.mp4"></video></figure>



<h2 class="wp-block-heading">Store secrets in a dedicated system</h2>



<p>Do not store passwords, API keys, certificates, or private keys in source control. Use a system such as <a href="https://www.hashicorp.com/en/products/vault" target="_blank" rel="noopener">HashiCorp Vault</a>, <a href="https://aws.amazon.com/secrets-manager/" target="_blank" rel="noopener">AWS Secrets Manager</a>, <a href="https://cloud.google.com/security/products/secret-manager" target="_blank" rel="noopener">Google Cloud Secret Manager</a>, <a href="https://azure.microsoft.com/en-us/products/key-vault" target="_blank" rel="noopener">Azure Key Vault</a>, or an equivalent platform service.</p>



<p>Ensure that secrets are excluded from logs, error messages, configuration metadata, and publicly accessible management endpoints.</p>



<p><strong>NOTE:</strong> In non-production environments, the <strong>Actuator</strong> <code>env</code> endpoint can help identify the source of an effective property. It should not be exposed publicly because configuration may contain sensitive information.</p>



<h2 class="wp-block-heading">Recommended configuration management</h2>



<p>There is no single configuration-management approach that works for every application. Choose a strategy based on the application&#8217;s architecture, deployment environment, and complexity.</p>



<h3 class="wp-block-heading">Monolith</h3>



<p>For a monolithic application, keep shared defaults in the application, use profile-specific files only where necessary, and supply deployment-specific overrides through environment variables. Store sensitive values in a dedicated secret manager.</p>



<h3 class="wp-block-heading">Containerized workloads</h3>



<p>For workloads running in a container platform such as Kubernetes, keep sensible defaults in the application and provide deployment-specific configuration through <a href="https://kubernetes.io/docs/concepts/configuration/configmap/" target="_blank" rel="noopener">ConfigMaps</a>. Store secrets separately in a dedicated secret-management system.</p>



<h3 class="wp-block-heading">Microservices</h3>



<p>For a microservices architecture, consider <a href="https://spring.io/projects/spring-cloud-config" target="_blank" rel="noopener">Spring Cloud Config Server</a> to centralize configuration, governance, and versioning. Continue to manage secrets through a dedicated secret-management system.</p>



<h2 class="wp-block-heading">Summary</h2>



<p>Effective application configuration starts with sensible defaults, type-safe <code>@ConfigurationProperties</code>, and startup validation. Keep environment-specific values outside the application, understand property-source precedence, and store secrets in a dedicated secret-management system.</p>



<p>The right configuration strategy should reflect the application&#8217;s architecture and deployment environment.</p>



<p>During local development and <a href="https://blog.jetbrains.com/idea/2026/01/spring-boot-debugging-now-remote/">debugging remotely</a>, IntelliJ IDEA helps reveal the effective configuration by showing resolved property values and their sources, highlighting overrides, and providing navigation between configuration files and bound Java properties.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Use AI Agents in IntelliJ IDEA With ACP</title>
		<link>https://blog.jetbrains.com/idea/2026/08/how-to-use-ai-agents-in-intellij-idea-with-acp/</link>
		
		<dc:creator><![CDATA[Anton Arhipov]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 14:50:50 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog-Featured-AI-Agents-1280x720-1.png</featuredImage>		<category><![CDATA[ai]]></category>
		<category><![CDATA[ai-assistant]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[acp]]></category>
		<category><![CDATA[ai-agent]]></category>
		<category><![CDATA[ai-in-ij]]></category>
		<category><![CDATA[intelij-idea]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=730793</guid>

					<description><![CDATA[The Agent Client Protocol (ACP) defines a common contract between a client – like IntelliJ IDEA – and an agent. IntelliJ IDEA already includes several ACP-compatible agents: Codex, Claude Agent, and Junie. Beyond these bundled options, the ACP Registry provides more choices, and teams can register internal or unlisted agents through acp.json. The key idea [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>The <a href="https://agentclientprotocol.com/protocol/overview" target="_blank" rel="noopener">Agent Client Protocol (ACP)</a> defines a common contract between a client – like IntelliJ IDEA – and an agent. IntelliJ IDEA already includes several ACP-compatible agents: Codex, Claude Agent, and Junie. Beyond these bundled options, the ACP Registry provides more choices, and teams can register internal or unlisted agents through acp.json.</p>



<p>The key idea is the <strong>boundary</strong>. IntelliJ IDEA remains the environment where you navigate the project, inspect code, and review changes. Across the ACP connection, each agent maintains its own models, behavior, authentication, and agent-side tools.</p>



<p>This makes the replaceable unit larger than an LLM. An ACP-compatible agent includes the full harness around the model: its planning logic, tools, model-routing behavior, and observability. Because ACP standardizes the boundary between the IDE and the agent, you can swap one agent for another smoothly, without changing how IntelliJ IDEA integrates with it.&nbsp;</p>



<h2 class="wp-block-heading"><strong>ACP in a nutshell</strong></h2>



<p>ACP is often described as the LSP (Language Server Protocol) for coding agents. The analogy works because the integration problem is similar.</p>



<p>Before the LSP, supporting each language meant writing a separate editor integration. The <a href="https://blog.jetbrains.com/idea/2026/08/intellij-idea-goes-lsp/">LSP</a> replaced that matrix with a single contract.</p>



<p>ACP applies the same idea to the connection between an editor or IDE and a coding agent. Any ACP-compatible agent can connect without requiring a bespoke plugin or&nbsp; private API for each pairing.</p>



<p>ACP originated from a <a href="https://blog.jetbrains.com/ai/2025/10/jetbrains-zed-open-interoperability-for-ai-coding-agents-in-your-ide/">collaboration between JetBrains and Zed</a>, with both JetBrains IDEs and Zed targeted as clients from the start.</p>



<p>For local agents, IntelliJ IDEA starts a subprocess and communicates with it via JSON-RPC over standard input and output. During initialization, the IDE and the agent negotiate protocol versions and capabilities. Once connected, prompts flow to the agent, while progress updates, file operations, and permission requests return to the IDE.</p>



<p>Agents can still behave differently under that shared contract. During initialization, each one declares the optional capabilities it supports. Plans, modes, slash commands, session loading, terminal operations, and other features can differ between agents.</p>



<p>ACP carries the interaction between IntelliJ IDEA and the agent. Additional tools and context can reach the agent through MCP (Model Context Protocol), including user-configured servers and the integrated IntelliJ MCP server.</p>



<h1 class="wp-block-heading">Start with an available agent</h1>



<p>IntelliJ IDEA ships with several agents that require no manual ACP configuration, including Codex, Claude Agent, and Junie. Choose one and describe the task you want it to handle.</p>



<p>Each agent has its own workflow style, which may include a planning mode, slash commands, or a particular authentication flow. ACP lets IntelliJ IDEA host that interaction through a shared contract while preserving those differences.</p>



<p>Ask the agent to make a small change. After it edits a file, AI Chat shows the changed file in the conversation. Click it to open the diff in the editor beside the chat and inspect exactly what changed.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image3-1.png" alt="" class="wp-image-730827"/></figure>



<p>That edit-and-review loop is the part worth keeping. If you switch agents later, having the loop inside the IDE prevents you from having to move the project or review changes in a separate tool.</p>



<h1 class="wp-block-heading">Install an agent from the ACP Registry</h1>



<p>The <a href="https://agentclientprotocol.com/registry" target="_blank" rel="noopener">ACP Registry</a> contains additional ACP-compatible agents, together with the metadata IntelliJ IDEA needs to install them.</p>



<p>In IntelliJ IDEA, open <em>Settings | Tools | AI Assistant | Agents</em> and then choose an agent from the registry. The <a href="https://www.jetbrains.com/help/ai-assistant/acp.html#install-agent-from-registry" target="_blank" rel="noopener">current ACP documentation</a> describes the complete installation flow.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image-44.png" alt="" class="wp-image-730797"/></figure>



<p>The agent&#8217;s configuration view also lets you expose the MCP servers configured in AI Assistant, the integrated IntelliJ MCP server, or both.</p>



<p>IntelliJ IDEA downloads the agent files when you apply the settings. The first session may ask you to authenticate using a method supported by that agent.</p>



<p>The registry also supplies the metadata used for updates and uninstallation. These operations remain in the <em>Agents</em> settings, so adding an agent does not require maintaining another IDE plugin.</p>



<p>Each registry agent retains its own license, service, credentials, and privacy terms. Check these details before granting repository access.</p>



<h1 class="wp-block-heading">Connect a custom agent with acp.json</h1>



<p>Registry agents are intended for broad distribution. An internal agent, however, often has a narrower role and should stay inside the company.</p>



<p>If an internal agent implements ACP, register it in ~/.jetbrains/acp.json. IntelliJ IDEA provides an <em>Add Custom Agent</em> action that creates and opens this file, but you can also edit the file directly.</p>



<p>The configuration below registers a hypothetical company migration agent:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{
  "agent_servers": {
    "Company Migration Agent": {
      "command": "/opt/company/bin/migration-agent",
      "args": ["acp"]
    }
  }
}
</pre>



<p>Each key under agent_servers becomes the agent&#8217;s display name. The command value must contain the full path to the executable that IntelliJ IDEA will start. Place the arguments required to activate that agent&#8217;s ACP mode in args; the exact values come from the agent&#8217;s documentation.</p>



<p>Use env when the process needs environment variables. Many agents expect you to authenticate through their CLI first and reuse credentials stored in the agent&#8217;s user configuration. If an agent accepts an API key through env, follow its documentation and avoid committing the file or its secrets to a repository.</p>



<p>Save <code>acp.json</code> and then select the configured agent in IntelliJ IDEA.</p>



<p>If any ACP-compatible agents are already installed on the machine, the IDE will detect them and offer to add them to the configuration.</p>



<h1 class="wp-block-heading">Why use more than one agent?</h1>



<p>Teams may want different agents for different kinds of work. ACP gives those agents a common way to connect to IntelliJ IDEA:</p>



<ul class="wp-block-list">
<li>Connecting an ACP-compatible agent through `<code>acp.json</code>` avoids developing and maintaining a separate IntelliJ IDEA plugin.</li>



<li>Navigation, editing, and diff review remain in IntelliJ IDEA while developers choose the agent for the work.</li>



<li>If an agent&#8217;s service or model provider is unavailable, developers can switch to another configured agent and continue working in the same IntelliJ IDEA project.</li>
</ul>



<h1 class="wp-block-heading">Keep the IDE, choose the agent</h1>



<p>Use an agent already available in IntelliJ IDEA, install one from the ACP Registry, or register an internal agent in <code>acp.json</code>.</p>



<p>ACP turns the coding agent from an IDE commitment into a replaceable choice you can revisit at any time.</p>
]]></content:encoded>
					
		
		
		                    <language>
                        <code><![CDATA[zh-hans]]></code>
                        <url>https://blog.jetbrains.com/zh-hans/idea/2026/08/how-to-use-ai-agents-in-intellij-idea-with-acp/</url>
                    </language>
                                    <language>
                        <code><![CDATA[ko]]></code>
                        <url>https://blog.jetbrains.com/ko/idea/2026/08/how-to-use-ai-agents-in-intellij-idea-with-acp/</url>
                    </language>
                                    <language>
                        <code><![CDATA[fr]]></code>
                        <url>https://blog.jetbrains.com/fr/idea/2026/08/how-to-use-ai-agents-in-intellij-idea-with-acp/</url>
                    </language>
                	</item>
		<item>
		<title>Top 5 AI Features in IntelliJ IDEA</title>
		<link>https://blog.jetbrains.com/idea/2026/08/top-5-ai-features-in-intellij-idea/</link>
		
		<dc:creator><![CDATA[Anton Arhipov]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 07:58:30 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog-Featured-5-AI-Features-1280x7200-1.png</featuredImage>		<category><![CDATA[agentic-ai]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[ai-agent]]></category>
		<category><![CDATA[ai-in-ij]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=727710</guid>

					<description><![CDATA[When developers hear &#8220;AI in the IDE&#8221;, the first thing that often comes to mind is a chat window. IntelliJ IDEA includes an AI chat, but JetBrains AI features also appear in many other parts of the development workflow. Some of those features are easy to miss because they are built into existing IDE actions: [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>When developers hear &#8220;AI in the IDE&#8221;, the first thing that often comes to mind is a chat window. IntelliJ IDEA includes an AI chat, but JetBrains AI features also appear in many other parts of the development workflow.</p>



<p>Some of those features are easy to miss because they are built into existing IDE actions: editing code, generating code in place, explaining selected code, working with stack traces, writing commit messages, and choosing which model or agent should handle a task. This overview focuses on five AI features in IntelliJ IDEA that are worth knowing about, with a few additional capabilities explained at the end. The list starts with one that can help before you even open the chat.</p>



<h1 class="wp-block-heading">1. AI completion</h1>



<p>You change a line, and the IDE points out the next line that needs to be changed to match. Rename a field, and it walks you to the other places that reference it, one keystroke at a time. Press <em>Tab</em> to jump to the spot, and <em>Tab</em> again to accept the edit. Ordinary completion guesses what should follow directly under your cursor. This type looks a step ahead, at the edit you haven&#8217;t made yet.</p>



<p>It runs on JetBrains&#8217; own models, tuned for coding, and it stays out of your way. Fix the spacing after one comma in a parameter list, and it will offer to fix every other comma in the file. On a larger scale, this is also where full-method generation is handled – the editor has enough local shape to fill in a method body without turning the task into a chat session.</p>



<p>The feature is easiest to understand in small edits. Change one line, and IntelliJ IDEA suggests the next related change in the file.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image4.png" alt="" class="wp-image-728576"/></figure>



<h1 class="wp-block-heading">2. In-editor code generation</h1>



<p>Press <em>Ctrl+\</em> anywhere in a file, type what you want in plain words, like &#8220;turn this loop into a stream&#8221; or &#8220;give me a builder for this class&#8221;, and the code appears right there at the cursor. The code lands in place, and you never have to switch to a chat to copy an answer back out of a conversation.</p>



<p>For a small, well-scoped change, the convenience is that the prompt starts where the edit happens. You type the instruction in the editor, review the generated code in place, and keep moving.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image10.png" alt="" class="wp-image-728587"/></figure>



<p>The generated code appears as an in-editor diff, so you can accept it, reject it, or refine the prompt. If the first version misses a constraint, add more context, ,like&nbsp; &#8220;Keep the method name&#8221; or &#8220;use Optional instead of null&#8221;. IntelliJ IDEA regenerates the code with the extra information and shows the new diff in the same place.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image7.png" alt="" class="wp-image-728598"/></figure>



<p>It is the hidden gem in the list – a general-purpose prompt you can invoke at the cursor for small, arbitrary edits, without opening the AI chat or copying code back into the file.</p>



<h1 class="wp-block-heading">3. AI Actions</h1>



<p><em>AI Actions</em> is the menu for people who never want to have a conversation with our model. Select a piece of code, press <em>Alt+Enter</em>, and choose from a variety of useful actions – no chat required.</p>



<p><em>Explain Code</em> takes a regex you didn&#8217;t write, a SQL query someone left you, or a cron expression, and tells you in plain English what it does. <em>Generate Unit Tests</em> opens the tests in a diff, so you can argue with them before they land in your project. <em>Generate Documentation</em> writes the doc comment for a public method. These actions keep you in the file. Point at your chosen code, pick an action, and get an actionable response immediately.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image8.png" alt="" class="wp-image-728609"/></figure>



<h1 class="wp-block-heading">4. Bring your own agent</h1>



<p>At some point, the task stops being a single editor action. It needs file changes, tests, and a diff you can review. Through the Agent Client Protocol (ACP), you connect an external coding agent and drive it from the same place where you already inspect files, diffs, tests, and problems. Think of ACP as the LSP for agents: one protocol, so the IDE doesn’t need a custom integration for every new agent that appears next month. JetBrains&#8217; own agent, Junie, shows up in the same registry as the third-party agents and receives no special treatment.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image5.png" alt="" class="wp-image-728620"/></figure>



<p>You ask the agent to change one endpoint. It proposes the steps, edits the service and the test, runs the test command, and leaves you with a diff you can open from the chat before accepting anything. That is the IDE part of the story. The agent can act, but the review still happens where you already review code.</p>



<p>Skills sit next to that. A skill is a reusable capability you set up once: triaging a CI failure, working through PR comments, converting Java to Kotlin, or nudging an agent away from the usual Spring Data JPA pagination mistake. You add skills from the <em>+</em> menu in the chat, and supported agents can use them without you having to retype the same long set of instructions every time.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image.png" alt="" class="wp-image-727730"/></figure>



<p>That is where the IDE demonstrates its worth. The agent can make a series of edits, but you can inspect each one as it appears – open the affected files, check test outputs, and review diffs before accepting anything. The chat, code, and review stay in the same window, making it easier to stay in control while the agent handles the mechanical work.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image9.png" alt="" class="wp-image-728631"/></figure>



<h1 class="wp-block-heading">5. Bring Your Own Key</h1>



<p>Once agents are in the IDE, the next decision is more straightforward – which provider your organization allows the IDE to call. Bring Your Own Key (BYOK) enables you to add the API key or endpoint for any provider your team already uses.</p>



<p>A configured key can sit behind the AI chat and selected IDE features, such as commit message generation, depending on what features the provider and model support. For some teams, the ability to use a provider that has already passed internal review matters more than support for the one at the top of the current model leaderboard.&nbsp;</p>



<p>You can configure your keys in <em>Settings | Tools | AI Assistant | Providers &amp; API keys</em>. Choose a provider under <em>Third-party AI providers</em>, enter the key or endpoint, and test the connection. Once it is connected, the provider&#8217;s models will appear in the AI chat&#8217;s model selector.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image6.png" alt="" class="wp-image-728642"/></figure>



<h1 class="wp-block-heading">Beyond the top five</h1>



<p>Aside from the top five headline features, a couple of smaller capabilities are still worth knowing about because they appear where the IDE already has context.</p>



<h2 class="wp-block-heading">When the stack trace is already in the console</h2>



<p>The run console is where optimism goes for a reality check. When your app throws an error and the stack trace lands there, the <em>Explain with AI </em>action is on hand. The IDE reads the trace and then gives you a likely cause and a suggested fix. That is a better use of thirty seconds than pasting the top line into a search engine and opening three tabs from 2017.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image3.png" alt="" class="wp-image-728653"/></figure>



<h2 class="wp-block-heading">When the staged diff needs a sentence</h2>



<p><em>Generate Commit Message</em> reads your staged diff and writes the message for you. Edit it, commit, and move on. It is a small feature, but it is exactly the kind you keep using once you know it exists.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image-1.png" alt="" class="wp-image-727785"/></figure>



<h1 class="wp-block-heading">The part you may have missed</h1>



<p>The <em>AI Chat</em> window is still here, and has gotten more interesting with agents and skills. The easy-to-miss part is the layer of AI-powered productivity features around it: edits suggested as you type, code generated at the cursor, explanations for selected code and stack traces, and commit messages written from the staged diff.</p>



<p>The final effect is boring in the best way: IntelliJ IDEA, doing a little more than it used to, without making a ceremony out of it.</p>



<p>If you tried AI Assistant a year ago and mostly remember the chat, this is the part you may have missed.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Agent Skills in IntelliJ IDEA</title>
		<link>https://blog.jetbrains.com/idea/2026/08/agent-skills-in-intellij-idea/</link>
		
		<dc:creator><![CDATA[Siva Katamreddy]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 07:58:12 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog-Featured-Agent-Skills-1280x720-1.png</featuredImage>		<category><![CDATA[ai]]></category>
		<category><![CDATA[ai-assistant]]></category>
		<category><![CDATA[ai-agent]]></category>
		<category><![CDATA[ai-skills]]></category>
		<category><![CDATA[ai-in-ij]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=728520</guid>

					<description><![CDATA[Agent Skills have become a key building block of the Agent Harness for AI-driven agentic development. They give AI agents additional capabilities and knowledge, enabling them to complete tasks in a way that aligns with your preferences. If you are new to Agent Skills, I recommend reading AI-Assisted Java Application Development with Agent Skills first. [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Agent Skills have become a key building block of the Agent Harness for AI-driven agentic development. They give AI agents additional capabilities and knowledge, enabling them to complete tasks in a way that aligns with your preferences.</p>



<p>If you are new to Agent Skills, I recommend reading <a href="https://blog.jetbrains.com/idea/2026/03/ai-assisted-java-application-development-with-agent-skills/"><em>AI-Assisted Java Application Development with Agent Skills</em></a> first.</p>



<p>IntelliJ IDEA and other JetBrains IDEs include AI Assistant, which helps developers with AI agentic development. AI Assistant provides an elegant and secure way to use and manage Agent Skills.</p>



<p>If you missed the announcement, see <a href="https://blog.jetbrains.com/ai/2026/04/skill-manager-and-skill-repository/"><em>Introducing the Skill Manager and Skill Repository</em></a>.</p>



<p>In this article, we will explore:</p>



<ul class="wp-block-list">
<li>How to install <a href="https://www.jetbrains.com/help/ai-assistant/agent-skills.html#skills-sources" target="_blank" rel="noopener">Agent Skills</a> via the Skills Manager.</li>



<li>Managing skills with the Skill Repository.</li>



<li>Installing skills globally, per project, or per agent.</li>



<li>Adding your own Skill Repository.</li>
</ul>



<h2 class="wp-block-heading">Skills Manager</h2>



<p>AI Assistant supports a wide range of AI agents through <a href="https://www.jetbrains.com/help/ai-assistant/acp.html" target="_blank" rel="noopener">ACP</a> (Agent Client Protocol).</p>



<p>The Skills Manager in AI Assistant lets you view the list of available skills and install them.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image1.png" alt="" class="wp-image-729897"/></figure>



<p>If you have already installed Agent Skills globally, the Skills Manager detects them and helps you install them as IntelliJ IDEA agent skills.</p>



<p>You can install a skill globally, at the project level, or per agent.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image4-1.png" alt="" class="wp-image-729908"/></figure>



<h2 class="wp-block-heading">Skill Repositories</h2>



<p>The Skill Repository lets you manage a list of locations where your verified skills are stored.</p>



<p>By default, JetBrains provides a Skill Repository hosted at <a href="https://github.com/JetBrains/skills" target="_blank" rel="noopener">https://github.com/JetBrains/skills</a>.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image5-1.png" alt="" class="wp-image-729919"/></figure>



<p>These skills are verified by JetBrains for security vulnerabilities.</p>



<p>It is essential to check for security issues before using agent skills downloaded from the internet. A better approach is to maintain an organization-wide Skill Repository, verified by your team, and add it to the Skill Repository list.</p>



<h2 class="wp-block-heading">Agent Skills in action</h2>



<p>Based on the prompt description, the AI agent automatically detects and uses relevant agent skills.</p>



<p>For example, I installed <a href="https://github.com/sivaprasadreddy/sivalabs-agent-skills" target="_blank" rel="noopener">spring-boot-skill</a>, and when I asked the AI agent to write tests for Spring Boot REST API endpoints, it used the <strong>spring-boot-skill</strong>.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image-8.png" alt="" class="wp-image-728554"/></figure>



<p>You can also explicitly invoke an agent skill using $skill-name [prompt] with Codex or `/skill-name [prompt]` with Claude.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image-9.png" alt="" class="wp-image-728556"/></figure>



<p></p>



<h2 class="wp-block-heading">Agentic Debugging using Debugger Skill</h2>



<p>IntelliJ IDEA also includes the bundled <code>ij-debugger</code> skill, which teaches AI agents such as Junie, Claude, and Codex how to use the IDE’s debugger. It is particularly useful when source code analysis and logs are not enough. For example, when an incorrect value appears only at runtime, execution follows an unexpected branch, or you need to inspect variables, expressions, threads, and call stacks. The agent can start and control a debugging session, manage breakpoints, step through the code, and collect runtime evidence to identify the root cause. See <a href="https://www.jetbrains.com/help/idea/agentic-debugging.html#install-skill" target="_blank" rel="noopener">Install the debugger skill</a> to make it available to your agent.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/ij-debugger-skill.png" alt="" class="wp-image-732809"/></figure>



<p>To debug your code, describe the problem in natural language and provide any useful details, such as how to reproduce it or where you suspect the issue occurs. <br><br>For example, <em>&#8220;A pet whose name differs only by case is not detected as a duplicate. Start a debug session, set a breakpoint in <code>Owner.getPet()</code>, and find out why.</em>&#8221; <br><br>The agent sets the necessary breakpoints, runs the application, inspects the call stack and runtime values, evaluates its hypotheses, and reports its findings. Because it controls the standard IntelliJ IDEA debugger, you can follow the investigation in the Debug tool window and take over at any time. <br><br>For more details, see <a href="https://www.jetbrains.com/help/idea/agentic-debugging.html?utm_source=chatgpt.com" target="_blank" rel="noopener">Agentic debugging</a>.</p>



<h2 class="wp-block-heading">Summary</h2>



<p>AI Assistant&#8217;s Skills Manager makes agent skills part of your regular IDE workflow. You can discover, install, and manage skills without leaving IntelliJ IDEA, then make them available globally, for a specific project, or only to a particular AI agent. The AI agent can automatically select a relevant skill from your prompt, while explicit invocation gives you control when you need it.</p>



<p>Just as importantly, the Skill Repository provides access to skills verified by JetBrains for security vulnerabilities. Teams can also add their own repositories containing internally reviewed skills. This makes it easier to benefit from reusable agent capabilities while maintaining control over which skills developers use in their projects.</p>
]]></content:encoded>
					
		
		
		                    <language>
                        <code><![CDATA[zh-hans]]></code>
                        <url>https://blog.jetbrains.com/zh-hans/idea/2026/08/agent-skills-in-intellij-idea/</url>
                    </language>
                	</item>
		<item>
		<title>IntelliJ IDEA 2026.2.1 Is Out!</title>
		<link>https://blog.jetbrains.com/idea/2026/08/intellij-idea-2026-2-1/</link>
		
		<dc:creator><![CDATA[Maria Kosukhina]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 13:33:37 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/IJ-IDEA-2026.2.1_blog.png</featuredImage>		<category><![CDATA[bug-fix-update]]></category>
		<category><![CDATA[intellij-idea]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=728861</guid>

					<description><![CDATA[We’ve just released the first minor update for IntelliJ IDEA 2026.2 – v2026.2.1. You can update to this version from inside the IDE, using the&#160;Toolbox App, or using snaps if you are a Ubuntu user. You can also download it from our&#160;website. Here are the most notable updates: To find out more details about the [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>We’ve just released the first minor update for IntelliJ IDEA 2026.2 – v2026.2.1.</p>



<p>You can update to this version from inside the IDE, using the&nbsp;<a href="https://www.jetbrains.com/toolbox-app/" target="_blank" rel="noreferrer noopener">Toolbox App</a>, or using snaps if you are a Ubuntu user. You can also download it from our&nbsp;<a href="https://www.jetbrains.com/idea/download/" target="_blank" rel="noreferrer noopener">website</a>.</p>



<p>Here are the most notable updates:</p>



<ul class="wp-block-list">
<li>Markdown shell scripts now execute in the correct order. [<a href="https://youtrack.jetbrains.com/issue/IJPL-92206/Markdown-shell-scripts-run-in-reverse-order" target="_blank" rel="noopener">IJPL-92206</a>]</li>



<li>Undo now works correctly after applying <em>Optimize imports on the fly</em>. [<a href="https://youtrack.jetbrains.com/issue/IDEA-285011/Optimize-Imports-On-The-Fly-breaks-Undo" target="_blank" rel="noopener">IDEA-285011</a>]<strong>&nbsp;</strong></li>



<li>Dragging a terminal tab after using the <em>Move to Editor</em> action no longer restarts the terminal session. [<a href="https://youtrack.jetbrains.com/issue/IJPL-165734/Terminal-restarts-when-dragged-after-Move-to-Editor" target="_blank" rel="noopener">IJPL-165734</a>]</li>



<li>The IDE no longer throws exceptions caused by the new <em>Resolve Conflicts</em> mechanism for Mercurial projects.[<a href="https://youtrack.jetbrains.com/issue/IJPL-249379/vcs.merge.conflict.iterative.resolution-causes-java.lang.IndexOutOfBoundsException" target="_blank" rel="noopener">IJPL-249379</a>]</li>



<li>Java code formatting now correctly respects the <em>Smart tabs</em> setting. [<a href="https://youtrack.jetbrains.com/issue/IDEA-388356/Does-not-respect-smart-tabs-setting" target="_blank" rel="noopener">IDEA-388356</a>]</li>
</ul>



<p></p>



<p>To find out more details about the issues resolved, please refer to the <a href="https://youtrack.jetbrains.com/articles/IDEA-A-2100662729/IntelliJ-IDEA-2026.2.1-262.9437.185-build-Release-Notes" target="_blank" rel="noopener">release notes</a>.</p>



<p>If you encounter any bugs, please report them to our&nbsp;<a href="https://youtrack.jetbrains.com/issues/IDEA" target="_blank" rel="noreferrer noopener">issue tracker</a>.</p>



<p>Happy developing!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Println Debugging Done Right</title>
		<link>https://blog.jetbrains.com/idea/2026/08/println-debugging-done-right/</link>
		
		<dc:creator><![CDATA[Igor Kulakov]]></dc:creator>
		<pubDate>Thu, 06 Aug 2026 11:30:00 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/JB-social-BlogFeatured-1280x720-1.png</featuredImage>		<category><![CDATA[idea]]></category>
		<category><![CDATA[tips-tricks]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[intellij-idea]]></category>
		<category><![CDATA[java]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=idea&#038;p=727050</guid>

					<description><![CDATA[The simplest tools are often the most useful, and debugging is a prime example of this. There are many advanced debugging techniques, and while they all have their use cases, println debugging is still the #1 choice, whether you&#8217;re doing it manually or with the help of coding agents. Inserting a println statement to inspect [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>The simplest tools are often the most useful, and debugging is a prime example of this.</p>



<p>There are many advanced debugging techniques, and while they all have their use cases, println debugging is still the #1 choice, whether you&#8217;re doing it manually or with the help of coding agents.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-45.png" alt="" class="wp-image-727139" style="width:539px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<p>Inserting a println statement to inspect a program&#8217;s state is one of the first things novice developers do, even before they hear the word &#8220;debugging&#8221;. Years later, simple debug logging remains one of the most useful tools for diagnosing all sorts of problems in real codebases. And now, it&#8217;s also one of the favorite tools of coding agents.</p>



<h2 class="wp-block-heading"><strong>Better foundation + modern workflows</strong></h2>



<p>The technique can be improved without losing its original simplicity. In fact, IntelliJ IDEA&#8217;s <em>logpoints</em> have long been a more capable superset of println debugging: While performing the same function, they can also be conditional, print stack traces, use hit counters and caller filters, be grouped, saved for later, and much more. If you try to imagine the most unusual debugging use case, chances are IntelliJ IDEA&#8217;s logpoints already have a feature for it (for example, did you know they can <a href="https://youtrack.jetbrains.com/issue/IJPL-88493" target="_blank" rel="noopener">beep</a>?).</p>



<p>In 2026.2, we&#8217;re taking logpoints further with several improvements focused less on specific use cases and more on strengthening the foundation:</p>



<ul class="wp-block-list">
<li><a href="https://docs.google.com/document/d/1Ul1aXOfQ99eDVETuwTJ_WnsN_-PRe_Q91UQ3ywk-1HE/edit?tab=t.0#heading=h.qhbngdwysub4" target="_blank" rel="noopener"><em>Instrumentation</em></a><em> instead of debugger-side evaluation</em>: IntelliJ IDEA now instruments your code directly for conditional and logging breakpoints, removing the bottleneck that other Java debuggers have.</li>



<li><a href="https://docs.google.com/document/d/1Ul1aXOfQ99eDVETuwTJ_WnsN_-PRe_Q91UQ3ywk-1HE/edit?tab=t.0#heading=h.khru2mg7b089" target="_blank" rel="noopener"><em>Agent compatibility</em></a>: We’ve revised logpoints so they can be used reliably by AI agents. Together with a new bundled skill, this gives agents the guidelines and handles they need to control the debugging session and use all the logpoint features of the IDE.</li>



<li><a href="https://docs.google.com/document/d/1Ul1aXOfQ99eDVETuwTJ_WnsN_-PRe_Q91UQ3ywk-1HE/edit?tab=t.0#heading=h.y874qnxxkw8i" target="_blank" rel="noopener"><em>New UI and navigation</em></a>: The new UI makes logpoints easier to set up. In addition, the console now tracks which println or logpoint produced each logging entry, letting you navigate there.</li>
</ul>



<p>All these features are supported in both local and remote JVM debug sessions.&nbsp;</p>



<p>Let&#8217;s look more closely at each of them.</p>



<h2 class="wp-block-heading"><strong>A short demo</strong></h2>



<p>For this demo, I set up a <a href="https://github.com/flounder4130/grpc-timeout.git" data-type="link" data-id="https://github.com/flounder4130/grpc-timeout.git" target="_blank" rel="noopener">mini gRPC server/client</a> scenario, in which we’re supposed to debug the server side. For those new to logpoints, the next post in this series will feature a detailed tutorial based on this project. For now, let’s examine the new features.</p>



<p>In this particular case, suspending the app with a regular breakpoint is useless. If we try to do so, the timeout expires quickly, and execution follows the cancellation path, hiding the state we were going to inspect.</p>



<p>So, instead of using a breakpoint, let&#8217;s <em>log</em> the state, preferably with a coding agent doing it for us:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-47.png" alt="" class="wp-image-727161" style="width:406px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<p>The agent not only sets the logpoints but also executes the run configurations, summarizes the resulting logs, finds the bug, and cleans up after itself.</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-43.png" alt="" class="wp-image-727083" style="width:433px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<p>Since IntelliJ IDEA tracks which logpoints belong to the agent, the agent cannot accidentally change yours. It does have full control over its own logpoints, though, including toggling them and modifying any properties that you can. When changing state is <em>necessary</em> for reproducing the bug, IntelliJ IDEA lets agents do that, but the skill steers the agent to avoid side effects and prefer logpoints whenever possible.</p>



<p>Here’s a short video showing how that works:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="IntelliJ IDEA 2026.2: ij-debugger skill" src="https://www.youtube.com/embed/HKOziq93gis?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<h2 class="wp-block-heading"><strong>Run from the terminal</strong></h2>



<p>In the example above, we&#8217;re running the agent through the AI chat, but the same works equally well with agents in the built-in terminal or outside the IDE. Once the skill is installed, it becomes available in the agent of your choice:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-46.png" alt="" class="wp-image-727150" style="width:620px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<p>The workflow is the same, this time with Claude Code:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-44.png" alt="" class="wp-image-727101" style="width:779px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<h2 class="wp-block-heading"><strong>Setting logpoints manually</strong></h2>



<p>Setting logpoints manually now takes a single click: Select the expression to log, and then click in the gutter between any two executable lines. If the expression is not in front of you in the editor, you can enter a custom expression afterwards:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-48.png" alt="" class="wp-image-727172" style="width:685px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<p>To find out which piece of code produced a specific line in the console, click the line and then select <em>Open</em>:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-49.png" alt="" class="wp-image-727183" style="width:412px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<p>IntelliJ IDEA will show the <em>Open</em> button that brings you to the corresponding code in the editor. Additionally, if it comes from an instrumented logpoint, the IDE will show the stack trace of how it got there – you get the same information as with the <a href="https://www.jetbrains.com/help/idea/logpoints.html#convert-breakpoint-to-logpoint" target="_blank" rel="noopener">Log stack trace</a> option, but now without cluttering the console with noisy messages.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="IntelliJ IDEA 2026.2: Logpoints" src="https://www.youtube.com/embed/swBXjtYGCjE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<h2 class="wp-block-heading"><strong>How much faster have logpoints become?</strong></h2>



<p>Many benefits of logpoints are shared across debuggers:</p>



<ul class="wp-block-list">
<li>Logpoints do not require source changes.</li>



<li>They are easy to turn on and off.</li>



<li>They let you inspect code that would otherwise be inconvenient to modify.</li>
</ul>



<p>IntelliJ IDEA&#8217;s implementation has many advantages beyond the basics, but performance is what stands out in this release.</p>



<p>If you’ve ever put a conditional or logging breakpoint in a hot path, you’ve seen the cost. With the traditional JPDA/JDWP breakpoint model, every hit suspends the application, evaluates the condition/log expression, and then resumes execution. For occasional hits this is fine, but in hot paths, this standard model becomes a bottleneck.</p>



<p>The overhead of traditional Java logpoints is not just a nuisance. The delay could hide a latency-sensitive failure, make a race condition disappear, or throttle the application load in a way that masks the problem. There’s a real risk that you would be debugging behavior introduced by the debugger itself.</p>



<p>Some time ago, I wrote about <a href="https://flounder.dev/posts/troubleshoot-slow-debugging/#conditional-breakpoints-in-hot-code" target="_blank" rel="noopener">ways to work around this</a>. Luckily, IntelliJ IDEA 2026.2 makes these hacks obsolete by instrumenting the debugged code for conditional and logging breakpoints. This chart shows the results of our internal benchmarking:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-50.png" alt="" class="wp-image-727194" style="width:643px;height:auto; width:100% !important; height:auto !important; max-width:100% !important;"/></figure>



<p>In our test set, the improvement is around 30x, making logpoints usable again in timing-sensitive code. You keep the same logpoint workflow, and so do agents, now with the performance of a regular print statement.</p>



<p>The best part is, you don’t need to do any setup to enjoy this speedup. IntelliJ IDEA will automatically detect logpoints that can be instrumented and do so behind the scenes in the same debug session, with no restart required.</p>



<h2 class="wp-block-heading"><strong>Summary</strong></h2>



<p>Logpoints are still the same simple idea that makes println debugging great: Obtain the right bit of information and put it in the most accessible place. And with the new pieces, this technique becomes even more useful for both developers and AI agents.</p>



<p>More agent skills and debugger capabilities are on the way. If there is a debugging workflow you would like agents to handle, tell us in the comments under this post. And if you have a concrete feature request for IntelliJ IDEA, please file it in our <a href="https://youtrack.jetbrains.com/issues/IDEA" target="_blank" rel="noopener">YouTrack</a> so it doesn&#8217;t go unnoticed.</p>



<p>Happy debugging!</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
