Contributing
Contributions are welcome — bug reports, documentation improvements, and new features alike. This page covers everything you need to go from a fresh clone to a passing build and an open PR.
Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Java | 25+ | Use the Gradle toolchain — no manual install needed if you have a JDK provider |
| Gradle | wrapper (./gradlew) | Never install Gradle globally; always use the wrapper |
| Node.js | 18+ | Only needed to run or build the documentation site |
| npm | bundled with Node.js | Run from the site/ directory |
No other global tools are required.
Clone and build
git clone https://github.com/domix/dmx-fun.gitcd dmx-fun./gradlew build./gradlew build compiles all modules, runs all tests, and generates the aggregated Javadoc and coverage report.
Running tests
# Run all tests across all modules./gradlew test
# Run tests for a specific module./gradlew :lib:test./gradlew :jackson:test./gradlew :assertj:test
# Run a single test class./gradlew :lib:test --tests "dmx.fun.OptionTest"To see the full test report after a run, open build/reports/tests/testAggregateTestReport/index.html.
Running the documentation site locally
cd sitenpm install # first time onlynpm run dev # starts dev server at http://localhost:4321/dmx-fun/The dev server watches for changes in site/src/ and reloads automatically.
The site is served under the /dmx-fun/ base path to match GitHub Pages.
Note:
npm run buildinsite/copies the Javadoc frombuild/reports/javadoc/into the site’spublic/directory. Run./gradlew aggregateJavadocfirst if you need the full build.
Code conventions
No null in the public API
All public methods must be annotated with @NullMarked (via the package-info or class-level annotation).
Return Option<T> instead of a nullable type; accept @Nullable parameters only when the absence is
meaningful to the implementation.
Sealed interfaces and records
Every sum type in the library is a sealed interface with record implementations.
This enables exhaustive pattern matching for callers and removes the need for instanceof chains.
// Correct — sealed + recordspublic sealed interface Option<T> permits Option.Some, Option.None { record Some<T>(T value) implements Option<T> {} record None<T>() implements Option<T> {}}No checked exceptions in the public API
Checked exceptions must not appear in public method signatures.
Wrap third-party code that throws in Try.of(...) and surface failures through Result or Try.
Test naming
Test method names follow the pattern <condition>_<expected outcome>, written in camelCase:
@Testvoid emptyOption_mapReturnsNone() { ... }Commit convention
This project uses Conventional Commits:
| Type | When to use |
|---|---|
feat | New public API or behaviour |
fix | Bug fix |
docs | Documentation only |
refactor | Code change with no behaviour change |
test | Adding or updating tests |
chore | Build, CI, dependencies |
perf | Performance improvement |
Always reference the issue number: feat(option): add mapBoth combinator (close #42).
Branch naming
<type>/<short-description>feat/option-map-bothfix/try-recover-npedocs/guide/adding-validated-examplesOpening a pull request
- Fork the repository and create a branch from
main. - Make your changes — one concern per PR.
- Ensure
./gradlew buildpasses locally. - Open the PR against
mainwith a title following the commit convention. - Link the related issue in the PR description (
close #NNN).
All PRs run the full CI pipeline (gradle.yml) automatically.
Note on lib/src/test/resources/
This directory is intentionally untracked (it appears in git status as untracked).
It is generated at test time and should not be committed.