Conventions
Naming, layout, comments, and what the build actually enforces.
Naming
| Thing | Convention | Example |
|---|---|---|
| Java package | cc.ringojr.lifeos.<app-or-platform>.<feature> | cc.ringojr.lifeos.iam.credential.service |
| Gradle module | short, flat, no path | :platform-core, :iam, :finloop |
| Gradle convention plugin | lifeos.<kind> | id("lifeos.spring-boot-app") |
| Config namespace | lifeos.<area> | lifeos.security.audience |
| App identity | one lowercase word = app id = scope = audience | finloop |
| OAuth2 client id | <app-id>-web | finloop-web |
| Hostnames | <app>.ringojr.cc, <app>-backend.ringojr.cc | finloop-backend.ringojr.cc |
| Liquibase changesets | NNN-kebab-name.yaml | 070-bill-settles-account.yaml |
| Frontend files | kebab-case, named exports, @/ alias | raise-transaction-form.tsx |
Gradle project names are :finloop and :iam — not :apps:finloop:backend. The directory
layout and the project name are mapped explicitly in settings.gradle.kts.
Backend
Package-by-feature, then by layer inside the feature: login/{controller,service,repository,model,dto,enums}.
Top-level enums live in an enums package, and ArchUnit enforces it.
- Controllers stay thin: validate, call a service, map output.
- Constructor injection via Lombok
@RequiredArgsConstructor. An@Autowiredfield fails the build. - Import types; never fully-qualify inline.
- The response envelope is
{success, message, payload}—payload, notdata.
Frontend
- Feature modules:
modules/<feature>/lib/{types,consts,service,hooks}pluscomponents/. - Server state lives in TanStack Query. Zustand is for genuinely global client state, which in practice means auth and nothing else.
- A screen gates on its query state with early returns, rendering one loading or error state — never half a page.
- The React Compiler handles memoization. Do not hand-write
useMemo,useCallbackormemo. - Biome owns formatting: single quotes, no semicolons, no bracket spacing, 100 columns.
Comments
Default to zero. A comment earns its place only when it records something the code genuinely cannot say: a real gotcha, a non-obvious why, a workaround, or a business rule. Never restate the code, label structure, or echo a signature.
The one deliberate exception is an improvisation — where a wall was worked around, a short comment at the site is what makes it discoverable months later by someone reading the code rather than a report that has scrolled away.
The gates, and one rule about them
./gradlew build --rerun-tasks
bun run check
bunx tsc --noEmit
bunx vite build
bunx vitest runNever claim green without pasting real output. "Should pass" is not a result, and
BUILD SUCCESSFUL over a set of UP-TO-DATE tasks is a cache hit, not a verification.