<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mihai-Daniel</title>
    <description>The latest articles on DEV Community by Mihai-Daniel (@mihaidaniel34).</description>
    <link>https://dev.to/mihaidaniel34</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4177277%2F7b8455e6-6e3e-413a-a9f9-f8c65b727c64.png</url>
      <title>DEV Community: Mihai-Daniel</title>
      <link>https://dev.to/mihaidaniel34</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mihaidaniel34"/>
    <language>en</language>
    <item>
      <title>I wanted something faster than gradle build for my coding agent, so I made a Java checker in Rust</title>
      <dc:creator>Mihai-Daniel</dc:creator>
      <pubDate>Sun, 11 Oct 2026 15:16:24 +0000</pubDate>
      <link>https://dev.to/mihaidaniel34/i-wanted-something-faster-than-gradle-build-for-my-coding-agent-so-i-made-a-java-checker-in-rust-16ee</link>
      <guid>https://dev.to/mihaidaniel34/i-wanted-something-faster-than-gradle-build-for-my-coding-agent-so-i-made-a-java-checker-in-rust-16ee</guid>
      <description>&lt;p&gt;I use Claude Code on Java projects a lot. After almost every change, the agent wants to run &lt;code&gt;./gradlew build&lt;/code&gt; to see whether it broke anything. On the projects I work on that takes 2 to 5 minutes, and most of the time it finds nothing. If you tell the agent to skip the build, mistakes pile up until the test run at the very end, and then it has to untangle five of them at once.&lt;/p&gt;

&lt;p&gt;I wanted something in between: a check that runs after every edit, takes well under a second, and catches the boring stuff. A method that no longer exists, a bean Spring won't find, a null that's obviously going to blow up.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/mihaidaniel34/grounds" rel="noopener noreferrer"&gt;grounds&lt;/a&gt;. It's a static analyzer for Java written in Rust. It doesn't need Gradle, Maven or a JVM to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heads up: it's experimental.&lt;/strong&gt; It's on version 0.0.6. It has been tested on 21 open-source projects and that's it. If your code looks different from those, expect false positives and setups it doesn't understand yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;It runs as a Claude Code hook. After the agent edits a file, grounds checks the lines that changed, usually in 0.1 to 0.3 seconds, and sends anything new back to the agent. When the agent tries to finish its turn, it runs the full set of rules on everything that changed and blocks the turn once if it found something.&lt;/p&gt;

&lt;p&gt;What it looks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compile breaks.&lt;/strong&gt; Calls to methods or constructors that don't exist anymore, wrong argument types, &lt;code&gt;@Override&lt;/code&gt; that overrides nothing. If you rename a method, it lists every caller in other files that you haven't updated yet. For agents this has been the most useful part.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spring / Micronaut wiring.&lt;/strong&gt; Missing beans, ambiguous beans, a &lt;code&gt;@Qualifier&lt;/code&gt; with a typo, constructor cycles, Lombok quietly dropping your &lt;code&gt;@Qualifier&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataflow.&lt;/strong&gt; Null dereferences, resources that never get closed, &lt;code&gt;Optional.get()&lt;/code&gt; without a check, dead stores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;About 300 rules&lt;/strong&gt; I reimplemented from Sonar, SpotBugs, PMD, Error Prone and IntelliJ. Each one says which of those tools' rules it corresponds to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what the agent sees after renaming a method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grounds: API changes in src/main/java/.../Owner.java broke 15 call site(s) in 6 other file(s) not yet updated:
src/main/java/.../PetController.java:103:9: `Owner` has no method `addPet`
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use it without an agent too. &lt;code&gt;grounds check&lt;/code&gt; runs on the whole project, and &lt;code&gt;grounds changed&lt;/code&gt; only on what you changed since HEAD.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it gets away without a build
&lt;/h2&gt;

&lt;p&gt;It parses everything with tree-sitter and turns each file into a small stub: signatures, annotations, constants. Stubs are cached by file hash. Method bodies only get analyzed for the files you're actually checking. A small daemon keeps the index warm between edits.&lt;/p&gt;

