magus v0.4.3 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
2 min read

Delete before you add

Every line you ship is a bill somebody else pays: the person installing the tool, or whoever maintains it after you. It almost never lands on the person who wrote it, and that is most of why we keep adding. Jeff Atwood said it in 2007 and it has only gotten truer: the best code is no code at all.1 Writing a feature is close to free now, so the only thing between a codebase and endless creep is somebody deciding not to add one, or deciding to take one out.

People are bad at this in a measurable way. Adams and colleagues found that people handed a problem reach for what they can add and barely consider what they could take away, even where removing something is the better answer.2 Deleting a feature reads as a loss. It is usually a gift to whoever maintains the thing next, and agents drift the same way: asked to make something work, they add a fallback, a flag, a shim.

Removal is always an option, for whole features too. A feature in magus has to be done really well. If it is half-built, or another tool does the job better and magus cannot honestly beat it, the answer can be to say so, remove it, and point people at the tool that does it well. Building it out further is the other answer; leaving it half-done is not one.

magus is pre-1.0, so deleting is cheap now and gets expensive later. A rename, a removed flag or a changed format ships without a migration path; a shim needs my yes and carries the condition that retires it (Break now, hold still later).

Enforced by

  • AGENTS.md makes deletion the first option: before adding code to keep something working, check whether removing the old path, flag, fallback, test or doc section reaches the goal.
  • An approved shim carries compat(until: <condition>):, a condition someone can observe, so it has a way out.
  • A record of refusals keeps the deletions that were measured, so a removed thing is not quietly re-added.

Good

A shim that knows how it ends:

// compat(until: no store still serves ed25519 envelopes): read the old envelope too.

Bad

func load(path string) (*Config, error) {
    cfg, err := loadV2(path)
    if err != nil {
        return loadV1(path) // fall back in case an old file is still around
    }
    return cfg, nil
}

Two readers now live forever, nobody can say when the second one is safe to delete, and a broken new file reads as an old one in silence.

See also


  1. Jeff Atwood, The Best Code is No Code At All, 2007. ↩︎

  2. Adams, Converse, Hales and Klotz, People systematically overlook subtractive changes, Nature 592, 2021. ↩︎

designprinciplescopemaintenance
Last updated (2df714e8)
Glossary

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). Op in full.

CI

An ordinary target you compose yourself with ctx.needs; magus does not hardcode its stages. It is the anchor magus affected ci keys off, and ci strips the rw charm. CI in full.

Agent

A model working through an agent host such as Claude Code, Codex, Cursor or OpenCode. magus treats an agent as one more caller of its CLI, MCP tools and console, held to the same checks as a person. Agent in full.

Tool

An action a model can call through its host: a name, a description, a typed input and a result. magus serves its tools over MCP, each a read of the workspace model or a run of the CLI it already has. Tool in full.

Job

The unit of delegated work, and one row of the job store: what an orchestrating agent handed out, with its goal, the checkpoint it was cut against, the paths it may write or must not touch, and the one check it runs. A job's holder is either a session, for work an orchestrator handed out, or the daemon, for its own maintenance. The store records; the agent guard is what reads those facts back when grading a write. Job in full.

Conventions

Placeholders

Angle brackets mark a value you replace with your own; never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.