Skip to content

3.3 Coding Standards

Consistent code reduces the time spent learning individual preferences. These standards provide a familiar baseline across Moonitoring projects unless the language itself enforces a different convention.

3.3.1 Formatting

Format every file with the official formatter of its language, with its default settings. Nobody argues about style when a tool decides it, and the same tool runs on every machine and in CI.

Language Formatter Indentation
Rust cargo fmt (rustfmt) 4 spaces
Python ruff format 4 spaces
TypeScript and Vue ESLint with the stylistic rules of Nuxt (pnpm lint:fix) 2 spaces
Terraform terraform fmt 2 spaces
Slint .editorconfig, with the indentation Slint's own tools write 4 spaces
Files without an official formatter: YAML, JSON, Markdown .editorconfig 2 spaces

Every project must include at least one formatting configuration file in its repository root. The configuration must allow the IDE, a development plugin, or a command-line formatter to apply the project's formatting rules automatically.

The appropriate file depends on the stack and may include .editorconfig, rustfmt.toml, pyproject.toml, or another formatter-specific configuration. CI fails when a file is not formatted.

Formatting configuration is required

Formatting must be reproducible from the files committed to the repository. It must not depend only on a developer's local IDE settings.

The editor applies the formatting by itself (decided 2026-11-10). Next to .editorconfig and the formatter's configuration, every repository commits .vscode/settings.json, which formats a file with its language's formatter when it is saved, and .vscode/extensions.json, which recommends the extensions that read these files. Editors that read .editorconfig on their own, such as the JetBrains IDEs and Zed, need nothing more.

3.3.2 Naming Conventions

Our two main languages, Rust and Python, agree on almost every name:

Element Rust Python Example
Types and classes PascalCase PascalCase AnimalHealthService
Functions and methods snake_case snake_case calculate_health_score
Variables and parameters snake_case snake_case animal_health_score
Constants UPPER_SNAKE_CASE UPPER_SNAKE_CASE MAX_RETRIES
Modules and files snake_case snake_case health_score.rs, health_score.py
Private fields snake_case, no visibility modifier _snake_case (by convention) Rust: animal_health_score; Python: _animal_health_score

When a name contains multiple words, separate them with underscores according to the convention shown above.

Private fields are read only through getters; entities and types are immutable, as described in Encapsulation and Immutability.

Rust field visibility

Rust fields without a visibility modifier are private to their module and its descendants (Rust Reference). A leading _ is allowed but does not control visibility; use it for intentionally unused names, not to mark fields private.

3.3.3 Language-Enforced Conventions

The only reason to deviate from these rules is when the language or its official tooling enforces a different coding style: the language's linter (cargo clippy, ruff check) has the last word. For a language not listed here, follow the output and conventions of its official formatter instead of overriding them to imitate another language.

Even when an exception applies, the repository must still include the relevant root-level formatting or tooling configuration so every contributor and automated workflow produces the same result.

3.3.4 File Size

A source file holds one thing and stays short (decided 2026-11-10): aim for under 400 lines, split a file that passes 600, and let no file reach 1,000. A long file holds several things: several use cases, the handlers of several resources, every request and response type of a domain.

  • Split by responsibility, not by line count: one use case per file, one file of handlers per resource, one file per group of related types.
  • Tests are split like the code they test: one test file per resource or behavior, with shared set-up in a common module.
  • Generated files and lock files are exempt.

3.3.5 Comments

Ideally, code should be self-documenting. Richer comments are appropriate for public interfaces, methods, classes or even complex internal logic. There are three types of comments:

  1. Inline comments: These are short comments that explain a single line of code. They should be used sparingly and only when the code is not self-explanatory.
  2. Block comments: These are longer comments that explain a section/subroutine of code. They should be used to provide context and explain the purpose of the code. (Mainly used for internal complex logic)
  3. Documentation comments: These are comments that are used to generate documentation for the code. They should be used to provide information about the code's purpose, parameters, and return values. (Mainly used for shared/public logic)

Every use case has a documentation comment that says what the operation does and who is expected to call it: which controller, consumer, scheduler or other use case, and on whose behalf (see Package Layout).

Style and formatting of comments

Comments should be written with the idiomatic style and formatting of the language. Block and documentation comments can be expressive and include examples or documentation links, concepts must always be introduced progressively and only use technical terms when necessary.