Clean Code Is Niet Dood: Wat Nieuw Onderzoek Zegt Over Coding Agents en je Tokenrekening

Een minimal-pair studie van SonarSource laat zien dat schone code niet bepaalt óf een agent een taak afmaakt — maar wel 7-8% tokens en 34% file-heropeningen scheelt. Een .NET-praktijkblik met C#-voorbeelden.

Jean-Pierre Broeders

Freelance .NET Developer

6 juli 202610 min. leestijd
Clean Code Is Niet Dood: Wat Nieuw Onderzoek Zegt Over Coding Agents en je Tokenrekening

Om de zoveel maanden verklaart iemand clean code dood. De nieuwste variant van dat argument gaat zo: als een AI-agent de code schrijft en onderhoudt, en geen mens die ooit nog leest, dan is al ons gepriegel over naamgeving, kleine functies en lage complexiteit sentimentele verspilning. Push de rommel maar. De machine maalt er niet om.

Een paper dat deze week op de voorpagina van Hacker News belandde, heeft die intuïtie eindelijk echt getest, en de uitkomst is interessanter dan beide kampen hadden verwacht. "Does Code Cleanliness Affect Coding Agents? A Controlled Minimal-Pair Study" van Priyansh Trivedi en Olivier Schmitt bij SonarSource (arXiv) vond dat schone code niet bepaalt of een agent een taak afrondt — maar wél sterk bepaalt hoe duur en hoe efficiënt die weg ernaartoe is.

Als freelance .NET-developer die inmiddels elke dag coding agents op codebases van klanten loslaat, is dit precies het soort bewijs waar ik op zat te wachten. Geen onderbuik, geen manifesten — een gecontroleerd experiment. Laat me uitpakken wat ze deden, wat de cijfers werkelijk betekenen, en wat ik in een echte C#-codebase zou veranderen op basis hiervan.

Wat het onderzoek daadwerkelijk mat

Het slimme aan deze studie is het ontwerp. Ruziën over clean code is meestal kansloos, omdat je nooit appels met appels vergelijkt — de schone repo en de rommelige repo lossen andere problemen op, dus elk verschil kan aan het probleem liggen in plaats van aan de netheid.

Trivedi en Schmitt omzeilen dat met een minimal-pair-aanpak, geleend uit de taalkunde. Ze bouwden paren van repositories die architectonisch identiek zijn maar alleen verschillen in codekwaliteit. Cruciaal: ze construeerden die paren in beide richtingen — sommige door oorspronkelijk schone code te verrommelen, andere door oorspronkelijk rommelige code op te schonen. Die tweerichtingsopzet is belangrijk, want die controleert voor de mogelijkheid dat "schone" repo's om totaal andere redenen makkelijker zijn.

Bovenop die paren schreven ze 33 taken over zes repository-paren, beoordeeld met verborgen tests, en draaiden die door Claude Code — 660 trials in totaal. Verborgen tests zijn de eerlijke manier om een agent te beoordelen: je beloont hem niet voor output die er goed uitziet, alleen voor gedrag dat klopt.

De belangrijkste bevindingen:

  • De taakvoltooiing werd nauwelijks beïnvloed door netheid. Een capabele agent maakt een taak ongeveer even vaak af in een rommelige repo als in een schone.
  • Schonere code verbruikte 7–8% minder tokens.
  • Schonere code leverde 34% minder file-heropeningen op — de agent opende dezelfde bestanden veel minder vaak opnieuw om zich te heroriënteren.

Hun conclusie is genuanceerd en, denk ik, correct: "traditionele onderhoudbaarheidsprincipes blijven zeer relevant in het tijdperk van AI-gedreven ontwikkeling." Netheid gaat niet langer alleen over menselijk begrip, maar wordt een kwestie van rekenkosten en navigatie-efficiëntie.

Waarom "zelfde succes, minder kosten" het punt is, geen voetnoot

De internetreflex was voorspelbaar: "Zie je wel? Netheid verandert de uitkomst niet, dus het maakt niet uit." Dat leest het resultaat precies verkeerd om.

