Files
handbook/docs/standards.md
T
dsql 350884c2b3 deploy: whole-flow as plain text, restore v2 diagram shape; admonition variety
- 'The whole flow' is now plain prose, not an abstract box.
- Restore the v2 diagram shape (git -> staging via develop-if-gitflow AND
  git -> deployment, converging on docker) instead of the linear main-only
  one, keeping the simplified 'libs (rethink-public, pip)' wording.
- Spread admonition types for variety across pages: index agent-callout
  tip -> example; environments uv.lock note -> info; standards logging
  note -> info. (deploy already mixes success/danger/example/warning/tip.)

Verified in-browser: whole-flow reads as text, diagram renders in the v2
shape; mkdocs build --strict clean.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-01 03:04:51 -04:00

5.2 KiB

Coding Standards

The house standards every Rethink Studios project follows. They keep our code consistent, readable, and predictable across the whole suite.

Files and style

  • Files end with a single trailing newline (LF / Unix line endings), and carry no trailing whitespace on any line.
  • 4 spaces for indentation by default. Respect language norms — TS/JS use 2 spaces, Go uses tabs — and when editing an existing file, follow that file's existing indentation.
  • flake8 clean, max line length 120. Type hints on public functions.

A well-formed module — module docstring, type hints, lowercase-start docstring:

"""async key/value store backed by a single json file"""

from pathlib import Path


def load_state(path: Path) -> dict[str, str]:
    """read the json state file, returning an empty dict if it's missing"""
    if not path.exists():
        return {}
    return _read_json(path)

flake8 tells you the moment you drift — keep the tree clean:

$ flake8
./aiokv/store.py:14:80: E501 line too long (96 > 79 characters)
./aiokv/store.py:22:1: F401 'json' imported but unused
./aiokv/store.py:31:5: E303 too many blank lines (3)

!!! tip "Run it locally" Wire the shared config into an alias so every project lints the same way: alias flake8='flake8 --config ~/.config/flake8' (max line 120). See the Workflow page.

Documentation

  • Public functions get a docstringlowercase-start, no trailing period.
  • Keep inline comments minimal; prefer docstrings. Use an inline comment only where the code is genuinely complex.
  • Each module/file states its scope and purpose via a module docstring (header string) — not a license or copyright header.
  • Every library and project has a README with the install line, what it does, and a usage example.

!!! note "Licensing — no per-file headers" Don't prepend license/copyright boilerplate to source files. If a repo needs a license, it's a single top-level LICENSE file — never repeated per file. Most internal repos don't carry one; add it only when a repo is meant for outside use and the terms are decided.

=== "Do"

```python
def mask_secret(value: str, keep: int = 4) -> str:
    """mask all but the last ``keep`` characters of a secret"""
    if len(value) <= keep:
        return "*" * len(value)
    return "*" * (len(value) - keep) + value[-keep:]
```

=== "Don't"

```python
# Masks a secret.   <- license-header-style noise, capitalized, trailing period
def mask_secret(value, keep=4):   # no type hints
    # loop over the chars and hide them
    return "*" * (len(value) - keep) + value[-keep:]   # crashes if short
```

Quality and error handling

Fail loud — never swallow exceptions. Catch the specific exception and log it; don't paper over failures with a bare except.

=== "Do"

```python
import logging

log = logging.getLogger(__name__)


def fetch(url: str) -> bytes:
    """fetch ``url``, logging and re-raising on failure"""
    try:
        return _client.get(url).content
    except TimeoutError:
        log.warning("fetch timed out: %s", url)
        raise
```

=== "Don't"

```python
def fetch(url):
    try:
        return _client.get(url).content
    except Exception:
        pass   # swallowed — the caller has no idea anything broke
```

When something does break, a loud failure gives you a real traceback to act on:

Traceback (most recent call last):
  File "run.py", line 42, in <module>
    data = fetch("https://example.test/feed")
  File "aioweb/session.py", line 88, in fetch
    return self._client.get(url).content
TimeoutError: request timed out after 30s

…and the log line that precedes it tells you where to look:

2026-06-29 14:03:11,204 WARNING aioweb.session fetch timed out: https://example.test/feed

!!! info "Logging belongs to the app, not the library" Libraries emit onlylog = logging.getLogger(__name__) and nothing else. Handlers, levels, and formatting are configured once at the application entry point, so a lib never dictates how its host logs.

Service compose

A deployable service ships a compose.yaml that names nothing repo-specific — the deploy layer injects identity and host paths. See the Deploy guide for the full convention and the variables you can rely on. The short version:

=== "Right"

```yaml
services:
  svc:
    user: "1337:1337"
    restart: unless-stopped
    volumes:
      - ${LOGS_DIR:-./logs}:/app/logs
      - profile:/app/profile

volumes:
  profile:
```

=== "Wrong (causes collisions)"

```yaml
services:
  nova:
    container_name: nova                 # repo-specific name -> collides
    volumes:
      - /srv/logs/ricky/nova:/app/logs   # hardcoded host path
      - nova_profile:/app/profile        # repo-prefixed volume

volumes:
  nova_profile:
```

Generic service key svc, no container_name, host paths from ${...} variables, and bare volume names — that's what makes a service collision-proof on the fleet.