ddi
Made in the European Union
Contributing

Help make the library
a little better.

Bug reports, pull requests, tutorials, documentation fixes — all welcome. No CLA, no copyright assignment. Everything contributed stays under EUPL 1.2.

Three ways to help

Pick whichever fits

🐛

File an issue

Found a bug, a confusing error message, or a doc that's wrong? Open an issue. A minimal reproducer goes a long way.

📝

Improve docs

Typo fixes, clearer examples, a missing tutorial — docs PRs are the easiest to get merged and the most appreciated.

🔧

Submit code

For non-trivial changes, open an issue first so we can sketch the design together. Saves both sides the dead-end PR experience.

Filing a good bug report

A useful report has three parts:

  1. What you ran — the smallest snippet that reproduces the problem, including the DI.activatePackages(...) calls used in the test or app.
  2. What you expected — including the JDK version, library version, and OS if relevant.
  3. What you got — the actual error, stack trace, or wrong implementation chosen by the resolver.
Library version: 06.01.01
JDK:             OpenJDK 21
OS:              macOS 14 / Linux 6.6

Setup:
  - Service interface with two impls (ServiceA, ServiceB).
  - One ClassResolver returning ServiceB.

Expected: DI.activateDI(Service.class) returns ServiceB.
Got:      DDIModelException: ambiguous resolution.

Open it at the [DDI issues tracker]({{ .Site.Params.github }}/issues).

Submitting a pull request

The flow is:

  1. Discuss first — for anything beyond a typo or a one-line fix, open an issue describing the change. We agree on the shape, then you write the code.
  2. Fork & branch — branch off main, prefix the branch with fix/, feat/ or docs/ plus a short slug.
  3. Stay small — one logical change per PR. A 200-line PR reviewed in a day is more useful than a 2000-line PR that sits for a month.
  4. Tests required for code changes — add at least one JUnit 5 test that fails without your change and passes with it. For resolver/producer changes, the test should clear the model with DI.clearReflectionModel() in @BeforeEach to keep test isolation.
  5. Run the build locallymvn clean install should pass before you push.
  6. Mutation tests — if you change resolver internals, run mvn -P pitest test and aim to keep the score above 90 %.
  7. Keep style consistent — match the surrounding code. No re-flowing unrelated files; the diff should be the change you want to land.

Local development

DDI lives in the umbrella dependencies repository alongside core, functional-reactive, core-properties and logger-adapter:

git clone https://github.com/svenruppert/dependencies
cd dependencies
mvn clean install

The build targets JDK 21 LTS. SDKMAN! is the easiest way to switch:

sdk install java 21.0.4-tem
sdk use java 21.0.4-tem

Run only the DDI module tests:

mvn -pl ddi -am test

Run a single test class:

mvn -pl ddi test -Dtest=ClassResolver010Test

Run mutation tests against DDI:

mvn -pl ddi -P pitest test

Code style

Nothing exotic. Match the surrounding code. A few specific points:

  • No checkstyle / spotless gate — readability beats mechanical conformance. The reviewer flags style issues during review.
  • Public API gets Javadoc. Internal helpers don’t have to.
  • No comments restating what the code does. Comment the why — the surprising invariant, the workaround for a specific bug, the design choice a future reader won’t infer from the names.
  • No new runtime dependencies without prior discussion. The current bar is reflections8, gson, javax.annotation-api, plus the sister projects (core, functional-reactive).
  • JDK 21 baseline. Records, sealed classes and pattern matching are fine. We don’t need to preserve JDK 8 compatibility in the 06.x line.

What gets accepted, and what doesn’t

Easy yes:

  • Bug fixes with a regression test.
  • Doc improvements, typo fixes, clearer examples.
  • New resolver / producer tutorials.
  • Test-coverage improvements for existing public API.

Discuss first:

  • New top-level types or packages in com.svenruppert.ddi.*.
  • Anything that changes a public API signature.
  • New built-in scopes (the model is intentionally small).
  • Build-tool changes, parent-POM bumps.

Probably no:

  • “Make it more like <framework X>” rewrites.
  • Mechanical reformatting that touches files you didn’t otherwise change.
  • Replacing the licence, the build tool, or the test framework on a whim.

Licensing & signing off

All contributions are licensed under EUPL 1.2, the same licence the library uses. By submitting a PR you confirm that you have the right to license your contribution that way. There is no separate CLA to sign — your PR description and Git history are enough.

If the change is non-trivial, please add a Signed-off-by line to your commits:

git commit -s -m "feat: add ClassResolver caching hook"

Community

By participating in this project’s discussions, issues, and PRs, you agree to follow the Code of Conduct. Be kind, be specific, assume good faith.

Thank you

Open source happens because people show up. If you take the time to file a careful bug report or a thoughtful PR, you’re keeping this library alive for everyone else using it. That matters. Thank you.