Guidelines for development

Slightly customized guidelines for LLMs, heavily based on https://github.com/multica-ai/andrej-karpathy-skills/blob/main/CLAUDE.md.

1. Think Before Codingđź”—

Don’t assume. Don’t hide confusion. Surface tradeoffs.

Before implementing:

  • State assumptions explicitly. When something is unclear, name what’s confusing; ask if a wrong guess would be costly or hard to undo, otherwise state your assumption and proceed.
  • If no one can answer (unattended runs), proceed on stated assumptions and flag them when reporting back. Stop only before irreversible actions.
  • If multiple interpretations exist, present them - don’t pick silently.
  • If a simpler approach exists, say so. Push back when warranted.

Once aligned, implement directly - no preamble or narration around the code. Prose belongs in questions, assumptions and the §4 plan. When done, report briefly: what changed, how it was verified, and what wasn’t.

2. Simplicity Firstđź”—

Minimum code that solves the problem. Nothing speculative.

  • No features beyond what was asked.
  • No abstractions for single-use code.
  • No “flexibility” or “configurability” that wasn’t requested.
  • No error handling for impossible scenarios.
  • No cleverness for its own sake - write code that reads plainly.
  • Before writing something new, look for existing work - in the codebase, the standard library, then current dependencies - and use or build on it when it fits. If it only partly fits, say so and propose reuse vs. new. Add new dependencies only when needed, and say why.
  • If you write 200 lines and it could be 50, rewrite it.

3. Surgical Changesđź”—

Touch only what you must. Clean up only your own mess.

When editing existing code:

  • Don’t “improve” adjacent code, comments, or formatting.
  • Don’t refactor things that aren’t broken.
  • Match existing style, even if you’d do it differently.
  • If you notice unrelated dead code, mention it - don’t delete it.
  • Remove imports/variables/functions that your changes made unused.

The test: Every changed line should trace directly to the user’s request.

4. Goal-Driven Executionđź”—

Define success criteria. Loop until verified.

Transform tasks into verifiable goals:

  • “Add validation” → “Write tests for invalid inputs, then make them pass”
  • “Fix the bug” → “Write a test that reproduces it, then make it pass”
  • “Refactor X” → “Ensure tests pass before and after”

For anything larger than a single function, state a brief plan and work one step at a time, finishing and verifying each step before starting the next:

1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]

A step isn’t done until it’s tested. Without a test setup, add one if that’s straightforward (ask early if unsure or if it’s significant work); otherwise use the smallest check that shows the step works. If a step can’t be verified, say so - don’t report it as done.

Split by change size. Use semver as a yardstick: a breaking (major) change never shares a step with other changes, and where practical is staged (add new → migrate callers → remove old) so only the final step is breaking.

When creating git commits:

  • Each verified step is normally one commit.
  • Keep each commit a single standalone logical change, even when several things are requested at once - prefer several small commits per PR over one large one.
  • Each commit must be mergeable on its own; within a PR, a commit may assume the PR’s earlier commits already exist.
  • Add a semver-based Impact: <major|minor|patch> trailer reflecting the commit’s contents.

5. Code & Documentation Standardsđź”—

Readable, typed, and documented at the function level.

In existing code, §3 takes precedence: follow local conventions for types and comments.

  • Use type hints (or the language’s equivalent) in new code. If the codebase doesn’t use them, ask before introducing them.
  • Prefer modern, idiomatic, stable language/library features over legacy patterns - not novel for novelty’s sake.
  • Every function carries at least a comment stating its purpose, inputs/outputs, and any non-obvious behavior - except trivial ones (simple getters, one-line wrappers) whose name and signature already say it all. Self-documenting names complement these comments; they don’t replace them.
  • For non-trivial logic, comment the why, not just the what.
  • When implementing a third-party integration, link the relevant API documentation in comments where possible - the specific endpoint/method, not just the product homepage. Only include links you’ve verified exist; if you can’t verify one, omit it rather than guess.
  • For each feature, maintain its user stories and/or the implementation and design choices the user has made, in docs/features/<feature>.md unless the project already has a place for them. Record choices as they’re made, and update the file in the same commit as the change it describes.

6. Safetyđź”—

Validate what comes from outside. Stay inside your output path.

  • Validate input at trust boundaries (user input, external data, API responses); fail clearly rather than continuing in a bad state. This is not a license to handle impossible internal states (§2).
  • Never create or write files outside the designated output path - the repository or working directory you were given - unless the user explicitly asks.
  • Never commit secrets or credentials. Confirm before destructive or irreversible actions (deleting data, force-pushing, migrations against shared environments).