AI-agents als senior engineer? Wat Senior SWE-Bench écht blootlegt
Frontier-modellen scoren nog geen 25% op Senior SWE-Bench. Een praktijkanalyse van correctness, taste en practice adherence — met concrete .NET-voorbeelden om je eigen agents te toetsen.
Jean-Pierre Broeders
Freelance .NET Developer
"We behandelen agents als senior engineers, dus waarom evalueren we ze dan als juniors?" Die ene zin, prominent op de landingspagina van Senior SWE-Bench, raakt precies de spanning die ik dagelijks voel bij klanten. De benchmark stond deze week op de voorpagina van Hacker News en het cijfer dat overal rondging was ontnuchterend: zelfs het beste frontier-model haalt een "tasteful solve"-rate van amper 24%. Meer dan driekwart van de tijd faalt het dus om een taak op senior-niveau af te ronden.
Als freelance .NET-developer die inmiddels dagelijks met coding agents werkt, vind ik dat geen reden tot cynisme maar tot precisie. De vraag is niet langer "kan een agent code schrijven" — dat kan-ie, indrukwekkend goed zelfs. De vraag is: kan een agent code schrijven die je zonder gêne door een senior review haalt? Senior SWE-Bench probeert dat te meten, en de manier waaróp is leerzamer dan het eindcijfer.
Wat maakt Senior SWE-Bench anders?
De oorspronkelijke SWE-bench en varianten als SWE-bench Pro toetsen agents op geïsoleerde GitHub-issues met scherp omschreven acceptatietests. Dat is nuttig, maar het lijkt niet op mijn werk. Niemand levert mij een ticket met een kant-en-klare testsuite en de exacte bestanden die ik moet aanraken.
Senior SWE-Bench draait die aanpak om op drie manieren die er echt toe doen:
- Realistische instructies. De opdrachten "lezen als natuurlijke berichten in plaats van over-gespecificeerde requirements", met een mediane instructielengte die 31% korter is dan SWE-bench Pro. Kortom: onderspecificatie, net als in de praktijk. De agent moet zelf de bedoeling reconstrueren.
- Echte omvang. Taken beslaan gemiddeld 11 bestanden per feature en vereisen honderden uitvoeringsstappen. Dit zijn geen one-liners maar meerfasige klussen, ontleend aan echte production-PR's.
- Adaptieve validatie. In plaats van rigide tests gebruikt een validatie-agent "expert-ontworpen recipes die gedragstests schrijven welke zich aanpassen aan de aangeleverde oplossing". Er is dus niet één goede diff; er zijn oneindig veel geldige oplossingen, precies zoals bij echte code.
De dataset bestaat uit 50 publieke en 50 private taken over Python, Go, TypeScript, SQL en Rust. C# ontbreekt — jammer, maar de dimensies zijn taalonafhankelijk en juist die dimensies maken deze benchmark interessant voor .NET'ers.
Correctness, taste en practice adherence
Senior SWE-Bench beoordeelt op drie assen. Ik herken ze stuk voor stuk uit mijn eigen reviews.
- Correctness — runtime-verificatie en gedragstests. Doet het wat het moet doen?
- Taste — kwaliteitsmetriek gebaseerd op de conventies van de codebase, inclusief een grens op code-bloat (minder dan 2× de omvang van een menselijke oplossing).
- Practice adherence — sluit de oplossing aan bij de waargenomen conventies van de repository (drempel groter dan 2 op 5).
De leaderboard-cijfers spreken boekdelen:
| Model | Tasteful solve rate |
|---|---|
| Claude Opus 4.8 | 24,0% |
| Claude Sonnet 5 | 19,4% |
| GPT-5.5 | 16,0% |
Let op wat hier gemeten wordt. Niet "is de code fout", maar "is de code fout, lelijk óf on-idiomatisch". Dat is precies het verschil tussen een junior die een groene testsuite aflevert en een senior die code aflevert waar het team over vijf jaar nog blij mee is.
Waarom "taste" het moeilijkste is voor een agent
Correctness is objectief en dus relatief makkelijk af te dwingen: schrijf tests, laat ze slagen. Taste is contextueel. Neem een simpel voorbeeld dat ik regelmatig terugzie in door agents gegenereerde C#. De taak: "filter actieve gebruikers en geef hun e-mailadressen terug." Een agent levert graag dit af:
public List<string> GetActiveUserEmails(List<User> users)
{
List<string> result = new List<string>();
for (int i = 0; i < users.Count; i++)
{
if (users[i].IsActive == true)
{
if (users[i].Email != null)
{
result.Add(users[i].Email);
}
}
}
return result;
}
Correct? Ja. De tests worden groen. Maar in een codebase die verder volledig LINQ-idiomatisch is, is dit smaakloos: == true, geneste if, een handmatige loop, een concreet List<string> als returntype. Een senior schrijft:
public IReadOnlyList<string> GetActiveUserEmails(IEnumerable<User> users) =>
users
.Where(u => u.IsActive && u.Email is not null)
.Select(u => u.Email!)
.ToList();
Beide slagen voor dezelfde gedragstests. Alleen de tweede past bij de conventies van het huis. Dit is exact het gat dat de taste-as van Senior SWE-Bench probeert te kwantificeren, en het is meteen duidelijk waarom modellen daar onderuit gaan: er is geen groene checkmark voor "past bij ons".
De onderspecificatie is het echte examen
Wat mij het meest aanspreekt is de keuze voor korte, ambigue instructies. In de praktijk is 70% van senior-werk het begrijpen van de opdracht, niet het typen ervan. Een instructie als "zorg dat de webhook-verwerking idempotent wordt" bevat een berg impliciete kennis: welke dedup-sleutel, hoe lang bewaar je die, wat doe je bij een race, hoe log je een dubbele levering.
Een agent die die instructie letterlijk neemt, bouwt misschien een naïeve in-memory set. Een senior weet dat dit onder load en over meerdere replica's stukloopt. Een tasteful oplossing in .NET leunt op de databron als waarheid:
public async Task<bool> TryProcessAsync(string eventId, CancellationToken ct)
{
// Idempotentie afgedwongen door een unique constraint op EventId,
// niet door een in-memory set die na een deploy of over replica's leeg is.
var record = new ProcessedEvent { EventId = eventId, ProcessedAt = DateTimeOffset.UtcNow };
_db.ProcessedEvents.Add(record);
try
{
await _db.SaveChangesAsync(ct);
return true; // eerste keer: verwerk verder
}
catch (DbUpdateException ex) when (ex.IsUniqueViolation())
{
// Al eerder verwerkt: stil negeren, geen dubbele side-effects.
return false;
}
}
Dit is het soort trade-off-denken dat een cijfer van 24% verklaart. Het model dat de opdracht letterlijk uitvoert scoort op correctness, maar zakt op practice adherence en taste zodra de reviewer vraagt: "en hoe gedraagt dit zich over drie pods?"
Wat dit betekent voor jouw .NET-workflow
Ik wil niet dat je hieruit concludeert dat agents onbruikbaar zijn — het tegendeel. Ik lever met agents meetbaar sneller. De les is dat je zelf een senior-reviewharnas moet bouwen, want de agent doet dat niet voor je. Drie concrete maatregelen die ik bij klanten inzet.
1. Maak "taste" expliciet en machinaal afdwingbaar
Een groot deel van wat Senior SWE-Bench "practice adherence" noemt, kun je vastleggen in analyzers. Zet .editorconfig en Roslyn-analyzers op error, niet op suggestion:
# .editorconfig
[*.cs]
dotnet_diagnostic.CA1826.severity = error # gebruik geen LINQ waar een property bestaat
dotnet_diagnostic.IDE0058.severity = error # onbenutte expressiewaarde
csharp_style_prefer_pattern_matching = true:error
dotnet_style_prefer_collection_expression = true:error
Wat een mens "smaak" noemt, wordt zo voor een deel een build-fout. De agent krijgt directe feedback en de reviewer hoeft niet te muggenziften.
2. Behandel elke agent-PR als een junior-PR met een strengere gate
Laat een agent nooit direct op main mergen. Een GitHub Actions-workflow die de drie assen van Senior SWE-Bench nabootst:
name: agent-pr-gate
on:
pull_request:
branches: [main]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
# Correctness
- run: dotnet test --configuration Release --logger trx
# Taste + practice adherence: analyzers als fouten
- run: dotnet build --configuration Release -warnaserror
# Code-bloat-grens à la Senior SWE-Bench (<2x)
- name: Diff size guard
run: |
ADDED=$(git diff --numstat origin/main...HEAD | awk '{s+=$1} END {print s}')
echo "Toegevoegde regels: $ADDED"
if [ "$ADDED" -gt 400 ]; then
echo "::warning::Grote diff — vraag om een kleinere, gerichtere wijziging."
fi
De diff size guard is bewust een echo van de code-bloat-grens uit de benchmark: een oplossing die twee keer zo groot is als nodig, is zelden de tasteful oplossing.
3. Schrijf gedragstests, geen implementatietests
De adaptieve validatie van Senior SWE-Bench toetst gedrag, niet een specifieke diff. Kopieer dat. Tests die vastklikken op interne implementatie breken bij elke refactor die de agent (of jij) doet. Test de waarneembare uitkomst:
[Fact]
public async Task Dubbele_levering_veroorzaakt_geen_tweede_side_effect()
{
var handler = new WebhookHandler(_db, _emailSpy);
await handler.HandleAsync(EventWith(id: "evt_123"));
await handler.HandleAsync(EventWith(id: "evt_123")); // exacte replay
_emailSpy.SentCount.Should().Be(1); // gedrag, niet implementatie
}
Zo'n test laat de agent vrij in het hoe, maar houdt de lat op het wat. Precies de filosofie die de benchmark hanteert.
De nuchtere conclusie
Senior SWE-Bench is geen bewijs dat AI faalt. Het is een eerlijke meetlat die laat zien waar de grens vandaag ligt: agents zijn sterk in correctness, zwak in taste en practice adherence, en dat gat is het grootst bij onderspecificatie — precies waar seniority om draait. 24% tasteful solve is geen falen, het is een momentopname van een curve die snel stijgt.
Voor mij verandert de praktijk niet fundamenteel: ik blijf de agent inzetten voor snelheid, maar de verantwoordelijkheid voor smaak, conventies en trade-offs ligt nog stevig bij mij. Het verstandigste dat je nu kunt doen is dat oordeel niet in je hoofd houden maar coderen — in analyzers, in gates, in gedragstests. Dan wordt de agent geen vervanger van de senior, maar een verbluffend snelle junior die werkt binnen een harnas dat jouw smaak afdwingt.
Wil je meepraten over de details van de benchmark? De discussie op Hacker News en de benchmark zelf zijn beide de moeite waard.
