Coding Conventions¶
This document is a reusable template for code-style rules. Copy it into a new repository and replace the placeholders, examples, and tool names with the standards used by that project.
How To Adapt This File¶
Set the target language or languages for the repository.
Replace formatter, linter, type checker, and test runner names with the tools you actually use.
Keep only the sections that apply to the codebase.
Add project-specific rules where consistency matters.
Table Of Contents¶
Language Standards¶
Document the language versions and tools used by the repository.
Python¶
Minimum supported version:
<python-version>Maximum version tested:
<python-version>Code style: PEP 8 or the repository’s equivalent
Formatting tool:
<formatter>Linting tool:
<linter>Type checker:
<type-checker>
Shell Scripts¶
Shebang:
#!/usr/bin/env bashor the repository’s standardShell version:
<shell-version>Linting tool:
<shell-linter>Formatting rule:
<indentation-and-style>
Python Coding Style¶
Indentation¶
Use the indentation style required by the language.
Keep indentation consistent within a file.
Avoid mixing tabs and spaces.
# Good
class ExampleProcessor:
def __init__(self, value: int) -> None:
self.value = value
def double(self) -> int:
return self.value * 2
# Bad
class ExampleProcessor:
def __init__(self, value: int) -> None:
self.value = value
Line Length¶
Keep lines within the repository’s configured limit.
Break long expressions at logical boundaries.
Prefer implicit continuation inside parentheses.
# Good
result = (
first_value
+ second_value
+ third_value
)
# Good
formatted_message = build_message(
prefix=prefix,
payload=payload,
suffix=suffix,
)
# Bad
result = first_value + second_value + third_value + fourth_value + fifth_value
Imports¶
Group imports by origin.
Keep the order consistent across the codebase.
Remove unused imports promptly.
# Standard library
from pathlib import Path
from typing import Final, Optional
# Third-party
import numpy as np
# Local
from .parser import parse_input
Blank Lines¶
Use two blank lines between top-level definitions.
Use one blank line between methods inside a class.
Use blank lines sparingly inside a function to separate logical steps.
class ExampleProcessor:
def __init__(self, value: int) -> None:
self.value = value
def double(self) -> int:
return self.value * 2
def parse_value(raw_value: str) -> int:
cleaned_value = raw_value.strip()
return int(cleaned_value)
Whitespace¶
# Good
x = 1 + 2
items = [1, 2, 3]
function(arg1, arg2)
# Bad
x=1+2
items=[1,2,3]
function (arg1)
Type Hints And Docstrings¶
Type Hints¶
Add type hints to public functions and methods.
Add type hints to internal helpers when they improve clarity.
Prefer explicit types over implied behavior.
Keep annotations readable, especially for long signatures.
from collections.abc import Sequence
from typing import Final
DEFAULT_LIMIT: Final[int] = 10
def normalize_values(values: Sequence[float]) -> list[float]:
return [value / DEFAULT_LIMIT for value in values]
Optional And Union Types¶
from pathlib import Path
from typing import Optional
def load_config(path: Path, fallback: Optional[str] = None) -> str:
if fallback is not None:
return fallback
return path.read_text(encoding="utf-8")
Complex Types¶
from collections.abc import Callable, Sequence
Processor = Callable[[Sequence[str]], list[str]]
def process_items(items: Sequence[str], processor: Processor) -> list[str]:
return processor(items)
Docstrings¶
Use docstrings for public modules, classes, functions, and methods when they add value.
Keep docstrings short and direct.
Describe what the code does, not how it does it.
Match the docstring style used by the repository.
"""Utilities for formatting output values."""
from pathlib import Path
def format_output(path: Path, value: str) -> str:
"""Format a value for writing to disk."""
return f"{path.name}: {value}"
class OutputWriter:
"""Write formatted output to a destination."""
def __init__(self, destination: Path) -> None:
"""Store the destination path."""
self.destination = destination
Module Docstrings¶
Use module docstrings when a file exposes public behavior or needs a short summary.
"""Parse and validate application input."""
Naming Conventions¶
Module Names¶
Use lowercase letters and underscores.
Keep names descriptive and concise.
Match the feature or responsibility of the module.
# Good
input_parser.py
result_formatter.py
cache_store.py
# Bad
InputParser.py
utils.py
misc.py
Class Names¶
Use PascalCase for classes.
Prefer noun-based names.
Avoid suffixes like
Class.
class InputParser:
pass
class ResultFormatter:
pass
Function And Method Names¶
Use snake_case for functions and methods.
Prefer action verbs for behavior.
Keep names specific enough to understand without the implementation.
def parse_input_file():
pass
def format_result_value():
pass
Variable Names¶
Use snake_case for variables.
Choose names that reflect purpose.
Avoid one-letter names except in small mathematical contexts.
value_count = 3
input_path = Path("input.txt")
normalized_values = []
Constants¶
Use uppercase_with_underscores for module-level constants.
Use
Finalwhere the language ecosystem supports it.
from typing import Final
MAX_RETRIES: Final[int] = 3
DEFAULT_TIMEOUT_SECONDS: Final[int] = 30
Private Members¶
Prefix internal helpers and attributes with a single underscore.
Use double underscores only when name collisions must be avoided.
class CacheStore:
def __init__(self) -> None:
self._items: dict[str, str] = {}
def _clear_expired_items(self) -> None:
self._items.clear()
Code Organization¶
Module Structure¶
A module that exposes public APIs can follow a structure like this:
"""Utilities for processing input data."""
from __future__ import annotations
from pathlib import Path
DEFAULT_SEPARATOR = ","
class InputProcessor:
"""Process input values from files or strings."""
def __init__(self, separator: str = DEFAULT_SEPARATOR) -> None:
"""Store the separator used during parsing."""
self.separator = separator
def parse(self, raw_value: str) -> list[str]:
"""Split a raw value into tokens."""
return [part.strip() for part in raw_value.split(self.separator)]
def load_text(path: Path) -> str:
"""Load text from a file path."""
return path.read_text(encoding="utf-8")
Class Organization¶
Put class constants near the top of the class.
Group public methods before private helpers when that improves readability.
Keep object state minimal and explicit.
class InputProcessor:
DEFAULT_SEPARATOR = ","
def __init__(self, separator: str = DEFAULT_SEPARATOR) -> None:
"""Store the separator used during parsing."""
self.separator = separator
def parse(self, raw_value: str) -> list[str]:
"""Split a raw value into tokens."""
return self._split(raw_value)
def _split(self, raw_value: str) -> list[str]:
"""Split the value using the configured separator."""
return [part.strip() for part in raw_value.split(self.separator)]
Public API Boundaries¶
Keep public APIs small and stable.
Validate inputs at the edges of the system.
Convert external formats into internal data structures early.
Keep internal helpers private unless they are meant to be reused.
Shell Script Conventions¶
Use shell scripts for automation, setup, or maintenance tasks.
Keep scripts deterministic and easy to read.
Quote variables and command substitutions.
Prefer explicit error handling.
#!/usr/bin/env bash
set -euo pipefail
input_file="${1:?input file required}"
output_file="${2:?output file required}"
printf 'Processing %s -> %s\n' "$input_file" "$output_file"
Shell Script Doc Comments¶
Use short comments to document script purpose and usage when it helps a future maintainer.
#!/usr/bin/env bash
# Usage: bash scripts/generate_report.sh <input> <output>
Tools And Analysis¶
Document the repository’s toolchain here.
Formatter:
<formatter>Linter:
<linter>Type checker:
<type-checker>Test runner:
<test-runner>Coverage tool:
<coverage-tool>
Use automated checks to catch style regressions early.
Best Practices¶
Prefer small, focused changes.
Keep code and tests aligned.
Make error messages actionable.
Document assumptions that are easy to forget.
Remove dead code rather than leaving it commented out.
Update examples when the public API changes.
Examples¶
Example Module¶
"""Format a path and value for display."""
from pathlib import Path
def format_entry(path: Path, value: str) -> str:
"""Create a human-readable entry."""
return f"{path.name}: {value}"
Example Class¶
class EntryFormatter:
"""Format entries for output."""
def __init__(self, prefix: str) -> None:
"""Store the prefix used for formatting."""
self.prefix = prefix
def format(self, value: str) -> str:
"""Format a value with the configured prefix."""
return f"{self.prefix}{value}"
Example Function With Validation¶
from pathlib import Path
def read_non_empty_text(path: Path) -> str:
"""Read text from a file and reject empty content."""
text = path.read_text(encoding="utf-8")
if not text.strip():
raise ValueError("file content must not be empty")
return text
Comments¶
Use comments to explain intent, constraints, or trade-offs.
Avoid comments that simply repeat the code.
Remove stale comments when code changes.