Java Code Quality

A complete checklist to help you review Java code for correctness, security and performance, ensuring every pull request is production ready.

What We Cover in the Java Code Analysis​

Here’s what this checklist helps you review before approving a pull request.

Code & Design Quality – clear structure, modern Java/OOP patterns, solid module boundaries
Security & Performance – input validation, safe deserialization, secrets management, efficient queries, caching, scalability
Reliability & Operability – robust error handling, testing, observability, safe CI/CD rollouts
Data & Integration – APIs/contracts, transactions, safe migrations, multi-tenant isolation, clean configuration

Java Code Review Checklist

Export this Checklist as Markdown

Use these points as a starting guide to automate code reviews with your agent.

Names are descriptive and follow Java conventions (classes PascalCase, methods/fields camelCase, constants UPPER_SNAKE_CASE).

Methods are short, single-purpose, and have clear intent; no “God” classes or 1K-line files.

No commented-out code or dead code; TODOs are tracked in issues, not left in source.

Consistent formatting via an auto-formatter (e.g., google-java-format or IDE profile); no style bikeshedding in review.

Public APIs have Javadoc on classes, public methods, and non-obvious behavior; internal code has focused comments explaining why, not what.

Clear package structure (domain-oriented or hexagonal); no cyclic package dependencies.

Avoid unnecessary abbreviations; avoid wildcard imports; static imports used only for test/assertions.

Use enhanced switch/pattern matching where it improves clarity; prefer var locally only when it increases readability.

Stream/λ usage is readable; no deeply nested stream pipelines—extract steps with named methods when complex.

Avoid primitive obsession when a value object conveys intent; prefer record for simple immutable aggregates.

Favor immutability: fields final where possible; objects defensive-copy mutable inputs/collections.

Correctly implement equals/hashCode/toString for value types; prefer record to get these for free.

Respect encapsulation: minimal visibility (private > package-private > protected > public).

Program to interfaces; invert dependencies; no hard wiring of implementations inside domain logic.

Avoid inheritance for code reuse; prefer composition; abstract classes used only for shared behavior contracts.

No business logic in constructors; use factories/builders for complex construction.

Optional only for return values, not fields/params; do not call get() without presence checks.

Null-handling strategy is explicit; avoid returning null collections; use List.of()/Collections.emptyList().

Concurrency aware: avoid shared mutable state; prefer thread-safe structures or confinement.

Java 17+/21 features: sealed classes for closed hierarchies; record for DTOs; virtual threads (if adopted) used intentionally.

Inputs validated and sanitized at boundaries; use bean validation (jakarta.validation) for DTOs.

No SQL injection risks: parameterized queries or ORM with safe binding; no string concatenation in queries.

Sensitive data never logged; use structured logging with redaction; PII masked by default.

Secrets/keys/credentials loaded from secure store or env, never from source control; no secrets in config, tests, or Git history.

Cryptography via vetted libs; no custom crypto; strong algorithms and proper key sizes; secure random (SecureRandom).

Authentication/authorization enforced on all endpoints and background jobs; least-privilege for service accounts.

CSRF, CORS, and clickjacking protections configured; HTTP security headers set (HSTS, X-Content-Type-Options, etc.).

Deserialization is safe: disable default typing; allow-list types; prefer JSON-B/Jackson with strict config.

File uploads: validate content type/size; store outside web root; scan if needed.

Command execution and path handling: no shell injection; use ProcessBuilder; validate/normalize paths with Path API.

Dependencies scanned and up to date; no known CVEs; use BOMs and lockfiles; evaluate transitive risk.

Multi-tenant/data isolation rules enforced at every data access path.

TLS everywhere; verify certificates; enforce modern cipher suites on servers/clients.

Rate limiting, abuse detection, and audit trails present on sensitive operations.

Hot paths avoid unnecessary allocations; reuse buffers; prefer StringBuilder for concatenation in loops.

Collections chosen appropriately (ArrayList vs LinkedList, EnumSet/EnumMap, ConcurrentHashMap when needed).

Avoid N+1 queries; batching/fetch graphs configured; pagination/streaming for large datasets.

Caching strategy defined (keys, TTLs, invalidation); no unbounded in-memory maps; consider Cache/Caffeine.

I/O uses NIO where beneficial; proper timeouts on network calls; no blocking calls on reactive threads (if using reactive).

Parallelism used intentionally: executors sized correctly; CompletableFuture or virtual threads where appropriate.

No synchronized bottlenecks; fine-grained locks or lock-free structures; avoid contention on shared monitors.

Algorithmic complexity is acceptable; avoid quadratic behavior on large inputs; include rough Big-O awareness in reviews.

JSON/XML mappers configured for performance (afterburner, record modules) where safe; avoid reflection heavy paths in hot loops.

Avoid premature micro-optimizations; add microbenchmarks (JMH) for disputed hotspots.

Exceptions are meaningful and actionable; no swallowed exceptions; never catch Throwable unless rethrowing safely.

