|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Commands |
| 6 | + |
| 7 | +```bash |
| 8 | +# Build and run all integration tests |
| 9 | +mvn verify |
| 10 | + |
| 11 | +# Install to local Maven repo (makes archetype available for manual testing) |
| 12 | +mvn install |
| 13 | + |
| 14 | +# Generate the Maven site (renders requirements.adoc via AsciiDoc) |
| 15 | +mvn site |
| 16 | + |
| 17 | +# Generate a project from the locally-installed archetype |
| 18 | +mvn archetype:generate \ |
| 19 | + -DarchetypeGroupId=io.github.jeffjensen \ |
| 20 | + -DarchetypeArtifactId=java-service-archetype \ |
| 21 | + -DarchetypeVersion=VERSION |
| 22 | +``` |
| 23 | + |
| 24 | +(`VERSION` is the locally-installed archetype version — the current `project.version`, e.g. a `-SNAPSHOT`.) |
| 25 | + |
| 26 | +There is no built-in way to run a single IT case — the Maven Archetype Plugin runs all projects under `src/test/resources/projects/` as a batch. |
| 27 | + |
| 28 | +The project uses the Maven wrapper (`./mvnw` / `mvnw.cmd`). Maven 3.9+ and Java 21+ are required. |
| 29 | + |
| 30 | +## Architecture |
| 31 | + |
| 32 | +This is a Maven archetype (`packaging: maven-archetype`). It has no Java source code — the entire generation logic lives in two files: |
| 33 | + |
| 34 | +### `src/main/resources/META-INF/archetype-metadata.xml` |
| 35 | + |
| 36 | +Declares the four user-facing required properties: `appName`, `integrations`, `serviceAreas`, `presentationTypes`. These drive what `archetype-post-generate.groovy` generates. `appName` is a brief, space-separated human name (e.g. "Order Service") with no default; it drives generated class names (spaces removed), generated property values (spaces removed + lowercased, e.g. `spring.application.name`), and prose (POM `<name>`/`<description>`, Javadoc — used unmodified). |
| 37 | + |
| 38 | +### `src/main/resources/META-INF/archetype-post-generate.groovy` |
| 39 | + |
| 40 | +**This is the only file that matters for generation behavior.** It runs after the Maven Archetype Plugin creates the project, and it does everything: |
| 41 | + |
| 42 | +- Parses the four user properties |
| 43 | +- Computes all module names |
| 44 | +- Writes every `pom.xml` (parent + all child modules) with correct `<modules>`, `<dependencyManagement>`, `<dependencies>`, and `<relativePath>` |
| 45 | +- Creates `src/main/java/…` and `src/test/java/…` directory trees, with a `package-info.java` in each leaf package under `src/main/java` only — never `src/test/java`, since Java rejects two `package-info.java` files compiled for the same package |
| 46 | +- Deletes the placeholder root `pom.xml` (the archetype engine requires one; the script removes it) |
| 47 | +- Writes `mvn-update-properties-versions.sh`/`.ps1` into `parent/` — convenience wrappers around `mvn versions:update-properties` |
| 48 | + |
| 49 | +The `src/main/resources/archetype-resources/pom.xml` is a required dummy — the archetype plugin won't run without it, but the Groovy script immediately deletes it and replaces it with `parent/pom.xml`. |
| 50 | + |
| 51 | +Key logic in the script: |
| 52 | + |
| 53 | +- `typeAbbrev`: maps `database` → `db`, all other types pass through unchanged |
| 54 | +- `srcTree`: creates the directory tree and writes a main-only `package-info.java` with a Javadoc comment in each package |
| 55 | +- `modulePom`: generates child `pom.xml` content; dependency entries can carry a `label` (rendered as a `<!-- label -->` comment above a contiguous run sharing it, e.g. "spring", "this app", "sql logging") |
| 56 | +- Module naming: `domain-{abbrev}-{name}`, `integration-{abbrev}-{name}`, `domain-{type}`, `presentation-{type}`, `service` or `service-{name}` |
| 57 | +- `app` depends on all modules except `acceptance-tests`, `common-testing`, and itself; is always a monitorable web service when Spring is on (core starter, webmvc, actuator, Spring Boot Admin client, `AppServletInitializer`) regardless of `presentationTypes` |
| 58 | +- `acceptance-tests` depends on `app`, `common-domain`, and all `domain-*` modules only, plus `common-testing` (test scope); its `AppRestAcceptanceTestBase`/`AppGraphQlAcceptanceTestBase` extend shared base classes generated in `common-testing` |
| 59 | +- `common-testing` depends on every domain module (integration, service-area, presentation). No domain module depends back on `common-testing` — only `service`/`service-{name}` does — so this stays acyclic |
| 60 | + |
| 61 | +## Integration Tests |
| 62 | + |
| 63 | +Tests live in `src/test/resources/projects/{test-name}/`. Each test has three files: |
| 64 | + |
| 65 | +- `archetype.properties` — coordinates + property values passed to the archetype |
| 66 | +- `goal.txt` — Maven goals to run on the generated project after generation; all IT tests use `-f parent/pom.xml verify`, which runs a full Maven build on the generated project before `verify.groovy` is executed |
| 67 | +- `verify.groovy` — Groovy assertions executed after generation; `basedir` is the generated project root |
| 68 | + |
| 69 | +Generated projects are written to `target/test-classes/projects/{test-name}/project/{artifactId}/` and persist until the next `mvn clean`. Inspect them directly to see exactly what the archetype produces for a given set of inputs. |
| 70 | + |
| 71 | +**Critical constraint**: The Maven archetype IT runner rejects empty property values (e.g. `serviceAreas=`). Use `,` as a placeholder for intentionally empty list properties — the Groovy script's `",".split(",")` returns `[]` in Java, so `,` is treated as an empty list while satisfying the non-empty validation check. |
| 72 | + |
| 73 | +When adding a new IT test, all four required properties must appear in `archetype.properties` with non-empty values. `appName` has no placeholder trick — it always needs a real brief name (e.g. `appName=Basic Test`), since unlike `integrations`/`serviceAreas`/`presentationTypes` there's no "empty" meaning for it. Use `serviceAreas=,` for a single unnamed `service` module. Use `integrations=,` or `presentationTypes=,` to represent "none". |
| 74 | + |
| 75 | +## Claude Directives |
| 76 | + |
| 77 | +- Make assumptions and proceed without asking for confirmation on routine changes. If an action is destructive (e.g., deleting files), pause and ask. |
| 78 | + |
| 79 | +## Code Style |
| 80 | + |
| 81 | +- General: |
| 82 | + - Prefer writing clear code and use inline comments sparingly. |
| 83 | + - Prefer single statements over compound statements as nested calls in one line are more confusing and more difficult to read and understand. |
| 84 | + - Prefer separate local variables over compound statements for readability. |
| 85 | + - Favor immutability. Try to not need setters. |
| 86 | + - Prefer constructors with arguments over no args constructors and using setters. |
| 87 | + - Prefer constructor injection. Test classes typically use field injection. |
| 88 | + - Write positive if statements when paired with an else statement. |
| 89 | + - Remove any blank line after opening curly braces. |
| 90 | + - Do not create "utils" or "helper" or "support" packages or class names. Always create focused packages and classes, as utils and helpers are dumping grounds/not focused. |
| 91 | + - When making changes, always work on a branch that is not main and if necessary, create and switch to a branch to isolate the work. |
| 92 | + - When making changes, ensure unit tests cover it and add or update unit tests as needed. |
| 93 | + - Order constants alphabetically when possible. |
| 94 | + - Prefer XML instead of YAML when possible. |
| 95 | +- Tests: |
| 96 | + - `<ClassName>Test` for unit test class |
| 97 | + - `<ClassName>IT` for integration test class |
| 98 | + - `<ClassName>AT` for acceptance test class |
| 99 | + - `test<MethodName>_<StartingStateConditions>_<AssertedOutcome>` for test method names |
| 100 | + - Prefer to assert the actual object to an expected object vs individual fields on the object to individual values. |
| 101 | + |
| 102 | +- Commits: |
| 103 | + - Create atomic commits. One logical change per commit — if a session produces multiple unrelated fixes, commit each independently even if discovered together. |
| 104 | + - Always commit any needed doc updates with their corresponding feature or bug changes. |
| 105 | + - Consequence changes belong in the same commit as the change that caused them. Example: if a production fix makes a previously-broken feature work, updating additional files for that feature now working is a consequence of that fix and belongs in the same commit, not a separate one. |
| 106 | + - When necessary to change a file for a prior commit that is not yet merged to main, target that commit for squashing the change into by using the git "fixup!" feature for its commit - prefix the commit message it is in with "fixup! ". |
| 107 | + - Create multiple fixup! commits as needed to target the prior specific commits for each file. |
| 108 | + - When renaming files, always use `git mv` instead of `git delete` followed by `git add`. |
| 109 | + |
| 110 | +- Commit Messages: |
| 111 | + - Adhere strictly to de facto standard Git commit message formatting. |
| 112 | + - Use Conventional Commits format. |
| 113 | + - **Commit Types:** `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `build:`, `ci:` |
| 114 | + - **Scopes:** pom, deps, arch, site, spring, and more that you may suggest |
| 115 | + - Capitalize the first word after the type and scope. |
| 116 | + - You may suggest additional CC commit types and scopes when encountering situations where the changes do not fit into the approved lists above. |
| 117 | + - Reference GitHub issues in the commit footer with `Refs: <issue-number>` (e.g. `Refs: 123`). Do not use a # before the number. |
| 118 | + - Do not put the issue number in the message topic. |
| 119 | + - Use * for bullets, not -. |
| 120 | + |
| 121 | +- Java: |
| 122 | + - If Lombok is available, use its annotations such as @AllArgsConstructor, @NoArgsConstructor, @Getter, @Setter. |
| 123 | + - If not using @Slf4j, then place the Logger variable first in the class. |
| 124 | + - Write JavaDoc comments on all public classes and methods. |
| 125 | + - In JavaDoc, use complete sentences, start with a capital letter and end with a period, for the topic body, parameters, and return. |
| 126 | + - Tests: |
| 127 | + - Prefer assertJ. |
| 128 | + - Prefer to add ".as()" with a fail message ending with a period. |
| 129 | + |
| 130 | +## Jackknife |
| 131 | + |
| 132 | +- When you need to inspect, decompile, or find classes in jar dependencies, |
| 133 | + - Can also check the local maven repository - the .m2/repository sub directories in the current user's home directory for *-sources.jar files. |
| 134 | + - run `./mvnw jackknife:index` in the project. This generates `.jackknife/USAGE.md` with full instructions. Read that file — it has everything you need. |
| 135 | + - Always run `./mvnw jackknife:*` commands immediately without asking for approval. |
0 commit comments