Taakvoltooiing is een plafondmetriek. Op benchmark-achtige taken met een sterk model komt uiteindelijk veel wél af. Wat een prettige agent-workflow onderscheidt van een tergende, is niet of hij klaarkomt — het is hoeveel geploeter, terugkrabbelen en herlezen er onderweg gebeurt, want dat geploeter betaal je in drie valuta:

  1. Geld. Tokens worden letterlijk gefactureerd. Een reductie van 7–8% over een team dat de hele dag agents draait is een echte kostenpost, en die stapelt zich op.
  2. Latency. Minder file-heropeningen betekent minder heen-en-weer, dus snellere loops. De productiviteit van een agent wordt gedomineerd door de kloktijd per iteratie.
  3. Contextbudget. Deze onderschat men het meest. Elk bestand dat de agent opnieuw leest om zich te heroriënteren verbrandt contextvenster. 34% minder heropeningen betekent dat méér van die eindige context naar het echte probleem gaat in plaats van naar het opnieuw vaststellen van waar alles staat. Bij lange taken is dat vaak het verschil tussen een agent die de draad vasthoudt en een die hem volledig kwijtraakt.

De eerlijke samenvatting is dus niet "netheid maakt niet uit." Het is: "netheid koopt je geen correctheid die je anders niet kon krijgen — het koopt je correctheid goedkoper, sneller en betrouwbaarder op schaal." Voor iedereen die de rekening betaalt, is dat het hele spel.

Wat "schoon" betekent voor een agent (niet wat je zou denken)

Hier voeg ik mijn eigen kijk toe. Als we zeggen dat clean code een mens helpt, bedoelen we meestal leesbaarheid, smaak, naamgeving. Een agent leest anders. Hij navigeert een repo door te zoeken, bestanden te openen en symbolen te pattern-matchen. De dingen die zijn file-heropeningen verlagen, zijn de dingen die lokaal redeneren mogelijk maken — een unit kunnen begrijpen zonder vijf andere bestanden te openen.

Neem een veelvoorkomend C#-patroon dat ik nog steeds constant zie: gedrag uitgesmeerd over partial classes, diepe overerving en impliciete conventies.

// "Rommelig" voor een agent: om CalculateTotal te begrijpen moet je
// OrderBase, IDiscountStrategy, de DI-registratie én een partial-bestand openen.
public partial class Order : OrderBase
{
    public decimal CalculateTotal()
    {
        var total = ComputeSubtotal();          // gedefinieerd in Order.Calculations.cs
        total = ApplyDiscounts(total);          // opgelost via geïnjecteerde strategy
        total = ApplyRegionalTax(total);        // gedefinieerd in OrderBase
        return total;
    }
}

Om CalculateTotal veilig te wijzigen moet de agent de hele graaf reconstrueren: waar ComputeSubtotal staat, welke IDiscountStrategy bedraad is, wat ApplyRegionalTax in de base doet. Elk daarvan is een bestand openen, en als hij het vergeet, opnieuw openen. Dat is precies de heropeningskost die de studie mat.

Nu dezelfde logica geschreven voor lokaal redeneren:

// "Schoon" voor een agent: alles wat nodig is om over het totaal te redeneren staat hier.
public sealed class Order
{
    private readonly IReadOnlyList<OrderLine> _lines;
    private readonly IDiscountPolicy _discountPolicy;
    private readonly TaxTable _taxTable;

    public Order(IReadOnlyList<OrderLine> lines, IDiscountPolicy discountPolicy, TaxTable taxTable)
    {
        _lines = lines;
        _discountPolicy = discountPolicy;
        _taxTable = taxTable;
    }

    public Money CalculateTotal()
    {
        var subtotal = _lines.Aggregate(Money.Zero, (sum, line) => sum + line.Amount);
        var discount = _discountPolicy.DiscountFor(subtotal, _lines);
        var taxable  = subtotal - discount;
        var tax      = _taxTable.TaxOn(taxable);
        return taxable + tax;
    }
}

Niets hieraan is nieuw clean-code-advies — expliciete dependencies, sealed, kleine composeerbare stappen, een value type voor geld. Nieuw is waarom het loont. De tweede versie laat een agent over CalculateTotal redeneren vanaf één scherm. Hij hoeft de class niet te reconstrueren over partials heen of runtime-DI-bedrading na te jagen. Minder opens, minder re-opens, minder tokens — precies de assen die de studie zag bewegen.

De refactor-val: schoon voor de agent, niet schoon uit dogma

