Files
trueref/CODE_STYLE.md
moze c5f950c2c0
Some checks failed
Build and publish Docker image / Build and push (push) Failing after 1m27s
Initial commit: trueref v0.1.0-SNAPSHOT
Java 21 / Spring Boot 3.5.3 multi-module Maven project.
Hybrid BM25+HNSW search with RRF, cross-encoder reranker,
ONNX Runtime 1.22.0 (CPU + CUDA 12 GPU variants).
2026-05-06 00:49:16 +02:00

92 lines
5.9 KiB
Markdown

# trueref — Code Style
## 1. Language & Toolchain
- **Java 21**, source/target 21.
- **Maven** with `spring-boot-maven-plugin` for the fat JAR.
- **Spotless** with **Palantir Java Format** for formatting (4-space indent, 120 col).
- **ErrorProne** + **NullAway** for static analysis. NullAway annotations: `@org.jspecify.annotations.Nullable` / `@NonNull`.
- Hexagonal boundaries are enforced by **Maven module dependencies** (no ArchUnit). See ARCHITECTURE §3.
## 2. Records, Sealed Types, Pattern Matching
- Prefer **records** for DTOs, value objects, and `port.in`/`port.out` parameter/result types.
- Use **sealed interfaces** for closed result/event hierarchies (`sealed interface IngestionEvent permits ...`).
- Use **pattern matching** (`switch` expressions, `instanceof`) over visitor pattern.
## 3. Nullability & Optional
- All API surfaces (public methods on ports, REST DTOs) are **non-null by default**; mark nullable explicitly with `@Nullable`.
- Use `Optional<T>` **only** as a return type from query-style methods. Never as a field, never as a parameter.
## 4. Concurrency
- Spawn virtual threads via `Thread.ofVirtual().start(...)` or `Executors.newVirtualThreadPerTaskExecutor()`. **Never** call `Thread.sleep` inside a synchronized block.
- Shared mutable state is forbidden in `domain` and discouraged in `application`. When unavoidable, use `java.util.concurrent.atomic.*` or a `ReentrantLock`.
- GPU work goes through `GpuSemaphore.acquire()` (a thin wrapper around `Semaphore`).
- Long-running orchestration uses **structured concurrency** (`StructuredTaskScope`) where it improves cancellation safety.
## 5. Error Handling
- **Domain errors** are sealed exception hierarchies rooted at `TrueRefException`. Adapters translate them to HTTP/JSON-RPC errors centrally (REST: `@ControllerAdvice`; MCP: dedicated translator).
- **No checked exceptions** at port boundaries. Wrap third-party checked exceptions at the adapter edge.
- Validation errors carry a stable `code` (string) so the UI can localize.
- Never `catch (Exception e)` and swallow. Either log + rethrow as a domain exception or let it propagate.
## 6. Logging
- **SLF4J** with parameterized messages: `log.info("indexed tag {} of repo {}", tag, repoName);` — never string-concatenate.
- Structured fields via MDC: `repoId`, `versionId`, `jobId`, `stage`. Cleared in a try/finally.
- Log levels:
- `ERROR`: unrecoverable, requires operator attention.
- `WARN`: degraded, automatic recovery in progress.
- `INFO`: lifecycle events (job started/finished, repo registered).
- `DEBUG`: per-file, per-chunk detail. Off by default.
## 7. Naming
- Use cases (`port.in`): imperative verb phrases — `IndexVersion`, `ResolveLibraryId`.
- SPIs (`port.out`): noun-ish role names — `EmbeddingService`, `ChunkStore`, `GitClient`.
- Adapter classes: `<Tech><Role>``LuceneChunkStore`, `OnnxEmbeddingService`, `JGitClient`.
- DTOs: `<Resource><Action>Request` / `<Resource>Response`.
- Records' field names are camelCase (no Hungarian, no `_` prefixes).
## 8. Package & File Discipline
- One public type per file.
- Internal helpers are package-private. Avoid `public` unless used across packages.
- Domain packages export **only** records and interfaces. No Spring annotations, no Lombok, no Jackson annotations.
- Adapter packages may use Spring stereotypes (`@Component`, `@Repository`, `@RestController`) but adapters depend on **port interfaces only** when interacting with the application.
## 9. Spring Wiring
- Wiring lives in `bootstrap`. Each adapter package may define a `@Configuration` (constructor-injected `@Bean` factories) but **does not** auto-`@ComponentScan` itself; bootstrap explicitly imports.
- Use `@ConfigurationProperties` records for typed config; never raw `@Value`.
- Prefer constructor injection. **No field injection.**
## 10. Persistence (H2)
- Migrations under `src/main/resources/db/migration` named `V<N>__<snake_case>.sql`. Flyway runs at startup.
- All access via Spring **`JdbcClient`** (Spring Boot 3.2+, fluent JDBC). **No JPA/Hibernate**, no `JdbcTemplate` directly.
- Mappers are explicit `RowMapper<T>` lambdas, not reflection-based.
- SQL lives next to the repository class, either as `static final String` constants or in `*.sql` files loaded via `ClassPathResource` for non-trivial queries.
## 11. REST
- Controllers in `adapter.in.rest`. They depend only on `port.in` interfaces and DTO records.
- DTOs are **separate** from domain records. Mapping via plain `static of(...)` factories. **No MapStruct.**
- All endpoints documented with `@Operation`, `@ApiResponses`, `@Schema` (springdoc).
- Request validation via `jakarta.validation` annotations on DTOs.
- SSE endpoints return `SseEmitter`; subscribe to `JobEventBus`, unsubscribe on completion/timeout.
## 12. MCP
- Tool definitions are **records** decorated to produce JSON Schema via Spring AI's MCP support. Schema strings stay verbatim (1:1 with Context7) so LLMs see identical contracts.
- Tool handlers depend only on `port.in` (`SearchLibraryDocs`, `ResolveLibraryId`).
## 13. Tests
- **JUnit 5** + **AssertJ** + **Mockito** (sparingly).
- Unit tests live next to the package they test. Integration tests under `src/test/java/.../it/` and use `@SpringBootTest`.
- Use **Testcontainers** only when truly required (we mostly avoid it via embedded stores).
- ArchUnit test suite is mandatory and runs in CI.
## 14. Dependency Hygiene
- BOM-managed versions only. Add a dependency only if it provides clear value over JDK + Spring Boot + already-included libs.
- No Lombok, no Guava (use JDK 21 equivalents), no Reactor (we use virtual threads + blocking).
- No Kotlin, no Scala.
## 15. Documentation
- All architectural decisions go in **ARCHITECTURE.md**.
- All research notes go in **FINDINGS.md** with sources.
- All conventions go in **this file**.
- Per-package `package-info.java` may exist for non-trivial packages, summarizing role and exported types.
- No README sprawl: `README.md` is a quickstart only and links to the three docs above.