Key words per RFC 2119: MUST = absolute requirement; MUST NOT = absolute prohibition; SHOULD = strong recommendation; SHOULD NOT = strong recommendation against; MAY = optional.
- Every suitable feature MUST be used in at least one quickstart
- Features considered complete only when: fully tested, fully documented, validated in realistic scenario
Documentation:
- Public API: All public classes/interfaces/methods MUST have Javadoc with
@param,@return,@throws(omit NPE for null params; omit if present on referenced overload).@sinceoptional. - User-facing:
- New features → user guide;
- Config changes → reference docs;
- Breaking changes → migration guides.
- Examples: Docs SHOULD include code examples; quickstart examples SHOULD be added externally.
- Implementation: Complex classes SHOULD have class-level Javadoc; non-obvious details SHOULD have comments. Comments SHOULD be kept short and to the point, unless a description of a complex mechanism is required.
- Formatting: Any form of documentation (being source code comments, Javadoc, or user documentation) must follow Semantic Line Breaks.
Names MUST be unambiguous and consistent across codebase, docs, and public communication.
Collections: include data structure type in variable name:
- ✅
tupleList,scoreMap,entitySet,problemArray - ❌
tuples,scores,entities,problems
Interfaces and Implementations:
- Single implementation (MUST): prefix with
Default- ✅
interface Solver→class DefaultSolver; ❌SolverImpl
- ✅
- Multiple implementations (SHOULD): descriptive names;
Defaultfor the most common - Abstract classes (MUST): prefix with
Abstract; neverBase- ✅
abstract class AbstractPhase; ❌abstract class BasePhase
- ✅
Validate invalid states ASAP, in priority order:
- Compile time — type safety
- Startup time — config validation
- Runtime — request parameter validation (see also: Security § Input Validation)
- Assertion mode — performance-sensitive checks gated on
EnvironmentMode
Exception messages MUST include variable names/states:
throw new IllegalArgumentException(
"The fooSize (%d) of bar (%s) must be positive.".formatted(fooSize, this));Actionable advice SHOULD be on new line starting with "Maybe":
throw new IllegalStateException("""
The valueRangeDescriptor (%s) is nullable, but not countable (%s).
Maybe the member (%s) should return CountableValueRange."""
.formatted(valueRangeDescriptor, isCountable, member));Exception Handling:
- Prefer unchecked exceptions for programming errors
- Never swallow exceptions; always handle or rethrow with context
- Don't catch
Exception/Throwable - Null params →
Objects.requireNonNull(param, "param")
All code MUST have tests before merging. Write the tests red-green: the test fails first, then the change makes it pass.
Naming (required for Maven Surefire/Failsafe):
- Unit tests:
*Test(e.g.,ScoreCalculatorTest) — Surefire,testphase - Integration tests:
*IT(e.g.,SolverEndToEndIT) — Failsafe,integration-testphase
Classpath (MUST): Tests MUST run on classpath, NOT modulepath. No module-info.java in test roots.
Performance/stress tests are in other repositories; not applicable here.
- Code MUST be clean and readable; readability over ease of writing
./mvnw clean installauto-formats; CI enforces style
Style:
- Newlines: formatter preserves them; use sparingly to separate logical blocks
- Field access order in methods SHOULD match declaration order
varpreferred over diamond; diamond MUST be used whenvarnot used and type is inferable- ✅
var list = new ArrayList<String>();; ✅List<String> list = new ArrayList<>(); - ❌
var list = new ArrayList<>();(compiler error); ❌List<String> list = new ArrayList<String>();
- ✅
- Asterisk imports MUST NOT be used
SonarCloud Quality Gates (MUST):
- Reliability and Maintainability grades MUST be B or better; PRs worsening below B fail CI
- Coverage MUST not fall below configured threshold
- Critical/major code smells SHOULD be resolved before merge
Visibility and Immutability (MUST):
- Fields/methods MUST be
privateunless broader visibility required - Classes/methods MUST be
finalunless designed for extension - Fields MUST be
finalunless value must change post-construction - Prefer immutable objects:
List.of(),Set.of(), records - Seal interfaces/abstract classes if no external implementations allowed
// ✅ Correct
public final class DefaultSolver<Solution_> implements Solver<Solution_> {
private final SolverScope<Solution_> solverScope;
DefaultSolver(SolverScope<Solution_> solverScope) { // package-private
this.solverScope = Objects.requireNonNull(solverScope);
}
@Override
public final Solution_ solve(Solution_ problem) { ... }
}- Minimum: JDK 21 (compile and runtime)
- SHOULD support latest JDK runtime
- All JDK 21 features encouraged
OptionalMUST NOT be used; use@Nullable(JSpecify) instead- Streams allowed but prefer simple
forloops in hot paths
Nullability (JSpecify):
- Use
@NullMarkedon classes/packages → everything non-null by default - Internal null is permitted
- Null MUST NOT escape public APIs without
@Nullable @Nullablerequired on any public method accepting/returning null
@NullMarked
public class ScoreCalculator {
private Solution cachedSolution = null; // internal null OK
public Solution calculate(Problem problem) { ... } // non-null by @NullMarked
public @Nullable Solution getCachedSolution() { return cachedSolution; }
}Enforcement: Build targets JDK 21; CI verifies compilation and tests on JDK 21; code reviews MUST reject Optional and null escaping APIs.
No external libraries in production code except:
- Java Standard Library
- JSpecify annotations (compile-time only)
- Module-specific frameworks (Quarkus in Quarkus modules, Spring in Spring modules only)
- Explicitly ratified exceptions in this constitution
Code reviews MUST reject unauthorized production dependencies. All dependencies of the project MUST either use Apache Software License 2.0 or compatible licenses. Strong copyleft dependencies (GPL, AGPL) MUST NOT be used; weak copyleft (EPL, MPL, LGPL) is permitted.
MUST use:
- JUnit — test execution, lifecycle annotations
- AssertJ (MUST) —
assertThat(actual).isEqualTo(expected); JUnit assertions (assertEqualsetc.) FORBIDDEN - Mockito — mocking/stubbing; prefer real objects when practical
- Other frameworks allowed when they provide clear value (test containers, property-based testing, etc.)
- No Secrets (MUST NOT): No credentials/keys/tokens in code; use env vars or config files excluded from VCS
- Dependency Security (MUST): High-severity CVEs MUST be addressed promptly; Dependabot provides weekly upgrade PRs; Aikido performs security scanning
- Input Validation (MUST): All external input MUST be validated; assume malicious
- Secure Defaults (MUST): Insecure options require explicit opt-in; document with warnings
- Vulnerability Reporting (MUST): Report privately; do NOT create public issues
- No sensitive logging (MUST NOT): No credentials, PII in logs
Logging (SLF4J):
- ERROR: unrecoverable failures; WARN: recoverable issues/deprecations; INFO: solver lifecycle; DEBUG: phase lifecycle; TRACE: step lifecycle + developer debugging
- No sensitive data; minimize in hot paths
| Package type | Stability |
|---|---|
*.api.* |
100% backwards compatible; breaking only in major versions |
*.config.* |
100% backwards compatible; breaking only in major versions |
*.preview.api.* |
Best-effort compatibility; may break in minor versions if necessary |
*.internal.* |
Stable but may break without public notice; for internal Timefold tools only |
| All others | No guarantees |
Versioning: MAJOR = breaking API/config change; MINOR = new backwards-compat feature; PATCH = bug fix.
API Design Principles (MUST):
- Public API packages MUST expose interfaces, not implementations
- Implementation constructors MUST be hidden (package-private); use factories/builders
- Object creation in public API MUST use factory pattern
- Public API methods MUST return interface types, not implementation types
// ✅ Correct
package ai.timefold.solver.core.api.solver;
public interface SolverFactory<Solution_> {
static <Solution_> SolverFactory<Solution_> create(SolverConfig config) {
return new DefaultSolverFactory<>(config);
}
Solver<Solution_> buildSolver();
}
public final class DefaultSolver<Solution_> implements Solver<Solution_> {
DefaultSolver(SolverScope<Solution_> solverScope) { ... } // package-private
}JPMS MUST be implemented; every generated jar MUST have module-info.java.
- Directives (
exports,opens,provides,requires,uses) MUST be alphabetically sorted. .api.*and.config.*packages MUST be exported..impl.*packages MUST NOT be exported; if unavoidable, use qualified exports (exports pkg to module;).- Modules MUST NOT be fully
open; only open specific packages to specific modules. - Our modules SHOULD
requires transitiveour other modules where possible; external modules MUST NOT betransitive.
module ai.timefold.solver.jaxb {
exports ai.timefold.solver.jaxb.api.score;
// ai.timefold.solver.core is a module of ours, SHOULD use transitive
requires transitive ai.timefold.solver.core;
// jakarta.xml.bind is not a module of ours, MUST NOT be transitive
requires jakarta.xml.bind;
}- Discuss significant changes in GitHub Discussions/Issues first
- Feature branch from main
- Conventional commits
./mvnw clean install— all checks pass- Submit PR; CI validates; maintainers review
Conventional Commits: <type>: <description>
Types: feat, fix, docs, perf, test, build, ci, revert, deps, chore
Present tense, imperative mood.
- Fast:
./mvnw clean install -Dquickly— skips checks (~1 min) - Normal:
./mvnw clean install— tests and checks (~17 min) - Docs:
./mvnw clean installindocs/(~2 min) - Full:
./mvnw clean install -Dfull— all checks + distribution (~20 min)
- Mark (MUST):
@Deprecatedannotation +@deprecatedJavadoc with explanation, alternative, and version info - Migration path (MUST): explain why deprecated, provide alternative, code example, removal timeline
- OpenRewrite recipe (SHOULD): automate migration; skip only if technically infeasible
- Deprecated features MUST NOT be removed without at least one major version notice period
/** @deprecated Use {@link #calculateScoreOptimized(Solution)} instead. Removed in 2.0.0. */
@Deprecated(since = "1.8.0", forRemoval = true)
public Score calculateScore(Solution solution) { ... }- All contributions MUST be Apache 2.0 compatible
- Third-party code MUST include attribution; original copyright MUST be preserved
- License headers in source files NOT required
- Contributors MUST have rights to contribute the code
This constitution supersedes all other development practices. All PRs MUST verify constitutional compliance.
Constitution amendments require maintainer discussion and approval. Constitution versioning: MAJOR = principle removal/redefinition; MINOR = new principles; PATCH = clarifications.
- CI enforces automated checks (commit format, style, coverage)
- MUST violations block PR approval
- Deviations MUST be explicitly justified
Version: 2.1.0