Wu Wei

ponytail: less code, better code?

· #agents #tools

Agent Report

Tool
ponytail (76.5k ★)
Type
Critique
Context
full-stack-fastapi-template (medium) + Django (large, 522k LOC) — 4 A/B tasks, strong model
Cost
8 agent sessions (baseline vs. ruleset); LOC measured with git diff

ponytail has been trending on GitHub for days: 76.5k stars, a perfect tagline ("says nothing, writes one line, it works") and a concrete promise — that your AI agent writes ~54% less code (up to 94%), ~20% cheaper, 100% safe. It's a plugin that injects a ruleset —a "laziness ladder"— into 16+ agents (Claude Code, Cursor, Codex…): before writing any code, the agent stops at the first rung that holds — does it need to exist? does it already live in the repo? does the stdlib do it? a native feature? an already-installed dependency? does it fit in one line? — and only then writes the minimum.

DietrichGebert/ponytail ★ 76.5k

The rare part: its benchmark is honest

Almost every viral project inflates its numbers. ponytail does the opposite. Its benchmark admits the original "80-94% less code" figure was inflated by a chatty baseline (flagged by Colin Eberhardt in #126), found and published a contamination bug in its own measurement (a hook that fired ponytail on the baseline too), rebuilt the test to be able to disprove itself, and corrected the figure to a defensible -54% agentic number, showing where it does not win. It's the opposite of the norm. There's no "gotcha" to make here.

But that benchmark has limits they list themselves: one model (Haiku 4.5, small, which over-builds more and needs more hand-holding), one medium template repo (full-stack-fastapi-template) and greenfield feature tickets where the headline trick —"a 404-line date picker → <input type="date">"— dominates the average. Their own table already shows backend CRUD converging to ~0%. That's where this study comes in.

What I tested that they didn't

Their limits are my test: a strong model (not Haiku), a genuinely large, mature repo (Django, 522k lines of Python, 7,072 files) and harder axes than adding a widget — reusing a helper that already exists in a huge codebase, writing a fresh utility, and an "irreducible" task. A long trace, not two examples.

Method (theirs, extended): A/B of the same agent with ponytail's ruleset injected vs. without it (baseline = the agent doing the job properly, not a chatty model — the fair correction they made themselves). Four real tickets against real repos, each arm in its own copy of the repo, LOC measured as git diff added lines — their own metric.

Task 1 — the color picker (medium, over-build)

Ticket: "add a color picker to the item form." This is their signature task type. Result: baseline 168 lines, ponytail 41 — a -76% cut. But the detail matters: neither installed a library. Both went straight to the browser's native input:

ponytail — color picker
<FormField
  control={form.control}
  name="color"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Color</FormLabel>
      <FormControl>
        <Input type="color" className="h-9 w-16 p-1" {...field} />
      </FormControl>
    </FormItem>
  )}
/>

On a strong model, the "install flatpickr and build a wrapper component" trap —the source of the -94% on Haiku— barely fires: the baseline also used <input type="color">. So where did the -76% come from? From scope. The baseline threaded the color end to end: backend model, Alembic migration, a table column, hex validation, a text field alongside the swatch. ponytail did only the frontend form — and the color is never stored in the database (its own report admits it: "backend out of the ticket's scope"). The ticket was ambiguous; ponytail took the narrowest reading. Cleaner, yes — but the cut left a real gap.

Task 2 — reuse in 522k lines (Django, the hard claim)

Rung 2 ("already in the codebase? reuse it") is easy to write in a ruleset and hard to honor: it requires the agent to find the helper in a huge repo. Ticket: "a title_to_slug function that turns a title into a slug." Django has django.utils.text.slugify buried among 522k lines. Does it find it?

Yes — and it works. ponytail located it and wrote a one-line wrapper:

ponytail — title_to_slug
from django.utils.text import slugify


def title_to_slug(title):
    """
    >>> title_to_slug("Hello, World!")
    'hello-world'
    """
    # ponytail: django.utils.text.slugify already does the work; just wrap it.
    return slugify(title)

baseline 18 lines, ponytail 10 (-44%). But here's the nuance: the baseline reused slugify too. The important decision —don't reinvent the slug with a regex— was made by both. ponytail's cut was ceremony (module docstring + __main__ block vs. one-liner + doctest), not a smarter decision. A competent agent already climbs to rung 2 unprompted.

Task 3 — a fresh utility in the large repo

Ticket: a retry function with exponential backoff. There's no public retry in Django or the stdlib, so both write it from scratch — fertile ground to over-build (a configurable decorator? jitter? a policy class?). Result: baseline 43, ponytail 32 (-26%). They nearly converge. Neither over-built: both wrote a lean function with stdlib time.sleep and left a runnable check. The real difference:

baseline — the guard ponytail skipped
def retry(fn, attempts=3, base_delay=0.1):
    if attempts < 1:
        raise ValueError("attempts must be at least 1.")   # baseline added this
    ...

The baseline added an input-validation guard (attempts < 1) that ponytail skipped. Ironic: ponytail's own ruleset says "not lazy about input validation at trust boundaries." Here, part of ponytail's cut was exactly that guard.

Task 4 — the "trivial" endpoint

Ticket: "an endpoint that returns how many items the current user has." I expected convergence (it's CRUD). It was the opposite: baseline 71 lines, ponytail 12 — a -83% cut. ponytail wrote a clean endpoint, reused the existing count query, and even ordered the route correctly (/count before /{id}, a real routing detail). But:

base
71 lines

Endpoint + a typed response model (OwnedItemsCount) + 3 tests: counts 0→3, does not count other users’ items, requires auth.

pony
12 lines

Endpoint returning a bare dict. No typed model, no test at all.

ponytail returned an untyped dict —breaking the typed-response convention that all the rest of the repo uses (its own rung 2)— and left no test, even though its rule says "non-trivial logic leaves ONE runnable check." It judged the endpoint trivial. The baseline wrote three tests that verify real things (that it doesn't count other users' items, that it requires authentication). Did the baseline over-test, or did ponytail under-test? Reasonable people differ — but the -83% cut wasn't bloat, it was coverage and types.

The pattern

Mean of the four cuts: ~57% — almost exactly their -54%, even on a strong model and a large repo. The LOC number reproduces. What changes is where the cut comes from:

On Haiku, the -54% comes mostly from avoiding real over-builds (the 404-line date picker). On a strong model, the baseline already avoids those traps — so ponytail's cut shifts to less scope (T1: color not persisted), less ceremony (T2), almost nothing (T3) and fewer tests and types (T4). In 2 of the 4 tasks, the cut removed something a reviewer would want back.

This is not a verdict against it. ponytail never wrote more (its core claim holds), reused well in every case —including finding slugify in half a million lines— and its team is one of the few that measures honestly. But "less code" is not automatically "better code": on a capable model the gain is modest and sometimes cuts scope, tests or a guard.

What others already knew

It's not just my measurement. An independent benchmark (#236, KuldeepB19: 480 builds, 24 jobs, Claude Opus 4.8, four levels) found ~44% less code with no general loss of correctness or security — but a real robustness cost on 5 jobs with unstated edge-cases: "one build crashed on bad input where the plain version handled it fine," and the ultra level degrades instead of improving. It matches exactly what I saw: the cut sometimes takes a guard with it. And the underlying question —does the always-on constraint hurt reasoning on hard tasks (SWE-bench, Terminal-bench)?— remains open and without data (16 👍, no answer).

Verdict

Code cut vs. what was announced 57% · anunciado 54%

Mean of -57% across my 4 tasks vs. the announced -54% — it reproduces the figure even on a strong model and a large repo. The LOC claim holds.

Reuses instead of reinventing (rungs 2-5) 85%

Found slugify in 522k lines, reused repo patterns and the stdlib, never wrote more. It does this well — though a strong baseline often already reuses just the same.

The cut preserves what matters 45%

2 of 4 cuts removed something: persistence (T1), tests + types (T4), a validation guard (T3). Its own "leave ONE check" rule was skipped once.

Honesty of the project and its benchmark 92%

Rare: corrected its own inflated figure, published a contamination bug of its own, answered critiques, shows where it does not win. A reference for how to measure.

The gain depends on the model 40%

The headline -94% is a Haiku figure. On a strong model the cut comes from scope and ceremony, not from avoiding over-build. They admit it and the independent benchmark (Opus 4.8) drops to -44%.

Robustness on edge-cases 60%

Their benchmark and #236: no general safety loss, but #236 saw a crash on an edge-case and here ponytail skipped a validation guard. "Write less" without review can cut the check.

ponytail is a good default: it doesn't bloat, it reuses well, and its team measures with an honesty almost nobody has. But the "-54%" headline is a Haiku figure; on a capable model the cut is modest and, in half of my tests, took something worth keeping with it — a field's persistence, a handful of tests, a validation guard. The tool itself ships a /ponytail-review command that hands you a delete-list: it assumes you review the diff. That's exactly this site's middle ground — use agents with judgment, measure before you believe, review before you trust. Neither rejecting laziness by reflex, nor swallowing the "-94%" without looking at what got cut.