Use specific exception types; wrap and add context; avoid leaking internal details to clients.

Try-with-resources for all Closeables; streams/sockets/files are closed deterministically.

Retries use bounded attempts with backoff and jitter; only for idempotent operations; circuit breaker where needed.

Timeouts on all external calls (HTTP, DB, message broker); no infinite waits or blocking joins without limits.

Fallbacks are safe and degrade gracefully; no silent data corruption.

Domain errors modeled explicitly (e.g., result types) where it improves clarity over exceptions.

Error messages/logs include correlation/request IDs; avoid stack traces in user-facing responses.

Distinguish transient vs permanent errors; do not retry on validation/authz failures.

Unit tests (JUnit 5) cover core logic; fast and deterministic; meaningful assertions (no “assertTrue(true)”).

Property-based tests for critical pure functions and parsers; boundary conditions included.

Integration tests cover DB, message brokers, and HTTP edges; use Testcontainers or equivalent.

API/contract tests for external integrations; consider Pact/contract testing where coupling exists.

Repro of prod bugs has a test before the fix; tests fail without the fix.

Fixtures are minimal and readable; factories/builders for complex objects; avoid global mutable state.

Concurrency tests (where applicable) simulate contention/races; use @Timeout to prevent hangs.

Performance/regression tests for hotspots with guardrails (not flaky microbenchmarks in CI).

Code coverage is monitored, but quality > %; critical paths and error handling are covered.

Test names describe behavior; Given/When/Then or Arrange/Act/Assert structure.

Module boundaries are clear; no cross-layer leaks (e.g., controllers aware of ORM internals).

Domain logic isolated from frameworks; ports/adapters or hexagonal seams exist for I/O.

Public APIs stable with versioning; backward compatibility considered; deprecations documented.

Config via env/props with sane defaults; 12-factor principles; no environment-specific branches in code.

Observability: structured logs, metrics (RED/USE), and traces added at key points; no log spam.

Idempotency for handlers and consumers; deduplication where messages may be delivered at-least-once.

Backpressure respected (reactive or queue-based); no unbounded queues; rate limits where needed.

Data migrations are incremental, backward compatible, and include rollback plans.

Feature flags used for risky changes; dark launches and canaries available.

Horizontal scaling considered: stateless services or sticky requirements justified; session state externalized.

GC and memory settings sane for workload; no large object retention; leaks checked with tooling.

Documentation updated (README/ADR/Runbook) for new modules, configs, and ops procedures.

Patterns used to simplify not complicate: only when they reduce duplication or clarify variability.

Factory/Builder for complex object creation; no telescoping constructors.

Strategy/Policy to encapsulate variable behaviors; avoid if/else pyramids on type codes—prefer polymorphism or sealed hierarchies.

Template Method/Hook points only when extension is needed and documented.

Adapter/Facade to isolate third-party SDKs; prevent vendor lock-in leaking across the codebase.

Observer/Eventing used with clear ownership and delivery guarantees; avoid hidden side effects.

Repository/Aggregate boundaries consistent (if using DDD); invariants enforced inside aggregates.

Anti-patterns avoided: service locator, God objects, anemic domain model (unless explicitly opting for transaction script), circular deps.

DTOs vs Entities separated; records for immutable DTOs; mapping centralized (MapStruct or equivalent).

Configuration/Feature toggles not sprinkled across domain logic; centralized toggle access with typed wrappers.

Dependency injection scopes correct; avoid field injection; prefer constructor injection.

Transaction boundaries explicit; correct isolation/propagation; no lazy loads outside transactions unless intended.

Web layer validates inputs and returns consistent error contracts; no leaking exceptions as 500 without mapping.

Schedulers and async execution backed by properly sized executors; no default unbounded pools.

HTTP clients configured with timeouts, connection pools, and resilience policies.

Message consumers are idempotent; dead-letter/retry policies configured.

Configuration properties are type-safe; no @Value stringly-typed sprawl for complex config.

Schema normalized/denormalized intentionally; indexes exist for query patterns; no full scans on hot paths.

Migrations (Flyway/Liquibase) are forward-only with checks; data backfills planned.

Lazy vs eager loading decisions explicit; projections used for read-heavy paths.

Transactions minimize lock contention; long transactions avoided; batch writes where safe.

Time handled with java.time (UTC storage, zone conversion at edges); no legacy Date/Calendar usage.

Reproducible builds; pinned plugin and dependency versions; BOMs used for alignment.

CI runs unit + integration tests, linters, security scans, and produces artifacts with provenance.

SBOM generated and stored; vulnerability gates enforced with clear SLAs.

Container images minimal, non-root, read-only fs; health/readiness endpoints implemented.

Startup/shutdown hooks graceful; SIGTERM respected; long tasks drain on shutdown.

Rollback is possible; versioned configs; database compatibility checked during deploys.

Runbooks/alerts defined for SLOs; alarms actionable; no noisy alerts.

Turn this checklist into automated reviews

Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.