Wu Wei

ponytail: meno codice, codice migliore?

· #agenti #strumenti

Agent Report

Strumento
ponytail (76,5k ★)
Tipo
Critica
Contesto
full-stack-fastapi-template (medio) + Django (grande, 522k LOC) — 4 task A/B, modello forte
Costo
8 sessioni di agente (baseline vs. ruleset); LOC misurato con git diff

ponytail è in tendenza su GitHub da giorni: 76,5k stelle, uno slogan perfetto (« non dice nulla, scrive una riga, funziona ») e una promessa concreta — che il tuo agente IA scriva ~54% di codice in meno (fino al 94%), ~20% più economico, 100% sicuro. È un plugin che inietta un ruleset —una « scala della pigrizia »— in 16+ agenti (Claude Code, Cursor, Codex…): prima di scrivere codice, l'agente si ferma al primo piolo che regge — serve davvero? esiste già nel repo? lo fa la stdlib? una feature nativa? una dipendenza già installata? sta in una riga? — e solo allora scrive il minimo.

DietrichGebert/ponytail ★ 76,5k

La cosa rara: il suo benchmark è onesto

Quasi ogni progetto virale gonfia i propri numeri. ponytail fa l'opposto. Il suo benchmark ammette che la cifra originale « 80-94% di codice in meno » era gonfiata da un baseline chiacchierone (segnalato da Colin Eberhardt in #126), ha trovato e pubblicato un bug di contaminazione nella propria misura (un hook che attivava ponytail anche sul baseline), ha rifatto il test per poter confutare se stesso, e ha corretto la cifra a un -54% agentico difendibile, mostrando dove non vince. È l'opposto della norma. Qui non c'è nessun « gotcha » da fare.

Ma quel benchmark ha limiti che elencano loro stessi: un solo modello (Haiku 4.5, piccolo, che sovra-costruisce di più e ha bisogno di più guida), un repo template medio (full-stack-fastapi-template) e ticket di feature greenfield dove il colpo a effetto —« un date picker di 404 righe → <input type="date"> »— domina la media. La loro stessa tabella mostra già il CRUD backend convergere a ~0%. È qui che entra questo studio.

Cosa ho provato che loro no

I loro limiti sono il mio test: un modello forte (non Haiku), un repo davvero grande e maturo (Django, 522k righe di Python, 7.072 file) e assi più difficili che aggiungere un widget — riusare un helper che esiste già in un codebase enorme, scrivere un'utility nuova, e un task « irriducibile ». Una traccia lunga, non due esempi.

Metodo (il loro, esteso): A/B dello stesso agente con il ruleset di ponytail iniettato vs. senza (baseline = l'agente che fa il lavoro per bene, non un modello chiacchierone — la correzione giusta che hanno fatto loro stessi). Quattro ticket reali su repo reali, ogni braccio in una propria copia del repo, LOC misurato come righe aggiunte del git diff — la loro stessa metrica.

Task 1 — il color picker (medio, sovra-costruzione)

Ticket: « aggiungi un selettore di colore al form degli item ». È il loro tipo di task simbolo. Risultato: baseline 168 righe, ponytail 41 — un taglio del -76%. Ma il dettaglio conta: nessuno dei due ha installato una libreria. Entrambi sono andati dritti all'input nativo del browser:

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>
  )}
/>

Su un modello forte, la trappola « installa flatpickr e monta un componente wrapper » —la fonte del -94% su Haiku— quasi non scatta: anche il baseline ha usato <input type="color">. Allora da dove viene il -76%? Dallo scope. Il baseline ha portato il colore da capo a fondo: modello backend, migrazione Alembic, colonna nella tabella, validazione dell'hex, campo di testo accanto allo swatch. ponytail ha fatto solo il form frontend — e il colore non viene mai salvato nel database (il suo stesso report lo ammette: « backend fuori dallo scope del ticket »). Il ticket era ambiguo; ponytail ha scelto la lettura più stretta. Più pulito, sì — ma il taglio ha lasciato un buco reale.

Task 2 — riusare in 522k righe (Django, la claim difficile)

Il piolo 2 (« già nel codebase? riusalo ») è facile da scrivere in un ruleset e difficile da onorare: richiede che l'agente trovi l'helper in un repo enorme. Ticket: « una funzione title_to_slug che trasforma un titolo in slug ». Django ha django.utils.text.slugify sepolto tra 522k righe. Lo trova?

Sì — e funziona. ponytail lo ha localizzato e ha scritto un wrapper di una riga:

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 righe, ponytail 10 (-44%). Ma ecco la sfumatura: anche il baseline ha riusato slugify. La decisione importante —non reinventare lo slug con una regex— l'hanno presa entrambi. Il taglio di ponytail è stato cerimonia (docstring di modulo + blocco __main__ vs. one-liner + doctest), non una decisione più furba. Un agente competente sale già al piolo 2 senza che glielo si dica.

Task 3 — un'utility nuova nel repo grande

Ticket: una funzione retry con backoff esponenziale. Non c'è retry pubblico in Django né nella stdlib, quindi entrambi la scrivono da zero — terreno fertile per sovra-costruire (un decoratore configurabile? jitter? una classe di policy?). Risultato: baseline 43, ponytail 32 (-26%). Quasi convergono. Nessuno ha sovra-costruito: entrambi hanno scritto una funzione asciutta con time.sleep della stdlib e lasciato un check eseguibile. La vera differenza:

baseline — il guard che ponytail ha saltato
def retry(fn, attempts=3, base_delay=0.1):
    if attempts < 1:
        raise ValueError("attempts must be at least 1.")   # baseline l'ha messo
    ...

