Skills for creating, testing, reviewing, migrating and guarding Spring Boot 4 projects with Java 25: project scaffolding with architecture patterns, Boot 4 test templates, code review, Boot 3 to 4 migration, and a team harness of build gates and agent hooks.
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Progen is an offline CLI that generates a Spring Boot 4 project: build file, Docker Compose, Testcontainers, security, migrations, CI, and ArchUnit/Modulith tests. Use it for the base project. Then add the architecture layer from this skill on top.
Progen has no architecture option. Its layout is always package-by-module with Spring Modulith (config/, shared/, users/).
Tested with progen v1.0.4 on 2026-10-31 (REST, Maven, PostgreSQL). Progen pins Spring Boot 4.1.2 and Java 25. v1.0.4 (2026-10-24) changed ArchUnitTests and has not been tested here.
Use progen when all of these are true:
command -v progen finds it, or the user agrees to install it.users module. Progen always generates it.Otherwise, use Spring Initializr (Step 3 of SKILL.md).
Install: go install github.com/sivaprasadreddy/progen@latest, or download a binary from the releases page. On macOS the binary is unsigned. The user must allow it in Finder (Control-click, Open).
./<AppName>/ before it generates. Never run it where that directory exists. The helper script refuses to run in that case.PATH. On JDK 27 the formatting step always fails with NoSuchFieldError ... JCCompilationUnit: Spotless 3.10.4 (progen pins 3.10.0) fixes its unused-imports step, but palantir-java-format fails on JDK 27 in every version up to 2.102.0 (checked 2026-10-21). The project then doesn't compile, because the step that failed is the one that removes an unused AntPathRequestMatcher import (Spring Security 7 removed the class) from config/WebSecurityConfig.java. The helper script stops with exit code 6 on a JDK newer than 25. If you're stuck on JDK 27: pass --ignore-jdk-check, delete that import and build with -Dspotless.check.skip=true. On JDK 25 progen's output passes ./mvnw verify as generated (10 tests).spotless:apply (Maven) or spotlessApply (Gradle) at the end. This needs Java on PATH and network access on the first run. If only that step fails, progen prints "failed to format project code" and exits with 1, but the files exist; the script reports this as a warning. Any other progen failure stops the script with exit code 4.| Assessment answer | Script option | Progen value |
|---|---|---|
| JSON API | --app-type rest (default) | REST API |
| Server-rendered UI | --app-type web | Web App (Thymeleaf is on) |
| Angular SPA | --app-type angular | Spring Boot + Angular Full Stack |
| Maven / Gradle | --build-tool maven|gradle | Maven / Gradle |
| JPA (all architectures in this skill) | --persistence jpa (default) | Spring Data JPA |
| SQL-first, user asks for it | --persistence jdbc|jooq | Spring JdbcClient / jOOQ |
| Database | --db postgresql|mysql|mariadb | PostgreSQL / MySQL / MariaDB |
| Migrations | --migration flyway|liquibase | Flyway / Liquibase |
| Optional features | --features aws,email,rabbitmq,redis,k8s,htmx | *Support: true |
| Java other than 25 | --java 21 | Script edits the build file after generation |
Rules:
--persistence jpa. The templates in assets/ assume JPA.jdbc or jooq, do not apply JPA templates (assets/rich-entity.java, assets/repository.java).htmx needs --app-type web.otel. Step 6 of SKILL.md runs the harness, whose observability pack replaces it.java-version-policy.md before you pass --java above 25.${CLAUDE_SKILL_DIR} is the creating-springboot-projects folder. Codex doesn't set it, so there use that folder's path.
python3 "${CLAUDE_SKILL_DIR}/scripts/progen_config.py" \
--app-name shop --base-package com.acme.shop \
--build-tool maven --db postgresql --features redis \
--out-dir /path/to/parent --run--run, the script only writes and prints .progen.json. Show it to the user before you generate.<out-dir>/<app-name>/.JAVA_HOME, else java on PATH) is newer than 25.| Architecture | What to do after generation |
|---|---|
package-by-module | Matches progen. Add feature packages next to users/. |
modular-monolith | Matches progen. Add *API.java and internal/ per module. ModularityTests.java exists; do not add assets/modularity-test.java again. Copy only extra checks from it. |
tomato | Add domain/vo/, rich entities, and rest/converters/ per module. Apply assets/value-object.java, assets/rich-entity.java, assets/spring-converter.java, assets/service-cqrs.java. |
ddd-hexagonal | Add application/, domain/, infra/, interfaces/ per module. On progen v1.0.3, change ArchUnitTests.java as shown below, because *Handler services fail the name rule. |
layered | Add controller/, service/, repository/, domain/. The tests pass, but Modulith treats each layer as a module, which adds no value. Keep users/ as the one feature package, and tell the user. |
| Skip this asset | Progen file |
|---|---|
assets/exception-handler.java | config/GlobalExceptionHandler.java. Add a DomainException handler there if needed. |
assets/base-entity.java | shared/BaseEntity.java (has @Version) |
assets/docker-compose.yml | compose.yml |
assets/testcontainers-test.java | TestcontainersConfig.java, BaseIT.java |
assets/modularity-test.java | ModularityTests.java |
assets/pom-additions.xml | Add only entries the pom does not have: jspecify, hypersistence-utils-* (TSID IDs), spring-modulith-starter-jpa (event registry). |
Keep src/test/java/<base>/ArchUnitTests.java as progen generates it. The harness (Step 6 of SKILL.md) adds CodeRulesTests next to it, with the runtime-trap rules progen doesn't have. Don't copy rules from one file into the other.
What ArchUnitTests checks depends on the progen version:
Controller, Service, Repository, Config), package-private controllers and test classes, the LOG logger convention, no generic throws Exception, no import cycles, and layer-direction and @Disabled rules.domain to web packages, package-private repositories and JPA entities, and @Service classes used only from their own module. Check that its BASE_PACKAGE constant is the project's base package. In the template it is a fixed dev.sivalabs.feedbackhub, which matches no other project.Change a rule only when it conflicts with the chosen architecture:
ddd-hexagonal on v1.0.3: services may be command handlers, so replace namesShouldEndWithService() with namesShouldMatch(".+(Service|Handler)").namesShouldEndWithRepository() with namesShouldMatch(".+Repository(Impl)?").layered on v1.0.4: the module rule reads the first package under the base as the module, so each layer counts as one. Remove services_must_not_be_used_from_other_modules.V001__create_users_table.sql. Start new Flyway files at V002__. Flyway reads V1 and V001 as the same version and fails.spring.modulith.events.republish-outstanding-events-on-restart=true and spring.modulith.events.jdbc.schema-initialization.enabled=true. Spring Modulith advises against both for production: republishing duplicates events when several instances run, and the table should come from a Flyway migration. Tell the user before production use.spring.threads.virtual.enabled=true. This skill enables virtual threads only with workload evidence. Tell the user, and keep or remove it based on their answer../mvnw test # or ./gradlew test; Testcontainers needs DockerRun ArchUnitTests and ModularityTests first. They run without Docker and catch layout errors quickly.
scripts/align_progen_project.py adjusts a new project to the harness rules. progen_config.py --run runs it after generating. When you run progen another way, run it yourself before the harness.
| File | Change | Why |
|---|---|---|
users/AuthUtils, users/TokenHelper | Marks three methods that can return null as @Nullable | NullAway in all mode |
config/GlobalExceptionHandler | Returns a generic detail from the catch-all handler | CodeRulesTests.catchAllHandlersKeepTheExceptionServerSide |
config/GlobalExceptionHandler | Leaves the logging of failures to HandledFailureFilter | Each 5xx is logged once |
users/AuthController, users/UserRestController | Removes the email address from log lines | LogLeakCheck |
Each edit matches exact generated text. If the script prints skipped, the generated code differs: check it against these rules, then update the script. Checked on progen v1.0.4 with REST, Maven, JPA and PostgreSQL.