&lt;p&gt;For dependencies it reads jars straight from your &lt;code&gt;~/.gradle&lt;/code&gt; and &lt;code&gt;~/.m2&lt;/code&gt; caches, so it works best when the project has been built on that machine at least once. For the JDK it reads &lt;code&gt;ct.sym&lt;/code&gt;. If you want an exact classpath, &lt;code&gt;grounds classpath&lt;/code&gt; runs Gradle or Maven once to dump it.&lt;/p&gt;

&lt;p&gt;The rule I kept coming back to: &lt;strong&gt;if it doesn't know a type, it says nothing.&lt;/strong&gt; A missing jar means fewer findings, not wrong ones. I'd rather it miss something than have the agent waste a turn on a false alarm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers
&lt;/h2&gt;

&lt;p&gt;Some full-project runs on an M4 Pro:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;project&lt;/th&gt;
&lt;th&gt;files&lt;/th&gt;
&lt;th&gt;time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;spring-petclinic&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;0.24 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;micronaut-core&lt;/td&gt;
&lt;td&gt;6,371&lt;/td&gt;
&lt;td&gt;3.4 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;thingsboard&lt;/td&gt;
&lt;td&gt;6,884&lt;/td&gt;
&lt;td&gt;4.9 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;camunda&lt;/td&gt;
&lt;td&gt;15,743&lt;/td&gt;
&lt;td&gt;10.2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A single-file check through the daemon is about 0.1 s. The biggest file I tried, a 6,800-line one, took 0.32 s.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I tested it
&lt;/h2&gt;

&lt;p&gt;This is where most of the time went. Writing rules is quick. Getting them to shut up on correct code takes much longer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 21 projects compile and their apps start, so any compile-break or DI finding on them is a false positive. Right now there are zero. The first run on the newer Spring projects gave over 1,400, mostly from Lombok features I hadn't modeled yet and from version mismatches between cached jars and the project.&lt;/li&gt;
&lt;li&gt;I compared it against PMD on the same code. That turned up a field lookup bug that produced 1,524 bogus "unused private field" reports, and a case where nested Hamcrest matchers made type inference exponential and hung on one test file.&lt;/li&gt;
&lt;li&gt;I planted a set of DI bugs in a copy of spring-petclinic-rest. A script checks that they're all still caught.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It did find real bugs in those projects too. An &lt;code&gt;int&lt;/code&gt; overflow in &lt;code&gt;1000 * 60 * 60 * 24 * 365 * 10&lt;/code&gt;. Two methods that call themselves unconditionally. A &lt;code&gt;private&lt;/code&gt; &lt;code&gt;@ParameterizedTest&lt;/code&gt; that never runs. About twenty AssertJ &lt;code&gt;assertThat(...)&lt;/code&gt; calls that don't actually check anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full disclosure
&lt;/h2&gt;

&lt;p&gt;Most of the code (around 30k lines of Rust) was written by Claude through Claude Code, over about two days. My job was mostly deciding what it should do, running it on real projects, and saying no a lot. I also had a second model (OpenAI's) review it and write Java test cases to try to break the rules. That found a good number of bugs I wouldn't have caught myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Only a prebuilt binary for Apple Silicon Macs. Other platforms have to build from source.&lt;/li&gt;
&lt;li&gt;Windows isn't tested. The daemon uses Unix sockets.&lt;/li&gt;
&lt;li&gt;Kotlin sources are ignored, so mixed Kotlin/Java projects will have blind spots.&lt;/li&gt;
&lt;li&gt;Spring auto-configured beans like &lt;code&gt;DataSource&lt;/code&gt; or &lt;code&gt;ObjectMapper&lt;/code&gt; aren't modeled, so it stays quiet about those.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it and it reports something wrong, a short code snippet that reproduces it is the most useful thing you can send me. Issues are open on &lt;a href="https://github.com/mihaidaniel34/grounds" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>java</category>
      <category>rust</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