Il baseline ha aggiunto una validazione dell'input (attempts < 1) che ponytail ha saltato. Ironico: il ruleset di ponytail dice lui stesso « non pigro sulla validazione ai confini di fiducia ». Qui, parte del taglio di ponytail è stato proprio quel guard.

Task 4 — l'endpoint « banale »

Ticket: « un endpoint che restituisce quanti item ha l'utente corrente ». Mi aspettavo convergenza (è CRUD). È stato l'opposto: baseline 71 righe, ponytail 12 — un taglio del -83%. ponytail ha scritto un endpoint pulito, riusato la query di conteggio esistente, e persino ordinato bene la route (/count prima di /{id}, un vero dettaglio di routing). Ma:

base
71 righe

Endpoint + un modello di risposta tipizzato (OwnedItemsCount) + 3 test: conta 0→3, non conta gli item altrui, richiede l'auth.

pony
12 righe

Endpoint che restituisce un dict nudo. Nessun modello tipizzato, nessun test.

ponytail ha restituito un dict non tipizzato —rompendo la convenzione di risposte tipizzate che usa tutto il resto del repo (il suo stesso piolo 2)— e non ha lasciato alcun test, anche se la sua regola dice « la logica non banale lascia UN check eseguibile ». Ha giudicato l'endpoint banale. Il baseline ha scritto tre test che verificano cose reali (che non conta gli item di altri utenti, che richiede l'autenticazione). Il baseline ha sovra-testato, o ponytail sotto-testato? Le persone ragionevoli divergono — ma il taglio del -83% non era bloat, era copertura e tipi.

Il pattern

Media dei quattro tagli: ~57% — quasi identico al loro -54%, anche su un modello forte e un repo grande. La cifra di LOC si riproduce. Ciò che cambia è da dove viene il taglio:

Su Haiku, il -54% viene soprattutto dall'evitare vere sovra-costruzioni (il date picker di 404 righe). Su un modello forte, il baseline evita già quelle trappole — quindi il taglio di ponytail si sposta su meno scope (T1: colore non persistito), meno cerimonia (T2), quasi niente (T3) e meno test e tipi (T4). In 2 dei 4 task, il taglio ha rimosso qualcosa che un revisore vorrebbe indietro.

Non è un verdetto contro. ponytail non ha mai scritto di più (la sua claim centrale regge), ha riusato bene in ogni caso —incluso trovare slugify in mezzo milione di righe— e il suo team è tra i pochi che misura onestamente. Ma « meno codice » non è automaticamente « codice migliore »: su un modello capace il guadagno è modesto e a volte taglia scope, test o un guard.

Quello che altri sapevano già

Non è solo la mia misura. Un benchmark indipendente (#236, KuldeepB19: 480 build, 24 job, Claude Opus 4.8, quattro livelli) ha trovato ~44% di codice in meno senza perdita generale di correttezza né sicurezza — ma un costo di robustezza reale su 5 job con edge-case non detti: « un build è crashato su input cattivo dove la versione senza plugin ha retto », e il livello ultra degrada invece di migliorare. Coincide esattamente con ciò che ho visto: il taglio a volte si porta via un guard. E la domanda di fondo —il vincolo always-on peggiora il ragionamento su task difficili (SWE-bench, Terminal-bench)?— resta aperta e senza dati (16 👍, senza risposta).

Verdetto

Taglio di codice vs. quanto annunciato 57% · anunciado 54%

Media di -57% sui miei 4 task vs. il -54% annunciato — riproduce la cifra anche su un modello forte e un repo grande. La claim di LOC regge.

Riusa invece di reinventare (pioli 2-5) 85%

Ha trovato slugify in 522k righe, riusato pattern del repo e la stdlib, mai scritto di più. Lo fa bene — anche se un baseline forte spesso riusa già uguale.

Il taglio preserva ciò che conta 45%

2 dei 4 tagli hanno rimosso qualcosa: persistenza (T1), test + tipi (T4), un guard di validazione (T3). La sua stessa regola « lascia UN check » è stata saltata una volta.

Onestà del progetto e del suo benchmark 92%

Raro: ha corretto la propria cifra gonfiata, pubblicato un bug di contaminazione interno, risposto alle critiche, mostra dove non vince. Un riferimento su come misurare.

Il guadagno dipende dal modello 40%

Il -94% di punta è una cifra Haiku. Su un modello forte il taglio viene da scope e cerimonia, non dall'evitare la sovra-costruzione. Lo ammettono e il benchmark indipendente (Opus 4.8) scende a -44%.

Robustezza sugli edge-case 60%

Il loro benchmark e #236: nessuna perdita generale di sicurezza, ma #236 ha visto un crash su un edge-case e qui ponytail ha saltato un guard di validazione. « Scrivi meno » senza revisione può tagliare il check.

ponytail è un buon default: non gonfia, riusa bene, e il suo team misura con un'onestà che quasi nessuno ha. Ma il titolo « -54% » è una cifra Haiku; su un modello capace il taglio è modesto e, nella metà dei miei test, si è portato via qualcosa che valeva la pena — la persistenza di un campo, una manciata di test, un guard di validazione. Lo strumento stesso include un comando /ponytail-review che ti restituisce una lista di cancellazione: dà per scontato che tu riveda il diff. È esattamente la via di mezzo di questo sito — usare gli agenti con criterio, misurare prima di credere, rivedere prima di fidarsi. Né rifiutare la pigrizia per riflesso, né bere il « -94% » senza guardare cosa è stato tagliato.