-
Notifications
You must be signed in to change notification settings - Fork 2
coding standards
Pre-Alpha. This page describes behavior that may change.
Ze has a set of coding rules that apply to every contribution, whether you write the code by hand or with AI assistance. They live in .claude/rules/ in the repo and main/CLAUDE.md points to them. This page summarises the ones most contributors trip over. The rules in .claude/rules/ are the authoritative version.
TDD. Tests are written before the implementation. The test exists, fails for the right reason, then the implementation makes it pass. No exceptions. This is not a style preference, it is the method by which the codebase stays coherent under heavy AI-assisted development.
Specs drive the work. No code without a spec. The spec is written under plan/ before any code is written, and it defines what "done" looks like. The maintainer reviews specs before implementation starts.
make ze-verify must pass before submission. That target runs the linter, the unit suite, the functional suite, and the ExaBGP suite. If any of them fails, the patch is not ready.
No partial deliveries. Code, tests, and documentation land in one piece. A change without tests is not a change. A change without documentation is not a change.
Registration, not imports. Components and plugins register themselves at startup. Core code discovers them through registries and never imports them directly. If you find yourself adding a direct import from one component to another, you are probably violating a boundary.
register.go at the top of each plugin and each component. The init() function registers the plugin with the family, capability, schema, and command registries. This is the unifying pattern.
Small files. Ze does not have a hard limit, but files that grow past about 500 lines typically need to be split along a natural seam. The existing files are the benchmark.
One concern per package. internal/component/bgp/reactor/ is the reactor. internal/component/bgp/wire/ is the wire parser. When a package starts doing two things, split it.
Tests next to code. foo.go has foo_test.go next to it. Functional tests live under test/ as .ci files. Do not mix the two styles.
Buffer-first encoding. Every BGP wire write goes into a pre-allocated buffer with a signature like WriteTo(buf, off) int. Allocating a new buffer per write is a performance regression. The rule is documented in .claude/rules/buffer-first.md and the review process catches violations.
No naked panic on user input. Panics are for programming errors, not for malformed peers. BGP peers are hostile until proven otherwise, and the wire parser must return errors, not panic.
Context everywhere. Any function that does I/O takes a context.Context as its first argument. Cancellation propagates. No background goroutines without a context.
Structured logging. slog with hierarchical subsystem names. Every log line has a subsystem tag. No fmt.Println, no log.Print.
Errors wrap. fmt.Errorf("parse config: %w", err) is the pattern. Bare return err loses the call stack, which makes production debugging harder.
No ioutil. It has been deprecated for years. Use os, io, and fs.
make ze-lint26 linters run on every commit. The .golangci.yml at the root is the authoritative list. Do not disable linters in your PR to make it green. Fix the finding, or if the finding is wrong, fix the linter rule separately.
Unit tests are cheap. Every public function has unit tests.
Functional tests are .ci files. Expected wire bytes, expected exit codes, expected log lines. The .ci format is documented under docs/functional-tests.md.
Fuzz every external-input parser. If your code reads bytes from a peer, a script, or the network, it has a fuzz harness in the same package.
No flaky tests. A flake is a bug. ze-test bgp encode --count 10 <test> is the stress-test mode that catches flakes.
make ze-verify-changed first. Runs the full verification on the packages your branch touched. Fast enough to run on every save.
Sign every commit. git commit -s to certify acceptance of the CLA.
One logical change per commit. Do not bundle a feature and a refactor.
Commit message bodies are paragraphs, not bullet lists. The "why" goes in the body, the "what" goes in the subject.
Never commit secrets. The .git/hooks/pre-commit hook catches obvious cases but is not foolproof.
Never force push to main. Force pushing is fine on your own branch before review, but main is sacrosanct.
The maintainer reviews contributions through Claude Code's /ze-review-deep workflow, which runs nine specialised review agents in parallel. Running the same workflow on your own branch before submission catches most of what the maintainer would have asked you to fix.
/ze-review-deep # Full branch review
/ze-review-deep path/to/file.go # Focused on one file
/ze-review-spec # Check implementation against spec
/ze-review-docs # Check documentation
The Claude Code cheat sheet lists every command.
The canonical list, with the ones most contributors reach for.
| Rule file | Covers |
|---|---|
session-start.md |
What to read at the start of a session. |
before-writing-code.md |
The pre-implementation checklist. |
tdd.md |
TDD workflow. |
testing.md |
Test coverage expectations. |
planning.md |
How to write a spec. |
rfc-compliance.md |
RFC adherence. |
git-safety.md |
Commit and push rules. |
buffer-first.md |
Buffer-first encoding. |
documentation.md |
Documentation expectations. |
The .claude/patterns/ directory has the "how to add a thing" patterns: CLI commands, web endpoints, plugins, config options, registration. Read the pattern before you add a new one.
- Contributing for the contribution workflow.
-
Building for
make ze-verifyand the rest of the make targets. - Claude Code for the project-specific slash commands.
-
main/CLAUDE.mdfor the canonical rules pointer.
Adapted from main/CLAUDE.md and main/CONTRIBUTING.md.
Unreviewed draft. This wiki was authored in bulk and has not been reviewed. File corrections on the issue tracker.
- Overview
- YANG Model
- Editor Workflow
- Archive and Rollback
- System
- Interfaces
- BFD
- FIB
- Firewall
- Traffic Control
- L2TP/PPP
- VPP Data Plane
- RPKI
- TACACS+ AAA
- Fleet
- BGP
- Starting and Stopping
- Show Commands
- Monitoring
- Logging
- Operational Reports
- Healthcheck
- MRT Analysis
- Upgrade and Restart
- Storage
- Policy
- Core
- Resilience
- Validation
- Capabilities
- Address Families
- Protocol
- Subsystems
- Infrastructure
- Route Server at an IXP
- Transit Edge with RPKI
- Public Looking Glass
- ExaBGP Migration Walkthrough
- FlowSpec Injection
- Chaos-Tested Peering
- AS Path Topology