Contribution Guide
This guide covers the development setup, code style, testing requirements, and contribution process for NIXL. NIXL is a C++20 project with strict standards for code quality and testing.
Getting Started
Before contributing, please:
- Review existing issues and PRs to avoid duplicate work
- For significant changes, open an issue for discussion before implementation
- Familiarize yourself with our code style and project structure
- Set up your development environment according to the guidelines below
All contributions require signing off on the Developer Certificate of Origin (DCO). Each commit must include a Signed-off-by line with your real name and email. See the DCO section below.
Development Setup
Cloning the Repository
Building with Meson
NIXL uses Meson and Ninja for building:
See the Quick Start for PyPI installation or Building NIXL from Source for source build options including Meson flags and Docker containers.
Setting Up clang-format
All new C++ code must be formatted using the provided .clang-format configuration. Code formatting is automatically checked in CI, and improperly formatted code will be rejected:
Pre-commit Hooks
The project uses pre-commit hooks for Python code quality. Install them with:
Building Docs Locally
To validate documentation changes:
Required Tools
- C++20-compatible compiler
- Meson build system
- Ninja build tool
- clang-format
- Python (for build scripts and testing)
- Git with DCO sign-off configured
Code Standards
C++20 Guidelines
NIXL follows the C++ Core Guidelines where appropriate:
- Use modern C++ features: Prefer
auto, range-based loops, structured bindings,std::optional, etc. - RAII everywhere: Resource management through constructors/destructors
- Smart pointers for ownership: Use
std::unique_ptr,std::shared_ptrappropriately - Prefer
constcorrectness: Mark methods and variablesconstwhen appropriate - Exception handling: Exceptions are recommended for control-path code, while error codes should be used for data-path
STL and Abseil Usage
-
Prefer STL types: Use rich STL types as the primary choice
- Standard containers:
std::vector,std::unordered_map, etc. - Modern utilities:
std::optional,std::variant,std::string_view - Algorithms from
<algorithm>and<numeric>
- Standard containers:
-
Fallback to Abseil: When STL lacks required functionality
- String formatting:
absl::StrFormat - Error handling:
absl::StatusOrfor data-path operations that return values with potential errors - High-performance containers:
absl::flat_hash_mapwhen needed - Logging utilities (integrated with NIXL logging)
- String formatting:
-
Never expose Abseil in public APIs
- Keep Abseil types internal to implementation files
- Plug-in and agent public APIs must only use STL types
- Convert between Abseil and STL types at API boundaries
Exposing Abseil types in public APIs will cause your PR to be rejected. Always use STL types at API boundaries.
Error Handling
-
Control-path code: Use exceptions for exceptional conditions
std::runtime_errorfor runtime failuresstd::invalid_argumentfor invalid parameters- Custom exceptions when appropriate
-
Data-path code: Use error codes for performance-critical paths
- Return
nixl_status_tor similar error codes - Avoid exceptions in hot paths
- Return
-
Logging: Use NIXL logging macros
NIXL_ERROR: Critical errors that prevent normal operation (system failures, resource exhaustion, unrecoverable errors)NIXL_WARN: Warning conditions that don’t prevent operation (deprecated API usage, performance degradation, recoverable errors, fallback behavior)NIXL_INFO: Informational messages about normal operation (initialization complete, configuration loaded, major state changes)NIXL_DEBUG: Detailed debugging information for development (function entry/exit, intermediate values, algorithm decisions)NIXL_TRACE: Very detailed trace information for deep debugging (per-packet processing, memory allocations, lock acquisitions)
Code Style
All code must adhere to these style guidelines and be formatted with .clang-format.
Naming Conventions
-
Lower camelCase (e.g.,
myVariable):- Type names — classes, structs, unions (e.g.,
myClass,dataPacket) - Template parameters (e.g.,
template <typename dataType>) - Class/struct members — public and protected data members (e.g.,
myField) - Functions — both member and non-member (e.g.,
getValue(),processCompletions())
- Type names — classes, structs, unions (e.g.,
-
snake_case (e.g.,
my_variable):- Variables — function arguments, local variables, global variables, and constants (e.g.,
my_var,constexpr int default_port = 8080) - Namespaces (e.g.,
namespace nixl_utils) - Type aliases with
_tsuffix (e.g.,using test_params_t = std::vector<int>) - Enum class names with
_tsuffix (e.g.,enum class status_t) - File names (e.g.,
my_backend.h,data_processor.cpp)
- Variables — function arguments, local variables, global variables, and constants (e.g.,
-
UPPER_SNAKE_CASE (e.g.,
MY_CONSTANT):- Enum values (e.g.,
SUCCESS,ERROR_TIMEOUT) - Preprocessor macros (e.g.,
#define MAX_BUFFER_SIZE 1024) - Header guards (e.g.,
#ifndef NIXL_BACKEND_H)
- Enum values (e.g.,
Class Design
Member Declaration Order:
Class members should be declared in this order:
- public section first
- protected section second
- private section last
Within each access level, group declarations logically:
- Type definitions and nested classes
- Static member variables
- Constructors, assignment operators, and destructor
- Member functions
- Data members
Private Member Naming:
Private class data members must use a trailing underscore suffix (e.g., memberName_). This clearly distinguishes private implementation details from the public interface:
File Organization
File Headers:
All source files must begin with the SPDX license header:
Header Guards:
Use traditional #ifndef/#define header guards (not #pragma once). Header guard names should be upper snake case based on the file path. Add a NIXL_ project prefix, convert the entire path to upper snake case (replacing / and . with _):
Formatting
Line Length: Maximum line length is 100 characters. Break long lines appropriately.
Indentation: Use 4 spaces for indentation (no tabs). Continuation lines should be indented by 4 spaces. Namespace content should be indented.
Function Declarations: Return type on a separate line from the function name. If the signature exceeds 100 characters, parameters break to one per line:
Function Calls: If the function call exceeds 100 characters, arguments break to one per line:
Braces: Opening brace on the same line for most constructs (functions, if, else, loops, try, catch). The catch keyword goes on a new line:
Short if-statements without else can be on a single line when appropriate. Empty functions/blocks can be on a single line: void empty() {}
Switch Statements: Case labels are not indented relative to the switch statement. Avoid default when switching on enum types to enable compiler warnings for unhandled cases:
Parentheses: No space before parentheses in function calls and declarations. Space required before parentheses in control statements (if, for, while, switch, catch):
Pointer and Reference Alignment: Pointers and references are right-aligned — the *, &, and && are placed next to the variable name, not the type:
Constructor Initializers: Constructor initializer lists break before the colon:
Comments
Documentation Comments: Use Doxygen-style block comments (/** ... */) for documenting public APIs, classes, functions, and types. Include @brief, @param, @return, and other Doxygen tags:
Inline Comments: Use ///< for trailing Doxygen documentation (enum values, struct/class members). Use // for regular code comments:
General Coding Practices
Prefer Functions over Macros: Prefer constexpr functions, inline functions, or templates over preprocessor macros. Functions provide type safety, scoping, and debugging support:
Anonymous Namespaces: In implementation files (.cpp), prefer anonymous namespaces over static for file-local classes and functions. Do not use anonymous namespaces in header files:
Type Deduction with auto: Use auto for variable declarations when the type is obvious from the initializer or when dealing with verbose type names:
Override Specifier: Always explicitly mark virtual methods that override base class methods with the override specifier:
Contributing Process
Contributions that fix documentation errors or make small changes to existing code can be contributed directly by following the rules below and submitting a PR.
For significant new functionality, open a GitHub issue first to discuss the design with the NIXL team.
- Agree on a design through the issue discussion before starting implementation.
- Include comprehensive tests. The NIXL team helps design tests compatible with existing infrastructure.
- User-visible features require documentation.
Contributions to the code under ./examples/device/ep (derived from DeepEP, licensed under MIT) must be licensed under Apache 2.0.
Review Process Expectations
Review standards:
Timeline and Iterations:
- Initial review typically takes 1-2 weeks depending on PR complexity
- Most PRs require 2-4 rounds of review before merging
- Complex features may take longer as we ensure architectural consistency
How We Support Contributors:
- Reviewers provide detailed feedback to improve contributions
- Reviewers collaborate, not just critique. Ask questions if feedback is unclear.
- For significant changes, we may suggest incremental PRs for easier review
- The team helps ensure contributions align with NIXL’s architecture
Tips for Smoother Reviews:
- Start with smaller PRs to familiarize yourself with our standards
- Engage early through issues for design discussions
- Be responsive to feedback and ask for clarification when needed
- Consider breaking large changes into logical, reviewable chunks
Commit Messages
Plugin Development
New plug-in contributions follow this structure:
Plugin Structure
Plug-ins are located in src/plugins/. Your plug-in should follow this structure:
Tests should be added in the GoogleTest-based test directory:
Build System Integration
Create a meson.build file for your plug-in. If your plug-in requires external dependencies:
Container Build Extension
If your plug-in requires system dependencies, update contrib/Dockerfile. See existing examples for compiling dependencies from source.
Plugin Documentation
Your plug-in’s README.md must include:
- Overview: Basic functionality description
- Dependencies: List all external requirements
- Build Instructions: How to build with/without the plug-in
- API Reference: Key classes and functions
- Example Usage: Simple, working example
The Southbound API and existing plug-ins in src/plugins/ provide implementation references.
Testing Requirements
Test Framework
- New tests should use GoogleTest framework in
test/gtest/ - Legacy tests may exist in other locations
- Run tests with:
meson test -C build
Test Coverage
- New features must include comprehensive tests
- Fixes must include regression tests
- Test both success and error paths
Test Organization
Documentation Standards
Code Documentation
Document public APIs and complex implementations using Doxygen-style comments:
PR Documentation
Use the provided template in .github/pull_request_template.md:
- What?: Clear description of changes
- Why?: Justification and issue references
- How?: Technical approach for complex changes
Pull Request Guidelines
Before Submitting
- Code follows style guidelines (
.clang-formatapplied) - Follows conventions in the Code Style section above
- All tests pass
- New tests added for new functionality
- Documentation updated where needed
- PR template filled out completely
- Commits are signed with DCO
Miscellaneous
- NIXL’s default build assumes recent versions of dependencies (CUDA, PyTorch, etc.). Contributions that add compatibility with older versions will be considered, but NVIDIA cannot guarantee all possible build configurations work or retain highest performance.
- Make sure you can contribute your work to open source (no license or patent conflict introduced by your code). You must certify compliance with the license terms and sign off on the DCO before your PR can be merged.
Developer Certificate of Origin
NIXL is an open source product released under the Apache 2.0 license. The Apache 2.0 license allows you to freely use, modify, distribute, and sell your own products that include Apache 2.0 licensed software.
We respect intellectual property rights and want to make sure all incoming contributions are correctly attributed and licensed. A Developer Certificate of Origin (DCO) is a lightweight mechanism to do that.
The DCO is a declaration attached to every contribution made by every developer. In the commit message of the contribution, the developer adds a Signed-off-by statement and thereby agrees to the DCO, which you can find at DeveloperCertificate.org.
We require that every contribution to NIXL is signed with a Developer Certificate of Origin. Additionally, please use your real name. We do not accept anonymous contributors nor those utilizing pseudonyms.
Each commit must include a DCO which looks like this:
You may type this line on your own when writing your commit messages. However, if your user.name and user.email are set in your git configs, you can use -s or --signoff to add the Signed-off-by line to the end of the commit message.