{"id":692555,"date":"2026-03-26T14:21:10","date_gmt":"2026-03-26T13:21:10","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=idea&#038;p=692555"},"modified":"2026-03-26T14:21:16","modified_gmt":"2026-03-26T13:21:16","slug":"ai-assisted-java-application-development-with-agent-skills","status":"publish","type":"idea","link":"https:\/\/blog.jetbrains.com\/zh-hans\/idea\/2026\/03\/ai-assisted-java-application-development-with-agent-skills","title":{"rendered":"AI-Assisted Java Application Development with Agent Skills"},"content":{"rendered":"\n<p>Agent-assisted development is quickly becoming a common mode of software development. New techniques are emerging to help LLMs generate code that matches your preferences and standards.<\/p>\n\n\n\n<p>One common approach is to create an <strong>AGENTS.md<\/strong>, <strong>CLAUDE.md<\/strong>, or <strong>GEMINI.md<\/strong> file with project details, build instructions, and coding guidelines. The AI agent loads this file into context on every request.<\/p>\n\n\n\n<p>This has two drawbacks:<\/p>\n\n\n\n<ul>\n<li>It consumes tokens on every request, increasing cost.<\/li>\n\n\n\n<li>Loading too much context into an LLM degrades its effectiveness.<\/li>\n<\/ul>\n\n\n\n<p><br><a href=\"https:\/\/agentskills.io\/\" target=\"_blank\" rel=\"noopener\">Agent Skills<\/a> is a new initiative that solves both problems by managing context progressively and extending AI agent capabilities on demand.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What are Agent Skills?<\/h2>\n\n\n\n<p>Agent Skills is an open standard introduced by Anthropic, to extend AI agent capabilities with specialized knowledge and workflows.<\/p>\n\n\n\n<p>Consider a use case where you want an AI to generate presentations using your company&#8217;s slide template and design guidelines. You can package those assets (the PPT template, font files, and design rules) into a <em>skill<\/em>. The agent then uses that skill to generate slides that match your standards automatically.<\/p>\n\n\n\n<p>A skill is a folder containing a <strong>SKILL.md<\/strong> file. This file includes metadata (<code>name<\/code> and <code>description<\/code> at minimum) and instructions that tell an agent how to perform a specific task. Skills can also bundle scripts, templates, and reference materials.<\/p>\n\n\n\n<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=\"\">skill-name\/\n\u251c\u2500\u2500 SKILL.md          # Required: instructions + metadata\n\u251c\u2500\u2500 scripts\/          # Optional: executable code\n\u251c\u2500\u2500 references\/       # Optional: documentation\n\u2514\u2500\u2500 assets\/           # Optional: templates, resources<\/pre>\n\n\n\n<p>The format of a <strong>SKILL.md<\/strong> file is:<\/p>\n\n\n\n<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=\"\">---\nname: name-of-the-skill\ndescription: Skill description.\nlicense: Apache-2.0\nmetadata:\n  author: author\/org\n  version: \"1.0\"\ncompatibility: Requires git, docker, jq, and access to the internet\n---\n\nSkill Content<\/pre>\n\n\n\n<p>In a <code>SKILL.md<\/code> file, <code>name<\/code> and <code>description<\/code> are required fields, and you can add optional fields like <code>licence<\/code>, <code>metadata<\/code>, <code>compatibility<\/code>, etc. You can explore more about the Skill Specification <a href=\"https:\/\/agentskills.io\/specification\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How do Agent Skills manage context?<\/h2>\n\n\n\n<p>At startup, agents load only the metadata (name and description) of installed skills. When you ask the agent to perform a task, it finds the relevant skill and loads only that <strong>SKILL.md<\/strong> into context.<\/p>\n\n\n\n<p>This progressive loading keeps context minimal and pulls in additional information only when needed, unlike a monolithic <strong>CLAUDE.md<\/strong> that loads everything upfront.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What can be a skill?<\/h2>\n\n\n\n<p>Skills extend AI capabilities across a wide range: from coding guidelines for a specific library, to step-by-step workflows with reference documents and helper scripts.<\/p>\n\n\n\n<p>For example, you can create a skill that:<\/p>\n\n\n\n<ul>\n<li>Specifies which library APIs to use and which anti-patterns to avoid.<\/li>\n\n\n\n<li>Bundles reference documentation in a <code>references\/<\/code> directory.<\/li>\n\n\n\n<li>Includes helper scripts in a <code>scripts\/<\/code> directory.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Case Study: Implementing Spring Data JPA Pagination<\/h2>\n\n\n\n<p>Suppose you ask an AI agent to implement a Spring Boot REST API endpoint that returns a paginated list of <code>Post<\/code> entities along with their <code>Comment<\/code> collections.<\/p>\n\n\n\n<p>Without guidance, the agent is likely to produce one of these common mistakes:<\/p>\n\n\n\n<ul>\n<li><strong>N+1 SELECT problem<\/strong> \u2014 lazy-loading comments trigger a separate query per post.<\/li>\n\n\n\n<li><strong>In-memory pagination<\/strong> \u2014 using <code>JOIN FETCH<\/code> with pagination loads all rows into memory, then paginates in the application layer.<\/li>\n<\/ul>\n\n\n\n<p><br>You can check out the sample code from the GitHub repository <a href=\"https:\/\/github.com\/sivaprasadreddy\/agent-skills-demo\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/sivaprasadreddy\/agent-skills-demo<\/a>&nbsp;<\/p>\n\n\n\n<p>Let us see how an AI Agent might generate code when asked to implement a REST API endpoint to return paginated posts along with comments.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video controls src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2026\/03\/AIA-Junie-without-Skills.webm\"><\/video><\/figure>\n\n\n\n<p>Without any specific guidelines or skills, the AI Agent generated the following implementation:<\/p>\n\n\n\n<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\n@RequestMapping(\"\/api\/posts\")\nclass PostController {\n   private final PostService postService;\n\n   PostController(PostService postService) {\n       this.postService = postService;\n   }\n\n   @GetMapping\n   PagedResult&lt;PostDto> getPosts(\n           @RequestParam(name = \"page\", defaultValue = \"1\") int pageNo,\n           @RequestParam(name = \"size\", defaultValue = \"10\") int pageSize) {\n       return postService.getPosts(pageNo, pageSize);\n   }\n\n}\n\n\n@Service\n@Transactional(readOnly = true)\npublic class PostService {\n   private final PostRepository postRepository;\n\n   public PostService(PostRepository postRepository) {\n       this.postRepository = postRepository;\n   }\n\n   public PagedResult&lt;PostDto> getPosts(int pageNo, int pageSize) {\n       Sort sort = Sort.by(Sort.Direction.ASC, \"id\");\n       Pageable pageable = PageRequest.of(pageNo &lt;= 0 ? 0 : pageNo - 1, pageSize, sort);\n       Page&lt;PostDto> postPage = postRepository.findAllWithComments(pageable).map(PostDto::from);\n       return PagedResult.from(postPage);\n   }\n\n}<\/pre>\n\n\n\n<p>If you run the application and invoke the <code>GET \/api\/posts<\/code> endpoint, you will get the results, but in the logs you will find the below <strong>WARNING<\/strong>:<\/p>\n\n\n\n<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=\"\">HHH000104: firstResult\/maxResults specified with collection fetch; applying in memory<\/pre>\n\n\n\n<p>This essentially means, Hibernate will load all the entities into memory and then apply pagination. This will result in poor performance and even OutOfMemory exceptions if there are a large number of rows in the <code>posts<\/code> table.<\/p>\n\n\n\n<p>A Spring Data JPA skill prevents both issues by giving the agent explicit guidelines and a working code example.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Spring Data JPA Agent Skill<\/h2>\n\n\n\n<p>Create a <code>spring-data-jpa\/SKILL.md<\/code> file with the following content:<\/p>\n\n\n\n<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=\"\">---\nname: spring-data-jpa-skill\ndescription: Implement the persistence layer using Spring Data JPA in Spring Boot applications.\n---\n\nFollow the below principles when using Spring Data JPA:\n\n1. Disable the Open Session in View (OSIV) filter: \nspring.jpa.open-in-view=false\n2. Disable in-memory pagination: \nspring.jpa.properties.hibernate.query.fail_on_pagination_over_collection_fetch=true\n\n3. Avoid the N+1 SELECT problem: use JOIN FETCH to load associated child collections in a single query.\n4. Avoid in-memory pagination: when loading a paginated list of parent entities with child collections:\n\t* First, load only the parent IDs using pagination\n\t* Then, load the full entities with their child collections using JOIN FETCH for those IDs\n\t* Assemble the final Page from the paginated IDs and the loaded entities\n\n\n## Pagination with child collections example:\n\nPostRepository.java\n\npublic interface PostRepository extends JpaRepository&lt;Post, Long> {\n\n   @Query(\"select p.id from Post p order by p.id\")\n   Page&lt;Long> findPostIds(Pageable pageable);\n\n   @Query(\"select distinct p from Post p left join fetch p.comments where p.id in :ids\")\n   List&lt;Post> findAllByIdInWithComments(@Param(\"ids\") Collection&lt;Long> ids);\n}\n\n\nPostService.java\n\n@Service\npublic class PostService {\n   private final PostRepository postRepository;\n\n   public PostService(PostRepository postRepository) {\n       this.postRepository = postRepository;\n   }\n\n   @Transactional(readOnly = true)\n   public Page&lt;Post> findPosts(Pageable pageable) {\n       Page&lt;Long> idsPage = postRepository.findPostIds(pageable);\n       if (idsPage.isEmpty()) {\n           return Page.empty(pageable);\n       }\n       List&lt;Post> posts = postRepository.findAllByIdInWithComments(idsPage.getContent());\n       return new PageImpl&lt;>(posts, pageable, idsPage.getTotalElements());\n   }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How to use Agent Skills?<\/h2>\n\n\n\n<p>Agent Skills work with Claude Code, Codex, Gemini CLI, JetBrains Junie, and other agents. Install a skill at the project level or user level depending on your preference.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Agent<\/td><td>Project-Level<\/td><td>User-Level<\/td><\/tr><tr><td>Junie<\/td><td><code>.junie\/skills\/<\/code><\/td><td><code>~\/.junie\/skills\/<\/code><\/td><\/tr><tr><td>Claude Code<\/td><td><code>.claude\/skills\/<\/code><\/td><td><code>~\/.claude\/skills\/<\/code><\/td><\/tr><tr><td>Codex<\/td><td><code>.agents\/skills\/<\/code><\/td><td><code>~\/.agents\/skills\/<\/code><\/td><\/tr><tr><td>Gemini CLI<\/td><td><code>.gemini\/skills\/<\/code>(or)<code>.agents\/skills\/<\/code><\/td><td><code>~\/.gemini\/skills\/<\/code>(or)<code>~\/.agents\/skills\/<\/code><\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\"><br><\/figcaption><\/figure>\n\n\n\n<p>To use the Spring Data JPA skill with Claude Code:<\/p>\n\n\n\n<ol>\n<li>Copy the <code>spring-data-jpa\/<\/code> directory into <code>{project-root}\/.claude\/skills\/<\/code>.<\/li>\n\n\n\n<li>Ask Claude Code to implement a paginated REST API endpoint.<\/li>\n\n\n\n<li>Claude Code discovers the skill automatically and follows the guidelines.<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-video\"><video controls src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2026\/03\/Agent-Skills-with-Claude-demo.webm\"><\/video><\/figure>\n\n\n\n<p>As you can see, Claude Code automatically discovered the Spring Data JPA skill and generated the following implementation following the guidelines given in the skill.<\/p>\n\n\n\n<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=\"\">@Service\npublic class PostService {\n   private final PostRepository postRepository;\n\n   public PostService(PostRepository postRepository) {\n       this.postRepository = postRepository;\n   }\n\n   @Transactional(readOnly = true)\n   public Page&lt;Post> findPosts(Pageable pageable) {\n       Page&lt;Long> idPage = postRepository.findPostIds(pageable);\n       if (idPage.isEmpty()) {\n           return Page.empty(pageable);\n       }\n       List&lt;Post> posts = postRepository.findAllByIdInWithComments(idPage.getContent());\n       return new PageImpl&lt;>(posts, pageable, idPage.getTotalElements());\n   }\n}<\/pre>\n\n\n\n<p>With this implementation, only the Post IDs of the desired page will be loaded first, and then a list of posts along with their comments will be fetched in a separate query. This will fix the pagination in-memory issue.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using Agent Skills with Junie<\/h2>\n\n\n\n<p>You can use the <a href=\"https:\/\/junie.jetbrains.com\/\" data-type=\"link\" data-id=\"https:\/\/junie.jetbrains.com\/\" target=\"_blank\" rel=\"noopener\">JetBrains Junie<\/a> Agent to generate code which automatically loads the necessary skills from <code>.junie\/skills<\/code>&nbsp; and directory.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video controls src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2026\/03\/AIA-Junie-Skills.webm\"><\/video><\/figure>\n\n\n\n<p>The Junie agent loaded <code>spring-data-jpa<\/code> skill based on the given task and applied the guidelines. You can also observe that Junie automatically runs the relevant tests to verify the generated code is working or not and iterate until the tests are passed.<\/p>\n\n\n\n<p>In the sample repository <a href=\"https:\/\/github.com\/sivaprasadreddy\/agent-skills-demo\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/sivaprasadreddy\/agent-skills-demo<\/a>, you can find the following branches to try out the spring-data-jpa Agent Skill:<\/p>\n\n\n\n<ul>\n<li><code>main<\/code>: Starting point to try implementing the mentioned usecase without any skills.<\/li>\n\n\n\n<li><code>in-memory-pagination-issue<\/code>: Usecase implementation generated by AI that results in in-memory pagination issue.<\/li>\n\n\n\n<li><code>skills<\/code>: With <code>spring-data-jpa<\/code> skill to try implementing the mentioned usecase.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>If the AI agent is generating code with any anti-patterns or not following team coding standards and conventions, instead of fixing issues one-by-one with follow-up prompts, consider creating a skill to provide those as guidelines.<\/p>\n\n\n\n<p>To explore more on Agent Skills, please refer to the following resources:<\/p>\n\n\n\n<ul>\n<li><a href=\"https:\/\/agentskills.io\/\" target=\"_blank\" rel=\"noopener\">https:\/\/agentskills.io\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/platform.claude.com\/docs\/en\/agents-and-tools\/agent-skills\/overview\" target=\"_blank\" rel=\"noopener\">https:\/\/platform.claude.com\/docs\/en\/agents-and-tools\/agent-skills\/overview<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developers.openai.com\/codex\/skills\/\" target=\"_blank\" rel=\"noopener\">https:\/\/developers.openai.com\/codex\/skills\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/geminicli.com\/docs\/cli\/skills\/\" target=\"_blank\" rel=\"noopener\">https:\/\/geminicli.com\/docs\/cli\/skills\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/junie.jetbrains.com\/docs\/agent-skills.html\" target=\"_blank\" rel=\"noopener\">https:\/\/junie.jetbrains.com\/docs\/agent-skills.html<\/a>&nbsp;<\/li>\n<\/ul>\n","protected":false},"author":1517,"featured_media":693117,"comment_status":"closed","ping_status":"closed","template":"","categories":[8899,4759,5088,8397],"tags":[6847,8724,9073,8759,3211],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/idea\/692555"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/idea"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/types\/idea"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/users\/1517"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/comments?post=692555"}],"version-history":[{"count":9,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/idea\/692555\/revisions"}],"predecessor-version":[{"id":693130,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/idea\/692555\/revisions\/693130"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media\/693117"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media?parent=692555"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/categories?post=692555"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/tags?post=692555"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/cross-post-tag?post=692555"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}