What it is
Computed, not declared.
maddi is a static analyzer that works across the whole
codebase, for Java — and for Kotlin, through a shared syntax tree. You don’t write annotations:
maddi computes them. It reads your sources and your classpath,
follows how objects flow between fields, parameters and return values, and derives
@Immutable, @Container, @Modified and
@Independent for every type, method and field.
You write
public class Range {
private final int lo;
private final int hi;
}maddi computes
@Immutable
@Container
public class Range {
private final int lo;
private final int hi;
}Nothing in your source has to change. maddi derives the annotations — and where you do write them yourself, it checks them against what it computed.
How it reports
How far each type got.
Real code is rarely deeply immutable, and a yes/no answer throws away everything useful. maddi reports how far a type got, and what stopped it going further.
Every type lands on one of four levels. Which one it reaches tells you how far you got — and the reason it stopped there is exactly what you would have to change to go further.
hc=true records the one catch: a link runs to the elements of a type parameter maddi cannot see into. It is not hc-free @Immutable, and the analyzer does not pretend otherwise.
@Mark, @Only and @Immutable(after=…) describe the builder and freeze patterns: types that are mutable while being constructed, and immutable ever after.
In your own code
Use the annotations without adopting the analyzer.
The one thing your own code compiles against is the annotations library. It is on Maven Central, targets Java 17, and is Apache-2.0 — so you can depend on it without taking on the analyzer’s LGPL.
Gradle
implementation("io.codelaser:maddi-annotation:0.9.1")Maven
<dependency> <groupId>io.codelaser</groupId> <artifactId>maddi-annotation</artifactId> <version>0.9.1</version> </dependency>
That gives you @Immutable, @Container,
@Independent, @Modified and friends, with no
dependencies at all — useful as documentation and as contracts on your
interfaces even before you run the analyzer, since maddi verifies them against
what it computes.
If you also want the eventually-final support classes — SetOnce,
Freezable, EventuallyFinal, Lazy and
FirstThen — depend on io.codelaser:maddi-support
instead. It declares exactly one dependency, on maddi-annotation, and
re-exports it. Versions up to 0.8.2 were LGPL-3.0;
0.9.0 onward is Apache-2.0.
Where it stands
Status — August 2026
0.9.1 is the first release where
every part is installable: the Gradle plugin on the Plugin Portal, the Maven plugin
and the annotations on Maven Central, and self-contained command-line distributions
on GitHub Releases.
| Part | State |
|---|---|
| Concepts, and the book | Stable |
| Parser / resolver (javac front end) | Solid; run against many open-source projects and one closed-source 3M-line codebase |
| Modification & immutability analysis | Settles on the same verified answer every time, on all our test projects — Timefold, LangChain4j, Fernflower, Guava, ActiveMQ, Jenkins, Camel |
| Gradle / Maven plugins | Published in 0.9.1; both tested against a set of real multi-module builds |
| Kotlin front end | Works; ships only via the maddi-kotlin command-line download |
The analyzer is stable on everything we run it on — and 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.
Where it came from
From e2immu to maddi.
maddi is e2immu, renamed and
extended. The analyzer started in 2019 under that name, and has since grown to cover
Kotlin as well as Java. The root Java package was org.e2immu.*; it
became io.codelaser.maddi.* in 0.9.1.
maddi is developed by Bart Naudts at CodeLaser, and is and will remain open
source. The analyzer is LGPL-3.0; the annotations
— the only artifacts your own code compiles against — are Apache-2.0 from
0.9.0 onward. CodeLaser’s commercial engine is built on this
analyzer; the analyzer stays here, under this licence.