Development Conventions
This page captures the engineering conventions that contributors are expected to follow when working in LAV2. Use it together with Tooling and Validation for command-level workflow and Backlog and Internal Docs for task tracking rules.
Required Contributor Workflow
Every contributor is expected to use the repository hook workflow described in Tooling and Validation. In practice this means:
- install the local
pre-commitorprekhooks before regular development; - make sure staged changes pass the configured lint and formatting checks;
- use Conventional Commit messages such as
feat(docs): add development conventions page.
The goal is not ceremony. The hooks give the project a predictable quality gate before review and keep commit history machine-readable for changelogs, release notes, and later triage.
Backlog Usage
Contributors should use Backlog.md whenever the work is more than a trivial mechanical edit. The detailed task workflow is documented in Backlog and Internal Docs, but the practical rule is simple:
- search for an existing task before starting;
- track non-trivial implementation work in a task;
- keep plans, scope changes, and validation notes attached to that task.
This keeps development history in one place and makes handoff between contributors or coding agents much easier.
Docstrings And Code Comments
Public-facing Python code should use Google Style docstrings. This is especially important for controllers, dynamics, task configuration, and helper functions that appear in the generated API reference.
At the moment, the repository lint configuration does not strictly enforce Google-style parameter sections, so this rule is not yet fully automated. Even so, contributors are expected to follow the style in new or substantially edited code so that generated documentation stays readable and consistent.
Use inline comments sparingly. Comments should explain intent, invariants, or non-obvious constraints, not restate what the code already says.
Repository Model
LAV2 is developed as a monorepo with a GitHub Flow style branching model.
- the package source, tasks, scripts, assets, and documentation live in one repository;
- contributors should branch from
mainfor scoped work and merge back through review; - changes should stay small enough that code, docs, and validation updates can land together.
This model works well for LAV2 because the simulation stack, controllers, tasks, and docs evolve together and frequently need coordinated changes across package boundaries.
Module Export Policy
Avoid broad __all__ exports unless a package-level convenience surface is genuinely useful for callers.
Prefer explicit imports and explicit re-export lists because they:
- keep the public surface easier to audit;
- reduce accidental namespace growth when helper symbols are added later;
- make static analysis, documentation generation, and refactoring less surprising;
- reduce the chance of import cycles or import-time side effects being pulled in implicitly.
If a module or package needs a curated convenience API, re-export only the small set of stable symbols that are meant to be public.
Module Design Style
Modules should stay small, focused, and easy to reason about. Prefer KISS-style decomposition over large utility modules that mix unrelated responsibilities.
For groups of similar implementations, use abstract base classes or a small shared interface to keep behavior aligned. This is already the pattern used by the controller and dynamics stacks, where shared bases help preserve naming, method signatures, and data layout expectations across concrete implementations.
A good rule of thumb is:
- one module should solve one coherent problem;
- shared behavior should be factored into a base class or helper only when it clarifies the design;
- sibling implementations should differ in behavior, not in naming conventions or call shapes.
Dependency Management
Dependencies should remain isolated, deterministic, and easy to reason about.
- use
uvfor installation, locking, syncing, and command execution across the full development lifecycle; - keep dependency groups separated by purpose, such as documentation, development tooling, and optional runtime integrations;
- prefer optional dependencies for backend-specific or framework-specific integrations instead of making the base install unnecessarily heavy;
- update the lockfile whenever dependency declarations change.
This helps contributors reproduce the same environment locally and keeps framework-specific integrations decoupled from the core package.
Interface And Parity Expectations
When a subsystem has both NumPy and Torch implementations, contributors should preserve interface parity unless there is a documented backend-specific reason not to. Matching names, shapes, and semantics are important for debugging, training backends, and future parallel implementations.
Similarly, contributors should prefer existing source-of-truth configuration objects such as VehicleParams over new hard-coded constants.
Documentation Maintenance
If a change affects a public workflow, entrypoint, dependency path, or contributor routine, update the relevant documentation in the same change. For most workflow changes that means updating:
- the public docs under
docs/; - contributor workflow notes such as Tooling and Validation;
- related internal references under
backlog/docs/when they are still part of the engineering process.
Keeping the documentation in the same change as the implementation prevents drift and makes reviews more reliable.