Thank you for your interest in contributing to this project. Community involvement is highly valued and crucial for the project's growth and success. This project accepts contributions via GitHub pull requests. This document outlines the process to help get your contribution accepted.
To ensure a clear direction and cohesive vision for the project, the project leads have the final decision on all contributions. However, these guidelines outline how you can contribute effectively.
There are several ways you can contribute:
- Reporting Issues: Help us identify and fix bugs by reporting them clearly and concisely.
- Suggesting Features: Share your ideas for new features or improvements.
- Improving Documentation: Help make the project more accessible by enhancing the documentation.
- Submitting Code Contributions: Code contributions that align with the project's vision are always welcome.
This project adheres to the Code of Conduct and Covenant. By participating, you are expected to uphold this code.
- Developer Slack: Join our developer Slack workspace to connect with the core maintainers and other contributors, ask questions, and participate in discussions.
- Weekly Meetings: Project updates, ongoing work discussions, and Q&A will be covered in our weekly project meetings. Please join by adding the shared calendar. You can also join our Google Group for access to shared content.
- Code: Hosted in the llm-d GitHub organization
- Issues: Project-scoped bugs or issues should be reported in this repo or in llm-d/llm-d
- Mailing List: llm-d-contributors@googlegroups.com
We follow a lazy consensus approach: changes proposed by people with responsibility for a problem, without disagreement from others, within a bounded time window of review by their peers, should be accepted.
All features involving public APIs, behavior between core components, or new core subsystems must be accompanied by an approved project proposal.
Process:
- Create a pull request adding a proposal document under
./docs/proposals/with a descriptive name - Include these sections: Summary, Motivation (Goals/Non-Goals), Proposal, Design Details, Alternatives
- Get review from impacted component maintainers
- Get approval from project maintainers
For changes that fix broken code or add small changes within a component:
- All bugs and commits must have a clear description of the bug, how to reproduce, and how the change is made
- Any other changes can be proposed in a pull request — a maintainer must approve the change
- For moderate size changes, create an RFC issue in GitHub, then engage in Slack
- All code changes must be submitted as pull requests (no direct pushes)
- All changes must be reviewed and approved by a maintainer other than the author
- All repositories must gate merges on compilation and passing tests
- All experimental features must be off by default and require explicit opt-in
- Pull requests should describe the problem succinctly
- Rebase and squash before merging
- Use minimal commits and break large changes into distinct commits
- Commit messages should have:
- Short, descriptive titles
- Description of why the change was needed
- Enough detail for someone reviewing git history to understand the scope
- DCO Sign-off: All commits must include a valid DCO sign-off line (
Signed-off-by: Name <email@domain.com>)- Add automatically with
git commit -s - See PR_SIGNOFF.md for configuration details
- Required for all contributions per Developer Certificate of Origin
- Add automatically with
- Components are the primary unit of code organization
- Maintainers own components and approve changes
- Contributors can become maintainers through sufficient evidence of contribution
- Code ownership is reflected in OWNERS files consistent with Kubernetes project conventions
We use three tiers of testing:
- Unit tests: Fast verification of code parts, testing different arguments
- Integration tests: Testing protocols between components and built artifacts
- End-to-end (e2e) tests: Whole system testing including benchmarking
Strong e2e coverage is required for deployed systems to prevent performance regression. Appropriate test coverage is an important part of code review.
# Unit tests (default, no external dependencies)
make test
# Integration tests (each test spawns its own in-process mock server)
make test-integration
# Both unit and integration tests
make test-all
# E2E tests (requires docker/podman, kind, helm, kubectl)
make test-e2eIntegration tests use the //go:build integration build tag and live in test/integration/. They are excluded from make test and only run when the tag is explicitly passed via make test-integration. Each test spawns its own mock server (miniredis, httptest.Server) — no external services are required.
To add a new integration test, create a *_test.go file in test/integration/ with the build tag:
//go:build integration
package integration_testThis repository is a multi-module Go repo. The lightweight sub-modules (api, pipeline, producer) each have their own go.mod so that external consumers can depend on them without pulling in the heavyweight root module.
- Update cross-module version references:
make set-version VERSION=v0.8.0This rewrites all require directives across go.mod files and runs go mod tidy. Sub-modules are detected automatically from subdirectories containing a go.mod.
- Commit the result and push a tag on the root module:
git tag v0.8.0
git push origin v0.8.0- The
CI - Tag Go Submodulesworkflow automatically creates the prefixed tags required by Go modules (e.g.api/v0.8.0,pipeline/v0.8.0,producer/v0.8.0).
Go requires sub-module tags to carry the directory prefix — see Managing module source for details.
User-facing changes are recorded as per-PR fragments and assembled into
RELEASE-NOTES.md at each release. One fragment file per PR
keeps this conflict-free.
-
Fill the
release-noteblock in the PR description (writeNONEif there is no user-facing impact). -
For a user-facing change, add a fragment at
release-notes.d/unreleased/<PR-number>.md:--- pr: 123 url: https://github.com/llm-d-incubation/llm-d-async/pull/123 author: your-handle date: 2026-07-09 --- Short description of the change. Prefix breaking changes with `Breaking:`.
-
make release-notes-lintvalidates fragment format (also enforced in CI via pre-commit).
At release time — as part of Preparing a release — a
maintainer runs make release-notes VERSION=vX.Y.Z, which moves the unreleased
fragments into a new RELEASE vX.Y.Z <date> section at the top of
RELEASE-NOTES.md and deletes them. Commit the result with the release.
These conventions are enforced by linters where possible (see .golangci.yml) and by code review otherwise.
- No mutable package globals. Use unexported state with constructors and accessors. Exceptions: Prometheus metric registrations (
pkg/metrics/) and build-time version vars (pkg/version/). - Compile-time interface assertions. Every interface implementation (and test fake) must include
var _ Interface = (*Impl)(nil)to catch breakage at compile time, not at runtime. - Wrap errors with context. When returning an error from an external package, wrap it:
fmt.Errorf("doing X: %w", err). This makes logs actionable. - YAGNI. Don't add exported functions, methods, or types without a caller in this repo. Unused API surface is maintenance burden.
- Porting rule. When adapting code from EPP or another repo, strip it to what is actually called and re-fit it to local idioms before opening the PR.
See SECURITY.md for our vulnerability disclosure process.
- No breaking changes: Once an API/protocol is in GA release, it cannot be removed or behavior changed
- Versioning: All protocols and APIs should be versionable with clear compatibility requirements
- Documentation: All APIs must have documented specs describing expected behavior
- Deprecation policy (N+2): A feature deprecated in release N must keep working — with no user-facing or configuration change required — through releases N and N+1, emitting a clear warning, and may be removed no earlier than release N+2. Record the deprecation (and any breaking change) in the PR's release note.