What it is

The core of CodeLaser’s static analysis.

maddi is a whole-program static analyzer for Java and Kotlin. The CodeLaser engine builds its restructuring operations on top of it. For every field, parameter and return value it works out which objects flow where and which of them share state. From that it derives what each method modifies and what is only read: the difference that decides what can safely move when a codebase is restructured.

Because it is open source, the core of the engine’s analysis can be read, run and checked by anyone. The details, from installation to the full list of options, are on GitHub and in the user manual.

Immutability

maddi computes levels of immutability.

Knowing what can change, and where, is what makes it safe to change code. An object nothing can modify can be shared, moved or reused without one part of the program altering state another part relies on.

Real code is rarely deeply immutable. A yes/no answer would throw away everything useful, so maddi gives each type a level. One line in this class moves it from one level to the next.

The caller keeps the map

@FinalFields @Container
public final class Config {
    private final Map<String, String> settings;

    public Config(Map<String, String> settings) {
        this.settings = settings;
    }

    public String get(String key) {
        return settings.get(key);
    }
}

The field is final and nothing in the class modifies it. The caller still holds the map it passed in, so anyone outside can change this object’s state after it is built.

The class copies the map

@Immutable(hc=true) @Container
public final class Config {
    private final Map<String, String> settings;

    public Config(Map<String, String> settings) {
        this.settings = Map.copyOf(settings);
    }

    public String get(String key) {
        return settings.get(key);
    }
}

After the copy, the only link left runs to the map’s elements. hc=true (hidden content) records that those elements are of a type Config cannot see into.

Four levels

@Mutable, @FinalFields, @Immutable(hc=true) and @Immutable. The reason a type stopped where it did is exactly what you would have to change to take it further.

Derived from the code

You write none of these annotations. maddi derives them for every type, method and field, and where you do write one yourself, it checks it against what it computed.

Eventual immutability

@Mark, @Only and @Immutable(after=…) describe the builder and freeze patterns: types that are mutable while being constructed, and immutable ever after.

Kotlin

Kotlin is analysed the same way as Java.

maddi reads Kotlin through K2, the Kotlin compiler’s own front end. From there on, Kotlin and Java share one syntax tree and one analysis, with no Kotlin-specific paths. Paired Kotlin and Java test cases check that both get the same verdicts.

A project that mixes the two languages is analysed as one program, so references from Java into Kotlin and back are part of the same analysis. In 0.9.5 Kotlin is close to parity with Java: constructs such as delegated properties, operator overloads and the scope functions apply, let and with are now handled correctly. The maddi-kotlin distribution takes the same command line as maddi.

Where it stands

How mature each part is in 0.9.5.

0.9.5 was released on 24 October 2026.

PartState
Concepts, and the bookStable
Java front endRobust, on JDK 27. Run against many open-source projects and one closed-source codebase of about 3M lines
Kotlin front endClose to parity with Java. Mixed Java and Kotlin projects are analysed as one program
Modification & immutability analysisSettles on the same verified answer every time, on all our test projects: Timefold, LangChain4j, Fernflower, Guava, ActiveMQ, Jenkins, Camel
NullabilityNew in 0.9.5
Translation to Kotlin and C#New in 0.9.5; C# is experimental
Gradle and Maven plugins, command linePublished

The analyzer is stable on everything we run it on. Your codebase will contain Java that ours do not. If maddi mis-reads your code, crashes, or works something out you can argue is wrong, that is exactly the report we want: please open an issue. What changed in this release is in the release notes.

Install

Run it from Gradle, Maven or the command line.

maddi needs JDK 25 or later to run; the Kotlin distribution needs JDK 26 or later.

Gradle plugin

plugins {
    java
    id("io.codelaser.maddi.analyzer") version "0.9.5"
}

Command line

./gradlew :your-module:compileJava --debug 2>&1 \
    | grep 'Compiler arguments:' > build.log
maddi --compile-log build.log \
      --analysis-steps modification --analysis-results-dir out

The Gradle plugin is io.codelaser.maddi.analyzer on the Gradle Plugin Portal. The Maven plugin is io.codelaser:maddi-mvnplugin on Maven Central. For any other build system there are two self-contained zips on GitHub Releases: maddi for Java, and maddi-kotlin, which adds Kotlin. The user manual covers configuration and how to point the command line at your build.

Licence

Open source under LGPL-3.0.

The analyzer is LGPL-3.0. The annotations (maddi-annotation, on Maven Central) are Apache-2.0, so your own code can depend on them without taking on the analyzer’s licence. maddi started in 2019 as e2immu and is developed by Bart Naudts at CodeLaser.

© 2026 CodeLaser BV · Blauwe Gaanweg 48, 9150 Beveren-Kruibeke-Zwijndrecht, Belgium · Enterprise no. BE 0797.734.730 · maddi is LGPL-3.0 · annotations Apache-2.0 · Privacy