candor · for the JVM · case studies
Real code.
No configuration.
Five runs of candor-java against real, third-party JVM projects — no config, no
annotations, no source changes. Each is candor <compiled-classes> plus, where shown,
a short arch.policy wired to fail the build. The point of the set is breadth: two Spring
styles, a Kotlin app, a non-Spring framework, a library supply-chain audit, and what candor does
at the edge of what it models.
Every study is reproducible. Build the project (JDK 17), then:
01 RealWorld “Conduit” — Spring, DDD + MyBatis
A genuine layered Spring Boot app (93 Java files; api /
application / core / infrastructure / graphql).
candor derived the layer map from bytecode: Db is 100% contained in
infrastructure, and the DDD domain (io.spring.core) performs
zero Db/Net/Fs/Exec.
The gate then bit on a real architecture decision: all five aggregate roots mint
their own IDs with UUID.randomUUID(), which a team practising strict DI would forbid.
deny Rand io.spring.core # domain entities must not self-generate IDs exit 1 — all five aggregate-root sites pinned
candor also named the 13 third-party packages it doesn’t model as invisible rather than “no effect”: disclosure over silence, even on a clean run.
02 Spring PetClinic — the smell review missed
The canonical Spring sample resolves cleanly — 47 functions,
Db ×21 + Clock ×6, zero Unknown —
and candor correctly attributes Db to the Spring Data repository interfaces,
whose implementations only exist at runtime. The interesting result is a real
cross-layer smell the gate catches:
# arch.policy deny Db org.springframework.samples.petclinic.owner.PetTypeFormatter [AS-EFF-006] PetTypeFormatter.parse performs { Db }, forbidden by policy (exit 1)
PetTypeFormatter is a presentation-layer Spring
Formatter, yet parse reaches the database directly. It looks up pet
types to convert a form field. Exactly the layering drift an architecture gate exists to catch:
surfaced from bytecode, pinned to the method, with no annotations.
03 PetClinic in Kotlin — language-agnostic
The same app rebuilt in Kotlin. candor reads the compiled classes, so the source
language is irrelevant: 47 functions, Db ×21, the same
repository→controller shape, Db 80% contained in the owner layer. The
architecture signal survives the language change because the analysis is on bytecode, not source.
One engine covers Java, Kotlin, Scala and Groovy.
04 Quarkus + Jakarta Data — not Spring
The Hibernate ORM / Jakarta Data quickstart — deliberately not Spring.
FruitRepository is a jakarta.data.repository.CrudRepository interface, and the
REST endpoints go through it into Hibernate’s Jakarta-Data-era API. candor reads that boundary and
Db lands on all five endpoints, fully contained:
FruitResource.create inferred=[Db] invisible=[org.hibernate.exception] FruitResource.delete inferred=[Db] invisible=[org.hibernate.query.criteria] FruitResource.get inferred=[Db] invisible=[…criteria, …specification] FruitResource.getSingle inferred=[Db] invisible=[] FruitResource.update inferred=[Db] invisible=[] candor containment Db 100% 1 layer
Two things make this the most telling study of the set. The analysis crosses frameworks:
the same engine that maps Spring Data and MyBatis models the Jakarta Data /
Hibernate 6 persistence path, so Db lands without a single annotation. And look at the
residual invisible: the pure criteria-builder packages candor doesn’t model are still
disclosed, not silently dropped. candor never reports “pure” for a call it
hasn’t actually understood, so a gate built on it is never silently wrong, even at the edge
of what it models.
This study drove a real engine change: an earlier run disclosed the whole
org.hibernate surface as invisible, and that disclosure became the worklist that
added the Hibernate 6 / Jakarta Data model to the classifier. A blind spot the tool
names is a blind spot that gets fixed.
05 gson — a supply-chain audit
“What does this dependency actually touch?” candor scanned the gson 2.11.0
jar: 386 functions, exactly one Net —
com.google.gson.internal.bind.TypeAdapters$23.read → Net
javap confirms it: that class is
TypeAdapter<java.net.InetAddress>, and read calls
InetAddress.getByName — a DNS lookup performed while deserializing JSON. A
real, non-obvious network touch in a library most people file under “pure parsing”, found
with zero fabrication. That is the supply-chain question candor answers directly: not “is this
library popular” but “what can it reach”.
06 Run it on your repo
The adopt starter is three steps to the gate on every push: a one-command scaffold proposes a policy from what your code already does, and the GitHub Action surfaces violations inline on the PR diff and in the Security tab. A live demo repo shows the whole loop, alert and all.
Add the CI gate Full write-ups →
Disclosure, not a completeness proof: candor catches more than
review, and tells you where it’s unsure (an explicit Unknown) rather than guessing.
Work with the team behind candor
candor is built and run by Polymorphism — a small senior team that designs, builds and runs large systems for enterprise and government. The judgement we put into AI-code safety here is what we’d bring to your platform.