# Code conventions These apply to every application built inside a Harbor confinement, in any language. They are short on purpose. A rule nobody remembers is not a rule. ## 1. Say why, not what The code already says what it does. A comment that restates it goes stale the moment the code changes and then actively lies. ```go // BAD: increments the counter count++ // GOOD: retries are counted per host, not per request, because a single bad // host would otherwise exhaust the global budget and stall healthy traffic. count++ ``` Write the reason, the rejected alternative, or the surprise. Nothing else. ## 2. Validate at the boundary, trust inside Check input once, where it enters the program — the HTTP handler, the CLI argument parser, the file reader. After that point, code may assume the value is good. Checking the same thing in five places means five places to get it wrong, and it hides which one is authoritative. Anything that reaches a shell, a path, or a query is a boundary. Reject with an allowlist (what is permitted), never a denylist (what is forbidden) — you cannot enumerate everything an attacker will try. ## 3. Errors travel with context, and are never swallowed An error that reaches a human should say what was being attempted, not just what failed. `permission denied` is useless. `open config /etc/app/db.yml: permission denied` is actionable. Never write an empty catch block or discard an error return. If an error truly is safe to ignore, say so in a comment explaining why — that comment is the whole point. ## 4. Make the failure mode the obvious one Given a choice, pick the design where a mistake produces a loud crash instead of quiet wrong behaviour. A service that refuses to start with a bad config is better than one that starts and serves wrong answers. ## 5. One reason to change per unit A function, file, or package should have one job. The test is whether you can describe it without "and". "Parses the manifest and writes the firewall rules" is two things wearing one coat. ## 6. Tests describe behaviour, not implementation A test named `TestFoo` that asserts internal state breaks every time you refactor, and tells a reader nothing. A test named `TestRejectsNamesThatLookLikeShellArguments` still passes after a rewrite, and documents a decision. Test the things that would be expensive to get wrong: boundaries, security checks, anything derived by arithmetic, anything with an off-by-one. ## 7. Dependencies are a liability Every dependency is code you did not write, cannot see, and must keep updated. Add one when it does something genuinely hard — cryptography, compression, parsing a real protocol. Do not add one to avoid writing twenty lines. ## 8. Secrets never touch the repository Not in code, not in config, not in a comment, not in a test fixture, not in a commit message. If a secret is ever committed, it is burned — rotate it, do not just delete the line. Git remembers. Read secrets from the environment or a file the deployment provides. ## 9. Formatting is not a discussion Use the language's standard formatter, on save, with no configuration. `gofmt`, `prettier`, `black`, `rustfmt`. Time spent arguing about layout is time not spent on the thing that matters. ## 10. Commits explain a change, not a file list The diff already lists the files. The message says why the change was needed and what would break without it. Write it in the imperative, as an instruction to the codebase: "reject names containing path separators", not "fixed stuff".