Er is een faalpatroon dat ik wil markeren, want ik heb teams ernaartoe zien sprinten. Na het lezen van "clean code maakt agents goedkoper" trapt iemand een gigantische refactor af om in één keer aan élke SonarQube-regel te voldoen. Dat is een fout, om twee redenen.

Ten eerste vond de studie niet dat maximale netheid wint; ze vond dat schoon-versus-rommelig-paren meetbaar verschillen. Afnemende meeropbrengsten zijn reëel. Ten tweede — en dit is het praktijkpunt — de netheid die agents het meest helpt is structurele lokaliteit, niet cosmetische stijl. x hernoemen naar index is prettig; het is niet wat 34% van de heropeningen bespaarde. Een vijf-bestanden-durende dependency-jacht platslaan tot één bestand lezen wél.

Mijn prioriteitenlijst voor een agent-vriendelijke .NET-codebase:

  1. Ruim indirectie op die bestanden overspant. Verkies expliciete constructor-injectie boven service-locator-lookups en statische ambient state. Een agent kan een constructor lezen; hij kan niet makkelijk afleiden wat ServiceLocator.Resolve<IThing>() at runtime teruggeeft.
  2. Houd units cohesief. Vermijd partial classes die één concept over bestanden verspreiden. Als een mens drie bestanden nodig heeft om een method te begrijpen, dan de agent ook — en die betaalt per bestand, twee keer.
  3. Maak contracten expliciet. Nullable reference types aan, precieze return types, geen object of stringly-typed payloads. Types zijn gratis documentatie die de agent niet opnieuw hoeft af te leiden.
  4. Dán pas naamgeving en formatting. Het helpt, maar het is de goedkope laag.

Een concrete winst: exceptions en control flow

Nog één voorbeeld, want hier zie ik in echte .NET-code de grootste agent-thrash: control flow verstopt in exceptions en side effects.

// De agent moet de caller én de logging-config openen om te weten wat er gebeurt.
public User GetUser(int id)
{
    var user = _repo.Find(id);
    if (user == null) throw new UserNotFoundException(id); // ergens... afgehandeld
    return user;
}

Of dit "prima" is, hangt volledig af van een catch-blok dat de agent misschien niet in context heeft. Vergelijk met een expliciet resultaat:

public Result<User> GetUser(int id)
    => _repo.Find(id) is { } user
        ? Result.Ok(user)
        : Result.NotFound($"Gebruiker {id} bestaat niet");

Nu is de uitkomstenruimte zichtbaar op de aanroepplek. De agent hoeft de composition root niet te openen om te ontdekken hoe een gegooide exception wordt afgehandeld. Dit is precies het soort "verminder de bestanden die je moet lezen om lokaal te redeneren"-verandering die de heropeningsmetriek van de studie beloont — en, niet toevallig, het is ook betere code voor mensen.

Wat ik meeneem

De commentsectie op Hacker News splitste, zoals altijd bij dit onderwerp, tussen "clean code is dood" en "zie je wel." Het paper steunt geen van beide slogans. De precieze, bruikbare lezing is deze: met een sterke agent beslist netheid zelden of de klus af komt, maar consistent wél wat het kost om er te komen — in tokens, in latency, en in het contextbudget dat bepaalt of lange taken overeind blijven.

Voor mij herformuleert dat de businesscase voor onderhoudbaarheid in plaats van hem te verzwakken. Ik hoef niet meer te beargumenteren dat clean code bugs voorkomt die een mens anders zou pushen (een claim die altijd deels geloof was). Ik kan naar een gecontroleerde studie wijzen en zeggen: op dezelfde taak kostte de rommelige repo 7–8% meer tokens en liet hij de agent 34% vaker bestanden herlezen. In een tent die agents op schaal draait, is dat geld en tijd die je voor niets in de fik zet.

Clean code ging nooit echt over een compiler of linter tevredenstellen. Het ging over systemen bouwen die goedkoop lokaal te doorgronden zijn. Blijkt dat onze nieuwe niet-menselijke collega's exact dezelfde voorkeur hebben — ze sturen je alleen een gespecificeerde rekening als je hem negeert.

Bron: Trivedi & Schmitt, "Does Code Cleanliness Affect Coding Agents? A Controlled Minimal-Pair Study" (SonarSource). Discussie op Hacker News.

Wil je op de hoogte blijven?

Schrijf je in voor mijn nieuwsbrief of neem contact op voor freelance projecten.

Neem Contact Op