
A compact checklist distilled from [Varyonic programming](/docs/varyonic/) and [Designing for mutation](/docs/mutation/design-guide/). Give this page (or its contents) to an LLM coding assistant as part of its system prompt or project instructions.

## Return structured data

| Do | Don't |
|----|-------|
| Return `data` types with named fields | Return `Bool` or `None` for success/failure |
| Return report objects with counts and lists | Print details to stdout |
| Return `Result[T, E]` for fallible ops | Call `exit()` from library code |

```vary-snippet
# Good
data CheckReport {
    success: Bool
    issues: List[Str]
    files_checked: List[Str]
}

def check_site(pages: List[Str]) -> CheckReport { ... }

# Bad
def check_site(pages: List[Str]) -> Bool { ... }
```

## Use domain types

| Do | Don't |
|----|-------|
| Define `enum` for closed choices | Use magic strings (`"prod"`) |
| Define `data` for domain concepts | Use `Dict[Str, Any]` |
| Use `Str?` for optional values | Use `""` as a sentinel for "missing" |

```vary-snippet
# Good
enum DeployTarget { Local, Preview, Production }

# Bad
def deploy(mode: Str) { if mode == "prod" { ... } }
```

## Write pure functions for core logic

| Do | Don't |
|----|-------|
| Mark decision logic `pure def` | Mix I/O and logic in one function |
| Put validation and planning in pure functions | Read config inside core logic |
| Return a plan; execute it separately | Perform effects inside decision logic |

```vary-snippet
# Good
pure def plan_build(sources: List[Str]) -> BuildPlan { ... }
def run_build(plan: BuildPlan) -> BuildReport { ... }

# Bad
def build(sources: List[Str]) {
    for s in sources { fs.write_text(fs.write_path(s + ".out").unwrap(), compile(s)).unwrap() }
}
```

## Separate decisions from effects

| Do | Don't |
|----|-------|
| Inputs -> planning -> execution -> report | One function that reads, decides, acts, prints |
| Keep effectful functions thin | Put `print()` or `exit()` in planning logic |
| Limit `exit()` to the CLI entry point | Call `exit()` from library functions |

## Test with `observe`, not just success checks

| Do | Don't |
|----|-------|
| `observe report.issues.len() == 0` | `observe result == true` |
| `observe report.files_checked.len() == 5` | `observe report is not None` |
| Assert every important field | Assert only that no error occurred |
| Test relationships between outputs | Test only one output in isolation |

```vary-snippet
# Good
test "plan has correct fields" {
    let plan = plan_deploy("prod", "/var/www/site")
    observe plan.strategy == DeployStrategy.Rsync
    observe plan.source_dir == "build/"
    observe plan.destination == "/var/www/site"
}

# Bad
test "plan works" {
    let plan = plan_deploy("prod", "/var/www/site")
    observe plan is not None
}
```

## Test negative paths

| Do | Don't |
|----|-------|
| Test empty lists, bad input, boundaries | Only test the happy path |
| Test "almost valid" input | Assume bad input never arrives |
| Test every `Result.Err` branch | Leave error branches unexercised |

For every important function, test at least: normal success, obvious invalid input, an edge case, a boundary condition, and "almost valid" input.

## Use enums and match for finite decisions

| Do | Don't |
|----|-------|
| `match kind { case PageKind.Doc { ... } }` | `if kind == "doc" { ... }` |
| Cover every enum variant | Leave a catch-all `else` hiding cases |

## Use contracts for invariants

| Do | Don't |
|----|-------|
| Add `in { }` preconditions | Silently accept invalid arguments |
| Add `out (result) { }` postconditions | Hope the caller checks the result |
| Use `old(expr)` for before/after state | Rely on manual inspection |

```vary-snippet
def parse_doc(md: Str) -> DocPage {
    in { len(md) > 0 }
    out (result) { result.slug != "" }
    ...
}
```

## Use `Result` for errors, not exceptions

| Do | Don't |
|----|-------|
| `def load(path: Str) -> Result[Doc, Str]` | `def load(path: Str) -> Doc` that raises |
| Propagate with `?else return Err(...)` | Catch and swallow exceptions |
| Return `Err("descriptive message")` | Return `None` for "went wrong" |

## Extract small helpers

| Do | Don't |
|----|-------|
| Extract: `normalize_link()`, `classify_change()` | Inline 20 lines inside a loop body |
| Name helpers by one responsibility | Create a `utils.vary` grab-bag |
| Keep helpers independently testable | Abstract one-time operations |

## Use `defer` for resource cleanup

| Do | Don't |
|----|-------|
| `defer { fs.remove_dir(fs.path(dir)).unwrap() }` after acquiring | Clean up manually at function end |
| One `defer` per resource, right after creation | Forget cleanup on early-return paths |

## Module organization

| Do | Don't |
|----|-------|
| Split types, logic, and effects into files | Put everything in one file |
| Name by responsibility: `check_plan.vary` | Name generically: `helpers.vary` |
| Keep `main.vary` as a thin CLI shell | Put business logic in `main.vary` |

## Anti-patterns to avoid

These patterns cause mutation survivors and weak test coverage:

| Pattern | Fix |
|---------|-----|
| Magic strings instead of enums | Define an `enum` |
| Functions returning `None` silently | Return structured data or `Result` |
| Side effects mixed with decisions | Separate pure planning from execution |
| Logging instead of structured reports | Return a report `data` type |
| Tests asserting string fragments | Assert on typed fields |
| Bare `try`/`except` with no re-raise | Use `Result` or re-raise |
| Large functions mixing plan and execute | Split into plan + execute |
| `Dict` instead of domain models | Define `data` types |
| Tests that only check "no error" | Assert exact output values |

## Quick reference

```text
Program structure:
  CLI -> Load context -> Pure planning -> Execution layer -> Structured report -> CLI prints + exit

Test structure:
  test "descriptive name" {
      let result = function_under_test(inputs)
      observe result.field1 == expected1
      observe result.field2 == expected2
      observe result.count == result.items.len()   # relational check
  }

File layout:
  types.vary           # data and enum definitions
  logic_plan.vary      # pure planning functions
  logic_execute.vary   # effectful execution
  main.vary            # thin CLI entry point
```
