Codekoll finds the Java bugs that compile perfectly and blow up in production.
A standalone static analyzer for Java source, built in Java 25 on the JDK's own compiler. Point one jar at a source tree; get back a short list of findings where every single one is worth reading — each with what's wrong, what happens at runtime, and how to fix it.
src/main/java/com/acme/JobService.java:42:16 ERROR CK-REF-EQUALITY
if (status == "ACTIVE") {
^^
`==` compares references, not contents. Use .equals().
✖ 3 errors, 5 warnings in 214 files (1.8s)
Honest answer first: most classic bug patterns overlap the incumbents. SpotBugs has ~400 patterns from 20 years of hard lessons; Error Prone's type analysis is excellent; PMD, SonarQube, find-sec-bugs, and NullAway each own a slice. Codekoll's own build runs several of them as quality gates. If a rule catalog were the whole product, you wouldn't need this tool.
Codekoll differs in how it works — and in one area, in what it finds:
-
Every finding is worth reading. A curated catalog of 110 rules, each with an explicit false-positive budget: exemptions are part of every rule's contract and covered by tests, heuristic rules are demoted to INFO and never break your build, and any false positive found in the wild becomes a regression test before it's fixed. The opposite philosophy of "400 patterns your team disables wholesale."
-
Zero build integration. SpotBugs needs your compiled bytecode. Error Prone must be wired into your javac invocation. Codekoll is one jar pointed at a source tree — no build-file changes, works on code you just checked out, drops into any CI as a single step with SARIF output for GitHub code scanning.
-
Rules that teach. Every rule carries what is wrong, what happens at runtime, and how to fix it as first-class metadata — shown in findings, via
codekoll --explain CK-…, and in a runnable examples project with a documented buggy/fixed pair for all 110 rules. Compare that to decodingRV_RETURN_VALUE_IGNORED. -
Modern Java, day one. Codekoll parses with the JDK's own compiler (
JavacTask), so new Java syntax works the day the JDK ships — no waiting for a third-party parser to catch up. -
Coverage where the incumbents are thin. Two whole packs of it. The
modernpack targets Java 21+ features that predate most incumbent catalogs:defaultbranches that silently defeat sealed-switch exhaustiveness, records with array components (brokenequals), pooled virtual threads,Subtask.get()beforejoin(),Instant.plus(MONTHS)(always throws), DST-unsafeDurationarithmetic, reused streams, FFM use-after-close. Theframeworkspack hunts silently ignored code — the sneakiest bug class there is, because nothing ever throws:@Transactionalon a private method (no transaction, no error),@Autowiredon a static field (stays null), a@Testthat's silently never run, a JPA entity that'sfinal, SLF4J placeholder mismatches, log calls that lose the stack trace. Plus JSpecify-aware nullness rules like overrides that weaken a supertype's nullness contract. -
Extensible without forking. Rules ship in packs discovered via
ServiceLoaderacross JPMS module boundaries. A team-specific rule pack is just a jar on--rule-path.
110 rules in ten packs — all bugs that compile cleanly and fail (or silently misbehave) at runtime:
| Pack | Focus | Rules | Flavor |
|---|---|---|---|
correctness |
Provably or almost-certainly wrong logic | 26 | two next() per one hasNext() guard, side effects inside assert (silently skipped in production), discarded String.trim(), == on Strings, x.equals(null), the YYYY week-year date bug, format-string mismatches, infinite recursion, getBytes() without a charset, arrays as hash keys |
numeric |
Arithmetic and overflow traps | 8 | long ms = days * 86_400_000 (overflowed before widening), comparator by subtraction, Math.abs(hashCode()), 1 << 32 |
concurrency |
Threading and synchronization | 12 | counter.set(counter.get() + 1) on an Atomic (a lost update), Thread.run() instead of start(), synchronized on a Lock, ++ on volatile, double-checked locking without volatile, collection modified while iterating, discarded CompletableFutures |
resources |
Leaks, exception handling, cleanup | 9 | unclosed streams, empty catch blocks, rethrow that drops the cause, throw inside finally |
security |
Crypto, injection, TLS, ReDoS | 11 | MD5/SHA-1, SQL/command built by concatenation, trust-all TLS, XXE-default XML factories, catastrophic-backtracking regexes |
performance |
Accidentally-quadratic code | 6 | string concat in loops, regex recompiled per iteration, keySet()+get(), boxed accumulators |
api-misuse |
Stdlib contracts violated | 13 | matcher.group(3) on a two-group regex, Arrays.asList(intArray) (a list of size 1, not of ints), Map.of("k", null) and duplicate Set.of/Map.of keys (both always throw), Boolean.getBoolean(value) (reads a system property, always false), Map<String,User>.get(12345) (always null), mutating List.of(), (String[]) list.toArray(), split("."), List<Integer>.remove(1) (index, not value!), toMap without a merge function |
nullness |
NPEs the compiler can't see | 8 | int x = map.get(k) (unboxing NPE), x != null & x.length() > 0 (non-short-circuit), impossible null conditions, JSpecify contract violations |
modern |
Java 21+ platform misuse | 10 | sealed-switch default, record array components, pooled virtual threads, structured-concurrency ordering, java.time unit traps, stream reuse, FFM use-after-close |
frameworks |
Silently ignored code | 7 | @Transactional on a private method, proxy-bypassing self-invocation, @Autowired static, final JPA entities, tests that never run, SLF4J placeholder mismatches, stack traces lost in log calls |
The complete generated catalog — every rule's id, severity and one-line summary — is in docs/RULES.md (produced by codekoll --catalog); each rule's detection algorithm and exemption list is in SPEC.md, and each has a runnable, documented example in codekoll-examples showing the bug, the runtime failure, and the fix.
Requires JDK 25.
# analyze a source tree
java -jar codekoll.jar src/main/java
# machine-readable output for CI / GitHub code scanning
java -jar codekoll.jar --format sarif --output codekoll.sarif src/main/java
# only some packs, stricter threshold
java -jar codekoll.jar --packs security,concurrency --fail-on warning src/main/java
# what does a rule mean?
java -jar codekoll.jar --explain CK-WEEK-YEAR-FORMATExit codes: 0 clean, 1 findings at/above the --fail-on threshold, 2 usage/internal error. Configure per-project via codekoll.toml (disable rules or packs, adjust severities, exclude generated sources); suppress single findings with @SuppressWarnings("codekoll:CK-…"). Full CLI and config reference in SPEC.md §3.4–3.5.
The 30-second demo: run codekoll on its own examples module and watch all 110 rules fire —
java -jar codekoll.jar codekoll-examples/src/main/javaCodekoll drives the JDK compiler itself: sources are parsed and attributed by javac (JavacTask.parse() + analyze()), giving every rule fully resolved types, generics, and symbols — the same answers the compiler has. A single combined traversal walks each file once, dispatching AST nodes to the rules subscribed to them; findings stream to console, JSON, or SARIF reporters.
Because analysis is source-level and compiler-backed: analyzed code must compile (files with hard errors are reported as skipped), results are exact rather than decompiled guesses, and there is exactly one runtime dependency (the CLI parser). Architecture details — module graph, rule SPI, dispatcher design — in ARCHITECTURE.md.
In active development — see PLAN.md for the milestone roadmap and SPEC.md for the complete design. The quality bar codekoll holds itself to: Checkstyle, PMD, SpotBugs, Error Prone + NullAway (JSpecify), ArchUnit, and ≥90 % line coverage all fail the build, and codekoll runs on its own source in CI (--fail-on error).
Performance is held to the same standard: a load-test harness (codekoll-load-test) measures CPU and peak memory on every build against a committed baseline, fails CI on regression, and charts every version — the trend diagrams live in docs/perf/.
This project is licensed under the MIT License - see the LICENSE file for details.