Coding Guidelines
Do not fight the tooling, follow it unless you have a real good reason.
These guidelines exist so that the code is consistent, reviewable, and safe to enforce automatically, whether it is authorized by developers or AI agents.
How Style Is Enforced
prek.toml defines the hooks. prek runs them both locally
and in CI, touch only new or changed files.
- Local:
pip install -e '.[dev]', thenprek install(thecommit-msgshim runs the commit-message linter). Hooks then run ongit commit. - CI: the style gate runs the same hooks on the MR/PR diff.
The tools below are all installable with pip.
Naming
The project is BioIR, short for BioNeMo Inference Runtime. Spell the
long form out only in a title or a document’s first mention — once per document,
then BioIR.
Anything you add that needs the project in its name takes one of two prefixes.
The full bionemo_ir belongs to the import path and to what a user sees; every
internal identifier takes the short bioir, which keeps declarations readable
inside the 120-column limit.
docker/Makefile is the one exception to the prefix rule: it is always invoked
as make -C docker <target>, so the directory already supplies the namespace
and each target is named for the Dockerfile stage it builds.
Name a new environment variable for what it controls, not for the component
reading it: BIOIR_CHECKPOINTS, not BIOIR_HUBS_CHECKPOINT_DIR. They are a
public interface — renaming one is a breaking change.
A handful of names belong to systems outside this repo — CI projects and registries, runner tags, and the artifact stores a downstream consumer fetches by. They keep whatever those systems call them, and each is commented where it appears with what breaks if it moves. Do not align them by hand.
Python
Follow PEP 8 unless noted. Target Python 3.12+.
Naming
- Files:
snake_case.py. Classes:PascalCase.Functions/methods/variables:snake_case. Constants:UPPER_SNAKE_CASE. - Prefix non-public module/class members with a single underscore.
- For host/device tensors whose location is ambiguous, suffix
_host/_device(or_cuda), especially when copies exist in both places.
Imports
- No wildcard imports. Let
ruff(isort rules) order imports. - Keep
__all__current to document the public interface.
Typing
- Annotate every function argument and return type. Use
-> Noneexplicitly when nothing is returned. - Prefer builtin generics and unions:
list[int],dict[str, int],int | None— nottyping.List/typing.Optional. - Avoid
typing.Anyand# type: ignore. UseLiteral[...]for a fixed set of string values;Protocolfor duck-typed interfaces.
Error Handling
- Catch the narrowest exception set possible; keep the
trybody minimal and put logic inelse. Prefer builtin exception types. RaiseValueError, do notassert, for invalid input. - Avoid reflection when a direct expression works.
Docstrings
- Google style, parsable by Sphinx. Public functions and class initializers get docstrings; document their arguments.
- For tensor-like arguments, document expected dimensions (for example,
[batch, seq_len, hidden]) and the allowed dtype(s) when constrained. - Reserve inline comments for non-obvious logic; do not restate the code.
Pydantic (User-Facing Config)
For any user-facing configuration class, use Pydantic, not dataclasses:
- Inherit from a strict base (
extra="forbid") to reject unknown fields. - No
__init__; use@field_validator/@model_validatorfor validation andmodel_post_init()for post-validation setup. - Every field gets
Field(description=...). Usedefault_factoryfor mutable defaults,Literal[...]for enumerations, and constrained types (PositiveInt,Field(ge=0), …) over custom validators. - Prefer
model_dump()/ direct construction overto_dict()/from_dict().
C/C++/CUDA
The cpp/ tree follows the TensorRT-LLM C++ guidelines (Allman
braces, east-const, 120-col, k/m naming). Do not fight .clang-format.
clang-formatruns in the style gate (fast, no build). It is pinned to a single version inprek.toml; use that exact version locally so output does not ping-pong..clangddrives editor LSP for C++/CUDA.
License Header
All source files carry the NVIDIA SPDX Apache-2.0 header (refer to
.license-header.txt) — Python, shell, and CMake with # comments, and
C/C++/CUDA in a /* ... */ block. insert-license adds it where missing and
leaves existing headers — including year ranges — untouched.
Commits
Commit messages follow Conventional Commits so history can drive changelog generation and other automation.
The project lives in two repos kept in sync by Copybara: an internal GitLab (source of truth) and GitHub (open source). Accepted internal MRs on non-proprietary paths mirror to GitHub. A GitHub PR, after it is approved, is imported as an internal MR, merged there, and synced back to GitHub; the original PR is then closed, so a merged PR shows as closed rather than merged. A change is a merge request or pull request — MR/PR below.
Merges are fast-forward (linear history). Squashing is the default and recommended, so an MR/PR usually lands as one commit whose subject is the MR/PR title. Write that title as the commit you want in history — it is the enforced unit:
- Title — ticket key first, then Conventional Commits. A GitHub, JIRA, or
NVBugs reference in brackets, then a conventional summary:
[PROJ-382] fix: bump deps. The internal pipeline enforces it — a bracketed key, thencz checkon the summary. Copybara scrubs the leading key when mirroring to GitHub, leaving a cleanfix: .... - Allowed types:
feat,fix,docs,style,refactor,perf,test,build,ci,chore,revert. A scope is allowed but not required; components are not enforced. - Per-commit (local aid). The
commitizencommit-msghook checks each commit title is Conventional Commits — the ticket key is not required on commits, so you can commit freely while experimenting. Squash discards these commits, so the MR/PR title is the real gate; the same tool generates the changelog later.
Only the title is enforced. Body conventions are recommended, not gated:
- Body wrap at ~72–80 cols for readability.
- Breaking changes:
type!:, aBREAKING CHANGE: <desc>footer, or both — drives a major version bump in the changelog. - Footer trailers (git-trailer
Token: value):Refs: PROJ-123,Signed-off-by:(DCO),Co-authored-by:. Copybara preserves trailers across the sync.
To carry an MR/PR description into the squashed commit body (so it reaches the changelog), set the platform’s squash commit template to include the description. Changelog generation itself (
commitizenorgit-cliff→ the Keep-a-Changelog sections) is a later step.