Code Style Guide
Consistent code style makes the codebase easier to navigate, review, and maintain. This guide covers the standards enforced by our tooling and the conventions followed by the team.
TypeScript / JavaScript
Section titled “TypeScript / JavaScript”Tooling
Section titled “Tooling”| Tool | Config File | Purpose |
|---|---|---|
| ESLint | web/.eslintrc.cjs | Linting and rules |
| Prettier | web/.prettierrc | Formatting |
| TypeScript | web/tsconfig.json | Type checking |
# Run lintingcd webnpm run lint
# Run formattingnpm run format
# Run type checkingnpm run typecheckStrict Mode
Section titled “Strict Mode”TypeScript is configured with strict: true. This enables all strict checks:
strictNullChecks— no implicitnullorundefinednoImplicitAny— every variable must have a typestrictFunctionTypes— function parameter types are contravariantstrictPropertyInitialization— class properties must be initialized
Do not add @ts-ignore or @ts-expect-error unless absolutely necessary,
and always include a comment explaining why.
Naming Conventions
Section titled “Naming Conventions”| Kind | Convention | Example |
|---|---|---|
| Variables | camelCase | stepCount, isTerminal |
| Functions | camelCase | runGenerator, createStep |
| Interfaces | PascalCase | StepInput, VisualAction |
| Type aliases | PascalCase | GeneratorFunction, ArrowHead |
| Constants | UPPER_SNAKE | STEP_FORMAT_VERSION, Z_INDEX |
| Enum members | UPPER_SNAKE | ElementShape.ROUNDED_RECT |
| File names | kebab-case | generator-runner.ts |
| React components | PascalCase | VisualizationPanel.tsx |
Type Annotations
Section titled “Type Annotations”Always annotate function parameters and return types. Let TypeScript infer local variables when the type is obvious.
// GOOD: Annotated parameters and return typefunction createStep(input: StepInput): Step { const index = steps.length; // Inferred as number — no annotation needed // ...}
// BAD: Missing parameter typesfunction createStep(input) { // ...}
// BAD: Unnecessary annotation on obvious localconst index: number = steps.length;Readonly by Default
Section titled “Readonly by Default”Use readonly for interface properties unless mutation is required. This
communicates intent and catches accidental mutations at compile time.
// GOOD: Immutable by defaultinterface StepInput { readonly id: string; readonly title: string; readonly state: Record<string, unknown>;}
// BAD: Mutable when it should not beinterface StepInput { id: string; title: string;}Avoid any
Section titled “Avoid any”Use unknown instead of any when the type is genuinely unknown. Then
narrow with type guards.
// GOOD: unknown + type guardfunction processValue(value: unknown): string { if (typeof value === "string") { return value; } return String(value);}
// BAD: any bypasses all type safetyfunction processValue(value: any): string { return value;}String Formatting
Section titled “String Formatting”Use template literals for string interpolation. Prefer them over concatenation for readability.
// GOODconst message = `Found ${target} at index ${index}!`;
// ACCEPTABLE for complex multi-line stringsconst explanation = `array[${mid}] = ${array[mid]} < target ${target}. ` + `Target must be in the right half.`;
// BAD: String concatenationconst message = "Found " + target + " at index " + index + "!";Python
Section titled “Python”Tooling
Section titled “Tooling”| Tool | Config File | Purpose |
|---|---|---|
| Ruff | python/pyproject.toml | Linting and formatting |
| mypy | python/pyproject.toml | Static type checking |
cd python
# Lintruff check .
# Auto-fix lint issuesruff check --fix .
# Formatruff format .
# Type checkmypy src/mypy Strict Mode
Section titled “mypy Strict Mode”Like TypeScript, Python uses strict type checking. The mypy configuration includes:
strict = truedisallow_untyped_defs = truedisallow_any_generics = true
All function signatures must have type annotations.
Naming Conventions
Section titled “Naming Conventions”| Kind | Convention | Example |
|---|---|---|
| Variables | snake_case | step_count, is_terminal |
| Functions | snake_case | run_generator, create_step |
| Classes | PascalCase | StepSequence, VisualAction |
| Constants | UPPER_SNAKE | STEP_FORMAT_VERSION |
| Module files | snake_case | binary_search.py |
| Package dirs | snake_case | eigenvue/generators/ |
| Private members | _prefix | _validate_steps |
Type Annotations
Section titled “Type Annotations”Annotate all function parameters and return types. Use from __future__ import annotations at the top of every file for forward reference support.
from __future__ import annotations
from typing import Any
def generate(inputs: dict[str, Any]) -> list[Step]: """Generate algorithm steps.""" array: list[int] = list(inputs["array"]) target: int = inputs["target"] # ...Docstrings
Section titled “Docstrings”Use NumPy-style docstrings for all public functions and classes:
def run_generator( algorithm_id: str, inputs: dict[str, Any] | None = None,) -> list[dict[str, Any]]: """Run an algorithm generator and return validated step dicts.
Parameters ---------- algorithm_id : str The algorithm identifier. inputs : dict or None Custom input parameters. If None, uses algorithm defaults.
Returns ------- list[dict[str, Any]] Validated step dicts in camelCase wire format.
Raises ------ ValueError If no generator is registered for this algorithm. """File Naming Conventions
Section titled “File Naming Conventions”TypeScript Files
Section titled “TypeScript Files”| Kind | Pattern | Example |
|---|---|---|
| Generator | generator.ts | binary-search/generator.ts |
| Tests | *.test.ts | generator.test.ts |
| Fixtures | *.fixture.json | found.fixture.json |
| Metadata | meta.json | binary-search/meta.json |
| Layout | kebab-case.ts | array-with-pointers.ts |
| React component | PascalCase.tsx | VisualizationPanel.tsx |
| Engine module | PascalCase.ts | GeneratorRunner.ts |
| Shared types | kebab-case.ts | step.ts |
Python Files
Section titled “Python Files”| Kind | Pattern | Example |
|---|---|---|
| Generator | snake_case.py | binary_search.py |
| Tests | test_*.py | test_binary_search.py |
| Package init | __init__.py | generators/__init__.py |
| Utility module | snake_case.py | math_utils.py |
Algorithm Directories
Section titled “Algorithm Directories”Algorithm directory names use kebab-case and must match the algorithm ID:
algorithms/classical/binary-search/ # ID: "binary-search"algorithms/deep-learning/perceptron/ # ID: "perceptron"algorithms/generative-ai/self-attention/ # ID: "self-attention"Import Ordering
Section titled “Import Ordering”TypeScript
Section titled “TypeScript”Imports should be ordered in three groups, separated by blank lines:
- External dependencies (npm packages)
- Internal aliases (
@/...) - Relative imports (
./...,../...)
Within each group, sort alphabetically.
// 1. Externalimport { describe, it, expect } from "vitest";
// 2. Internal aliasesimport { runGenerator } from "@/engine/generator";import type { Step, VisualAction } from "@/shared/types/step";
// 3. Relativeimport binarySearchGenerator from "../generator";import foundFixture from "./found.fixture.json";Separate import type from value imports when both exist:
import { createGenerator } from "@/engine/generator";import type { VisualAction } from "@/engine/generator";Python
Section titled “Python”Follow the standard Python import ordering (enforced by Ruff’s isort rules):
- Standard library
- Third-party packages
- Local imports
# 1. Standard libraryfrom __future__ import annotations
import jsonimport sysfrom pathlib import Pathfrom typing import Any
# 2. Third-partyimport pytest
# 3. Localfrom eigenvue._step_types import Step, VisualAction, CodeHighlightfrom eigenvue.runner import run_generatorComments Philosophy
Section titled “Comments Philosophy”When to Comment
Section titled “When to Comment”Comment the why, not the what. If the code is clear, it does not need a comment explaining what it does. It does need a comment explaining why it does it that way.
// GOOD: Explains WHY// Kahan summation maintains a running compensation term that tracks// the low-order bits lost during each addition, achieving O(1) error// independent of the number of terms.let sum = 0;let compensation = 0;for (const w of weights) { const y = w - compensation; const t = sum + y; compensation = (t - sum) - y; sum = t;}
// BAD: Explains WHAT (obvious from the code)// Add w to sumsum += w;When Comments Are Required
Section titled “When Comments Are Required”-
Mathematical derivations. When the code implements a formula, cite the formula and explain the mapping from math to code.
-
Non-obvious invariants. When a loop or algorithm maintains an invariant that is not immediately apparent.
-
Performance decisions. When you chose a specific approach for performance reasons (e.g., Kahan summation instead of naive reduce).
-
Cross-language parity notes. When TypeScript and Python implementations differ in their approach to achieve identical output.
-
Safety constraints. When a copy is necessary to avoid shared mutable state (the
[...array]pattern in state snapshots).
File Headers
Section titled “File Headers”Every source file should have a @fileoverview JSDoc comment (TypeScript) or
a module docstring (Python) explaining its purpose:
/** * @fileoverview Binary Search — Step Generator * * Generates a step-by-step visualization of iterative binary search on a * sorted array of numbers. * * ALGORITHM: * Given a sorted array and a target value, binary search maintains two * pointers (left, right) defining the search space... */"""Binary Search — Step Generator (Python)
Produces step-by-step visualization data identical to the TypeScriptgenerator when given the same inputs."""Documentation Style
Section titled “Documentation Style”JSDoc (TypeScript)
Section titled “JSDoc (TypeScript)”Use JSDoc for all exported functions, interfaces, and type aliases:
/** * Runs a generator with the given inputs and returns a validated StepSequence. * * @typeParam TInputs - The algorithm's input parameter shape. * @param definition - The generator definition (from `createGenerator()`). * @param inputs - The algorithm's input parameters. * @param options - Optional configuration (max steps, etc.). * @returns A complete, validated StepSequence. * @throws {GeneratorError} If the generator fails or produces invalid output. * * @example * ```typescript * const result = runGenerator(binarySearchGenerator, { * array: [1, 3, 5, 7, 9], * target: 5, * }); * ``` */NumPy-Style Docstrings (Python)
Section titled “NumPy-Style Docstrings (Python)”Use the NumPy docstring format with sections for Parameters, Returns, Raises, and Examples:
def verify_fixture(fixture_path: Path) -> bool: """Verify that a fixture round-trips through Python dataclasses.
Args: fixture_path: Path to a .fixture.json file.
Returns: True if the round-trip produces identical JSON, False otherwise.
Examples -------- >>> verify_fixture(Path("fixtures/binary-search-found.fixture.json")) True """Key Principles
Section titled “Key Principles”-
Document contracts, not implementations. Describe what a function promises to do, not how it does it internally.
-
Include examples. A short usage example is worth more than a paragraph of description.
-
Document mathematical invariants. When a function enforces or relies on mathematical properties, state them explicitly with the notation.
-
Keep docs close to code. Inline documentation (JSDoc, docstrings) stays up to date better than separate documentation files.