Developer-experience evaluation: pilot-boot (Phase 4, first pass)¶
App: examples/pilot-boot. Spring Boot 3.5.16, Spring Data JPA + H2, web, 7 components/entities.
Strict mode. It is a small synthetic app, not a third-party codebase; treat results as a first signal only.
Adoption burden¶
| Metric | Result |
|---|---|
| Business-code changes | 0 |
| New annotations in application code | 0 |
| Handwritten configuration | 1 processor dependency pair + 2 compiler options + 2 boundary declarations (was 3 before repository recognition) |
| Boundaries needed | java.time.Clock (from an uninterpreted @Configuration), ObjectMapper (Boot). OrderRepository is recognized automatically. |
| Duplicate Boot configuration | None |
| Statically validated | 2 of 6 dependencies (33%) |
Validated by the @SpringBootTest context test |
The 3 boundaries (context starts; 1 test passes) |
False positives found¶
| # | Finding | Action |
|---|---|---|
| 1 | @Value("${...}") String reported as SG001 missing java.lang.String, with a fix suggesting String be declared a boundary |
Fixed. @Value parameters are now classified as non-bean values. Regression test added (valueInjectionIsNotAMissingBean). |
| 2 | @Profile-switched implementations of one interface reported as SG007 ambiguity (7 in spring-petclinic-rest) | Fixed. Conditional candidates are never resolved; dependency becomes SG006 or a declared boundary. 5 regression tests. See petclinic-rest.md. |
| 3 | Two candidates where the parameter name equals one bean name: SG007, though Spring starts when compiled with -parameters | Fixed. Now SG006 with a @Qualifier suggestion. Found by comparing against a real Spring context in maven-lombok.md. 3 regression tests. |
Two further apps: petclinic.md (controllers + repositories, 0 false positives) and petclinic-rest.md (service layer, 1 false-positive class found and fixed, 8 boundaries needed, 58% validated).
Friction observed (not fixed)¶
- Low coverage on infrastructure-heavy code. In this app most edges are boundaries, so strict mode validates only the business-to-business edges. That is the honest result, but the guarantee is small.
- Boundaries for beans declared in the same module.
Clockis defined by@BeaninAppConfig, yet must be declared a boundary because@Configurationis not interpreted. Limited@Beananalysis (Phase 7) would remove this one. - Spring Data repositories: resolved by a decision (see graph-boundaries.md). They are recognized as a distinct kind and never counted as validated. Only checked against one repository in one app.
- Boundary declarations are stringly-typed FQNs in build files. Unused, misspelled, and shadowed entries are now reported as SG009
(see
graph-boundaries.md). The check immediately found a staleObjectMapperentry in this project's ownsample-basic.
Seeded mistakes (plan scenarios 1-3)¶
Run by a local script (3 repetitions per scenario, warm daemon; raw data in
seeded-mistakes-raw.csv, messages in seeded-mistakes-messages.md). Each mistake is added to the pilot as a
temporary source file and removed afterwards.
| Scenario | SpringGraph: compileJava fails |
Baseline: gradlew test (compile + Boot context test) fails |
Detected |
|---|---|---|---|
| Missing implementation | ~2.9-3.7 s (SG001) | ~11.3-11.6 s (NoSuchBeanDefinitionException) |
3/3 both |
| Second implementation (ambiguity) | ~2.8 s (SG007) | ~11.2-12.4 s (NoUniqueBeanDefinitionException) |
3/3 both |
| Constructor cycle | ~2.9-3.7 s (SG003) | ~11.3-12.4 s (BeanCurrentlyInCreationException) |
3/3 both |
Medians over three reruns of the whole script (3 reps each) varied by up to about 0.9 s for SpringGraph and 1.2 s for the baseline.
What this shows:
- Feedback arrives about 3-4x sooner (about 3 s vs about 11 s). Most of the baseline time is Gradle's test task plus starting a Spring Boot context, so
the gap grows with application startup time and shrinks if the developer already runs a fast slice test.
- The baseline only catches these because a @SpringBootTest exists. Without one, the mistake surfaces when the app starts.
- Message quality is comparable on content. Spring already names the missing type and lists ambiguous candidates.
SpringGraph adds the source location, the dependency path from a root component, and fix suggestions. The cycle comparison
used the root exception from the test (BeanCurrentlyInCreationException), which does not list the cycle; Spring Boot's
startup failure analysis does show the beans in a cycle, but only at startup. SpringGraph shows the cycle at compile time
(see SpringGraphProcessorTest).
- All 9 detections per side were correct and there were no false positives in these runs.
Not measured yet¶
- Time to understand and fix a diagnostic (needs other people).
- Scenarios 4-7 from the plan on this app (Boot-provided bean was exercised; repository was exercised; cross-module refactor and conditional configuration were not).
- Incremental build behavior; multi-module; conditional configuration.
- Only wiring mistakes the processor supports were seeded. Mistakes in unsupported areas (qualifiers, conditions) are not caught and were not tested for being missed.
Reading against the success criteria¶
- No business-code changes: met on this app.
- Useful diagnostics: demonstrated on unit tests and one real false-positive fix; not yet on seeded mistakes in the pilot.
- No duplicate provider methods: met (boundaries only).
- Boundaries visible and not misrepresented: met (report labels them
[not statically validated]). - Build overhead: see
performance.md; negligible here, not yet shown on a large app. - No regular rejection of valid apps: one false positive found in the first valid app; fixed. Sample size of one.