[{"content":"Ik heb een bekentenis. Elke maandagochtend neem ik me voor om mijn NuGet-packages te checken op updates. Woensdag ben ik het vergeten. Vrijdag heeft Dependabot al drie PR\u0026rsquo;s geopend die ik \u0026ldquo;volgende week\u0026rdquo; ga reviewen.\nWat als Claude Code dat gewoon voor me deed? Elke ochtend om 9 uur mijn packages checken, de tests draaien, en vertellen wat er aandacht nodig heeft. Geen GitHub Actions workflow configureren. Geen YAML debuggen. Gewoon een prompt en een schema.\nDat is precies wat scheduled tasks in Claude Code doen.\nDrie manieren om werk in te plannen Claude Code biedt drie scheduling-opties, elk met eigen voor- en nadelen. Het overzicht:\nCloud Desktop /loop Draait op Anthropic cloud Je eigen machine Je eigen machine Machine moet aan staan Nee Ja Ja Sessie moet open zijn Nee Nee Ja Overleeft herstart Ja Ja Nee Toegang tot lokale bestanden Nee (verse clone) Ja Ja Minimale interval 1 uur 1 minuut 1 minuut De korte versie: gebruik cloud-taken als betrouwbaarheid belangrijker is dan lokale toegang. Gebruik Desktop-taken als je je lokale bestanden en tools nodig hebt. Gebruik /loop om snel iets te pollen tijdens een sessie.\n/loop — de snelle optie /loop is de snelste manier om iets in te plannen. Typ het in een sessie en Claude begint je prompt op interval te draaien.\n/loop 5m check of de dotnet test run geslaagd is en toon failures Dat is alles. Claude parseert het interval, zet een achtergrondtaak op, en checkt elke 5 minuten. Geen configuratiebestanden, geen dashboards, geen setup-wizards.\nHet standaardinterval is 10 minuten als je er geen opgeeft. Je kunt s gebruiken voor seconden, m voor minuten, h voor uren, of d voor dagen. Seconden worden afgerond naar de dichtstbijzijnde minuut, omdat de scheduler op cron draait.\nWanneer ik /loop gebruik Ik pak /loop erbij tijdens actief development. Een paar voorbeelden:\n/loop 5m check of de staging deploy klaar is /loop 15m check mijn open PR op nieuwe review-comments /loop 30m draai dotnet test op het integratie-testproject en vat failures samen Je kunt zelfs een ander commando loopen:\n/loop 20m /review-pr 1234 Elke keer dat de taak afgaat, voert Claude het commando uit alsof je het zelf had getypt.\nDe beperkingen /loop is sessie-gebonden. Sluit je terminal, en alle geplande taken zijn weg. Taken verlopen ook automatisch na 7 dagen. Er is geen inhaalslag voor gemiste fires — als Claude bezig is wanneer een taak moet draaien, wacht hij tot Claude idle is en draait dan één keer.\nZie /loop als een tijdelijke assistent tijdens je werksessie. Voor alles dat een herstart moet overleven, lees verder.\nDesktop scheduled tasks — je lokale automatiseringslaag Desktop scheduled tasks draaien op je machine, maar hebben geen open sessie nodig. Je maakt ze aan in de Claude Code Desktop-app, en ze draaien automatisch zolang de app open is en je computer wakker.\nHet grote voordeel: ze hebben volledige toegang tot je lokale bestanden, tools en MCP-servers. Dat betekent dat ze je .sln kunnen lezen, dotnet test kunnen draaien, je lokale Git-status kunnen checken, en met elke tool kunnen werken die je hebt geconfigureerd.\nEen taak opzetten In de Desktop-app klik je op Schedule in de sidebar, dan New task → New local task. Je configureert:\nName: iets beschrijvends zoals nightly-test-run Prompt: de instructies die Claude elke keer volgt Frequency: elk uur, dagelijks, doordeweeks, wekelijks, of handmatig Hier is een prompt die ik zou gebruiken voor een nachtelijke testrun op een ASP.NET Core project:\nDraai dotnet test op de solution in de huidige directory. Als er tests falen, geef een samenvatting van welke projecten en tests gefaald zijn en wat de foutmeldingen zeggen. Als alle tests slagen, zeg gewoon \u0026#34;Alles groen\u0026#34; met het aantal tests. Elke run maakt een nieuwe sessie in de sidebar. Je kunt hem later openen, precies zien wat Claude heeft gedaan, en reageren als dat nodig is.\nOmgaan met slaapstand en gemiste runs Desktop-taken draaien alleen terwijl je computer wakker is. Als je MacBook slaapt tijdens het geplande tijdstip, wordt de run overgeslagen. Wanneer de app weer opstart, checkt hij of er runs gemist zijn in de afgelopen zeven dagen en doet precies één inhaalrun voor het meest recente gemiste tijdstip.\nDit betekent dat een dagelijkse 9-uurtaak die het weekend mist, maandagochtend één keer draait — niet drie keer. Schrijf je prompts met dit in gedachten. Een taak gepland voor 9 uur kan in werkelijkheid om 11 uur draaien als je laptop dicht was.\nAls je wilt dat je computer wakker blijft voor scheduled tasks, schakel Keep computer awake in onder de Desktop-app instellingen. De klep dichtdoen zet hem nog steeds in slaapstand.\nPermissions zonder geklik Elke taak heeft zijn eigen permission mode. De slimme aanpak: klik na het aanmaken op Run now, kijk naar permission-prompts, en selecteer \u0026ldquo;always allow\u0026rdquo; voor elk. Toekomstige runs keuren diezelfde tools automatisch goed zonder te vragen.\nCloud scheduled tasks — werkt als jij slaapt Dit is de grote. Cloud scheduled tasks draaien op Anthropic\u0026rsquo;s infrastructuur. Je laptop kan dicht zijn, je computer uit, je telefoon in vliegtuigmodus. Claude draait je prompt gewoon op schema.\nHoe het werkt Je maakt een cloud-taak aan op claude.ai/code/scheduled, via de Desktop-app (onder New remote task), of vanuit de CLI met /schedule. Je geeft op:\nEen prompt — wat Claude elke run moet doen Een of meer GitHub-repo\u0026rsquo;s — Claude kloont ze vers bij elke run Een omgeving — netwerktoegang, secrets, setup-scripts Een schema — elk uur, dagelijks, doordeweeks, of wekelijks Connectors — MCP-integraties zoals Slack, Linear, of Google Drive Elke run begint vanaf de standaard-branch van je repo, doet het werk, en pusht wijzigingen naar een claude/-prefixed branch. Je reviewt de sessie op claude.ai, ziet wat er veranderd is, en maakt een PR als het er goed uitziet.\n.NET use cases die echt zin hebben Hier wordt het praktisch voor .NET developers:\nNachtelijke test suite validatie. Je ASP.NET Core project heeft 400 integratietests die 12 minuten duren. Plan een cloud-taak om de repo te klonen, dotnet test te draaien, en een samenvatting te posten. Als er iets stuk is gegaan vannacht, weet je het vóór je eerste koffie.\nWekelijkse dependency-audit. NuGet-packages krijgen kwetsbaarheden. Plan een wekelijkse taak:\nKloon de repo, draai dotnet list package --vulnerable --include-transitive. Als er kwetsbaarheden gevonden worden, maak een branch met de updates en push die. Lijst op wat er geüpdatet is en waarom. Claude maakt een claude/dependency-updates branch met de fixes. Jij reviewt de PR.\nDagelijkse PR-review. Als je team open PR\u0026rsquo;s heeft die te lang blijven liggen:\nCheck alle open pull requests. Voor elke PR ouder dan 2 dagen zonder review, vat samen wat er verandert en wijs op mogelijke problemen. Focus op testdekking en breaking changes. Koppel een Slack-connector en Claude kan de samenvatting direct in je teamkanaal posten.\nPost-merge documentatie-sync. Na het mergen van PR\u0026rsquo;s driften API-docs weg van de werkelijkheid:\nVergelijk het publieke API-oppervlak van de Controllers/ directory met de docs in docs/api/. Markeer endpoints, parameters of response types die gedocumenteerd zijn maar niet bestaan, of bestaan maar niet gedocumenteerd zijn. Wat cloud-taken niet kunnen Cloud-taken hebben je lokale bestanden niet. Ze klonen je repo vers bij elke run. Dat betekent geen toegang tot lokale databases, geen lokale Docker-containers, geen bestanden buiten je repo. Als je tests een draaiende SQL Server of Redis nodig hebben, zijn cloud-taken niet de juiste keuze — gebruik dan Desktop-taken.\nCloud-taken kunnen ook niet naar willekeurige branches pushen. Ze zijn beperkt tot claude/-prefixed branches, tenzij je expliciet onbeperkte branch-pushes inschakelt voor een repo.\nDe juiste optie kiezen Na het gebruik van alle drie, is dit mijn vuistregel:\nGebruik /loop als je in een sessie zit en iets wilt pollen. Deploy-status, PR-comments, buildvoortgang. Snel, wegwerpbaar, nul setup.\nGebruik Desktop-taken als het werk je lokale omgeving nodig heeft. dotnet test draaien tegen je daadwerkelijke solution met lokale dependencies, bestanden checken die niet in Git staan, lokale MCP-servers gebruiken.\nGebruik cloud-taken als betrouwbaarheid telt. Nachtelijke audits, dagelijkse reviews, wekelijkse rapporten. Dingen die moeten gebeuren of je nu achter je bureau zit of niet.\nDe drie opties vullen elkaar aan. Ik gebruik /loop overdag, een Desktop-taak voor mijn ochtendlijke testrun, en cloud-taken voor de wekelijkse dependency-audit. Samen zorgen ze ervoor dat het repetitieve werk dat ik altijd vergat, gewoon gebeurt.\nProbeer het zelf Begin met /loop — het kost vijf seconden:\n/loop 10m draai dotnet test en vertel me of er iets stuk is Kijk hoe het een paar keer draait. Wen aan het idee dat Claude op de achtergrond werk doet terwijl jij je op iets anders focust.\nZet dan een Desktop-taak op voor iets dat je altijd vergeet. De dagelijkse testrun. De dependency-check. De PR-review die je steeds uitstelt tot vrijdag.\nAls je het patroon vertrouwt, maak een cloud-taak voor werk dat moet gebeuren als je laptop dicht is. Dán valt het kwartje: Claude Code is niet alleen een assistent waarmee je praat. Het is een assistent die voor je werkt, de klok rond.\nDie NuGet-kwetsbaarheid die je volgende maand gevonden zou hebben? Claude vond hem vanochtend.\n","permalink":"https://renedekkers.nl/nl/posts/scheduled-tasks-claude-code-werkt-door-terwijl-jij-slaapt/","summary":"Claude Code kan nu prompts op schema draaien — van snel pollen in een sessie tot volledig autonome cloud-taken die draaien terwijl je laptop dicht is. Drie opties, één doel: automatiseer het werk dat je steeds vergeet te doen.","title":"Scheduled Tasks — Claude Code werkt door terwijl jij slaapt"},{"content":"Een paar weken geleden bracht Anthropic Claude Opus 4.6 uit — en maakte het meteen het standaardmodel in Claude Code. Ik gebruik het dagelijks sindsdien. Het verschil is niet subtiel.\nDe cijfers zijn indrukwekkend: 1 miljoen tokens context (in extended beta), 80.8% op SWE-bench Verified, en extended thinking waarmee het model stap voor stap redeneert voordat het een antwoord geeft. Maar cijfers vertellen je niet hoe het voelt om ermee te werken. Dus laat ik je laten zien wat er concreet verandert als je met .NET code werkt.\nHet contextvenster verandert alles Eerdere Claude-modellen hadden een maximum van 200K tokens. Dat klinkt als veel — totdat je een echte .NET solution probeert te refactoren. Een typisch enterprise project heeft tientallen .csproj-bestanden, honderden klassen, Entity Framework models, service layers, controllers en tests. Bij 200K tokens raakte Claude het overzicht kwijt over hoe je OrderService zich verhoudt tot je PaymentGateway drie projecten verderop.\nMet 1M tokens past je hele solution in één sessie. Elke modelklasse, elk interface, elke DI-registratie — Claude ziet het allemaal tegelijk.\nDit is wat dat in de praktijk betekent. Stel dat je een domeinconcept wilt hernoemen door je hele solution. Je Customer wordt een Tenant omdat je app multi-tenant wordt. Dat raakt models, DTO\u0026rsquo;s, database-migraties, API-contracten, validatieregels en tests in meerdere projecten.\n// Ervoor: verspreid over 40+ bestanden public class Customer { ... } public class CustomerService : ICustomerService { ... } public class CustomerRepository : ICustomerRepository { ... } public record CreateCustomerRequest(string Name, string Email); // Erna: Claude hernoemt alles consistent public class Tenant { ... } public class TenantService : ITenantService { ... } public class TenantRepository : ITenantRepository { ... } public record CreateTenantRequest(string Name, string Email); Met Opus 4.6 leest Claude je hele solution, begrijpt de relaties tussen projecten, en hernoemt consistent — inclusief de EF Core-configuratie, de DI-registraties in Program.cs, en de test fixtures. Met eerdere modellen miste het onvermijdelijk iets drie projecten diep.\nDieper redeneren, minder fouten Opus 4.6 heeft extended thinking — het model werkt stap voor stap door een probleem voordat het code schrijft. Je ziet het denkproces niet (dat gebeurt intern), maar je merkt het aan de resultaten.\nIk vroeg Claude om soft delete toe te voegen aan een bestaande Entity Framework setup. Met eerdere modellen kreeg ik meestal de IsDeleted-property en de global query filter — maar het miste de cascade delete-configuratie, of vergat de Include-calls aan te passen die nu rekening moeten houden met verwijderde gerelateerde entiteiten.\nMet Opus 4.6 pakte het alles mee:\n// 1. Base entity met soft delete public abstract class SoftDeletableEntity { public bool IsDeleted { get; set; } public DateTime? DeletedAt { get; set; } } // 2. Global query filter in DbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { // Toegepast op elke entity die SoftDeletableEntity overerft foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(SoftDeletableEntity).IsAssignableFrom(entityType.ClrType)) { modelBuilder.Entity(entityType.ClrType) .HasQueryFilter( GenerateSoftDeleteFilter(entityType.ClrType)); } } } // 3. Override SaveChanges om deletes te onderscheppen public override int SaveChanges() { foreach (var entry in ChangeTracker.Entries\u0026lt;SoftDeletableEntity\u0026gt;() .Where(e =\u0026gt; e.State == EntityState.Deleted)) { entry.State = EntityState.Modified; entry.Entity.IsDeleted = true; entry.Entity.DeletedAt = DateTime.UtcNow; } return base.SaveChanges(); } Het paste ook de repository-methodes aan die .IgnoreQueryFilters() nodig hadden voor admin-scenario\u0026rsquo;s, voegde de migratie toe, en stelde de tests bij. Dat is extended thinking in actie — het redeneert door alle implicaties voordat het één regel schrijft.\nWat Opus 4.6 vs. Sonnet 4.6 betekent voor je workflow Claude Code wordt nu geleverd met twee modellen: Opus 4.6 (standaard) en Sonnet 4.6. Ze delen dezelfde architectuur maar verschillen in waar ze uitblinken.\nOpus 4.6 Sonnet 4.6 SWE-bench 80.8% 79.6% GPQA Diamond 91.3% 74.1% Context Tot 1M tokens 200K tokens Snelheid Langzamer, dieper Sneller, lichter Prijs $15/$75 per M tokens $3/$15 per M tokens De SWE-bench scores liggen dicht bij elkaar, maar het GPQA Diamond verschil (91.3% vs 74.1%) vertelt het echte verhaal. GPQA meet redeneren op academisch niveau — het soort denken dat je nodig hebt bij het debuggen van een race condition in je async middleware, of het ontwerpen van een multi-tenant data-isolatiestrategie.\nMijn vuistregel: gebruik Opus voor architectuurbeslissingen, complexe refactorings, en debugging van problemen die meerdere services overspannen. Gebruik Sonnet voor snelle aanpassingen, boilerplate genereren, en routinetaken waar snelheid belangrijker is dan diepgang. Je kunt wisselen in Claude Code met /model.\nPraktijkvoorbeeld: een achtergrondtakensysteem migreren Vorige week gebruikte ik Opus 4.6 om een Hangfire-setup te migreren naar een eigen IHostedService-implementatie. Het bestaande systeem had 12 terugkerende jobs, elk met eigen retry-logica, en een dashboard dat de jobstatus toonde.\nMet eerdere modellen had ik dit in kleinere taken moeten opsplitsen — één job tegelijk migreren, testen, herhalen. Het model kon niet het volledige plaatje vasthouden van hoe alle jobs op elkaar inwerkten.\nMet Opus 4.6 laadde ik de hele solution en vroeg Claude om de migratie te ontwerpen. Het:\nAnalyseerde alle 12 jobklassen en hun afhankelijkheden Identificeerde gedeelde patronen (retry-logica, logging, foutafhandeling) Maakte een basis RecurringJobService-klasse Migreerde elke job naar het nieuwe patroon Paste de DI-registraties aan Verving de Hangfire dashboard-calls door een health check endpoint Schreef integratietests voor de nieuwe scheduling De hele migratie gebeurde in één sessie. Niet omdat Opus sneller is — het is eigenlijk langzamer dan Sonnet — maar omdat het over alle 12 jobs tegelijk kon redeneren in plaats van de retry-logica van job #3 te vergeten terwijl het met job #8 bezig was.\nDe prijsvraag Opus 4.6 kost 5x meer dan Sonnet 4.6. Dat is significant. Bij $15 per miljoen input tokens en $75 per miljoen output tokens kan een stevige refactoring-sessie echt geld kosten.\nMaar hier is het punt: de kosten zijn niet per uur, maar per token. Een complexe taak waarbij Sonnet drie pogingen nodig heeft (omdat het steeds edge cases mist) kan duurder uitpakken dan Opus die het in één keer goed doet. Ik ben gestopt met denken over modelkosten per request en denk nu in kosten per afgeronde taak.\nVoor de meeste .NET development is Opus als standaard logisch. Je schrijft geen wegwerpscripts — je bouwt systemen waar de architectuur er goed uit moet komen. Het diepere redeneren verdient zichzelf terug in minder iteraties en minder bugs die de code review halen.\nWat niet veranderd is Opus 4.6 is nog steeds Claude Code. De workflow is hetzelfde: beschrijf wat je wilt, bekijk wat het oplevert, stuur bij als het de verkeerde kant opgaat. Het CLAUDE.md-bestand, de git-integratie, de permission modes — niets daarvan is veranderd.\nWat wel veranderd is, is het plafond. Taken die te complex waren voor één sessie — refactorings door je hele solution, cross-project dependency updates, architectuurmigraties — zijn nu binnen bereik. Niet omdat de tool een nieuwe knop kreeg, maar omdat het model slimmer is en meer context kan vasthouden.\nProbeer het zelf Als je Claude Code al gebruikt, werk je al met Opus 4.6 — het is de standaard. Probeer iets wat je eerder niet zou hebben geprobeerd:\nLaad je hele solution en vraag Claude om inconsistenties te vinden in je error handling patronen Refactor een domeinconcept dat elke laag van je architectuur raakt Vraag het om een migratiestrategie te ontwerpen voor een legacy component, met alle edge cases meegenomen Het 1M token contextvenster betekent dat Claude je hele codebase tegelijk kan zien. Extended thinking betekent dat het door implicaties redeneert voordat het code schrijft. Samen maken ze Claude Code minder als een autocomplete-tool en meer als een senior developer die daadwerkelijk elk bestand in je repo heeft gelezen.\nDat is de echte verschuiving met Opus 4.6. Niet alleen betere benchmarks — maar beter begrip van de code waar je daadwerkelijk aan werkt.\n","permalink":"https://renedekkers.nl/nl/posts/claude-opus-4-6-wat-het-beste-ai-model-betekent-voor-je-dotnet-code/","summary":"Opus 4.6 is nu het standaardmodel in Claude Code. Met 1M tokens context en een SWE-bench score van 80.8% is dit de grootste sprong in één generatie. Dit verandert er voor .NET developers.","title":"Claude Opus 4.6 — wat het beste AI-model betekent voor je .NET code"},{"content":"Je kent het ritme. Claude Code stelt een edit voor — je klikt approve. Het wil dotnet build draaien — approve. Dan dotnet test — approve. Het leest een bestand — approve. Nog een edit — approve. Een refactoring-sessie verandert in een klikoefening.\nIk betrap mezelf erop dat ik commando\u0026rsquo;s goedkeur zonder ze te lezen. Op dat moment beschermt het permission-systeem me niet meer. Het vertraagt me alleen maar.\nAuto Mode lost dit op.\nWat Auto Mode precies is Auto Mode is een permission mode in Claude Code waarmee Claude acties uitvoert zonder je eerst te vragen. Maar anders dan --dangerously-skip-permissions (dat alle veiligheidschecks uitschakelt), draait Auto Mode een achtergrond-classifier die elke actie beoordeelt voordat die wordt uitgevoerd.\nZie het als een beveiliger die je project kent. Hij zwaait je dagelijkse routine door — bestanden bewerken, builds draaien, tests uitvoeren — maar stopt alles wat echte schade zou kunnen aanrichten. Een dotnet build? Prima. Een curl | bash vanaf een onbekende URL? Geblokkeerd.\nDe classifier draait op Claude Sonnet 4.6 als een apart model-verzoek. Hij ziet je conversatie en de geplande actie, maar niet de ruwe tool-resultaten — dus kwaadaardige content in een bestand of webpagina kan hem niet misleiden om iets gevaarlijks goed te keuren.\nWaarom dit belangrijk is voor .NET developers Een typische .NET-workflow met Claude Code gaat zo: je vraagt Claude om een service class te refactoren, unit tests toe te voegen en de test suite te draaien. Dat zijn makkelijk 15-20 permission prompts in een sessie. Bewerk de interface, approve. Bewerk de implementatie, approve. Maak het testbestand aan, approve. Draai dotnet test, approve. Fix een falende test, approve. Draai tests opnieuw, approve.\nMet Auto Mode draait die hele flow ononderbroken. Claude bewerkt je bestanden, draait dotnet build, voert dotnet test uit, leest de output, fixt wat kapot is, en draait de tests opnieuw. Jij bekijkt de voortgang, beoordeelt de resultaten, en grijpt alleen in als je het niet eens bent met de richting.\nDit is vooral krachtig voor:\nTest-driven development. De red-green-refactor cyclus vereist constant heen-en-weer tussen bewerken en tests draaien. Auto Mode laat Claude door die cyclus itereren zonder dat je bij elke stap op approve klikt. Grote refactorings. Een service hernoemen door je hele solution, alle DI-registraties bijwerken, de tests fixen — dat zijn tientallen file edits en build commando\u0026rsquo;s. Auto Mode handelt het af als één continue flow. Onbekende code verkennen. Als Claude bestanden leest, referenties checkt en door je solution-structuur navigeert, wil je niet elke Read call goedkeuren. Auto Mode laat Claude vrij verkennen. Wat wordt geblokkeerd, wat gaat door Auto Mode vertrouwt je werkmap en de geconfigureerde remotes van je repo. Al het andere wordt als extern behandeld totdat je het anders instelt.\nStandaard toegestaan:\nFile edits in je projectmap Build- en testcommando\u0026rsquo;s draaien (dotnet build, dotnet test) Dependencies installeren vanuit je lock files .env-bestanden lezen en credentials gebruiken met hun bijbehorende API\u0026rsquo;s Read-only HTTP-verzoeken Pushen naar je huidige branch of een branch die Claude heeft aangemaakt Standaard geblokkeerd:\nRemote code downloaden en uitvoeren (curl | bash) Gevoelige data naar onbekende endpoints sturen Productie-deploys en database-migraties Massale verwijdering op cloud storage IAM-rechten of gedeelde infrastructuur wijzigen Force push of direct naar main pushen Bestanden vernietigen die al bestonden vóór de sessie De geblokkeerde lijst is verstandig. Dit zijn precies de acties waarbij je een mens in de loop wilt — zelfs als je de commando\u0026rsquo;s zelf zou uitvoeren.\nHoe je het inschakelt Auto Mode vereist een Team-, Enterprise- of API-plan (niet Pro of Max), Claude Sonnet 4.6 of Opus 4.6, en de Anthropic API-provider. Als je op een geschikt plan zit, schakel het in met:\nclaude --enable-auto-mode Daarna voegt Auto Mode zich bij de Shift+Tab-cyclus. Je drukt op Shift+Tab om door default → acceptEdits → plan → auto te gaan. De statusbalk toont wanneer Auto Mode actief is.\nJe kunt het ook als standaardmodus instellen in je settings:\n{ \u0026#34;permissions\u0026#34;: { \u0026#34;defaultMode\u0026#34;: \u0026#34;auto\u0026#34; } } In VS Code klik je op de mode-indicator onderaan het promptvak — Auto Mode verschijnt zodra je admin het heeft ingeschakeld.\nWanneer het terugvalt Auto Mode is geen zwarte doos. Als de classifier een actie blokkeert, zie je een notificatie. Geblokkeerde acties verschijnen in /permissions onder het tabblad \u0026ldquo;Recently denied\u0026rdquo;, waar je op r kunt drukken om het opnieuw te proberen met handmatige goedkeuring.\nAls de classifier 3 acties achter elkaar of 20 in totaal in een sessie blokkeert, pauzeert Auto Mode en gaat Claude Code terug naar prompting. Een actie handmatig goedkeuren hervat Auto Mode. Dit is een vangnet — als iets consistent wordt geblokkeerd, is er waarschijnlijk een reden.\nIn de praktijk bereik ik deze limieten zelden. De classifier is goed in het begrijpen van wat routine is en wat risicovol. Als hij iets blokkeert, is het meestal omdat ik iets ongewoons doe — zoals pushen naar een remote die ik nog niet eerder heb gebruikt.\nConfigureren voor je omgeving Als de classifier acties blokkeert die routine zijn voor je team — bijvoorbeeld deployen naar een staging-omgeving of schrijven naar een specifieke S3-bucket — kan je admin vertrouwde infrastructuur configureren:\n{ \u0026#34;autoMode\u0026#34;: { \u0026#34;environment\u0026#34;: [ \u0026#34;Trusted staging: deploy.staging.example.com\u0026#34;, \u0026#34;Trusted bucket: s3://our-staging-bucket\u0026#34; ] } } Dit gaat in je settings.json (niet de gedeelde project settings, voor de veiligheid). De classifier gebruikt deze hints om te begrijpen wat normaal is voor je setup.\nAuto Mode vs de alternatieven Claude Code geeft je een spectrum van permission modes:\nModus Wat het doet Wanneer gebruiken default Vraagt voor alles Gevoelig werk, opstarten acceptEdits Keurt alleen file edits automatisch goed Snel itereren met handmatige build-controle plan Read-only, stelt wijzigingen voor zonder ze uit te voeren Verkennen voordat je beslissingen maakt auto Keurt goed met achtergrond safety checks Lange sessies, TDD, refactoring bypassPermissions Geen checks Alleen in geïsoleerde containers Voor de meeste .NET-development wissel ik tussen acceptEdits en auto. Als ik een nieuwe feature bouw en hands-on wil blijven, is acceptEdits genoeg. Als ik in een refactoring-sessie of TDD-cyclus zit, bespaart Auto Mode me het klikken op approve bij hetzelfde dotnet test commando voor de vijftigste keer.\nProbeer het zelf De volgende keer dat je een refactoring-sessie of TDD-cyclus start, schakel Auto Mode in:\nclaude --enable-auto-mode Druk op Shift+Tab totdat je de auto-indicator ziet. Geef Claude dan een stevige taak — refactor een service, voeg tests toe voor een ongeteste class, hernoem iets door je hele solution.\nKijk hoe het door het probleem werkt zonder elke drie seconden te stoppen. Controleer de resultaten als het klaar is. Als er iets mis ging, laat git diff precies zien wat er is veranderd, en git checkout . maakt alles ongedaan.\nDe eerste keer dat Claude een complete red-green-refactor cyclus doorloopt zonder een enkele permission prompt, realiseer je je hoeveel tijd je besteedde aan het klikken op approve bij commando\u0026rsquo;s die je toch altijd ging goedkeuren.\n","permalink":"https://renedekkers.nl/nl/posts/auto-mode-einde-van-approve-approve-approve/","summary":"Elke Claude Code-gebruiker kent het ritueel: approve de file edit, approve het build commando, approve de test run. Auto Mode vervangt dat klik-festival met een achtergrond safety classifier — zodat je je kunt focussen op bouwen.","title":"Auto Mode — het einde van approve, approve, approve"},{"content":"Je bent halverwege een nieuw API-endpoint. De code compileert, de tests zijn groen, en Claude Code heeft volledige context van wat je aan het doen bent. Dan licht Slack op: een productie-bug die nu gefixt moet worden.\nDus stash je je wijzigingen. Switch je van branch. Probeer je te herinneren hoe de codebase eruitzag vóór je feature-werk. Fix de bug. Switch terug. Pop de stash. Leg opnieuw aan Claude Code uit waar je mee bezig was.\nHerkenbaar? Er is een betere manier.\nWat zijn git worktrees? Een git worktree laat je meerdere branches van dezelfde repository uitchecken in aparte mappen — zonder de repo opnieuw te clonen. Elke map heeft zijn eigen working tree, zijn eigen branch, en zijn eigen bestanden. Maar ze delen dezelfde .git-history.\n# Maak een worktree aan voor een hotfix git worktree add ../mijn-project-hotfix fix/payment-timeout Dat is alles. Je hebt nu twee mappen:\nmijn-project/ — je hoofdkopie, nog steeds op je feature branch mijn-project-hotfix/ — een aparte map, uitgecheckt op fix/payment-timeout Geen clone. Geen download. Het duurt seconden omdat de bestanden al lokaal staan. De nieuwe map is een volledige werkkopie — je kunt er onafhankelijk in builden, testen en runnen.\nWaarom worktrees en Claude Code zo goed bij elkaar passen Claude Code draait in je terminal, in een specifieke map. Het leest de bestanden om zich heen, bouwt context op vanuit wat het ziet, en werkt binnen die scope. Eén map, één sessie, één focus.\nDat past perfect bij worktrees. Elke worktree is een aparte map met een aparte branch. Start een Claude Code-sessie in elk ervan, en je hebt onafhankelijke AI-sessies — elk met hun eigen context, hun eigen gesprek, hun eigen focus.\nGeen bestandsconflicten. Geen stash-gegoochel. Geen opnieuw uitleggen waar je mee bezig was. Elke sessie pakt precies op waar die is.\nOpzetten voor een .NET solution Stel, je werkt aan een ASP.NET Core solution genaamd OrderSystem. Je hoofdkopie staat in ~/projects/OrderSystem/, en je zit op een feature branch.\nEr komt een hotfix binnen. Dit doe je:\n# Vanuit je hoofdmap cd ~/projects/OrderSystem # Maak een worktree aan voor de hotfix git worktree add ../OrderSystem-hotfix fix/order-total-rounding # Open een nieuw terminal-tabblad en navigeer ernaartoe cd ~/projects/OrderSystem-hotfix De worktree bevat je volledige solution — .sln-bestand, alle projecten, alle configuratie. Je kunt direct draaien:\ndotnet build dotnet test Alles werkt. Het is een complete kopie van je repo, alleen op een andere branch.\nStart nu Claude Code:\nclaude Claude Code leest de solution-structuur, vindt de .csproj-bestanden, ziet de CLAUDE.md — en is klaar om te werken. Volledig onafhankelijk van je andere sessie.\nScenario: feature + hotfix tegelijkertijd Zo werkt dit in de praktijk.\nTerminal 1 — je feature-werk, in ~/projects/OrderSystem/:\nJe bouwt een klantendashboard-endpoint. Claude Code helpt je al een uur. Het kent de controller-patronen, de service-laag, de DTO\u0026rsquo;s die je hebt aangemaakt. Je zit midden in een gesprek:\n\u0026ldquo;Voeg paginering toe aan het GetOrderHistory-endpoint. Gebruik hetzelfde PagedResult-patroon als in de ProductsController.\u0026rdquo;\nClaude Code werkt hieraan. Laat het met rust.\nTerminal 2 — de hotfix, in ~/projects/OrderSystem-hotfix/:\nJe opent een verse Claude Code-sessie:\n\u0026ldquo;Er zit een afrondingsbug in OrderTotalCalculator.cs. Bij het toepassen van een procentuele korting komt het totaal soms uit op drie decimalen in plaats van twee. Fix het en voeg een unit test toe die dit edge case dekt.\u0026rdquo;\nDeze sessie weet niets van je feature-werk. Dat hoeft ook niet. Het focust puur op de afrondingsbug, vindt het probleem, schrijft de fix, voegt de test toe.\nVijftien minuten later is de hotfix klaar. Je commit, pusht, maakt een PR aan. Ondertussen heeft Terminal 1 het paginering-werk afgerond — volledig ongestoord.\nCLAUDE.md werkt automatisch mee Als je een CLAUDE.md-bestand in je repository root hebt — en dat zou je moeten hebben — dan reist het mee met elke worktree. Wanneer Claude Code start in een worktree-map, vindt en leest het CLAUDE.md net zoals in je hoofdkopie.\nJe projectconventies, architectuurnotities, codestandaarden — alles geldt automatisch. Je hoeft geen bestanden te kopiëren of iets in te stellen. De worktree is een echte checkout van je repository, dus alles wat in git wordt bijgehouden is er.\nOpruimen Als je klaar bent met een worktree, ruim je het op:\n# Zorg eerst dat je je wijzigingen hebt gecommit en gepusht cd ~/projects/OrderSystem-hotfix git push origin fix/order-total-rounding # Verwijder dan de worktree cd ~/projects/OrderSystem git worktree remove ../OrderSystem-hotfix De map verdwijnt, de branch blijft. Je git-history is schoon, en je bent terug bij één werkmap.\nAls je wilt zien welke worktrees er actief zijn:\ngit worktree list Ik maak er een gewoonte van om worktrees op te ruimen zodra de branch is gemerged. Het houdt je schijf netjes en voorkomt verwarring over welke mappen nog actief zijn.\nWanneer worktrees overkill zijn Laten we eerlijk zijn — je hebt dit niet altijd nodig.\nSnelle fixes op dezelfde branch. Als je maar één bestand hoeft te bewerken en te committen, is git stash prima. Maak geen worktree aan voor een wijziging van twee minuten.\nSterk gekoppelde wijzigingen. Als beide taken dezelfde bestanden moeten aanpassen, helpen worktrees niet. Je krijgt toch merge conflicts. Werk er dan sequentieel aan.\nKleine repositories. Als je project een enkele API is met vijf bestanden, voegt het beheren van meerdere mappen meer complexiteit toe dan het wegneemt.\nWorktrees schitteren wanneer je solution groot genoeg is dat verschillende taken verschillende delen van de codebase raken. Een typische .NET solution met meerdere projecten — API, services, shared libraries, tests — is precies waar deze aanpak rendeert.\nProbeer het zelf De volgende keer dat er een onderbreking komt terwijl je midden in een feature zit, weersta de drang om te stashen. Doe in plaats daarvan:\n# Maak een worktree aan voor de onderbreking git worktree add ../mijn-project-fix fix/het-urgente-ding # Open een nieuwe terminal cd ../mijn-project-fix claude Geef Claude Code een duidelijke, gerichte instructie. Laat het werken terwijl je andere sessie precies blijft waar je hem achterliet.\nDe eerste keer dat je terugschakelt naar je feature-terminal en alles precies aantreft zoals je het achterliet — context intact, gesprek bewaard, bestanden onaangeroerd — vraag je je af waarom je ooit git stash gebruikte.\n","permalink":"https://renedekkers.nl/nl/posts/git-worktrees-met-claude-code/","summary":"Je zit midden in een feature branch. Er komt een urgente bug binnen. In plaats van stashen, switchen en je flow kwijtraken — open je een tweede map en werk je gewoon door. Git worktrees maken dit mogelijk, en Claude Code maakt het krachtig.","title":"Git worktrees met Claude Code — werk aan meerdere features zonder context te verliezen"},{"content":"Ik schrijf minder code dan ooit. En toch ben ik productiever dan ik in jaren ben geweest. Dat voelt paradoxaal, maar het klopt: mijn rol is verschoven. Ik typ minder, maar ik denk meer. De vraag is niet meer \u0026ldquo;hoe implementeer ik dit?\u0026rdquo; maar \u0026ldquo;wat moet hier gebouwd worden, en waarom?\u0026rdquo;\nDat is geen kleine verschuiving. Voor de meeste van ons is onze identiteit als developer nauw verbonden met code schrijven. Het vakmanschap om een complex algoritme goed te krijgen, de voldoening van een schone refactor, de flow-state als alles klikt. En nu komt er een tool die dat deel voor je doet — vaak sneller en soms beter.\nDe echte vraag is: wat blijft er over? En het antwoord, denk ik, is interessanter dan je zou verwachten.\nDe oude workflow vs. de nieuwe Een jaar geleden zag het bouwen van een nieuw endpoint in onze ASP.NET Core API er zo uit: ik bekeek een bestaand endpoint, kopieerde de structuur, schreef de controller of minimal API handler, voegde de validatie toe, registreerde de DI-dependencies, schreef de tests. Meestal wist ik precies wat er moest gebeuren. Het werk zat in het typen.\nNu begint diezelfde feature anders. Ik beschrijf wat het endpoint moet doen, hoe het request en response eruitzien, en hoe het past bij de bestaande patronen. Claude Code leest de codebase, ziet dat we Minimal APIs gebruiken met een specifieke mappenstructuur, en genereert het endpoint inclusief validatie, foutafhandeling en tests. Mijn taak is te reviewen of het past.\nDat klinkt sneller. En dat is het ook — voor het implementatiedeel. Maar hier is wat ik niet verwacht had: het denkwerk duurt nu langer. Niet omdat AI het moeilijker maakt, maar omdat ik me niet meer kan verschuilen achter het typen. Als je zelf code schrijft, kun je beginnen met een vaag idee en het laten uitkristalliseren terwijl je typt. Met AI moet je precies weten wat je wilt voordat er iets gegenereerd wordt.\nDie precisie is de nieuwe vaardigheid.\nWat je kunt delegeren — en wat niet Na maanden dagelijks werken met Claude Code heb ik een redelijk helder beeld van wat ik uit handen kan geven en wat niet. De grens ligt niet waar ik hem verwacht had.\nWat goed te delegeren is:\nBoilerplate: CRUD-endpoints, DTO\u0026rsquo;s, mapping-code, DI-registratie Tests: unit tests volgens een bestaand patroon, vooral als je naar een voorbeeld-testbestand verwijst Refactoring: een klasse extraheren, consistent hernoemen, een grote methode opsplitsen Standaardpatronen: repository-implementaties, event handlers, middleware — alles waar het patroon duidelijk is Wat niet te delegeren is:\n\u0026ldquo;Moet dit een aparte service worden of onderdeel van de bestaande?\u0026rdquo; — architectuurbeslissingen die afhangen van je domein \u0026ldquo;Is eventual consistency hier acceptabel, of hebben we een transactie nodig?\u0026rdquo; — afwegingen die begrip van de businesscontext vereisen \u0026ldquo;Past dit bij hoe ons team werkt?\u0026rdquo; — teamconventies die nergens vastliggen Een concreet voorbeeld. Ik vroeg Claude om caching toe te voegen aan een service. Het genereerde een perfect geïmplementeerde IMemoryCache-integratie. Schone code, correct gebruik, zelfs met goede cache-invalidatie. Maar wij gebruiken Redis in productie omdat onze API op meerdere instanties draait. IMemoryCache was de verkeerde keuze — niet omdat het niet werkt, maar omdat het niet past bij onze infrastructuur.\nClaude kon dat niet weten. En dat is het punt: de implementatie was technisch correct, maar architecturaal fout. Dat opmerken is jouw taak. Niet het typen — het denken.\nDe vaardigheden die er nu toe doen Drie vaardigheden zijn belangrijker geworden in mijn dagelijkse werk. Geen van drieën gaat over code schrijven.\nSysteemdenken Als Claude code genereert, lost het het lokale probleem op. Endpoint toevoegen? Klaar. Test schrijven? Klaar. Maar software is geen verzameling losse onderdelen. Het is een systeem waar alles samenhangt: de DI-container koppelt services aan elkaar, middleware handelt cross-cutting concerns af, events stromen door handlers.\nJouw taak is het overzicht houden. Moet deze nieuwe service scoped of singleton zijn? Moet deze event handler idempotent zijn omdat de message broker mogelijk herhaalt? Moet dit endpoint rate limiting krijgen omdat het publiek toegankelijk is?\nDit zijn geen implementatievragen. Het zijn architectuurvragen. En ze vereisen begrip van het hele systeem, niet alleen het bestand waar je naar kijkt.\nCode review als kernvaardigheid Code review was altijd belangrijk. Maar het was iets dat je deed voor collega\u0026rsquo;s. Nu is het iets dat je doet voor elk stuk gegenereerde code — en je doet het tientallen keren per dag.\nDat verandert de vaardigheid. Je zoekt niet alleen bugs. Je vraagt: past dit? Volgt het onze naamgevingsconventies? Komt de foutafhandeling overeen met wat we elders doen? Is het abstractieniveau juist, of is het over-engineered?\nIk heb gemerkt dat mijn reviews scherper zijn geworden sinds ik met AI werk. Doordat ik meer code review, heb ik een beter oog ontwikkeld voor inconsistenties. Dat is een onverwacht voordeel.\nHelder specificeren Dit is de grote. De kwaliteit van je output hangt volledig af van hoe goed je kunt beschrijven wat je wilt. Niet in code — in woorden.\nDat betekent: precies zijn over gedrag, niet over implementatie. Constraints expliciet benoemen. Verwijzen naar bestaande patronen in plaats van hopen dat Claude ze vindt. Weten hoe \u0026ldquo;klaar\u0026rdquo; eruitziet voordat je begint.\nDit is, in zekere zin, de nieuwe vorm van code schrijven. In plaats van if (order.Status == OrderStatus.Cancelled) schrijf je: \u0026ldquo;Als een order wordt geannuleerd, stuur een notificatie via ons bestaande IEventHandler-patroon. Kijk naar hoe we OrderShipped afhandelen voor de structuur.\u0026rdquo;\nDe precisie is dezelfde. Het medium is anders.\nDe ongemakkelijke waarheid Niet iedereen vindt dit comfortabel. En ik denk dat het waard is om dat te zeggen.\nAls je identiteit als developer gebouwd is op \u0026ldquo;ik schrijf schone, elegante code\u0026rdquo;, voelt het vreemd om dat uit handen te geven. Er is een echt gevoel van verlies als je beseft dat het vakmanschap waar je jarenlang aan hebt geslepen nu geautomatiseerd kan worden.\nMaar ik ben gaan geloven dat de waarde nooit in het typen zat. Het zat in het begrijpen. Weten waarom een bepaald patroon de juiste keuze is. Weten wanneer een abstractie helpt en wanneer het schaadt. Weten welke vragen je moet stellen voordat er één regel code is geschreven.\nDat begrip wordt niet minder belangrijk met AI. Het wordt belangrijker. Want nu ben je niet alleen verantwoordelijk voor de code die je schrijft. Je bent verantwoordelijk voor de code die je accepteert. En slechte code accepteren omdat het snel gegenereerd is, is erger dan het langzaam zelf schrijven.\nDe developers die ik het meest zie worstelen met AI zijn degenen die de review overslaan. Niet omdat ze lui zijn, maar omdat de code er goed uitziet. Het compileert, de tests slagen, de structuur lijkt vertrouwd. Maar \u0026ldquo;ziet er goed uit\u0026rdquo; en \u0026ldquo;is goed\u0026rdquo; zijn verschillende dingen — vooral als het gaat om hoe code past in een groter systeem.\nJe titel verandert niet. Je werk wel. Ik noem mezelf nog steeds een developer. Ik bouw nog steeds elke dag software. Maar wat dat betekent is verschoven. Minder tijd in de editor, meer tijd nadenken over ontwerp. Minder typen, meer reviewen. Minder \u0026ldquo;hoe\u0026rdquo;, meer \u0026ldquo;wat\u0026rdquo; en \u0026ldquo;waarom\u0026rdquo;.\nDe beste developers van de komende jaren worden niet de snelste typisten. Het worden de scherpste denkers. Degenen die naar gegenereerde code kunnen kijken en niet alleen zien of het werkt, maar of het thuishoort.\nDe volgende keer dat je aan een feature begint, probeer dit: schrijf — in gewone taal — wat je wilt bouwen, waarom, en hoe het moet passen bij wat er al staat, voordat je ook maar één prompt typt. Beschrijf het alsof je een ervaren collega briefed die je codebase niet kent. Die oefening is het werk nu. De implementatie volgt.\n","permalink":"https://renedekkers.nl/nl/posts/van-reviewer-naar-architect/","summary":"Ik schrijf minder code dan ooit. En toch ben ik productiever. De vraag is niet meer \u0026lsquo;hoe implementeer ik dit?\u0026rsquo; maar \u0026lsquo;wat moet hier gebouwd worden en waarom?\u0026rsquo;","title":"Van code schrijven naar software-architectuur — hoe AI je rol verandert"},{"content":"Eén Claude Code sessie is krachtig. Het leest je code, begrijpt de context en schrijft werkende oplossingen. Maar er is iets dat ik wekenlang niet probeerde: meerdere sessies tegelijk draaien.\nToen ik het eenmaal deed, veranderde mijn workflow fundamenteel. Dit is wat ik leerde.\nWaarom parallelle sessies werken Elke Claude Code sessie draait in zijn eigen terminal. Het heeft zijn eigen context, zijn eigen gespreksgeschiedenis en zijn eigen focus. Sessies weten niet van elkaars bestaan — en dat is precies het punt.\nZie het als twee developers in je team. De één bouwt het API-endpoint terwijl de ander aan het frontend-component werkt. Daar hoeven ze niet samen voor te zitten. Ze moeten alleen uit elkaars bestanden blijven.\nHetzelfde geldt voor Claude Code sessies. Zolang ze aan verschillende delen van je codebase werken, draaien ze volledig onafhankelijk.\nScenario 1: API + Frontend Je bouwt een nieuwe feature in je .NET solution. De backend heeft een nieuw endpoint nodig in je Web API project. De frontend heeft een nieuw Blazor-component nodig dat dit endpoint aanroept.\nTerminal 1:\n\u0026ldquo;Voeg een nieuw endpoint toe aan OrdersController dat de bestelgeschiedenis van een klant teruggeeft. Inclusief paginering. Volg de patronen in de bestaande controllers.\u0026rdquo;\nTerminal 2:\n\u0026ldquo;Maak een nieuw Blazor-component OrderHistory.razor dat een gepagineerde lijst van bestellingen toont. Gebruik dezelfde styling als het bestaande CustomerDetails-component.\u0026rdquo;\nBeide sessies werken tegelijkertijd. De API-sessie schrijft controller-code, voegt een service-methode toe, werkt het repository bij. De frontend-sessie bouwt het component, voegt de service-aanroep toe, schrijft de HTML.\nAls beide klaar zijn, integreer je. Het Blazor-component verwacht al het response-formaat dat de API teruggeeft, omdat beide sessies je bestaande patronen volgden.\nScenario 2: Feature + Tests Dit is mijn favoriete patroon. Eén sessie implementeert een feature. Een andere schrijft tests voor bestaande code die nog geen coverage heeft.\nTerminal 1:\n\u0026ldquo;Implementeer de e-mailnotificatie-feature volgens de spec in docs/email-notifications.md.\u0026rdquo;\nTerminal 2:\n\u0026ldquo;Schrijf unit tests voor de OrderService class. Dek alle publieke methoden, inclusief edge cases voor null-invoer en lege collecties.\u0026rdquo;\nDe test-sessie hoeft niet te wachten tot de feature klaar is. Die werkt aan code die al bestaat. En als de feature-sessie klaar is, kun je meteen een nieuwe sessie starten die tests schrijft voor de zojuist geschreven code.\nScenario 3: Meerdere bugfixes De sprint review is morgen. Er staan drie ongerelateerde bugs in de backlog. Elk raakt andere bestanden in andere projecten.\nDrie terminals. Drie bugs. Allemaal tegelijk.\nTerminal 1: \u0026#34;Fix #142 — het datumfilter op de rapportenpagina negeert de tijdzone\u0026#34; Terminal 2: \u0026#34;Fix #156 — CSV-export faalt wanneer de productnaam puntkomma\u0026#39;s bevat\u0026#34; Terminal 3: \u0026#34;Fix #161 — e-mailvalidatie accepteert adressen zonder domein\u0026#34; Tien minuten later heb je drie branches met drie fixes. Elk klaar voor review.\nGit worktrees: de sleutel om conflicten te voorkomen Hier wordt het praktisch. Als je twee Claude Code sessies draait in dezelfde directory op dezelfde branch, gaan ze gegarandeerd in elkaars vaarwater zitten. De ene sessie bewerkt een bestand, de andere bewerkt hetzelfde bestand, en je eindigt met een puinhoop.\nDe oplossing: git worktrees.\nEen git worktree laat je meerdere branches van dezelfde repository uitchecken in aparte mappen — zonder het hele repo opnieuw te clonen. Elke map heeft zijn eigen working tree, maar ze delen dezelfde .git geschiedenis.\n# Maak worktrees aan voor parallel werk git worktree add ../mijn-project-api feature/bestelgeschiedenis-api git worktree add ../mijn-project-frontend feature/bestelgeschiedenis-frontend Nu heb je drie mappen:\nmijn-project/ — je hoofdkopie mijn-project-api/ — uitgecheckt op feature/bestelgeschiedenis-api mijn-project-frontend/ — uitgecheckt op feature/bestelgeschiedenis-frontend Start een Claude Code sessie in elke map. Ze werken op aparte branches, in aparte mappen. Geen conflicten mogelijk.\nAls je klaar bent, ruim je op:\ngit worktree remove ../mijn-project-api git worktree remove ../mijn-project-frontend Praktische setup Je hebt geen fancy tooling nodig. Meerdere terminal-tabs werken prima. Maar als je dit regelmatig doet, maakt een terminal multiplexer zoals tmux het makkelijker te beheren.\nEen simpele tmux-setup:\n# Start een nieuwe tmux sessie tmux new-session -s werk # Splits in panelen (Ctrl+B, dan %) # Of maak nieuwe windows (Ctrl+B, dan C) Mijn typische layout is drie panelen naast elkaar. Elk met Claude Code in een andere worktree. Ik kan alle drie tegelijk zien werken en wissel van focus met Ctrl+B en een pijltjestoets.\nMaar eerlijk gezegd — drie losse terminal-tabs werken net zo goed. Laat de tooling je niet weerhouden om de aanpak te proberen.\nWanneer parallelle sessies problemen veroorzaken Laten we eerlijk zijn over de valkuilen.\nDezelfde bestanden. Als twee sessies hetzelfde bestand moeten bewerken, draai ze dan niet parallel. De ene overschrijft de wijzigingen van de andere. Zelfs met worktrees en aparte branches krijg je later merge-conflicten.\nGedeelde state. Als je sessies afhankelijk zijn van een gedeelde database of een draaiende service, kunnen ze interfereren. De ene sessie dropt een tabel, de andere verwacht dat die er is. Gebruik aparte database-instanties of draai tests in isolatie.\nTe veel tegelijk. Drie parallelle sessies is beheersbaar. Vijf wordt chaotisch. Je moet nog steeds de output van elke sessie reviewen, en dat kost mentale bandbreedte. Ik draai zelden meer dan drie.\nComplexe afhankelijkheden. Als feature B afhankelijk is van de code die sessie A aan het schrijven is, kun je ze niet paralleliseren. Begin met A, maak die af, en start dan B met de nieuwe code beschikbaar.\nKostenoverweging Laten we het over geld hebben. Elke Claude Code sessie doet zijn eigen API-aanroepen. Drie sessies parallel draaien betekent ruwweg drie keer het tokenverbruik voor die periode.\nVoor mij klopt de rekensom. Als drie parallelle sessies me een uur sequentieel werk besparen, is dat uur developer-tijd veel meer waard dan de extra API-kosten. Maar dit hangt af van je abonnement en gebruikspatroon.\nMijn advies: begin met twee sessies. Raak vertrouwd met de workflow. Check je usage-dashboard na een week en besluit of de kosten opwegen tegen de tijdsbesparing. Voor de meeste taken is dat absoluut het geval.\nProbeer het zelf De volgende keer dat je twee ongerelateerde taken hebt — misschien een feature en wat test-coverage, of twee onafhankelijke bugs — probeer dit:\n# Maak een worktree aan voor de tweede taak git worktree add ../mijn-project-taak2 feature/taak-2 # Terminal 1: in je hoofdmap claude # Terminal 2: in de worktree cd ../mijn-project-taak2 claude Geef elke sessie een duidelijke, gerichte instructie. Kijk hoe ze parallel werken. Review de resultaten.\nJe zult merken dat het knelpunt nooit de snelheid van Claude Code was — het was het feit dat je taken één voor één invoerde.\n","permalink":"https://renedekkers.nl/nl/posts/parallelle-claude-code-sessies/","summary":"Eén Claude Code sessie is krachtig. Twee of drie tegelijk, elk gericht op een ander deel van je solution? Dat is een vermenigvuldiger.","title":"Meerdere Claude Code sessies tegelijk — parallel ontwikkelen aan je .NET solution"},{"content":"Je werkt aan een nieuw API-endpoint. Claude Code schrijft de controller, de servicelaag, de DTOs. Nu heb je tests nodig. En een code review. En XML-documentatie voor de publieke methoden. Dat zijn drie verschillende taken, elk met een andere mindset.\nWat als je ze niet een voor een hoeft te doen?\nWat zijn sub-agents? Als je Claude Code een taak geeft, werkt het als een enkele agent. Het leest bestanden, bewerkt code, voert commando\u0026rsquo;s uit — allemaal in een gesprek met een set instructies. Dat werkt prima voor de meeste dingen.\nMaar Claude Code kan ook sub-agents aanmaken: gerichte child-agents die elk een specifiek deel van het werk oppakken. Jij bepaalt wat elke sub-agent doet, waar het toegang toe heeft, en wat het moet opleveren. De parent-agent coordineert.\nZie het als een senior developer die werk delegeert aan specialisten. \u0026ldquo;Jij schrijft de tests. Jij reviewt de code. Jij werkt de documentatie bij.\u0026rdquo; Elke specialist focust op een ding en doet dat goed.\nDe Task tool Sub-agents draaien op de Task tool in Claude Code. Als Claude de Task tool gebruikt, spawnt het een nieuwe agent met een eigen context window en een specifieke prompt die jij definieert. De sub-agent doet zijn werk en geeft het resultaat terug aan de parent.\nJe hoeft niets te installeren. De Task tool zit ingebouwd in Claude Code. Wat het krachtig maakt is hoe je het instrueert — en dat begint in je CLAUDE.md.\nDit is het basispatroon. In je CLAUDE.md beschrijf je de sub-agents die Claude moet gebruiken:\n## Sub-agents Gebruik bij .NET-code deze gespecialiseerde agents via de Task tool: ### Testschrijver Spawn een sub-agent met deze prompt: \u0026#34;Je bent een .NET test-specialist. Gegeven een klasse, schrijf uitgebreide xUnit tests met FluentAssertions. Dek het happy path, edge cases en foutscenario\u0026#39;s. Gebruik het Arrange-Act-Assert patroon. Mock dependencies met NSubstitute.\u0026#34; ### Code Reviewer Spawn een sub-agent met deze prompt: \u0026#34;Je bent een .NET code reviewer. Review het gegeven bestand op: beveiligingsproblemen, performance-issues, naleving van SOLID-principes, null safety, async/await correctheid. Geef een gestructureerde lijst met bevindingen en ernst-niveaus.\u0026#34; ### Documentatie-agent Spawn een sub-agent met deze prompt: \u0026#34;Je bent een .NET documentatie-specialist. Genereer XML-documentatie-comments voor alle publieke types en members. Volg Microsoft\u0026#39;s documentatierichtlijnen. Wees bondig maar volledig.\u0026#34; Als Claude een situatie tegenkomt waar een van deze agents nuttig is, spawnt het die automatisch. Je hoeft niet om elke agent apart te vragen.\nVoorbeeld 1: De testschrijver Stel, je hebt net een PaymentService gebouwd met drie publieke methoden. Je zegt tegen Claude:\n\u0026ldquo;Schrijf tests voor PaymentService.\u0026rdquo;\nMet de sub-agent-configuratie hierboven spawnt Claude een testschrijver-agent. Die agent krijgt het PaymentService.cs-bestand, leest de dependencies, en produceert een volledige testklasse:\npublic class PaymentServiceTests { private readonly IPaymentGateway _gateway; private readonly IOrderRepository _orderRepository; private readonly PaymentService _sut; public PaymentServiceTests() { _gateway = Substitute.For\u0026lt;IPaymentGateway\u0026gt;(); _orderRepository = Substitute.For\u0026lt;IOrderRepository\u0026gt;(); _sut = new PaymentService(_gateway, _orderRepository); } [Fact] public async Task ProcessPayment_ValidOrder_ReturnsSuccess() { // Arrange var order = new Order { Id = 1, Amount = 99.99m, Status = OrderStatus.Pending }; _orderRepository.GetByIdAsync(1).Returns(order); _gateway.ChargeAsync(Arg.Any\u0026lt;PaymentRequest\u0026gt;()).Returns(new PaymentResult { Success = true }); // Act var result = await _sut.ProcessPaymentAsync(1); // Assert result.Should().NotBeNull(); result.Success.Should().BeTrue(); } [Fact] public async Task ProcessPayment_OrderNotFound_ThrowsNotFoundException() { _orderRepository.GetByIdAsync(99).Returns((Order?)null); var act = () =\u0026gt; _sut.ProcessPaymentAsync(99); await act.Should().ThrowAsync\u0026lt;NotFoundException\u0026gt;(); } } De sub-agent focust uitsluitend op testen. Het probeert niet de service te refactoren of verbeteringen voor te stellen. Het schrijft tests — dat is zijn taak.\nVoorbeeld 2: De code reviewer Nu wil je een tweede paar ogen op diezelfde PaymentService. Claude spawnt de code reviewer-agent, die gestructureerde feedback teruggeeft:\n## Code Review: PaymentService.cs **HOOG** - Geen CancellationToken-ondersteuning op async-methoden. ProcessPaymentAsync en RefundAsync moeten CancellationToken accepteren. **MIDDEL** - Decimal-vergelijking zonder tolerantie in CalculateDiscount. Overweeg een kleine epsilon voor financiele berekeningen. **LAAG** - Overweeg het magische getal 3 (max retry count) te extraheren naar een constante. **OK** - Null-checks op constructor-parameters ✓ **OK** - Async/await-patroon correct gebruikt ✓ **OK** - Geen duidelijke SQL-injectie of beveiligingsproblemen ✓ De reviewer fixt de code niet. Het identificeert problemen. Jij bepaalt waar je actie op onderneemt. Als je wilt, kun je tegen Claude zeggen: \u0026ldquo;Fix de HOGE en MIDDEL issues\u0026rdquo; — en de parent-agent pakt het op.\nVoorbeeld 3: De documentatie-agent De documentatie-agent handelt het vervelende-maar-noodzakelijke werk van XML-docs af:\n/// \u0026lt;summary\u0026gt; /// Verwerkt een betaling voor de opgegeven bestelling. /// \u0026lt;/summary\u0026gt; /// \u0026lt;param name=\u0026#34;orderId\u0026#34;\u0026gt;De unieke identifier van de bestelling waarvoor betaald wordt.\u0026lt;/param\u0026gt; /// \u0026lt;returns\u0026gt;Een \u0026lt;see cref=\u0026#34;PaymentResult\u0026#34;/\u0026gt; dat de uitkomst van de betaling aangeeft.\u0026lt;/returns\u0026gt; /// \u0026lt;exception cref=\u0026#34;NotFoundException\u0026#34;\u0026gt;Wordt gegooid als er geen bestelling bestaat met het opgegeven ID.\u0026lt;/exception\u0026gt; /// \u0026lt;exception cref=\u0026#34;PaymentException\u0026#34;\u0026gt;Wordt gegooid als de payment gateway de transactie afwijst.\u0026lt;/exception\u0026gt; public async Task\u0026lt;PaymentResult\u0026gt; ProcessPaymentAsync(int orderId) Het leest de implementatie, begrijpt de exceptions, en schrijft documentatie die daadwerkelijk bij de code past. Geen boilerplate — echte documentatie.\nAgents parallel draaien Hier wordt het interessant. Je kunt tegen Claude zeggen:\n\u0026ldquo;Voor elke service in de Services-map: draai de testschrijver en code reviewer parallel.\u0026rdquo;\nClaude spawnt meerdere sub-agents tegelijk. De testschrijver werkt aan PaymentService terwijl de reviewer aan OrderService werkt. Dan wisselen ze. De parent-agent verzamelt alle resultaten en presenteert ze aan jou.\nVoor een solution met tien services kan dit je serieus veel tijd besparen. In plaats van sequentieel testen schrijven, reviewen, documenteren — gebeurt alles tegelijk.\nWanneer sub-agents zinvol zijn Sub-agents blinken uit als:\nDe taak duidelijk opsplitsbaar is. Testen en reviewen zijn onafhankelijke activiteiten. Ze hebben elkaars output niet nodig. Je consistente patronen hebt. Als elke service dezelfde structuur volgt, dekt een prompt ze allemaal. Het werk repetitief is. Tests schrijven voor 15 services is saai handwerk. Delegeren aan een sub-agent is efficient. Sub-agents zijn overkill als:\nDe taak klein is. Als je tests schrijft voor een methode, vraag het gewoon direct aan Claude. Een sub-agent spawnen voegt overhead toe. De taken sterk verweven zijn. Als de test afhangt van de review-bevindingen, heeft parallel draaien geen zin. Je aan het verkennen bent. Als je nog niet weet wat je wilt, is een enkel gesprek met Claude flexibeler dan een gestructureerde agent-pipeline. Beperkingen om te weten Sub-agents hebben geisoleerde context. De testschrijver weet niet wat de reviewer gevonden heeft. De documentatie-agent ziet de testoutput niet. Elke sub-agent begint blanco met alleen de bestanden en prompt die je meegeeft.\nEr is geen gedeelde state tussen agents. Als de reviewer een bug vindt en de testschrijver moet daar rekening mee houden, dan moet je dat handmatig coordineren via de parent-agent.\nSub-agents verbruiken tokens. Elk krijgt een eigen context window. Vijf agents parallel draaien op een grote codebase kost aanzienlijk meer tokens dan een enkele agent die alles sequentieel doet. Houd je verbruik in de gaten.\nEn tot slot: sub-agents zijn zo goed als hun prompts. Een vage prompt als \u0026ldquo;review deze code\u0026rdquo; geeft vage resultaten. Hoe specifieker je bent over frameworks, patronen en verwachtingen, hoe beter de output.\nBegin met een agent Bouw geen vloot sub-agents op dag een. Begin met een. De testschrijver is een goede eerste keuze — het is de meest mechanische taak, en de output is direct verifieerbaar met dotnet test.\nVoeg het toe aan je CLAUDE.md. Probeer het op een paar services. Stel de prompt bij tot de tests passen bij de stijl van je team. Voeg dan de reviewer toe. Dan de documentatie-agent.\nHet doel is niet om je eigen oordeel te vervangen. Het is om je capaciteit te vermenigvuldigen — door specialisten het werk te laten doen dat een patroon volgt, zodat jij je kunt focussen op het werk dat dat niet doet.\n","permalink":"https://renedekkers.nl/nl/posts/subagents-bouwen-voor-dotnet-workflow/","summary":"Claude Code is krachtig op zichzelf. Maar als je het werk verdeelt over gespecialiseerde sub-agents — een testschrijver, een reviewer, een documentatie-agent — wordt het een team.","title":"Sub-agents bouwen voor je .NET workflow"},{"content":"Je .NET solution heeft 15 projecten, honderden bestanden en een berg gegenereerde code. Je opent Claude Code, vraagt om een feature toe te voegen, en halverwege de sessie begint het dingen te vergeten die je vijf minuten geleden hebt verteld.\nDat is geen bug. Dat is het contextvenster dat volloopt. En in een grote .NET solution loopt het snel vol.\nIk werk al maanden met Claude Code in solutions van 5 tot meer dan 30 projecten. Dit is wat ik heb geleerd over het schoonhouden van je context en het productief houden van je sessies.\nHet probleem: .NET solutions zijn luidruchtig Een typische .NET solution genereert een hoop spul dat je niet wilt dat Claude Code leest. Elk project heeft bin/ en obj/ mappen. Entity Framework maakt migratiebestanden die honderden regels lang kunnen zijn. Visual Studio genereert .Designer.cs bestanden, .g.cs bestanden en allerlei XML-artefacten.\nAls Claude Code je solution scant, leest het alles. Elke migratie. Elk gegenereerd bestand. Elk gecompileerd artefact dat het kan vinden. Dat is contextbudget dat je uitgeeft aan ruis in plaats van signaal.\nHet resultaat: Claude Code heeft geen ruimte meer voor de code die er echt toe doet — je business logic, je endpoints, je domeinmodellen.\n.claudeignore — je eerste verdedigingslinie Net zoals .gitignore aan Git vertelt wat het moet overslaan, vertelt .claudeignore aan Claude Code wat het moet negeren. Maak dit bestand aan in de root van je solution en zet erin wat Claude Code niet hoeft te lezen.\nDit is de .claudeignore die ik gebruik voor de meeste .NET solutions:\n# Build output bin/ obj/ publish/ # Gegenereerde code *.Designer.cs *.g.cs *.g.i.cs *.generated.cs # Entity Framework migraties **/Migrations/ # Frontend build-artefacten (als je een SPA hebt) **/wwwroot/lib/ **/node_modules/ # IDE en tooling .vs/ .idea/ *.user *.suo # Test output TestResults/ coverage/ # NuGet packages/ # Grote databestanden **/*.bacpac **/*.bak Dit ene bestand kan de ruis in je solution met 60-80% verminderen. Migraties alleen al kunnen duizenden regels zijn die Claude Code niet nodig heeft om je huidige code te begrijpen.\nBelangrijk: sluit je testprojecten niet uit. Claude Code heeft die nodig om tests te draaien en wijzigingen te verifiëren. En sluit ook je .csproj bestanden niet uit — die bevatten dependency-informatie die Claude Code helpt je architectuur te begrijpen.\n/compact — context terugwinnen tijdens je sessie Zelfs met een goede .claudeignore vreten lange sessies context. Je bent 20 minuten bezig, hebt Claude Code meerdere bestanden laten lezen, een paar wijzigingen gemaakt, en je merkt dat het langzamer en minder precies wordt.\nDan gebruik je /compact. Dit commando vertelt Claude Code om het gesprek tot nu toe samen te vatten en contextruimte vrij te maken. Het behoudt de belangrijke zaken — waar je mee bezig bent, welke bestanden je hebt gewijzigd, welke beslissingen je hebt genomen — en laat de details vallen die het niet meer nodig heeft.\nIk gebruik /compact ongeveer elke 15-20 minuten in een grote solution. Zie het als je bureau opruimen: je houdt de documenten waar je actief mee werkt en bergt de rest op.\nEen goede gewoonte: nadat Claude Code een grote wijziging heeft afgerond en je hebt geverifieerd dat het werkt, draai /compact voordat je aan de volgende taak begint.\nCLAUDE.md voor monorepos en multi-project solutions Als je solution een dozijn projecten heeft, is één CLAUDE.md in de root niet genoeg. Claude Code ondersteunt CLAUDE.md bestanden in subdirectories — en die worden alleen geladen als Claude Code in die directory werkt.\nStructureer het zo:\nMijnSolution/ ├── CLAUDE.md # Solution-niveau: architectuur, conventies ├── .claudeignore # Wat overslaan ├── src/ │ ├── MijnApp.Api/ │ │ └── CLAUDE.md # API-specifiek: endpoints, auth, middleware │ ├── MijnApp.Domain/ │ │ └── CLAUDE.md # Domeinregels, entity-conventies │ └── MijnApp.Infrastructure/ │ └── CLAUDE.md # EF Core config, externe services └── tests/ └── CLAUDE.md # Testconventies, hoe tests draaien De root CLAUDE.md beschrijft de algehele architectuur en gedeelde conventies. De project-niveau bestanden beschrijven specifics die alleen relevant zijn als je in dat project werkt.\nZo laadt Claude Code alleen de context die het nodig heeft. Werk je aan een endpoint? Het leest de API CLAUDE.md. Los je een databaseprobleem op? Het leest die van Infrastructure. Geen verspilde context aan informatie die het nu niet nodig heeft.\nWerk in gerichte sessies Dit is de grootste mindshift voor werken met Claude Code in grote solutions: probeer niet alles in één sessie te doen.\nEén sessie, één focus. \u0026ldquo;Voeg het notificatie-endpoint toe en de bijbehorende tests.\u0026rdquo; Niet \u0026ldquo;Refactor het notificatiesysteem, voeg het nieuwe endpoint toe, werk de database bij, fix de logging, en oh update ook even de README.\u0026rdquo;\nAls je naar een compleet ander deel van de solution overschakelt — zeg van de API-laag naar een background worker project — begin dan een nieuwe sessie. De context van het API-werk helpt je niet in het worker project. Het neemt alleen maar ruimte in.\nIk werk meestal in sessies van 20-30 minuten met een duidelijke focus. Eén feature, één projectgebied, één helder doel. Dan commit ik, begin opnieuw en ga verder.\n/clear — wanneer opnieuw beginnen /clear wist het hele gesprek en start een nieuwe sessie zonder je terminal te verlaten. Gebruik het wanneer:\nJe een taak hebt afgerond en iets heel anders wilt doen Claude Code verward raakt of zichzelf herhaalt Je heen en weer bent gegaan over een aanpak en een schone lei wilt Gebruik /clear niet als je midden in een wijziging zit die meerdere stappen beslaat. Als Claude Code halverwege is met het implementeren van een feature over meerdere bestanden, dan verliest het door het wissen van de context het overzicht van wat het al heeft gedaan. Gebruik dan /compact.\nDe vuistregel: /compact om bij te knippen, /clear om opnieuw te beginnen.\nStructureer je prompts rond specifieke bestanden In een klein project kun je zeggen \u0026ldquo;kijk naar de code en voeg een feature toe.\u0026rdquo; In een grote solution vraag je Claude Code dan om 200 bestanden te lezen om de 3 te vinden die het nodig heeft.\nWees specifiek:\nIn plaats van: \u0026ldquo;Voeg een notificatie-endpoint toe.\u0026rdquo;\nZeg: \u0026ldquo;Voeg in src/MijnApp.Api/Endpoints/ een notificatie-endpoint toe volgens hetzelfde patroon als UserEndpoints.cs. De query handler hoort in src/MijnApp.Application/Notifications/.\u0026rdquo;\nJe wijst Claude Code naar precies de juiste bestanden en directories. Het besteedt context aan de code die ertoe doet, niet aan het scannen van je hele solution om uit te zoeken waar dingen thuishoren.\nHetzelfde geldt voor het fixen van bugs. In plaats van \u0026ldquo;de notificatie-feature is kapot,\u0026rdquo; zeg \u0026ldquo;de GetNotifications handler in src/MijnApp.Application/Notifications/GetNotificationsHandler.cs geeft een lege lijst terug terwijl er resultaten zouden moeten zijn. De test in tests/MijnApp.Tests/Notifications/GetNotificationsTests.cs laat het verwachte gedrag zien.\u0026rdquo;\nMeer context in je prompt betekent minder context die Claude Code moet besteden om dingen uit te zoeken.\nEen praktisch voorbeeld Zo ziet mijn typische workflow eruit in een grote .NET solution:\nBegin een nieuwe sessie in de solution root Vertel Claude Code waar je mee bezig bent: \u0026ldquo;Ik moet een soft-delete feature toevoegen aan de Order entity in het Domain project, de EF Core configuratie updaten in Infrastructure, en een delete endpoint toevoegen in de API.\u0026rdquo; Werk het stap voor stap door: eerst domain, dan infrastructure, dan API Draai tests na elke stap: \u0026ldquo;Draai de tests in tests/MijnApp.Domain.Tests\u0026rdquo; /compact nadat de domeinwijzigingen klaar zijn voordat je naar infrastructure gaat Commit als de feature compleet is /clear voordat je aan de volgende taak begint Deze aanpak houdt de context gefocust, de sessies behapbaar en de resultaten voorspelbaar.\nBegin vandaag met optimaliseren Als je met Claude Code werkt in een .NET solution met meer dan een handvol projecten, maak dan vandaag nog een .claudeignore bestand aan. Het kost vijf minuten en het verschil is direct merkbaar.\nKijk dan naar je CLAUDE.md. Is het één groot bestand dat alles probeert te beschrijven? Splits het op. Zet projectspecifieke context waar het thuishoort.\nEn het allerbelangrijkste: stop met proberen alles in één sessie te doen. Gericht werken met schone context wint het altijd van marathonsessies. Je contextvenster is een resource — besteed het aan de code die ertoe doet.\n","permalink":"https://renedekkers.nl/nl/posts/context-beheren-in-dotnet-solutions/","summary":"Je .NET solution heeft 15 projecten, honderden bestanden en een berg gegenereerde code. Het contextvenster van Claude Code is niet oneindig. Zo werk je slim.","title":"Claude Code en je .NET solution-structuur — slim omgaan met context"},{"content":"Je kent het gevoel. Het is vrijdagmiddag, je opent GitHub en er staan drie PR\u0026rsquo;s klaar voor review. Eentje is 40 bestanden. De tweede raakt de registratie-flow die je al maanden niet hebt ingezien. De derde is \u0026ldquo;een kleine refactor\u0026rdquo; — 800 regels gewijzigd.\nCode review is essentieel. Het vangt bugs, behoudt consistentie en verspreidt kennis over het team. Maar het is ook langzaam, mentaal vermoeiend en het eerste dat sneuvelt als deadlines dichtbij komen.\nWat als Claude Code die eerste pass zou kunnen doen?\nLokaal reviewen voordat je pusht De simpelste manier om te beginnen is nog voordat je een PR aanmaakt. Je hebt je wijzigingen gemaakt, alles compileert, tests slagen. Voordat je pusht, vraag je aan Claude Code:\nReview mijn staged changes. Focus op potentiële bugs, ontbrekende validatie en alles wat afwijkt van onze code-standaarden. Claude Code leest de diff, volgt referenties door de rest van je codebase en geeft je feedback. Geen generiek advies — feedback gebaseerd op jouw daadwerkelijke code, jouw patronen.\nIk heb zo al gênante fouten gevonden. Een FirstOrDefault() zonder null-check twee regels later. Een nieuw endpoint zonder het [Authorize] attribuut dat elk ander endpoint in de controller wél heeft. Dingen die je niet meer ziet als je urenlang naar je eigen code staart.\nEen PR reviewen met de gh CLI Als iemand anders\u0026rsquo; PR gereviewed moet worden, kan Claude Code daar ook bij helpen. Je kunt de PR-diff er direct in voeden:\ngh pr diff 42 | claude -p \u0026#34;Review deze .NET PR. Focus op bugs, ontbrekende error handling en afwijkingen van onze patronen.\u0026#34; Dit stuurt de volledige diff naar Claude Code als prompt. Claude leest elke gewijzigde regel, begrijpt de context uit bestandsnamen en omringende code, en geeft je een gestructureerde review.\nVoor een diepere review waarbij Claude de volledige codebase moet zien — niet alleen de diff — check je de PR eerst lokaal uit:\ngh pr checkout 42 claude Vraag Claude Code dan om de wijzigingen te reviewen ten opzichte van de main branch. Op die manier kan het referenties volgen, controleren of nieuwe code bij bestaande patronen past, en inconsistenties spotten die je mist als je alleen de diff leest.\nWaar Claude Code goed in is Na maanden Claude Code gebruiken voor reviews op .NET-projecten, zie ik duidelijke sterktes.\nNull reference risico\u0026rsquo;s. Claude is opmerkelijk goed in het traceren van nullable values door je code. Het vangt FirstOrDefault() calls waar het resultaat zonder null-check wordt gebruikt, nullable reference types die niet afgehandeld worden, en string.IsNullOrEmpty() checks die IsNullOrWhiteSpace() zouden moeten zijn.\n// Claude vlagt dit direct var user = await _context.Users.FirstOrDefaultAsync(u =\u0026gt; u.Email == email); var displayName = user.FirstName + \u0026#34; \u0026#34; + user.LastName; // mogelijke NullReferenceException Ontbrekende validatie. Als je andere endpoints request models valideren met FluentValidation, merkt Claude het op wanneer een nieuw endpoint dat overslaat. Hetzelfde geldt voor ontbrekende [Required] attributen, ongecontroleerde enum-waarden of request models zonder enige validatie.\nInconsistente patronen. Hier blinkt Claude echt uit. Het ziet wanneer één controller NotFound() teruggeeft terwijl alle andere Problem() gebruiken. Het merkt op wanneer een nieuwe service niet hetzelfde DI-registratiepatroon volgt. Het vangt wanneer iemand DateTime.Now gebruikt terwijl de rest van het project IDateTimeProvider gebruikt.\nDependency injection problemen. Scoped services geïnjecteerd in singletons, ontbrekende registraties, circulaire dependencies — Claude leest je Program.cs en vangt mismatches.\nWaar het tekortschiet Laten we eerlijk zijn over de beperkingen.\nCorrectheid van business logic. Claude kan je vertellen dat de code compileert en nulls afhandelt. Het kan je niet vertellen of de kortingsberekening daadwerkelijk klopt voor je bedrijfsregels. Het weet niet dat klanten in België andere btw-regels hebben dan klanten in Nederland.\nArchitectuurcontext die het niet heeft. Als je team vorige maand heeft besloten dat alle nieuwe services het mediator-pattern moeten gebruiken, weet Claude dat niet — tenzij je het hebt gedocumenteerd. Het reviewt wat het ziet, niet wat jullie in meetings hebben besproken.\nPerformance op schaal. Claude vlagt niet dat je nieuwe LINQ-query een full table scan doet op een tabel met 50 miljoen rijen. Het kent je datavolumes niet. Het vangt voor de hand liggende N+1 queries, maar subtiele performance-issues vereisen menselijk oordeel en kennis van je productieomgeving.\nCross-systeem impact. Als je API-wijziging een downstream consumer breekt die in een andere repository leeft, heeft Claude geen manier om dat te weten.\nGeautomatiseerde review met /install-github-app Als je wilt dat Claude Code elke PR automatisch reviewt, kun je de GitHub-app installeren:\n/install-github-app Dit zet Claude Code op als geautomatiseerde reviewer op je repository. Elke nieuwe PR krijgt een review-comment met bevindingen — voordat een mens er ook maar naar heeft gekeken.\nDe geautomatiseerde reviews volgen dezelfde patronen: null checks, validatie-gaten, inconsistenties. Het verschil is dat het gebeurt zonder dat iemand eraan hoeft te denken.\nCLAUDE.md als de stijlgids van je team Hier wordt het pas echt krachtig. Claude Code leest het CLAUDE.md bestand van je project vóór elke review. Dat betekent dat je het de conventies van je team kunt leren.\n## Code Review Standards - Alle controllers moeten het `[Authorize]` attribuut hebben, tenzij expliciet publiek - Gebruik FluentValidation voor alle request models, geen data annotations - Geef ProblemDetails terug voor alle foutresponses (RFC 7807) - Alle async methoden moeten CancellationToken accepteren - Gebruik IDateTimeProvider in plaats van DateTime.Now/UtcNow - Repository methoden geven domeinmodellen terug, nooit EF entities Nu controleert elke review — handmatig of geautomatiseerd — tegen de specifieke regels van je team. Geen generieke best practices. Jullie regels.\nDit is de vermenigvuldiger. Zonder CLAUDE.md krijg je een degelijke generieke review. Mét CLAUDE.md krijg je een review die klinkt als je meest ervaren teamlid dat de stijlgids uit het hoofd kent.\nVoorbeeld: een echte review-sessie Dit is hoe een typische review eruitziet. Ik vroeg Claude Code om een PR te reviewen die een nieuw endpoint toevoegt voor het exporteren van gebruikersdata:\nReview de wijzigingen in deze PR. Controleer op bugs, ontbrekende validatie en afwijkingen van onze bestaande patronen in dit project. Claude kwam terug met vijf bevindingen:\nOntbrekende null check — GetUserById geeft null terug als de gebruiker niet bestaat, maar de export-methode controleert daar niet op Geen autorisatie — Het endpoint mist [Authorize(Policy = \u0026quot;Admin\u0026quot;)], terwijl alle andere admin-endpoints dat wél hebben Inconsistente foutresponse — Geeft BadRequest(\u0026quot;User not found\u0026quot;) terug in plaats van NotFound() met ProblemDetails Ontbrekende CancellationToken — De async methode accepteert of forwardt geen CancellationToken, in tegenstelling tot elk ander endpoint in de controller Geen inputvalidatie — Het ExportUserRequest model heeft geen FluentValidation validator, terwijl alle andere request models dat wél hebben Vier van de vijf waren legitieme issues die ik zelf ook zou hebben gevlagd in mijn eigen review. De vijfde (CancellationToken) was technisch correct maar lage prioriteit. Nul false positives, nul verzonnen problemen.\nDat is een goede eerste pass.\nTips voor bruikbare reviews Niet alle review-prompts zijn gelijk. Dit heb ik geleerd:\nWees specifiek over wat je zoekt. \u0026ldquo;Review deze code\u0026rdquo; geeft generieke feedback. \u0026ldquo;Controleer op ontbrekende validatie, null reference risico\u0026rsquo;s en afwijkingen van onze bestaande patronen\u0026rdquo; geeft actionable bevindingen.\nVraag Claude om te vergelijken met bestaande code. \u0026ldquo;Vergelijk deze nieuwe controller met OrdersController.cs en vlag eventuele inconsistenties\u0026rdquo; dwingt Claude om daadwerkelijk naar je conventies te kijken in plaats van generieke aan te nemen.\nVraag om een severity level. Laat Claude bevindingen categoriseren als \u0026ldquo;moet gefixt,\u0026rdquo; \u0026ldquo;zou gefixt moeten worden\u0026rdquo; en \u0026ldquo;overweeg.\u0026rdquo; Dit helpt je om snel te triagen.\nVraag niet om goedkeuring. Claude vindt altijd iets. Dat is het hele punt. Gebruik het als bugvinder, niet als poortwachter.\nBegin bij je volgende PR Je hoeft geen automatisering op te zetten om hier profijt van te hebben. De volgende keer dat je een PR moet reviewen — die van jou of van iemand anders — probeer dit:\ngh pr checkout 42 claude \u0026ldquo;Review de wijzigingen in deze PR ten opzichte van main. Focus op bugs, ontbrekende error handling en alles wat niet past bij onze bestaande patronen.\u0026rdquo;\nJe zult merken dat het saaie deel van code review — elke regel lezen, controleren op voor de hand liggende fouten, consistentie verifiëren — seconden duurt in plaats van minuten. Wat overblijft is het deel dat daadwerkelijk je hersens nodig heeft: architectuur beoordelen, business logic en afwegingen maken.\nAI-review vervangt geen menselijke review. Het maakt menselijke review de moeite waard.\n","permalink":"https://renedekkers.nl/nl/posts/claude-code-als-reviewer-voor-dotnet/","summary":"Claude Code kan een grondige eerste review doen van je .NET pull requests — null references, ontbrekende validatie en inconsistente patronen vinden voordat een mens de PR opent.","title":"Claude Code als code reviewer voor je .NET pull requests"},{"content":"Claude Code kent C# al. Het kan controllers schrijven, dependency injection opzetten, EF Core-migraties genereren. Maar het kent jouw project niet. Het weet niet dat je team overal MediatR gebruikt, of dat integratietests altijd in een apart project staan met een specifieke naamconventie.\nDat is precies waar skills en plugins voor zijn. Ze maken van Claude Code een .NET-specialist die jouw exacte werkwijze begrijpt.\nWat zijn skills? Skills zijn herbruikbare prompttemplates die Claude Code specifieke workflows aanleren. Zie het als ervaring in een bestand. Je schrijft op hoe iets gedaan moet worden, en Claude volgt dat patroon elke keer.\nEen skill wordt gedefinieerd in een SKILL.md-bestand. Het leeft in een repository (van jou of van iemand anders), en eenmaal geinstalleerd leest Claude Code het als context wanneer je aan een relevante taak werkt. Geen API-calls, geen externe services — gewoon gestructureerde instructies die Claude\u0026rsquo;s gedrag sturen.\nHet verschil met een gewone CLAUDE.md is de scope. Je CLAUDE.md beschrijft jouw project. Een skill beschrijft een workflow die werkt over projecten heen. Testpatronen. Code-generatietemplates. Architectuurconventies.\nWat zijn plugins? Plugins breiden Claude Code uit met externe tools en integraties. Waar skills instructies zijn, zijn plugins mogelijkheden. Ze voegen nieuwe tools toe die Claude kan aanroepen tijdens een sessie.\nEen plugin kan Claude Code verbinden met je database, je CI-pipeline of een externe API. Het gebruikt het Model Context Protocol (MCP) om deze tools op een gestandaardiseerde manier beschikbaar te stellen. Ik schreef eerder over het bouwen van MCP-servers in een eerdere post.\nVoor .NET-developers zijn plugins interessant wanneer je wilt dat Claude interactie heeft met dingen buiten je codebase — specifieke dotnet CLI-commando\u0026rsquo;s draaien, een package registry bevragen, of verbinden met Azure DevOps.\nDe marketplace: het /install-commando Claude Code heeft een ingebouwde manier om community skills en plugins te browsen en te installeren. In een Claude Code-sessie typ je:\n/install Dit opent de marketplace — een gecureerde lijst van skills en plugins die je direct kunt installeren. Je kunt zoeken op trefwoord, browsen per categorie, of kijken wat populair is in de community.\nAls je iets interessants vindt, is installeren een enkel commando. Claude Code downloadt de skill of plugin en voegt het toe aan je configuratie. Voor skills betekent dit dat de SKILL.md-bestanden lokaal worden opgeslagen. Voor plugins wordt de MCP-serverconfiguratie opgezet.\nJe kunt ook direct vanuit een GitHub-repository installeren:\n/install aaronontheweb/dotnet-skills Dit haalt skills op uit een specifieke repo zonder eerst de marketplace te browsen. Handig wanneer iemand een link deelt in een blogpost of op social media.\n.NET-specifieke skills die het installeren waard zijn De meest uitgebreide .NET skill-collectie die ik heb gevonden is dotnet-skills van Aaron Stannard (Aaronontheweb). Het bevat 30+ skills voor:\nEF Core — migratiestrategieen, query-optimalisatie, relatie-configuratie Testing — xUnit-patronen, integratietesten met WebApplicationFactory, mocking met NSubstitute Aspire — service discovery, orchestratie, dashboard-setup Architectuur — clean architecture templates, MediatR-patronen, vertical slice structuur Performance — benchmarking met BenchmarkDotNet, memory profiling patronen Wat deze skills waardevol maakt is de specificiteit. De EF Core skill zegt niet alleen \u0026ldquo;gebruik EF Core.\u0026rdquo; Het beschrijft wanneer je AsNoTracking() inzet, hoe je complexe queries structureert, wanneer je terugvalt op raw SQL. Het legt het soort kennis vast dat normaal alleen in het hoofd van een senior developer zit.\nAndere community skills die het bekijken waard zijn:\nMinimal API-patronen — endpoints structureren, validatie met FluentValidation, OpenAPI-configuratie Blazor component skills — component lifecycle, state management, JS interop patronen Azure deployment skills — ARM templates, Bicep, App Service configuratie Een geinstalleerde skill gebruiken Laat me een concreet voorbeeld doorlopen. Na het installeren van de dotnet-skills collectie open ik een .NET-project en vraag Claude om een integratietest te schrijven.\nZonder de skill schrijft Claude een basale test met HttpClient en hardcoded URL\u0026rsquo;s. Het werkt, maar het is niet hoe een productie .NET-project integratietesten doet.\nMet de testing skill geinstalleerd weet Claude:\nWebApplicationFactory\u0026lt;Program\u0026gt; te gebruiken als testbasis Een custom IServiceCollection op te zetten voor testdependencies IClassFixture te gebruiken voor gedeelde setup Een echte testdatabase te maken met een unieke naam per testklasse public class OrderEndpointTests : IClassFixture\u0026lt;WebApplicationFactory\u0026lt;Program\u0026gt;\u0026gt; { private readonly HttpClient _client; public OrderEndpointTests(WebApplicationFactory\u0026lt;Program\u0026gt; factory) { _client = factory.WithWebHostBuilder(builder =\u0026gt; { builder.ConfigureServices(services =\u0026gt; { // Vervang de echte database met een in-memory variant services.RemoveAll\u0026lt;DbContextOptions\u0026lt;AppDbContext\u0026gt;\u0026gt;(); services.AddDbContext\u0026lt;AppDbContext\u0026gt;(options =\u0026gt; options.UseInMemoryDatabase($\u0026#34;TestDb-{Guid.NewGuid()}\u0026#34;)); }); }).CreateClient(); } [Fact] public async Task CreateOrder_ReturnsCreated() { var order = new CreateOrderRequest(\u0026#34;Test Product\u0026#34;, 2); var response = await _client.PostAsJsonAsync(\u0026#34;/api/orders\u0026#34;, order); response.StatusCode.Should().Be(HttpStatusCode.Created); } } De skill heeft Claude de juiste manier aangeleerd. Niet zomaar een werkende manier, maar de manier die je team verwacht bij een code review.\nJe eigen skill maken Community skills zijn geweldig, maar de echte kracht zit in skills maken voor de conventies van je eigen team. Een SKILL.md-bestand is gewoon Markdown met gestructureerde instructies.\nHier is een eenvoudig voorbeeld voor een team dat een specifiek servicepatroon gebruikt:\n# Service Layer Conventies ## Wanneer toepassen Bij het aanmaken of wijzigen van serviceklassen in de `Services/`-directory. ## Regels - Elke service implementeert een interface in `Abstractions/` - Alleen constructor injection — geen service locator - Alle publieke methoden retourneren `Result\u0026lt;T\u0026gt;` (geen exceptions voor businesslogica) - Logging: injecteer `ILogger\u0026lt;T\u0026gt;`, log bij method entry (Debug) en bij errors (Error) - Validatie gebeurt in de service, niet in de controller ## Voorbeeldstructuur \\```csharp public class OrderService : IOrderService { private readonly ILogger\u0026lt;OrderService\u0026gt; _logger; private readonly AppDbContext _context; public OrderService(ILogger\u0026lt;OrderService\u0026gt; logger, AppDbContext context) { _logger = logger; _context = context; } public async Task\u0026lt;Result\u0026lt;OrderDto\u0026gt;\u0026gt; GetByIdAsync(int id) { _logger.LogDebug(\u0026#34;Getting order {OrderId}\u0026#34;, id); var order = await _context.Orders.FindAsync(id); if (order is null) return Result\u0026lt;OrderDto\u0026gt;.NotFound($\u0026#34;Order {id} not found\u0026#34;); return Result\u0026lt;OrderDto\u0026gt;.Success(order.ToDto()); } } \\``` Zet dit in je repo, en elke developer in het team krijgt hetzelfde Claude-gedrag. Nieuwe teamleden hoeven de conventies niet uit hun hoofd te leren — Claude kent ze al.\nPlugins vs. skills: wanneer gebruik je wat? Dit was voor mij in het begin verwarrend. Hier is de simpele regel:\nGebruik een skill als je wilt dat Claude een patroon of conventie volgt. Skills zijn instructies. Ze voegen geen nieuwe mogelijkheden toe — ze sturen hoe Claude de mogelijkheden gebruikt die het al heeft.\nGebruik een plugin als je wilt dat Claude interactie heeft met iets externs. Een database, een CI-systeem, een API. Plugins voegen tools toe die Claude kan aanroepen.\nIn de praktijk gebruik je als .NET-developer veel meer skills dan plugins. Je dagelijkse werk is code schrijven die patronen volgt, en dat is precies waar skills in uitblinken. Plugins zijn voor specifieke integraties — verbinden met Azure, NuGet bevragen, interactie met je ticketsysteem.\nBeperkingen en kanttekeningen Skills zijn geen magie. Het is context, en context heeft grenzen.\nTokenbudget. Elke geinstalleerde skill gebruikt tokens uit je contextvenster. Installeer dertig skills en Claude heeft minder ruimte om na te denken over je daadwerkelijke code. Wees selectief — installeer wat je ook echt gebruikt.\nKwaliteit varieert. Community skills zijn geschreven door mensen met verschillende ervaringsniveaus. Sommige zijn uitstekend. Sommige zijn verouderd. Review een skill altijd voordat je hem installeert, net zoals je een NuGet-package zou reviewen.\nSkills dwingen niet af. Een skill vertelt Claude wat het moet doen, maar Claude kan er nog steeds van afwijken. Als je instructie conflicteert met wat er in de codebase staat, volgt Claude mogelijk de code in plaats van de skill. Combineer skills met hooks voor daadwerkelijke afdwinging.\nPlugins vergen onderhoud. MCP-servers zijn software. Ze breken, ze moeten geupdate worden, ze hebben bugs. Als een plugin verbindt met een externe API, kan die API veranderen. Houd je pluginlijst compact.\nBegin met verkennen Dit is wat ik deze week zou doen:\nDraai /install in je volgende Claude Code-sessie en browse wat er beschikbaar is Installeer dotnet-skills van Aaronontheweb en probeer het op een echt project Schrijf een SKILL.md voor het meestvoorkomende patroon van je team — dat patroon dat je steeds opnieuw uitlegt bij code reviews Skills zijn de snelste manier om te gaan van \u0026ldquo;Claude schrijft degelijk C#\u0026rdquo; naar \u0026ldquo;Claude schrijft C# zoals mijn team het schrijft.\u0026rdquo; En dat verschil is groter dan je denkt.\n","permalink":"https://renedekkers.nl/nl/posts/claude-code-skills-en-plugins-voor-dotnet/","summary":"Claude Code is out of the box al krachtig. Met skills en plugins wordt het een .NET-specialist die jouw patronen, frameworks en conventies kent.","title":"Claude Code skills en plugins voor .NET — de marketplace ontdekken"},{"content":"Een developer plakt een connection string in ChatGPT om een configuratieprobleem te debuggen. Een collega kopieert een stuk klantdata naar een AI-tool \u0026ldquo;even om de mapping te testen.\u0026rdquo; Weer iemand anders deelt een intern API-contract om sneller clientcode te genereren.\nHerkenbaar? Waarschijnlijk wel. Risicovol? Absoluut.\nGeen van deze mensen had slechte bedoelingen. Ze probeerden gewoon hun werk te doen. Maar in de haast om productief te zijn deelden ze dingen die het pand nooit hadden mogen verlaten.\nHet probleem: waar gaat je input naartoe? Als je een prompt typt in een AI-tool, verdwijnt je input niet na het antwoord. Afhankelijk van de dienst kan het worden opgeslagen, gelogd, of zelfs gebruikt om toekomstige modellen te trainen. De meeste diensten zijn hier transparant over in hun voorwaarden — maar laten we eerlijk zijn, wie leest die?\nDe kernvraag is niet \u0026ldquo;is deze AI-tool goed?\u0026rdquo; Het is: waar komt mijn input terecht, en wie kan erbij?\nVoor tools die op je eigen infrastructuur draaien is het antwoord simpel. Voor externe diensten ligt het genuanceerder. Sommige aanbieders hebben enterprise-plannen waarbij je data niet voor training wordt gebruikt. Anderen maken dat onderscheid niet. En zelfs als ze het wel doen — data reist nog steeds over het internet, wordt opgeslagen op hun servers, en valt onder hun securitybeleid.\nDit betekent niet dat je geen externe AI-tools moet gebruiken. Het betekent dat je moet nadenken over wat je erin stopt.\nWat je NOOIT moet delen De lijst is korter dan je denkt, maar de gevolgen als het misgaat zijn serieus:\nCredentials: API-keys, connection strings, wachtwoorden, tokens. Niet \u0026ldquo;even snel\u0026rdquo; om iets te debuggen. Strip ze er eerst uit. Klantdata: Namen, e-mailadressen, financiële gegevens, gezondheidsdata — alles wat herleidbaar is tot een persoon of onder de AVG valt. Productiedata: Echte database-exports, logbestanden met gebruikersinformatie, interne systeemconfiguraties. Bedrijfsgeheimen: Eigen algoritmes, nog niet uitgebrachte productdetails, interne strategiedocumenten. De rode draad: als het een probleem zou zijn wanneer het opduikt in een datalekmelding, plak het dan niet in een externe AI-tool.\nWat je WEL veilig kunt delen Het goede nieuws: er is genoeg dat je zonder risico kunt delen.\nPublieke code en open-source patronen: Alles wat al op GitHub staat of in openbare documentatie. Generieke architectuurvragen: \u0026ldquo;Hoe implementeer ik het mediator-pattern in .NET?\u0026rdquo; geeft niets gevoeligs prijs. Foutmeldingen — zolang je de gevoelige context eruit haalt. NullReferenceException op regel 42 in OrderService.cs is prima. Dezelfde fout met een volledige stacktrace inclusief klant-ID\u0026rsquo;s is dat niet. Testdata en mock-objecten: Synthetische data die je hebt gemaakt voor testdoeleinden. Algemene best practices: Vragen over design patterns, coderingconventies, of framework-gebruik. De vuistregel: zou je dit zonder nadenken op Stack Overflow posten? Dan is het ook prima voor een AI-tool.\nHet kernprincipe: jij bent verantwoordelijk Ik heb een AI-richtlijnendocument geschreven voor mijn team, en de openingszin zegt alles:\nAI is een hulpmiddel, geen verantwoordelijke. Wanneer werk wordt opgeleverd, is AI daar niet verantwoordelijk voor. De verantwoordelijkheid ligt altijd bij degene die het werk maakt, reviewt en incheckt.\nDit komt neer op vier regels:\n1. Jij reviewt alles. Code, tests, documentatie — alles wat AI genereert moet door jou worden gecontroleerd voor het wordt gecommit. Inclusief checken op per ongeluk meegenomen secrets.\n2. Jij begrijpt wat je commit. Check nooit code in die je niet kunt uitleggen. Als AI iets genereert dat je niet begrijpt, vraag om uitleg of schrijf het zelf.\n3. Jij test het resultaat. AI-gegenereerde tests moeten ook gevalideerd worden. Een groene test suite betekent niets als de tests niet daadwerkelijk het juiste gedrag controleren.\n4. Jij bent de eigenaar. Op het moment dat je op \u0026ldquo;commit\u0026rdquo; drukt, is het jouw werk. Niet van AI, niet van een collega. Als er een bug in productie zit, is \u0026ldquo;maar AI schreef het\u0026rdquo; geen acceptabel antwoord.\nGovernance in een team: maak het expliciet Individueel bewustzijn is goed. Maar in een team heb je gedeelde afspraken nodig. Want de developer die per ongeluk een connection string deelt is niet onzorgvuldig — die heeft gewoon nooit het gesprek gehad over wat wel en niet mag.\nDit werkt:\nSchrijf de regels op. Documenteer wat wel en niet gedeeld mag worden met AI-tools. Houd het kort — een bulletlijst is genoeg. Zet het ergens waar iedereen het ziet: je wiki, je onboardingdocs, of je CLAUDE.md.\nEn nu we het daarover hebben — als je Claude Code gebruikt, is je CLAUDE.md dé plek voor \u0026ldquo;doe dit niet\u0026rdquo;-regels. Dingen als:\n## Security - Nooit API-keys, connection strings of secrets in prompts opnemen - Geen echte klantdata verwerken — gebruik test fixtures uit /tests/fixtures/ - Persoonsgegevens uit error logs strippen voor je ze deelt Claude leest dit bestand aan het begin van elke sessie. Het is niet alleen documentatie — het is actieve context die bepaalt hoe de AI met je codebase werkt.\nPraat erover. Heb een gesprek van tien minuten in je volgende retro of standup. Niet om mensen bang te maken, maar om een gedeeld begrip te creëren. \u0026ldquo;Wat vinden we oké om te delen met AI-tools?\u0026rdquo; Die ene vraag levert meer edge cases op dan welk beleidsdocument ook.\nReview erop. Voeg \u0026ldquo;geen secrets in AI-prompts\u0026rdquo; toe aan je code review checklist. Check .env-bestanden, check commit history op per ongeluk gecommitte credentials. Dit is goed practice ongeacht AI — maar AI maakt het urgenter.\nHet gaat niet om angst Ik wil helder zijn: dit is geen argument tegen het gebruik van AI-tools. Ik gebruik ze elke dag. Ze maken me sneller, ze vangen dingen die ik mis, en ze zijn oprecht nuttig om problemen door te denken.\nMaar \u0026ldquo;nuttig\u0026rdquo; en \u0026ldquo;standaard veilig\u0026rdquo; zijn niet hetzelfde. AI-tools weten niet wat vertrouwelijk is in jouw context. Ze weten niet dat de database-URL die je net plakte productiecredentials bevat. Ze weten niet dat de JSON-blob die je debugt echte e-mailadressen van klanten bevat.\nJij wel. Dat is het punt.\nGovernance gaat niet over beperken wat je mag doen. Het gaat over weten wat je doet. Het is het verschil tussen hard rijden omdat je de weg kent, en hard rijden omdat je niet oplet.\nBegin hier Als je nog geen AI-governance hebt in je team, zijn hier drie dingen die je vandaag kunt doen:\nMaak een \u0026ldquo;nooit delen\u0026rdquo;-lijst. Vijf minuten, vijf bullets. Credentials, klantdata, productieconfiguraties, tokens, bedrijfsgeheimen. Pin het in je teamkanaal.\nCheck je CLAUDE.md (of vergelijkbare config). Voeg een securitysectie toe met de regels waar je AI-tools zich aan moeten houden. Kost twee minuten en werkt vanaf dag één.\nStel de vraag. In je volgende teamoverleg: \u0026ldquo;Wat vinden we oké om te delen met AI-tools?\u0026rdquo; Je zult verbaasd zijn hoeveel mensen hetzelfde hebben afgevraagd maar het niet hebben aangekaart.\nAI governance klinkt groot en corporate. Dat hoeft het niet te zijn. Het begint met één gesprek en een paar duidelijke regels. De tools zijn krachtig — zorg dat je ze gebruikt met je ogen open.\n","permalink":"https://renedekkers.nl/nl/posts/ai-governance-voor-developers/","summary":"Een developer plakt een connection string in ChatGPT. Een ander kopieert klantdata om iets te testen. We zijn er allemaal dichtbij geweest. Zo denk je na over wat je wel en niet kunt delen.","title":"AI governance voor developers — wat mag je delen en wat niet?"},{"content":"Claude Code kent je code. Het leest je bestanden, begrijpt je architectuur, en kan een NullReferenceException in seconden debuggen. Maar vraag of je laatste deployment is gelukt, query een database, of lees een e-mail — en het weet van niets.\nDat voelde altijd als een gemiste kans. De AI begrijpt mijn codebase, maar kan niet praten met de systemen waar ik dagelijks mee werk.\nWat als je dat zou kunnen veranderen?\nWat is MCP? MCP staat voor Model Context Protocol. Het is een open standaard waarmee AI-tools zoals Claude Code kunnen communiceren met externe systemen via zogenaamde \u0026ldquo;MCP servers.\u0026rdquo;\nZie het als een USB-poort voor AI. Je laptop weet niet welk apparaat je gaat aansluiten — een toetsenbord, een webcam, een externe schijf — maar kan met allemaal praten via een standaard interface. MCP werkt op dezelfde manier. Je bouwt een server die \u0026ldquo;tools\u0026rdquo; aanbiedt, en Claude Code kan die tools aanroepen wanneer het ze nodig heeft.\nGeen custom integraties. Geen API-lijmcode in je prompts. Gewoon een gestandaardiseerd protocol.\nWat kun je ermee bouwen? De mogelijkheden zijn breder dan je zou verwachten. Een paar voorbeelden:\nCI/CD status — Vraag Claude Code: \u0026ldquo;Is de build geslaagd?\u0026rdquo; en het bevraagt je build server direct. Database queries — \u0026ldquo;Hoeveel gebruikers hebben zich deze week aangemeld?\u0026rdquo; zonder je terminal te verlaten. Deployment triggers — \u0026ldquo;Deploy de staging branch naar test\u0026rdquo; — Claude Code roept je deployment pipeline aan. E-mail — \u0026ldquo;Staan er urgente berichten in mijn inbox?\u0026rdquo; — Claude Code leest je mail en geeft een samenvatting. Monitoring — \u0026ldquo;Zijn er fouten in het laatste uur?\u0026rdquo; — Claude Code checkt je logging platform. Het kernidee: alles wat je kunt scripten, kun je als MCP tool beschikbaar maken.\nJe eerste MCP server bouwen Laten we een simpele bouwen. Een server die build status en test coverage ophaalt — het soort informatie waarvoor je normaal alt-tab naar een browser.\nJe hebt Python nodig en het fastmcp package:\npip install fastmcp Maak dan een bestand aan, bijvoorbeeld build_server.py:\nfrom fastmcp import FastMCP mcp = FastMCP(\u0026#34;build-status\u0026#34;) @mcp.tool() def get_build_status(project: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34;Get the latest build status for a project.\u0026#34;\u0026#34;\u0026#34; # In de praktijk: roep hier je CI/CD API aan return f\u0026#34;Build for {project}: passed\u0026#34; @mcp.tool() def get_test_coverage(project: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34;Get the test coverage for a project.\u0026#34;\u0026#34;\u0026#34; # In de praktijk: ophalen uit SonarQube, Coverlet, etc. return f\u0026#34;Coverage for {project}: 87%\u0026#34; if __name__ == \u0026#34;__main__\u0026#34;: mcp.run() Dat is alles. Twee decorators, twee functies, en je hebt een MCP server met twee tools.\nDe @mcp.tool() decorator registreert de functie als een tool die Claude Code kan ontdekken en aanroepen. De docstring wordt de toolbeschrijving — Claude Code gebruikt die om te beslissen wanneer het de tool aanroept. De type hints vertellen welke parameters verwacht worden.\nKoppelen aan Claude Code Open je Claude Code settings bestand (~/.claude/settings.json of de project-level .claude/settings.json) en voeg je server toe:\n{ \u0026#34;mcpServers\u0026#34;: { \u0026#34;build-status\u0026#34;: { \u0026#34;command\u0026#34;: \u0026#34;python\u0026#34;, \u0026#34;args\u0026#34;: [\u0026#34;path/to/build_server.py\u0026#34;] } } } Herstart Claude Code. Je tools zijn nu beschikbaar. Vraag \u0026ldquo;Wat is de build status van my-api?\u0026rdquo; en Claude Code roept achter de schermen get_build_status(\u0026quot;my-api\u0026quot;) aan.\nGeen prompt engineering. Geen API-responses copy-pasten. Claude Code handelt de toolselectie en parametermapping zelf af.\nVan speelgoedvoorbeeld naar echte tool Ik ben niet gestopt bij build status. Ik heb een MCP server gebouwd voor Apple Mail — apple-mail-mcp — met meer dan 25 tools. E-mails lezen, zoeken op afzender, berichten verplaatsen, concepten beheren, conversaties exporteren.\nHet begon klein. Eén tool om de inbox te lezen. Toen eentje om te zoeken op onderwerp. Toen eentje om e-mails naar mappen te verplaatsen. Voor ik het wist had ik een complete e-mailbeheerlaag die Claude Code kon bedienen.\nHet praktische resultaat: ik kan zeggen \u0026ldquo;Check mijn inbox op berichten van de klant over de migratie\u0026rdquo; en Claude Code leest mijn mail, vindt de relevante berichten, en vat ze samen. Geen apps wisselen, geen door threads scrollen.\nEen paar dingen die ik leerde bij het bouwen:\nDocstrings zijn belangrijk. Claude Code gebruikt ze om te bepalen welke tool het aanroept. Vage beschrijvingen leiden tot verkeerde toolselectie. Houd tools gefocust. Eén tool per actie. Bouw geen \u0026ldquo;doe alles\u0026rdquo;-tool — Claude Code werkt beter met veel kleine, specifieke tools. Voeg veiligheidslimieten toe. Mijn mailserver kapt batch-operaties af op 10 berichten. Je wilt niet dat een AI per ongeluk 500 e-mails archiveert. Test met echte scenario\u0026rsquo;s. De tool werkt anders wanneer Claude Code zelf de parameters bepaalt dan wanneer je ze hardcoded in tests. De eerlijke nuance MCP is nog jong. Dit moet je weten:\nHet ecosysteem groeit, maar is niet volwassen. Er zijn MCP servers voor GitHub, Slack, databases en meer — maar veel zijn community-built en variëren in kwaliteit. Verwacht nog geen plug-and-play voor elke dienst.\nDebuggen kan lastig zijn. Als een tool faalt, zijn de foutmeldingen niet altijd duidelijk. Soms roept Claude Code een tool aan met onverwachte parameters. Alles loggen in je server helpt.\nHet voegt een dependency toe. Je workflow hangt nu af van een extern serverproces. Als het crasht, verliest Claude Code de toegang tot die tools. Een startup wrapper script (zoals een start_mcp.sh) dat venv-creatie en herstarts afhandelt helpt.\nSecurity is jouw verantwoordelijkheid. Een MCP tool die je productiedatabase bevraagt is krachtig — en gevaarlijk. Denk na over wat je beschikbaar stelt en voeg passende beveiligingen toe.\nNiets hiervan is een dealbreaker. Maar het is goed om te weten waar je aan begint.\nWaar te beginnen Als dit iets heeft aangewakkerd, is dit mijn suggestie:\nKies één ding waar je vijf keer per dag naar alt-tabt. Je build server. Je issue tracker. Je deployment dashboard. Bouw dan een MCP server met één tool die je die informatie geeft.\nBegin met een hardcoded response — gewoon om het werkend te zien in Claude Code. Vervang dan de echte API-call. Voeg dan een tweede tool toe. Je zult verrast zijn hoe snel het nuttig wordt.\nDe FastMCP documentatie is goed. En als je een volledig voorbeeld wilt zien, mijn apple-mail-mcp server is open source.\nMCP maakt van Claude Code meer dan een code-assistent — het wordt iets dat dichter bij een echte collega komt. Eentje die de build kan checken, de logs kan lezen, en je kan vertellen wat er aan de hand is. Dat is de twintig minuten waard die het kost om je eerste server te bouwen.\nHeb je al een MCP server gebouwd, of ben je van plan? Ik ben benieuwd welke tools je als eerste zou koppelen. Stuur me een bericht via de contactpagina.\n","permalink":"https://renedekkers.nl/nl/posts/mcp-servers-bouwen/","summary":"Claude Code kent je code. Maar het kent je build server niet, je deployment pipeline niet, en je inbox niet. MCP verandert dat.","title":"MCP servers bouwen — Claude Code koppelen aan je eigen tools"},{"content":"Je kent hem wel. Die ene service class die klein en overzichtelijk begon, en toen maar bleef groeien. Sprint na sprint, feature na feature. Tot je hem op een dag opent en denkt: dit ding is 800 regels en doet alles.\nBij mij was het OrderProcessingService.cs. Het valideerde orders, berekende prijzen, beheerde statussen, verstuurde notificaties, en maakte waarschijnlijk ook nog koffie. Het stond al weken op de refactoring-backlog. Niemand wilde eraan.\nWaarom handmatig refactoren zo vervelend is Een grote service opsplitsen is technisch niet moeilijk. Je weet wat er moet gebeuren: verantwoordelijkheden identificeren, nieuwe classes maken, logica extraheren, dependency injection doorvoeren, en tests aanpassen.\nMaar de uitvoering is waar het vervelend wordt. Je moet zorgvuldig uitzoeken welke methodes afhankelijk zijn van welke private fields. Je moet bepalen welke groepen methodes bij elkaar horen. En je moet er zeker van zijn dat je niks breekt — en in een bestand van 800 regels zijn er genoeg plekken waar het stilletjes mis kan gaan.\nHet is het soort taak waarbij je denkt \u0026ldquo;dit kost me een uur\u0026rdquo; en dan is het 16:00 en zit je nog steeds compileerfouten op te lossen.\nStap 1: Vraag om een analyse Ik opende mijn terminal in het project en startte Claude Code. Eerste vraag:\n\u0026ldquo;Analyseer OrderProcessingService.cs. Welke afzonderlijke verantwoordelijkheden heeft deze class?\u0026rdquo;\nClaude Code opende het bestand, las alle 800 regels, en kwam terug met een overzicht:\nOrdervalidatie — voorraad checken, adressen valideren, kredietlimieten controleren Prijsberekening — basisprijzen, kortingen, belasting, verzendkosten Statusbeheer — statusovergangen, statushistorie, notificatie-triggers Notificatieverzending — e-mails, webhook-calls naar externe systemen Vier duidelijke verantwoordelijkheden in één class. Ik had al een vermoeden, maar het concreet zien met specifieke methodenamen per groep maakte het tastbaar.\nStap 2: Eerst plannen, dan knippen Ik wilde niet dat Claude Code meteen begon met hakken. Dus schakelde ik over naar Plan Mode (Shift+Tab) en vroeg:\n\u0026ldquo;Maak een plan om deze service op te splitsen volgens het Single Responsibility Principle. Houd de originele service als orchestrator.\u0026rdquo;\nClaude Code stelde voor:\nIOrderValidator + OrderValidator — alle validatielogica IPriceCalculator + PriceCalculator — prijs- en kortingslogica IOrderStatusManager + OrderStatusManager — statusovergangen en historie OrderProcessingService behouden als dunne orchestrator die de drie aanstuurt Goed plan. Akkoord.\nStap 3: Claude Code voert het uit Hier wordt het interessant. Claude Code verplaatste niet zomaar wat methodes. Het:\nMaakte de interfaces aan met de juiste method signatures Extraheerde de implementaties, inclusief de private helper-methodes die elke groep nodig had Paste OrderProcessingService aan om de nieuwe dependencies te injecteren Registreerde alles in Program.cs De originele service zag er ongeveer zo uit (vereenvoudigd):\npublic class OrderProcessingService { private readonly AppDbContext _db; private readonly ILogger\u0026lt;OrderProcessingService\u0026gt; _logger; public OrderProcessingService(AppDbContext db, ILogger\u0026lt;OrderProcessingService\u0026gt; logger) { _db = db; _logger = logger; } public async Task\u0026lt;OrderResult\u0026gt; ProcessOrderAsync(Order order) { // Validatie (150+ regels logica) if (!await HasSufficientStock(order)) return OrderResult.Failed(\u0026#34;Insufficient stock\u0026#34;); if (!IsValidShippingAddress(order.ShippingAddress)) return OrderResult.Failed(\u0026#34;Invalid address\u0026#34;); if (!await CheckCreditLimit(order.Customer, order.Total)) return OrderResult.Failed(\u0026#34;Credit limit exceeded\u0026#34;); // Prijsberekening (200+ regels logica) var basePrice = CalculateBasePrice(order.Items); var discount = ApplyDiscounts(order.Customer, basePrice); var tax = CalculateTax(order.ShippingAddress, discount); var shipping = CalculateShipping(order.Items, order.ShippingAddress); order.Total = discount + tax + shipping; // Statusbeheer (100+ regels logica) order.Status = OrderStatus.Confirmed; await RecordStatusChange(order, OrderStatus.Confirmed); await NotifyCustomer(order); await _db.SaveChangesAsync(); return OrderResult.Success(order); } // ... 20+ private methodes per verantwoordelijkheid } Na de refactor waren de interfaces strak en gefocust:\npublic interface IOrderValidator { Task\u0026lt;ValidationResult\u0026gt; ValidateOrderAsync(Order order); } public interface IPriceCalculator { PriceBreakdown CalculatePrice(IReadOnlyList\u0026lt;OrderItem\u0026gt; items, Customer customer, Address shippingAddress); } public interface IOrderStatusManager { Task TransitionStatusAsync(Order order, OrderStatus newStatus); } En de orchestrerende service werd overzichtelijk:\npublic class OrderProcessingService { private readonly IOrderValidator _validator; private readonly IPriceCalculator _calculator; private readonly IOrderStatusManager _statusManager; private readonly AppDbContext _db; public OrderProcessingService( IOrderValidator validator, IPriceCalculator calculator, IOrderStatusManager statusManager, AppDbContext db) { _validator = validator; _calculator = calculator; _statusManager = statusManager; _db = db; } public async Task\u0026lt;OrderResult\u0026gt; ProcessOrderAsync(Order order) { var validation = await _validator.ValidateOrderAsync(order); if (!validation.IsValid) return OrderResult.Failed(validation.ErrorMessage); var pricing = _calculator.CalculatePrice( order.Items, order.Customer, order.ShippingAddress); order.Total = pricing.Total; await _statusManager.TransitionStatusAsync(order, OrderStatus.Confirmed); await _db.SaveChangesAsync(); return OrderResult.Success(order); } } Van 800 regels naar zo\u0026rsquo;n 30. De logica is niet verdwenen — die zit nu waar hij hoort.\nStap 4: DI-registratie en verificatie Claude Code paste ook Program.cs aan:\nbuilder.Services.AddScoped\u0026lt;IOrderValidator, OrderValidator\u0026gt;(); builder.Services.AddScoped\u0026lt;IPriceCalculator, PriceCalculator\u0026gt;(); builder.Services.AddScoped\u0026lt;IOrderStatusManager, OrderStatusManager\u0026gt;(); builder.Services.AddScoped\u0026lt;OrderProcessingService\u0026gt;(); Daarna draaide het dotnet build. Compileert netjes. Dan dotnet test. Alle 47 bestaande tests slaagden. Geen regressies.\nHet hele traject kostte ongeveer 40 minuten, inclusief review. Dat backlog-item waar niemand aan wilde beginnen? Klaar voor de lunch.\nWanneer dit goed werkt — en wanneer niet Dit soort refactoring is waar Claude Code uitblinkt. De verantwoordelijkheden waren duidelijk te scheiden. Validatielogica hoeft niks te weten over prijsberekeningen. Statusbeheer heeft niks te maken met verzendkosten. Heldere grenzen, heldere splitsingen.\nHet wordt lastiger als de verantwoordelijkheden verweven zijn. Als je validatieregels afhangen van de berekende prijs, die afhangt van de klantstatus, die weer wordt bijgewerkt tijdens de validatie — dan is het een ander verhaal. Claude Code helpt je er nog steeds doorheen, maar de splitsing wordt minder strak en je moet meer zelf beslissen waar je de grenzen trekt.\nMijn advies: begin met de makkelijke winst. De service met vier duidelijke verantwoordelijkheden waar iedereen het over eens is dat ze apart horen. Bouw vertrouwen op met die gevallen voordat je de lastige aanpakt.\nTips voor je eigen refactoring Begin altijd met analyse. Spring niet direct naar \u0026ldquo;refactor dit.\u0026rdquo; Vraag Claude Code eerst om de verantwoordelijkheden te identificeren. Het ziet vaak groeperingen die jij over het hoofd had gezien.\nGebruik Plan Mode. Een refactoring-plan dat je kunt reviewen voor de uitvoering bespaart je het terugdraaien van slechte splitsingen. Shift+Tab is je vriend.\nBehoud de orchestrator. Verwijder de originele class niet. Maak er een dunne coordinator van. Zo blijven bestaande aanroepers werken en beperk je de impact van de wijziging.\nDraai de tests na elke stap. Claude Code kan dit voor je doen. Een dotnet test na elke geextraheerde class vangt problemen vroeg op, als ze nog makkelijk te fixen zijn.\nReview de interfaces. De method signatures die Claude Code maakt zijn meestal logisch, maar jij kent je domein beter. Als een interface niet goed voelt, zeg het — Claude Code past het aan.\nProbeer het zelf Heb je een service die ongecontroleerd is gegroeid? Open je terminal:\nclaude \u0026ldquo;Analyseer [JouwService].cs. Welke verantwoordelijkheden heeft deze class? Stel een plan voor om het op te splitsen.\u0026rdquo;\nJe zult verrast zijn hoe snel dat backlog-item gaat van \u0026ldquo;niemand wil eraan\u0026rdquo; naar \u0026ldquo;klaar.\u0026rdquo;\n","permalink":"https://renedekkers.nl/nl/posts/refactoring-met-claude-code/","summary":"Iedereen heeft die ene service class die te groot is geworden. Ik gebruikte Claude Code om een monoliet van 800 regels op te splitsen — in minder dan een uur.","title":"Refactoring met Claude Code — een service van 800 regels opsplitsen"},{"content":"Je bent een uur bezig met Claude Code. Het heeft een nieuw endpoint gebouwd, de validator aangepast, de mapping bijgewerkt. Alles ziet er goed uit. Je typt dotnet test en drie tests falen.\nNiet omdat de code fout is. Maar omdat Claude een bestand opnieuw heeft geformateerd, een namespace heeft aangepast en een test-assertion brak die afhankelijk was van een specifiek stringformaat. Kleine dingen. Dingen die je meteen had gezien — als je na elke edit de tests had gedraaid in plaats van pas aan het eind.\nDit is precies het probleem dat hooks oplossen.\nWat zijn hooks? Claude Code hooks zijn shell-commando\u0026rsquo;s die automatisch draaien op specifieke momenten tijdens een sessie. Je hoeft niet meer te onthouden dat je \u0026ldquo;draai de tests\u0026rdquo; moet vragen. Je configureert het één keer, en het gebeurt elke keer.\nEr zijn vier typen hooks:\nPreToolUse — draait voordat Claude een tool uitvoert (zoals een bestand lezen of bewerken) PostToolUse — draait nadat een tool is uitgevoerd (perfect voor formatting) Stop — draait wanneer Claude een taak afrondt (ideaal voor tests) Notification — voor meldingen Je configureert ze in .claude/settings.json. Klaar. Geen plugins, geen extensies, geen third-party tools.\nDe Stop hook: automatisch tests draaien In een eerdere post schreef ik over het principe \u0026ldquo;vertrouw, maar verifieer altijd.\u0026rdquo; De Stop hook maakt van dat principe een automatische check.\nDit gebruik ik in mijn .NET-projecten:\n{ \u0026#34;hooks\u0026#34;: { \u0026#34;Stop\u0026#34;: [ { \u0026#34;matcher\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;hooks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;command\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;dotnet test --no-build --verbosity quiet 2\u0026gt;\u0026amp;1 | tail -5\u0026#34; } ] } ] } } Elke keer dat Claude een taak afrondt, draait dotnet test. De --no-build flag slaat de buildstap over (Claude\u0026rsquo;s edit heeft in de meeste setups al een build getriggerd). De tail -5 houdt de output kort — je ziet alleen de samenvatting.\nAls tests falen, ziet Claude de output en kan het direct fixen. Geen context switch. Geen handmatige stap. De feedbackloop is direct.\nHet matcher-veld is hier leeg, wat betekent dat het bij elke Stop draait. Je kunt ook matchen op specifieke patronen als je het alleen in bepaalde situaties wilt triggeren.\nPostToolUse: formatteren na elke edit Als je in een team werkt met een .editorconfig of dotnet format-regels, ken je het verhaal. Iemands PR heeft formatting-wijzigingen door de logica-wijzigingen heen. De diff is onleesbaar. De reviewer is geïrriteerd.\nMet een PostToolUse hook wordt elk bestand dat Claude aanraakt direct geformateerd:\n{ \u0026#34;hooks\u0026#34;: { \u0026#34;PostToolUse\u0026#34;: [ { \u0026#34;matcher\u0026#34;: \u0026#34;Edit|Write\u0026#34;, \u0026#34;hooks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;command\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;dotnet format --include \\\u0026#34;$CLAUDE_FILE_PATH\\\u0026#34; --verbosity quiet\u0026#34; } ] } ] } } De matcher is hier \u0026quot;Edit|Write\u0026quot; — het triggert alleen wanneer Claude een bestand bewerkt of schrijft. Niet bij lezen of zoeken. De $CLAUDE_FILE_PATH-variabele geeft je exact het bestand dat is gewijzigd.\nDit betekent dat elk bestand dat Claude aanraakt wordt geformateerd volgens de regels van je team. Geen uitzonderingen. Geen \u0026ldquo;oeps, ik was vergeten de formatter te draaien.\u0026rdquo;\nPreToolUse: bewaken voordat het gebeurt PreToolUse hooks draaien voordat Claude iets doet. Zie ze als vangrails.\nEen simpel voorbeeld: edits blokkeren op bestanden waar je niet wilt dat Claude aankomt.\n{ \u0026#34;hooks\u0026#34;: { \u0026#34;PreToolUse\u0026#34;: [ { \u0026#34;matcher\u0026#34;: \u0026#34;Edit|Write\u0026#34;, \u0026#34;hooks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;command\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;echo \\\u0026#34;$CLAUDE_FILE_PATH\\\u0026#34; | grep -q \u0026#39;migrations/\u0026#39; \u0026amp;\u0026amp; echo \u0026#39;BLOCK: Do not edit migration files\u0026#39; \u0026amp;\u0026amp; exit 1 || exit 0\u0026#34; } ] } ] } } Als Claude iets probeert te bewerken in de migrations/-map, blokkeert de hook het. In een .NET-project met EF Core wil je absoluut geen AI-gegenereerde wijzigingen in je migratiebestanden.\nTeam vs. persoonlijk: settings.json vs. settings.local.json Hier wordt het praktisch voor teams.\n.claude/settings.json wordt ingecheckt in git. Elke developer in het team krijgt dezelfde hooks. De Stop hook die tests draait? Teambeslissing. De PostToolUse formatter? Teambeslissing. Je commit het, iedereen profiteert.\n.claude/settings.local.json is persoonlijk. Staat niet in git. Hier zet je je eigen voorkeuren — misschien een notification hook die je een Slack-bericht stuurt, of een PreToolUse hook die past bij jouw persoonlijke workflow.\nHet lokale bestand overschrijft het gedeelde bestand voor matchende hooks. Het team bepaalt de baseline, en jij kunt aanpassen voor je eigen setup.\nVoor een .NET-team zou ik deze hooks in de gedeelde settings.json zetten:\nStop hook: draai dotnet test na elke taak PostToolUse hook: draai dotnet format na elke edit PreToolUse hook: blokkeer edits op migratiebestanden Al het andere — notificaties, persoonlijke linting-voorkeuren, experimentele hooks — gaat in settings.local.json.\nEerlijk over beperkingen Hooks zijn krachtig, maar het is geen magie.\nZe kosten tijd. Elke Stop hook voegt de uitvoertijd van dotnet test toe aan elke taakafronding. Voor een grote solution met trage tests kan dit frustrerend zijn. Gebruik --no-build en overweeg om alleen snelle unit tests te draaien, geen integratietests.\nHet zijn shell-commando\u0026rsquo;s. Als je commando stil faalt of onverwacht een non-zero exit code teruggeeft, wordt het verwarrend. Test je hook-commando\u0026rsquo;s eerst handmatig.\nZe vervangen geen code review. Een groene test suite betekent niet dat de code goed is. Het betekent dat de code bestaand gedrag niet breekt. Architectuurbeslissingen, naamgeving, leesbaarheid — dat is nog steeds jouw verantwoordelijkheid.\nMatcher-patronen zijn simpel. Het matcher-veld matcht op toolnamen, niet op bestandspatronen of projectnamen. Voor meer controle moet je de logica in je shell-commando afhandelen.\nBegin met één hook Je hoeft niet alles tegelijk in te richten. Begin met de Stop hook die je tests draait. Leef er een week mee. Je zult merken dat Claude zijn eigen fouten vaker opvangt, en dat je feedbackloop krimpt van \u0026ldquo;einde van de sessie\u0026rdquo; naar \u0026ldquo;einde van elke taak.\u0026rdquo;\nVoeg daarna de formatter toe. Dan de vangrails.\nHet doel is niet om alles te automatiseren. Het doel is om de dingen te automatiseren die je steeds vergeet — zodat je je kunt richten op de dingen die echt jouw oordeel nodig hebben.\n","permalink":"https://renedekkers.nl/nl/posts/claude-code-hooks-automatisch-testen/","summary":"Claude Code hooks draaien automatisch tests, formatteren code en bewaken standaarden — zonder dat je eraan hoeft te denken.","title":"Claude Code hooks — automatisch testen na elke wijziging"},{"content":"Je bedrijf draait nog op .NET Framework 4.x. De codebase is tien jaar oud, misschien ouder. Iedereen is het erover eens dat het naar .NET 8 of 9 moet. Niemand wil degene zijn die begint.\nBegrijpelijk. Framework-migraties zijn saai, riskant en ondankbaar. Je bouwt niks nieuws — je herschrijft wat al werkt, en hoopt dat je onderweg niks breekt.\nMaar ik gebruik Claude Code om te helpen bij dit soort migraties, en het maakt een uitzichtloos project behapbaar. Zo pak ik het aan.\nStap 1: Inventarisatie — weet wat je hebt Voordat je ook maar een regel wijzigt, moet je begrijpen wat je hebt. Vraag Claude Code om je solution te analyseren:\n\u0026ldquo;Scan deze solution. Maak een lijst van alle projecten, hun target frameworks, NuGet-pakketten en Framework-specifieke APIs zoals System.Web, WCF of COM interop.\u0026rdquo;\nClaude Code gaat door je .csproj-bestanden, packages.config-bestanden en broncode. Het bouwt een dependency-overzicht dat je normaal dagen kost om in een spreadsheet te zetten.\nDe uitkomst vertelt je welke projecten eenvoudig zijn (class libraries zonder Framework-afhankelijkheden) en welke pijn gaan doen (ASP.NET WebForms, WCF-services, alles wat System.Web aanraakt).\nAlleen deze stap bespaart je al een week handmatig inventariseren.\nStap 2: Bepaal het migratiepad Big bang of incrementeel? Dat hangt af van je solution-structuur, en Claude Code kan je helpen kiezen.\nVraag: \u0026ldquo;Gegeven deze afhankelijkheden tussen projecten, wat is de migratievolgorde? Welke projecten kan ik onafhankelijk migreren?\u0026rdquo;\nClaude Code brengt de dependency-graph in kaart en stelt een bottom-up aanpak voor: begin met gedeelde libraries die geen Framework-afhankelijkheden hebben, en werk dan omhoog naar de web- en serviceprojecten.\nVoor de meeste echte solutions is incrementeel het juiste antwoord. Je migreert een project tegelijk, houdt de solution buildend en levert tussentijds op. Big bang werkt alleen als je solution klein is of als je geniet van een feature-freeze van drie maanden.\nStap 3: Converteer naar SDK-style projectbestanden Hier blinkt Claude Code echt uit. Oude .csproj-bestanden zijn XML-nachtmerries — honderden regels die elk .cs-bestand apart opsommen. SDK-style projectbestanden zijn schoon.\nVraag Claude Code om een projectbestand te converteren, en het transformeert dit:\n\u0026lt;Project ToolsVersion=\u0026#34;15.0\u0026#34; xmlns=\u0026#34;http://schemas.microsoft.com/developer/msbuild/2003\u0026#34;\u0026gt; \u0026lt;Import Project=\u0026#34;$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\u0026#34; /\u0026gt; \u0026lt;PropertyGroup\u0026gt; \u0026lt;TargetFrameworkVersion\u0026gt;v4.8\u0026lt;/TargetFrameworkVersion\u0026gt; \u0026lt;OutputType\u0026gt;Library\u0026lt;/OutputType\u0026gt; \u0026lt;AssemblyName\u0026gt;MyApp.Core\u0026lt;/AssemblyName\u0026gt; \u0026lt;/PropertyGroup\u0026gt; \u0026lt;ItemGroup\u0026gt; \u0026lt;Reference Include=\u0026#34;System\u0026#34; /\u0026gt; \u0026lt;Reference Include=\u0026#34;System.Core\u0026#34; /\u0026gt; \u0026lt;Reference Include=\u0026#34;Newtonsoft.Json, Version=13.0.0.0, ...\u0026#34;\u0026gt; \u0026lt;HintPath\u0026gt;..\\packages\\Newtonsoft.Json.13.0.3\\lib\\net45\\Newtonsoft.Json.dll\u0026lt;/HintPath\u0026gt; \u0026lt;/Reference\u0026gt; \u0026lt;/ItemGroup\u0026gt; \u0026lt;ItemGroup\u0026gt; \u0026lt;Compile Include=\u0026#34;Models\\User.cs\u0026#34; /\u0026gt; \u0026lt;Compile Include=\u0026#34;Models\\Order.cs\u0026#34; /\u0026gt; \u0026lt;Compile Include=\u0026#34;Services\\UserService.cs\u0026#34; /\u0026gt; \u0026lt;!-- 200 meer regels --\u0026gt; \u0026lt;/ItemGroup\u0026gt; \u0026lt;/Project\u0026gt; Naar dit:\n\u0026lt;Project Sdk=\u0026#34;Microsoft.NET.Sdk\u0026#34;\u0026gt; \u0026lt;PropertyGroup\u0026gt; \u0026lt;TargetFramework\u0026gt;net8.0\u0026lt;/TargetFramework\u0026gt; \u0026lt;ImplicitUsings\u0026gt;enable\u0026lt;/ImplicitUsings\u0026gt; \u0026lt;Nullable\u0026gt;enable\u0026lt;/Nullable\u0026gt; \u0026lt;/PropertyGroup\u0026gt; \u0026lt;ItemGroup\u0026gt; \u0026lt;PackageReference Include=\u0026#34;Newtonsoft.Json\u0026#34; Version=\u0026#34;13.0.3\u0026#34; /\u0026gt; \u0026lt;/ItemGroup\u0026gt; \u0026lt;/Project\u0026gt; Van 50+ regels naar 12. Claude Code handelt de packages.config naar PackageReference-conversie af, verwijdert de expliciete bestandsverwijzingen (SDK-style projecten gebruiken globbing) en schrapt de boilerplate-imports.\nStap 4: Vervang verouderde APIs Dit is het saaie deel van elke migratie. Framework-specifieke APIs die niet bestaan in modern .NET. Claude Code handelt de gangbare patronen goed af.\nHttpContext en System.Web:\n// Voor: .NET Framework var userId = HttpContext.Current.Session[\u0026#34;UserId\u0026#34;]?.ToString(); var ip = HttpContext.Current.Request.UserHostAddress; // Na: .NET 8 — inject IHttpContextAccessor public class UserService(IHttpContextAccessor httpContextAccessor) { public string? GetUserId() =\u0026gt; httpContextAccessor.HttpContext?.Session.GetString(\u0026#34;UserId\u0026#34;); public string? GetClientIp() =\u0026gt; httpContextAccessor.HttpContext?.Connection.RemoteIpAddress?.ToString(); } ConfigurationManager:\n// Voor: .NET Framework var connectionString = ConfigurationManager.ConnectionStrings[\u0026#34;DefaultDb\u0026#34;].ConnectionString; var apiKey = ConfigurationManager.AppSettings[\u0026#34;ExternalApiKey\u0026#34;]; // Na: .NET 8 — gebruik IConfiguration of het options pattern public class ApiSettings { public string ExternalApiKey { get; set; } = \u0026#34;\u0026#34;; } // In Program.cs: builder.Services.Configure\u0026lt;ApiSettings\u0026gt;(builder.Configuration.GetSection(\u0026#34;Api\u0026#34;)); Claude Code doet geen simpele zoek-en-vervang. Het begrijpt de patronen en herschrijft ze idiomatisch. Het stelt het options pattern voor waar dat past, en injecteert IConfiguration waar een snelle fix genoeg is.\nStap 5: Migreer dependency injection Als je Framework-project Ninject, Unity of Autofac gebruikt, moet je een keuze maken. Modern .NET heeft ingebouwde DI die de meeste scenario\u0026rsquo;s dekt.\nClaude Code kan je DI-registraties herschrijven:\n// Voor: Ninject kernel.Bind\u0026lt;IUserRepository\u0026gt;().To\u0026lt;SqlUserRepository\u0026gt;().InRequestScope(); kernel.Bind\u0026lt;IOrderService\u0026gt;().To\u0026lt;OrderService\u0026gt;().InTransientScope(); kernel.Bind\u0026lt;ICacheService\u0026gt;().To\u0026lt;RedisCacheService\u0026gt;().InSingletonScope(); // Na: Ingebouwde Microsoft DI builder.Services.AddScoped\u0026lt;IUserRepository, SqlUserRepository\u0026gt;(); builder.Services.AddTransient\u0026lt;IOrderService, OrderService\u0026gt;(); builder.Services.AddSingleton\u0026lt;ICacheService, RedisCacheService\u0026gt;(); De lifetime-mapping is rechttoe rechtaan: InRequestScope wordt AddScoped, InTransientScope wordt AddTransient, InSingletonScope wordt AddSingleton. Claude Code doet dit betrouwbaar.\nBij complexe Autofac-registraties — modules, decorators, keyed services — wordt het lastiger. Claude Code kan het meeste aan, maar controleer zorgvuldig. Of houd gewoon Autofac. Dat werkt prima met .NET 8.\nStap 6: Configuratiemigratie De overstap van web.config XML naar appsettings.json is nog een taak die Claude Code goed afhandelt. Vraag het je configuratie te converteren, en het herstructureert de data:\n{ \u0026#34;ConnectionStrings\u0026#34;: { \u0026#34;DefaultDb\u0026#34;: \u0026#34;Server=.;Database=MyApp;Trusted_Connection=true\u0026#34; }, \u0026#34;Api\u0026#34;: { \u0026#34;ExternalApiKey\u0026#34;: \u0026#34;your-key-here\u0026#34;, \u0026#34;Timeout\u0026#34;: 30 }, \u0026#34;Logging\u0026#34;: { \u0026#34;LogLevel\u0026#34;: { \u0026#34;Default\u0026#34;: \u0026#34;Information\u0026#34; } } } Het genereert ook de bijbehorende Program.cs-setup met het minimal hosting model. Geen Startup.cs meer met ConfigureServices en Configure — gewoon een schone Program.cs.\nWat Claude Code niet kan Laat me eerlijk zijn over de grenzen.\nWCF-services. Als je WCF-services host (niet alleen consumeert), is er geen schoon migratiepad. CoreWCF bestaat, maar het is een compatibiliteitslaag, geen echte migratie. Claude Code kan helpen beoordelen welke services je als minimal APIs kunt herschrijven, maar de architectuurbeslissingen zijn aan jou.\nASP.NET WebForms. Er is geen geautomatiseerd pad van .aspx-pagina\u0026rsquo;s met code-behind naar Razor Pages of Blazor. Claude Code kan individuele pagina\u0026rsquo;s helpen herschrijven, maar het kan je ViewState-afhankelijke page lifecycle niet automatisch doorgronden. Dit is een herschrijving, geen migratie.\nCOM interop. Als je applicatie COM-componenten aanroept, kan Claude Code die afhankelijkheden identificeren maar ze niet magisch laten werken op Linux of in containers. Je moet kiezen: Windows houden, een .NET-alternatief zoeken, of de COM-afhankelijkheid isoleren.\nCustom build targets en MSBuild-magie. Sommige Framework-projecten hebben uitgebreide MSBuild-aanpassingen. Claude Code handelt standaardpatronen af maar mist mogelijk custom BeforeCompile-targets of conditionele property groups die iets onverwachts doen.\nRealistische verwachtingen Een migratie als deze is geen weekendproject. Dit is wat ik in de praktijk zie:\nClass libraries zonder Framework-afhankelijkheden: 30 minuten per project met Claude Code. Libraries met ConfigurationManager of System.Web: 1-2 uur per project. ASP.NET MVC-projecten: een hele dag, inclusief routing- en middleware-wijzigingen. WCF-services: dagen tot weken, afhankelijk van complexiteit. Claude Code helpt, maar dit is vooral handmatig architectuurwerk. De eerlijke waarheid: Claude Code maakt van een migratie van zes maanden een migratie van twee maanden. Het elimineert niet het werk — het elimineert de saaie delen. De dependency-analyse, de csproj-conversies, de API-vervangingen, de DI-herschrijvingen. Dat is 70% van het werk, en Claude Code doet het goed.\nDe overige 30% — architectuurbeslissingen, edge cases testen, omgaan met WCF en WebForms — dat blijft bij jou.\nWaar te beginnen Probeer niet alles tegelijk te migreren. Pak de kleinste, meest onafhankelijke library in je solution. Vraag Claude Code om het te converteren. Build. Run de tests. Fix wat breekt.\nDan de volgende. En de volgende.\nVoor je het weet draait de helft van je solution op .NET 8 en zijn de Framework-projecten de uitzondering, niet de regel. Dat is het moment waarop de migratie niet meer onmogelijk voelt, maar onvermijdelijk.\nSta je voor een .NET Framework-migratie? Of zit je er middenin? Ik hoor graag waar je tegenaan loopt. Neem contact op via de contactpagina.\n","permalink":"https://renedekkers.nl/nl/posts/migreren-van-dotnet-framework-naar-dotnet8/","summary":"Je bedrijf draait nog op .NET Framework 4.x. Iedereen weet dat het moet migreren. Niemand wil beginnen. Zo helpt Claude Code — en dit zijn de grenzen.","title":"Migreren van .NET Framework naar .NET 8+ met Claude Code"},{"content":"Blazor is een van die frameworks waar twee werelden botsen. Je schrijft C#-logica en HTML-markup in hetzelfde bestand, met component parameters, event callbacks en een render lifecycle die zijn eigen ideeën heeft over wanneer dingen updaten.\nDe meeste AI-tools worstelen daarmee. Ze geven je React-achtig advies voor een Blazor-probleem, of genereren code die compileert maar niet rendert wanneer je het verwacht.\nClaude Code is anders. Het leest je .razor-bestanden, begrijpt de component-hiërarchie en kent het verschil tussen StateHasChanged en InvokeAsync. Laat me je laten zien hoe dat er in de praktijk uitziet.\nEen component vanaf nul bouwen Stel, je hebt een notificatiebanner nodig. In plaats van zelf de structuur op te zetten, beschrijf je wat je wilt:\n\u0026ldquo;Maak een Blazor component NotificationBanner die een bericht toont met een severity level (info, warning, error). Hij moet wegklikbaar zijn en na 5 seconden automatisch verdwijnen voor info-berichten.\u0026rdquo;\nClaude Code maakt het .razor-bestand:\n@if (IsVisible) { \u0026lt;div class=\u0026#34;notification-banner @SeverityCss\u0026#34; role=\u0026#34;alert\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;notification-message\u0026#34;\u0026gt;@Message\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026#34;notification-close\u0026#34; @onclick=\u0026#34;Dismiss\u0026#34;\u0026gt;\u0026amp;times;\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; } @code { [Parameter] public string Message { get; set; } = string.Empty; [Parameter] public Severity Level { get; set; } = Severity.Info; [Parameter] public EventCallback OnDismissed { get; set; } private bool IsVisible { get; set; } = true; private Timer? _autoHideTimer; private string SeverityCss =\u0026gt; Level switch { Severity.Warning =\u0026gt; \u0026#34;banner-warning\u0026#34;, Severity.Error =\u0026gt; \u0026#34;banner-error\u0026#34;, _ =\u0026gt; \u0026#34;banner-info\u0026#34; }; protected override void OnParametersSet() { IsVisible = true; if (Level == Severity.Info) { _autoHideTimer?.Dispose(); _autoHideTimer = new Timer(_ =\u0026gt; InvokeAsync(Dismiss), null, 5000, Timeout.Infinite); } } private async Task Dismiss() { IsVisible = false; _autoHideTimer?.Dispose(); await OnDismissed.InvokeAsync(); } public void Dispose() =\u0026gt; _autoHideTimer?.Dispose(); } Let op wat het goed doet: InvokeAsync voor de timer-callback (omdat die niet op de render-thread zit), OnParametersSet in plaats van OnInitialized (zodat het component reageert op nieuwe berichten), en een nette IDisposable-implementatie. Dit zijn de details waarvoor je normaal tien minuten documentatie zou doorspitten.\nComponent parameters en cascading values Waar Claude Code echt uitblinkt is parent-child communicatie. Blazor\u0026rsquo;s model met [Parameter], [CascadingParameter] en EventCallback is krachtig maar verbose. Beschrijf de relatie en Claude Code regelt de bedrading.\n\u0026ldquo;Ik heb een DataGrid component nodig met een ColumnDefinition child component. De parent stuurt de databron mee, en elke kolom bepaalt hoe een veld gerenderd wordt.\u0026rdquo;\nClaude Code genereert de parent:\n\u0026lt;CascadingValue Value=\u0026#34;this\u0026#34;\u0026gt; \u0026lt;table class=\u0026#34;data-grid\u0026#34;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt;@ChildContent\u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody\u0026gt; @foreach (var item in Items) { \u0026lt;tr\u0026gt; @foreach (var column in _columns) { \u0026lt;td\u0026gt;@column.CellTemplate(item)\u0026lt;/td\u0026gt; } \u0026lt;/tr\u0026gt; } \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/CascadingValue\u0026gt; @code { [Parameter] public IEnumerable\u0026lt;object\u0026gt; Items { get; set; } = []; [Parameter] public RenderFragment? ChildContent { get; set; } private readonly List\u0026lt;ColumnDefinition\u0026gt; _columns = []; internal void AddColumn(ColumnDefinition column) { _columns.Add(column); StateHasChanged(); } } En het child component dat zichzelf registreert bij de parent:\n@code { [CascadingParameter] public DataGrid? ParentGrid { get; set; } [Parameter] public string Title { get; set; } = string.Empty; [Parameter] public RenderFragment\u0026lt;object\u0026gt;? CellTemplate { get; set; } protected override void OnInitialized() { ParentGrid?.AddColumn(this); } } Het weet dat je CascadingValue gebruikt om de parent-referentie door te geven, en het plaatst de kolomregistratie in OnInitialized — niet in OnParametersSet, want dat zou dubbele registraties veroorzaken bij re-renders.\nEvent handling en state management Blazor\u0026rsquo;s event-model is een van die gebieden waar Claude Code je beschermt tegen subtiele bugs. Vraag het om een zoekcomponent te bouwen met debounced input:\n\u0026lt;input type=\u0026#34;text\u0026#34; @oninput=\u0026#34;OnSearchInput\u0026#34; placeholder=\u0026#34;Zoeken...\u0026#34; /\u0026gt; @if (_isSearching) { \u0026lt;span class=\u0026#34;spinner\u0026#34;\u0026gt;Bezig met zoeken...\u0026lt;/span\u0026gt; } @code { [Parameter] public EventCallback\u0026lt;string\u0026gt; OnSearch { get; set; } private CancellationTokenSource? _debounceToken; private bool _isSearching; private async Task OnSearchInput(ChangeEventArgs e) { var query = e.Value?.ToString() ?? string.Empty; _debounceToken?.Cancel(); _debounceToken = new CancellationTokenSource(); try { await Task.Delay(300, _debounceToken.Token); _isSearching = true; StateHasChanged(); await OnSearch.InvokeAsync(query); } catch (TaskCanceledException) { } finally { _isSearching = false; } } } Het gebruikt @oninput in plaats van @onchange (zodat de zoekactie afgaat terwijl je typt), voegt debouncing toe met cancellation, en roept StateHasChanged expliciet aan omdat de spinner-state midden in de methode verandert.\nFormulieren en validatie Formulieren bouwen in Blazor betekent EditForm, DataAnnotationsValidator en een serie componenten die samenwerken. Claude Code handelt de hele keten af.\n\u0026ldquo;Maak een contactformulier met naam, e-mail en bericht. E-mail moet gevalideerd worden. Toon validatiemeldingen inline.\u0026rdquo;\n\u0026lt;EditForm Model=\u0026#34;@_model\u0026#34; OnValidSubmit=\u0026#34;HandleSubmit\u0026#34;\u0026gt; \u0026lt;DataAnnotationsValidator /\u0026gt; \u0026lt;div class=\u0026#34;form-group\u0026#34;\u0026gt; \u0026lt;label for=\u0026#34;name\u0026#34;\u0026gt;Naam\u0026lt;/label\u0026gt; \u0026lt;InputText id=\u0026#34;name\u0026#34; @bind-Value=\u0026#34;_model.Name\u0026#34; class=\u0026#34;form-control\u0026#34; /\u0026gt; \u0026lt;ValidationMessage For=\u0026#34;@(() =\u0026gt; _model.Name)\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;form-group\u0026#34;\u0026gt; \u0026lt;label for=\u0026#34;email\u0026#34;\u0026gt;E-mail\u0026lt;/label\u0026gt; \u0026lt;InputText id=\u0026#34;email\u0026#34; @bind-Value=\u0026#34;_model.Email\u0026#34; class=\u0026#34;form-control\u0026#34; /\u0026gt; \u0026lt;ValidationMessage For=\u0026#34;@(() =\u0026gt; _model.Email)\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;form-group\u0026#34;\u0026gt; \u0026lt;label for=\u0026#34;message\u0026#34;\u0026gt;Bericht\u0026lt;/label\u0026gt; \u0026lt;InputTextArea id=\u0026#34;message\u0026#34; @bind-Value=\u0026#34;_model.Message\u0026#34; class=\u0026#34;form-control\u0026#34; /\u0026gt; \u0026lt;ValidationMessage For=\u0026#34;@(() =\u0026gt; _model.Message)\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button type=\u0026#34;submit\u0026#34; disabled=\u0026#34;@_submitting\u0026#34;\u0026gt; @(_submitting ? \u0026#34;Verzenden...\u0026#34; : \u0026#34;Verstuur\u0026#34;) \u0026lt;/button\u0026gt; \u0026lt;/EditForm\u0026gt; @code { private ContactFormModel _model = new(); private bool _submitting; private async Task HandleSubmit() { _submitting = true; await ContactService.SubmitAsync(_model); _model = new ContactFormModel(); _submitting = false; } } Het weet dat je OnValidSubmit moet gebruiken (niet OnSubmit), combineert DataAnnotationsValidator met ValidationMessage, en reset het formulier door een nieuw model-object te maken — de Blazor-manier, niet de \u0026ldquo;alle velden handmatig leegmaken\u0026rdquo;-manier.\nHerbruikbare componenten extraheren Een van mijn favoriete toepassingen: vraag Claude Code om bestaande pagina\u0026rsquo;s te refactoren naar herbruikbare componenten. Het leest je pagina, herkent herhaalde patronen en extraheert ze.\n\u0026ldquo;Bekijk de admin-pagina\u0026rsquo;s in /Pages/Admin/. Haal gemeenschappelijke patronen eruit en maak er gedeelde componenten van.\u0026rdquo;\nClaude Code leest alle admin-pagina\u0026rsquo;s, ziet dat elke pagina een header heeft met een titel, een breadcrumb en een actieknop. Het maakt een PageHeader.razor component en vervangt de gedupliceerde markup in alle pagina\u0026rsquo;s — inclusief het bijwerken van de parameter-bindings.\nDat soort cross-file refactoring is waar volledige codebase-toegang het verschil maakt. Een chat-gebaseerde tool kan dit niet, omdat die niet alle bestanden tegelijk ziet.\nWaar Claude Code moeite mee heeft bij Blazor Ik wil eerlijk zijn over de beperkingen.\nRender lifecycle edge cases. Claude Code plaatst soms logica in OnInitialized terwijl het in OnAfterRender hoort, vooral bij alles wat de DOM raakt of vereist dat het component volledig gerenderd is. Als je JS interop doet of elementgroottes meet, check dan welke lifecycle-methode het gekozen heeft.\nJavaScript interop. Claude Code kan IJSRuntime-calls schrijven, maar de JavaScript-kant klopt niet altijd. Het genereert soms een .js-bestand dat niet past bij hoe jij je JS-modules hebt opgezet, of vergeet dat IJSObjectReference disposed moet worden.\nStreaming rendering en enhanced navigation. De nieuwere .NET 8 render-modes — @rendermode InteractiveServer, StreamRendering, enhanced nav — zijn nog lastig. Claude Code mixt soms patronen van verschillende render-modes of genereert code die volledige interactiviteit veronderstelt terwijl je in statische SSR zit.\nCSS isolation-eigenaardigheden. Het kent .razor.css scoped styles, maar genereert af en toe selectors die niet werken met Blazor\u0026rsquo;s scoping-mechanisme (zoals het direct targeten van child components).\nNiets hiervan is een dealbreaker. Maar je moet weten waar je twee keer moet kijken.\nTips voor het prompten van Claude Code voor Blazor Na maanden Blazor componenten bouwen met Claude Code, dit is wat werkt:\nGeef de render-mode aan. Begin met \u0026ldquo;Ik gebruik Interactive Server rendering\u0026rdquo; of \u0026ldquo;Dit is een statische SSR-pagina.\u0026rdquo; Het verandert de gegenereerde code aanzienlijk.\nNoem je .NET-versie. Blazor in .NET 6 en .NET 8 zijn behoorlijk verschillend. Zeg \u0026ldquo;.NET 8\u0026rdquo; van tevoren.\nBeschrijf de component tree. \u0026ldquo;Dit component is een child van MainLayout en ontvangt de huidige gebruiker via CascadingParameter\u0026rdquo; geeft Claude Code de context die het nodig heeft.\nVraag ook om de modelklasse. Bij formulieren: zeg \u0026ldquo;Genereer ook het model met DataAnnotations.\u0026rdquo; Anders krijg je een component dat verwijst naar een klasse die nog niet bestaat.\nGebruik je CLAUDE.md. Documenteer je component-conventies, je CSS-framework (Bootstrap? Tailwind?) en je projectstructuur. Claude Code gebruikt dit iedere keer.\nProbeer het zelf De volgende keer dat je een Blazor component nodig hebt, probeer dit:\nclaude Beschrijf het component — wat het toont, welke parameters het heeft, hoe het samenwerkt met zijn parent. Wees specifiek over de render-mode en .NET-versie. Laat Claude Code de Razor-syntax, de lifecycle-methodes en de parameter-bedrading afhandelen.\nJe zult merken dat het meest vervelende deel van Blazor-development — onthouden welke lifecycle-methode je moet gebruiken, de event callback-signatures goed krijgen, cascading parameters aansluiten — precies het deel is waar Claude Code het beste in is.\nBouw je Blazor componenten met Claude Code? Ik hoor graag welke patronen voor jou werken. Stuur me een bericht via de contactpagina.\n","permalink":"https://renedekkers.nl/nl/posts/blazor-development-met-claude-code/","summary":"Blazor mixt C# en HTML op een manier waar de meeste AI-tools niet mee overweg kunnen. Claude Code begrijpt het wel — en dat verandert hoe je componenten bouwt.","title":"Claude Code voor Blazor development — componenten bouwen met AI"},{"content":"Als je met .NET werkt, werk je met Entity Framework. Het zit in vrijwel elk project — van kleine API\u0026rsquo;s tot enterprise-applicaties met tientallen entiteiten en complexe relaties.\nEn als je eerlijk bent, is veel EF Core-werk gewoon saai. Entity-configuraties schrijven. Migrations aanmaken. LINQ queries in elkaar puzzelen die ook nog eens efficiënte SQL opleveren. Het is niet moeilijk, maar het kost tijd en aandacht.\nClaude Code begrijpt je DbContext. En dat verandert je workflow aanzienlijk.\nEen nieuwe entiteit toevoegen Stel, je moet een Order-entiteit toevoegen aan je project. Je hebt al een AppDbContext met een paar entiteiten. Je zegt tegen Claude Code:\n\u0026ldquo;Voeg een Order-entiteit toe met Id, CustomerId (FK naar Customer), OrderDate, Status (enum) en een collectie OrderLines met ProductId, Quantity en UnitPrice.\u0026rdquo;\nClaude Code leest je bestaande DbContext, ziet hoe je je entiteiten structureert, en genereert code die bij je conventies past. Gebruik je aparte configuratieklassen? Dan maakt het er een aan. Configureer je alles in OnModelCreating? Dan komt het daar.\npublic class Order { public int Id { get; set; } public int CustomerId { get; set; } public Customer Customer { get; set; } = null!; public DateTime OrderDate { get; set; } public OrderStatus Status { get; set; } public List\u0026lt;OrderLine\u0026gt; Lines { get; set; } = new(); } public class OrderLine { public int Id { get; set; } public int OrderId { get; set; } public Order Order { get; set; } = null!; public int ProductId { get; set; } public Product Product { get; set; } = null!; public int Quantity { get; set; } public decimal UnitPrice { get; set; } } En de configuratie:\npublic class OrderConfiguration : IEntityTypeConfiguration\u0026lt;Order\u0026gt; { public void Configure(EntityTypeBuilder\u0026lt;Order\u0026gt; builder) { builder.HasOne(o =\u0026gt; o.Customer) .WithMany(c =\u0026gt; c.Orders) .HasForeignKey(o =\u0026gt; o.CustomerId); builder.HasMany(o =\u0026gt; o.Lines) .WithOne(l =\u0026gt; l.Order) .HasForeignKey(l =\u0026gt; l.OrderId); builder.Property(o =\u0026gt; o.Status) .HasConversion\u0026lt;string\u0026gt;(); builder.Property(o =\u0026gt; o.OrderDate) .HasDefaultValueSql(\u0026#34;GETUTCDATE()\u0026#34;); } } Daarna draait het dotnet ef migrations add AddOrderEntity en controleert of de build slaagt. Eén prompt, drie bestanden, een werkende migration.\nComplexe LINQ queries Dit is waar Claude Code echt uitblinkt. In plaats van puzzelen met LINQ-syntax beschrijf je gewoon wat je nodig hebt in gewoon Nederlands.\n\u0026ldquo;Geef alle orders met meer dan 3 items die deze maand zijn verzonden, gegroepeerd per klant, met het totaalbedrag per klant.\u0026rdquo;\nClaude Code leest je entiteiten, begrijpt de relaties en schrijft:\nvar result = await dbContext.Orders .Where(o =\u0026gt; o.Status == OrderStatus.Shipped \u0026amp;\u0026amp; o.OrderDate.Month == DateTime.UtcNow.Month \u0026amp;\u0026amp; o.OrderDate.Year == DateTime.UtcNow.Year) .Where(o =\u0026gt; o.Lines.Count \u0026gt; 3) .GroupBy(o =\u0026gt; new { o.CustomerId, o.Customer.Name }) .Select(g =\u0026gt; new { CustomerId = g.Key.CustomerId, CustomerName = g.Key.Name, OrderCount = g.Count(), TotalAmount = g.SelectMany(o =\u0026gt; o.Lines) .Sum(l =\u0026gt; l.Quantity * l.UnitPrice) }) .OrderByDescending(x =\u0026gt; x.TotalAmount) .ToListAsync(); Dat is een query waar ik normaal tien minuten over doe. Claude Code levert het in seconden — en omdat het je daadwerkelijke entity-klassen leest, kloppen de property-namen en relaties.\nMigration management Migrations zijn een van die dingen die simpel lijken totdat ze dat niet meer zijn. Een kolom hernoemen? EF Core genereert standaard een drop-and-add. Een tabel opsplitsen? De migration heeft handmatige aanpassingen nodig.\nClaude Code helpt op twee manieren. Ten eerste genereert het het migration-commando voor je:\ndotnet ef migrations add RenameCustomerEmail \\ --project src/MyApp.Infrastructure \\ --startup-project src/MyApp.Api Ten tweede — en dat is belangrijker — reviewt het de gegenereerde migration en signaleert problemen. Als EF Core een destructieve migration genereert (een kolom droppen in plaats van hernoemen), dan waarschuwt Claude Code je en stelt de juiste aanpak voor:\nmigrationBuilder.RenameColumn( name: \u0026#34;EmailAddress\u0026#34;, table: \u0026#34;Customers\u0026#34;, newName: \u0026#34;Email\u0026#34;); Dit voorkomt dat je dataverlies naar productie deployt.\nPerformance-valkuilen Vraag Claude Code om je EF Core-queries te reviewen en het signaleert veelvoorkomende performance-problemen die je tijdens development makkelijk over het hoofd ziet.\nN+1 queries. Je laadt orders in een loop en benadert order.Customer.Name zonder Include. Claude Code herkent het patroon en stelt eager loading voor:\n// Voorheen: N+1 probleem var orders = await dbContext.Orders.ToListAsync(); foreach (var order in orders) Console.WriteLine(order.Customer.Name); // Extra query per order // Daarna: één query var orders = await dbContext.Orders .Include(o =\u0026gt; o.Customer) .ToListAsync(); Ontbrekende indexes. Als je regelmatig filtert op OrderDate of Status, suggereert Claude Code een index in je configuratie:\nbuilder.HasIndex(o =\u0026gt; new { o.Status, o.OrderDate }); Onnodige tracking. Voor read-only queries adviseert het AsNoTracking() — een kleine aanpassing die een groot verschil kan maken in performance.\nDbContext-configuratie De Fluent API van EF Core is krachtig maar verbose. Relaties, value conversions, owned types — er is veel te configureren, en de documentatie heeft niet altijd precies het voorbeeld dat je nodig hebt.\nClaude Code gaat hier goed mee om. Heb je een value conversion nodig voor een strongly-typed ID? Een owned type voor een adres? Een table-per-hierarchy mapping met een discriminator?\n// Strongly-typed ID conversie builder.Property(o =\u0026gt; o.Id) .HasConversion( id =\u0026gt; id.Value, value =\u0026gt; new OrderId(value)); // Owned type voor Address builder.OwnsOne(c =\u0026gt; c.ShippingAddress, address =\u0026gt; { address.Property(a =\u0026gt; a.Street).HasMaxLength(200); address.Property(a =\u0026gt; a.City).HasMaxLength(100); address.Property(a =\u0026gt; a.ZipCode).HasMaxLength(10); }); Beschrijf gewoon wat je wilt en Claude Code schrijft de configuratie die past bij de rest van je DbContext.\nWaar Claude Code de mist in gaat Laten we eerlijk zijn. Claude Code is niet perfect met EF Core, en er zijn een paar gebieden waar je moet opletten.\nMigration-bestanden direct bewerken. Soms probeert Claude Code een gegenereerd migration-bestand aan te passen. Laat dat niet toe. Migrations zijn gegenereerde code — als je wijzigingen nodig hebt, pas de entiteit of configuratie aan en genereer een nieuwe migration.\nComplexe inheritance mapping. TPH, TPT, TPC — Claude Code haalt de configuratiepatronen soms door elkaar, vooral bij table-per-concrete-type mappings. Controleer altijd de gegenereerde SQL.\nDatabase provider-specifiek. Claude Code suggereert soms SQL Server-syntax terwijl je PostgreSQL gebruikt, of andersom. Als je je provider noemt in je CLAUDE.md-bestand, gebeurt dit minder vaak.\nBestaande migration-historie. Claude Code weet niet altijd welke migrations al zijn toegepast op je database. Het kan een migration voorstellen die conflicteert met je huidige staat. Draai altijd eerst dotnet ef migrations list.\nPraktische tips Na maanden Claude Code gebruiken met EF Core zijn dit de dingen die het grootste verschil maken:\nDocumenteer je database provider in CLAUDE.md. Schrijf \u0026ldquo;We gebruiken PostgreSQL met Npgsql\u0026rdquo; of \u0026ldquo;SQL Server met EF Core 8.\u0026rdquo; Dit voorkomt verkeerde dialect-suggesties.\nLaat Claude Code migrations genereren, maar review ze altijd. Draai dotnet ef migrations script om de SQL te zien voordat je toepast.\nVraag om uitleg bij queries. Zeg \u0026ldquo;leg deze LINQ query uit en welke SQL het oplevert\u0026rdquo; — Claude Code loopt het stap voor stap met je door.\nGebruik het voor refactoring. \u0026ldquo;Splits deze God-DbContext op in aparte bounded contexts\u0026rdquo; is precies het soort saaie, context-intensieve werk waar Claude Code in uitblinkt.\nLaat het niet aan bestaande migration-bestanden komen. Dit is de nummer één regel. Migrations zijn append-only historie.\nProbeer het zelf De volgende keer dat je naar een complexe LINQ query staart of weer een entity-configuratie aan het schrijven bent, probeer dit:\nclaude Beschrijf wat je nodig hebt in gewoon Nederlands. Laat Claude Code je DbContext lezen, je conventies begrijpen en code genereren die past. Je zult merken dat de repetitieve delen van EF Core — de delen die tijd kosten maar geen creativiteit — gewoon verdwijnen.\nWat overblijft is het deel dat ertoe doet: het ontwerpen van je datamodel.\n","permalink":"https://renedekkers.nl/nl/posts/entity-framework-en-claude-code/","summary":"Entity Framework zit in vrijwel elk .NET project. Migrations zijn saai, LINQ kan lastig zijn, en DbContext-configuratie is verbose. Claude Code begrijpt je datamodel en doet het zware werk.","title":"Entity Framework en Claude Code — migrations, queries en DbContext"},{"content":"Je kent die service. Vierhonderd regels business logic, nul tests, en een backlog-item dat al sinds Q3 zegt \u0026ldquo;unit tests toevoegen.\u0026rdquo; Niemand pakt het op, want tests achteraf schrijven is vervelend werk. Je weet al wat de code doet — waarom zou je een halve dag besteden aan het bewijzen ervan?\nTot iemand een method signature wijzigt en de hele orderstroom in productie omvalt. Dan denk je: die tests hadden dit gevangen.\nClaude Code kan ze voor je schrijven. Niet perfecte tests. Maar een stevig startpunt waar je handmatig uren over had gedaan.\nStap 1: Laat Claude Code analyseren wat getest moet worden Start Claude Code in je project en wijs het naar de class:\n\u0026ldquo;Analyseer OrderService.cs en vertel me welke testcases ik nodig heb. Schrijf nog geen code — geef alleen de lijst.\u0026rdquo;\nClaude Code leest het bestand, volgt de dependencies, en komt terug met zoiets:\nPlaceOrder — happy path met geldige order PlaceOrder — gooit exception bij null customer PlaceOrder — gooit exception bij lege orderitems PlaceOrder — past korting toe als totaal boven drempel komt PlaceOrder — roept repository precies één keer aan CalculateTotal — telt items correct op CalculateTotal — handelt items met hoeveelheid nul af Dit is de stap die de meeste mensen overslaan. Ze springen direct naar \u0026ldquo;schrijf tests.\u0026rdquo; Maar eerst een testplan hebben betekent dat je kunt beoordelen of de cases kloppen voordat er code gegenereerd wordt.\nStap 2: Genereer de eerste testclass Nu vraag je Claude Code om de tests te schrijven:\n\u0026ldquo;Maak een xUnit-testclass voor OrderService. Gebruik FluentAssertions voor assertions en NSubstitute voor mocking. Begin met de happy path en de validatie-cases.\u0026rdquo;\nClaude Code genereert zoiets als dit:\npublic class OrderServiceTests { private readonly IOrderRepository _repository; private readonly IDiscountCalculator _discountCalculator; private readonly OrderService _sut; public OrderServiceTests() { _repository = Substitute.For\u0026lt;IOrderRepository\u0026gt;(); _discountCalculator = Substitute.For\u0026lt;IDiscountCalculator\u0026gt;(); _sut = new OrderService(_repository, _discountCalculator); } [Fact] public async Task PlaceOrder_WithValidOrder_SavesOrderToRepository() { // Arrange var customer = new Customer { Id = 1, Name = \u0026#34;Acme Corp\u0026#34; }; var items = new List\u0026lt;OrderItem\u0026gt; { new() { ProductId = 10, Quantity = 2, UnitPrice = 25.00m } }; // Act await _sut.PlaceOrder(customer, items); // Assert await _repository.Received(1).Save(Arg.Is\u0026lt;Order\u0026gt;(o =\u0026gt; o.CustomerId == 1 \u0026amp;\u0026amp; o.Items.Count == 1)); } [Fact] public async Task PlaceOrder_WithNullCustomer_ThrowsArgumentException() { // Arrange var items = new List\u0026lt;OrderItem\u0026gt; { new() { ProductId = 1, Quantity = 1, UnitPrice = 10m } }; // Act var act = () =\u0026gt; _sut.PlaceOrder(null!, items); // Assert await act.Should().ThrowAsync\u0026lt;ArgumentException\u0026gt;() .WithMessage(\u0026#34;*customer*\u0026#34;); } } Let op de Arrange-Act-Assert structuur, de _sut-naamconventie, en de leesbare FluentAssertions-syntax. Claude Code pikt de patronen van je project op als je een CLAUDE.md hebt die je testconventies beschrijft.\nStap 3: Theory-tests voor meerdere scenario\u0026rsquo;s Voor cases met variërende input houdt [Theory] met [InlineData] het overzichtelijk:\n\u0026ldquo;Voeg een Theory-test toe voor CalculateTotal met verschillende combinaties van hoeveelheden en prijzen.\u0026rdquo;\n[Theory] [InlineData(1, 10.00, 10.00)] [InlineData(3, 15.50, 46.50)] [InlineData(0, 99.99, 0.00)] public void CalculateTotal_WithVariousItems_ReturnsExpectedTotal( int quantity, decimal unitPrice, decimal expectedTotal) { // Arrange var items = new List\u0026lt;OrderItem\u0026gt; { new() { Quantity = quantity, UnitPrice = unitPrice } }; // Act var result = _sut.CalculateTotal(items); // Assert result.Should().Be(expectedTotal); } Hier bespaart Claude Code echt tijd. Vijf [InlineData]-variaties handmatig uitschrijven is saai. Het patroon in één zin beschrijven is snel.\nStap 4: NSubstitute voor dependencies De mock-setup is waar het interessant wordt. Claude Code begrijpt constructor injection en zet de substitutes correct op:\n[Fact] public async Task PlaceOrder_WhenTotalExceedsThreshold_AppliesDiscount() { // Arrange var customer = new Customer { Id = 1, Name = \u0026#34;Big Spender\u0026#34; }; var items = new List\u0026lt;OrderItem\u0026gt; { new() { ProductId = 1, Quantity = 10, UnitPrice = 100.00m } }; _discountCalculator.Calculate(Arg.Any\u0026lt;decimal\u0026gt;()) .Returns(50.00m); // Act await _sut.PlaceOrder(customer, items); // Assert await _repository.Received(1).Save(Arg.Is\u0026lt;Order\u0026gt;(o =\u0026gt; o.DiscountApplied == 50.00m)); } De Arg.Any\u0026lt;\u0026gt;() en .Returns()-syntax is NSubstitute op z\u0026rsquo;n best. Claude Code gebruikt de juiste patronen zonder dat je het mocking-framework hoeft uit te leggen.\nStap 5: Vraag naar ontbrekende edge cases Dit is mijn favoriete truc. Nadat de eerste tests zijn geschreven:\n\u0026ldquo;Bekijk de testclass en de broncode. Welke edge cases mis ik?\u0026rdquo;\nClaude Code vindt typisch dingen als:\nWat gebeurt er als de repository een exception gooit? Wat als de kortingscalculator een negatieve waarde teruggeeft? Wat bij gelijktijdige aanroepen met dezelfde klant? Wat als de orderitems-lijst null is (niet leeg)? Sommige hiervan zijn oprecht nuttig. Andere zijn over-engineering. Jij bepaalt welke je houdt. Maar het identificeren van edge cases doet Claude Code goed — het is systematischer dan wij mensen vaak zijn.\nStap 6: Uitvoeren en itereren Nu draai je de tests:\ndotnet test Een paar zullen falen. Misschien ging Claude Code ervan uit dat een methode Task teruggeeft terwijl het Task\u0026lt;Order\u0026gt; is. Misschien klopt een property-naam niet helemaal. Dat is normaal.\nVertel Claude Code wat er faalde, en het fixt de test. De iteratieloop is snel: draaien, fixen, draaien, fixen. Binnen een paar rondes heb je een groene suite.\nWanneer Claude Code slechte tests schrijft Eerlijk zijn hierover. Claude Code produceert slechte tests op voorspelbare manieren:\nImplementatiedetails testen. Het verifieert graag dat een methode met specifieke argumenten is aangeroepen. Dat levert fragiele tests op die breken zodra je de interne werking refactort, ook al is het gedrag niet veranderd. Als je Received(1) in elke test ziet staan, duw terug.\nTe veel mocks. Als je service vijf dependencies heeft en elke test alle vijf substitutes opzet, dan zeggen de tests je iets — maar over het ontwerp, niet over de tests. Claude Code zal niet zeggen \u0026ldquo;deze class heeft te veel verantwoordelijkheden.\u0026rdquo; Dat is jouw inschattingsvermogen.\nTriviale assertions. Testen dat een property die je net hebt gezet de waarde heeft die je eraan gaf. Dat is geen test, dat is een spiegel. Verwijder deze.\nCopy-paste patronen. Claude Code genereert soms tien tests die voor 90% identiek zijn. Vraag het om de gedeelde setup te refactoren naar helper-methodes.\nDe oplossing is simpel: review de tests zoals je elke code zou reviewen. Merge geen AI-gegenereerde tests zonder ze gelezen te hebben.\nTips voor betere prompts Een paar dingen die ik heb geleerd over het prompten voor tests:\n\u0026ldquo;Test het gedrag, niet de implementatie\u0026rdquo; — zeg dit expliciet. Het vermindert mock-zware tests. \u0026ldquo;Eén assertion per test\u0026rdquo; — dwingt gerichte tests af in plaats van testmethodes die zeven dingen controleren. \u0026ldquo;Gebruik beschrijvende testnamen die het scenario uitleggen\u0026rdquo; — je bedankt jezelf als een test over zes maanden faalt in CI. \u0026ldquo;Test private methodes niet direct\u0026rdquo; — Claude Code probeert soms reflection te gebruiken. Stop dat. Beschrijf je conventies in CLAUDE.md — als je een naampatroon gebruikt zoals MethodNaam_Scenario_VerwachtResultaat, vertel het Claude Code één keer en het volgt het overal. Begin vandaag Pak een service zonder tests. Open Claude Code en zeg:\n\u0026ldquo;Analyseer deze class en schrijf xUnit-tests met FluentAssertions en NSubstitute. Test gedrag, niet implementatie. Gebruik Arrange-Act-Assert.\u0026rdquo;\nReview wat er terugkomt. Verwijder de slechte tests. Houd de goede. Draai dotnet test.\nJe gaat van nul naar 80% coverage in een middag. De laatste 20% — de edge cases, de integratiescenario\u0026rsquo;s, de dingen die menselijk oordeel vereisen — dat blijft jouw werk. Maar de sleur van de eerste 80% schrijven hoeft niet meer.\n","permalink":"https://renedekkers.nl/nl/posts/unit-tests-schrijven-met-claude-code/","summary":"Je hebt een service zonder tests. Op de backlog staat \u0026rsquo;tests toevoegen\u0026rsquo; maar niemand pakt het op. Zo helpt Claude Code je van nul naar volledige coverage.","title":"Unit tests schrijven met Claude Code — van nul naar volledige coverage"},{"content":"Je hebt Claude Code geinstalleerd. Je terminal staat open in de root van een .NET solution. De cursor knippert. En nu?\nIk weet nog hoe mijn eerste sessie ging. Geen idee waar ik moest beginnen. Moest ik iets vragen? Een opdracht geven? Eerst mijn project uitleggen? Het voelde alsof er een nieuwe collega naast me zat die niets van mijn codebase wist — alleen kan deze collega je hele project in seconden lezen.\nDit is wat ik graag had geweten voor dat eerste uur.\nStap 1: Laat Claude Code verkennen Start Claude Code in je terminal:\nclaude Voer dan het /init commando uit. Dit laat Claude Code je solution scannen en een CLAUDE.md bestand aanmaken — een soort geheugenbestand voor je project.\n\u0026gt; /init Claude Code leest je .sln bestand, ontdekt de projecten, bekijkt de dependencies in je .csproj bestanden en analyseert je mappenstructuur. Binnen een minuut heeft het een compleet beeld van je solution.\nEen typische solution met een Web API, een domeinlaag en een infrastructuurproject? Claude Code pikt dat allemaal op. De projectreferenties, de NuGet packages, de patronen die je gebruikt — het leest het gewoon.\nStap 2: Vraag wat het ziet Deze stap slaan de meeste mensen over, maar het is de waardevolste om vertrouwen op te bouwen. Vraag Claude Code wat het heeft gevonden:\n\u0026ldquo;Wat doet deze solution? Welke patronen en conventies worden er gebruikt?\u0026rdquo;\nClaude Code geeft je dan zoiets als:\n\u0026ldquo;Dit is een ASP.NET Core Web API met Clean Architecture. Het Domain project bevat entities en interfaces, Infrastructure regelt EF Core en externe services, en het API project heeft controllers met MediatR voor CQRS. Er wordt FluentValidation gebruikt voor request-validatie en AutoMapper voor object mapping.\u0026rdquo;\nAls dit klopt — en in mijn ervaring klopt het meestal — dan weet je dat Claude Code je codebase goed genoeg begrijpt om nuttige wijzigingen te maken. Mist het iets? Dan is dat je signaal om context toe te voegen aan het CLAUDE.md bestand.\nStap 3: Maak je eerste wijziging Begin klein. Vraag Claude Code niet om je architectuur te herontwerpen op dag een. Kies iets concreets met laag risico.\nEen goed eerste taakje: voeg een eenvoudig health check endpoint toe.\n\u0026ldquo;Voeg een health check endpoint toe op /health dat de applicatienaam en versie teruggeeft.\u0026rdquo;\nClaude Code maakt het endpoint aan en respecteert daarbij de patronen die het in je project heeft gevonden. Gebruik je minimal APIs? Dan krijg je een MapGet. Gebruik je controllers? Dan krijg je een controller. Het past zich aan aan wat er al staat.\napp.MapGet(\u0026#34;/health\u0026#34;, () =\u0026gt; Results.Ok(new { Status = \u0026#34;Healthy\u0026#34;, Application = \u0026#34;MyApi\u0026#34;, Version = typeof(Program).Assembly .GetCustomAttribute\u0026lt;AssemblyInformationalVersionAttribute\u0026gt;()? .InformationalVersion ?? \u0026#34;unknown\u0026#34; })); Het belangrijkste hier: bekijk de code voordat je het accepteert. Claude Code vraagt bevestiging voordat het bestanden wijzigt. Lees wat het voorstelt. Dezelfde werkwijze als bij een collega — je merged hun PR ook niet blindelings.\nStap 4: Draai de tests Na een wijziging vraag je Claude Code om te verifieren:\n\u0026ldquo;Draai de tests om te controleren of alles nog werkt.\u0026rdquo;\nClaude Code voert dotnet test uit en toont de output. Als er iets faalt, leest het de testoutput en probeert het probleem op te lossen. Deze feedbackloop — wijzigen, testen, fixen — is waar Claude Code echt tot zijn recht komt.\ndotnet test --verbosity normal Heeft je solution nog geen tests? Dat is eigenlijk een prima tweede opdracht:\n\u0026ldquo;Kun je een test schrijven voor het health check endpoint dat we net hebben aangemaakt?\u0026rdquo;\nJe krijgt een nette integratie-test met WebApplicationFactory, in het testframework dat je al gebruikt — xUnit, NUnit of MSTest.\nStap 5: Richt je CLAUDE.md in Het /init commando heeft een basis CLAUDE.md bestand aangemaakt. Nu is het tijd om het echt van jou te maken. Open het bestand en voeg de dingen toe die Claude Code niet zelf kan ontdekken:\nHoe je het project lokaal draait (connection strings, Docker containers, etc.) Naamconventies die je team hanteert Dingen om te vermijden (\u0026ldquo;gebruik geen Repository pattern, wij werken met CQRS\u0026rdquo;) Het testcommando en hoe je specifieke testprojecten draait ## Commands - Run: `dotnet run --project src/MyApi` - Test: `dotnet test tests/MyApi.Tests` - Database: `docker compose up -d` (start PostgreSQL op poort 5432) ## Conventions - Gebruik MediatR handlers, geen service classes - Alle request validatie via FluentValidation - Geen business logic in controllers Dit bestand is de grootste hefboom voor goede resultaten met Claude Code. Hoe specifieker je bent over je conventies, hoe beter Claude Code ze volgt.\nTips voor je eerste sessie Wel proberen:\nVraag Claude Code om bestaande code uit te leggen die je al tijden wilt begrijpen Fix een kleine bug die al een tijdje in de backlog staat Voeg een ontbrekende test toe voor een bestaande feature Laat een DTO of mapping genereren die vervelend is om met de hand te schrijven Nog niet proberen:\nGrote architectuurwijzigingen op dag een Een complete feature laten bouwen zonder tussenstappen te reviewen Vage instructies geven als \u0026ldquo;maak dit beter\u0026rdquo; De gegenereerde code niet bekijken — altijd reviewen Eerlijk over beperkingen Een paar dingen om te weten voordat je begint:\nClaude Code draait je applicatie niet. Het kan dotnet build en dotnet test uitvoeren, maar het start je API niet op en raakt geen endpoints aan. Integratietesten met een draaiende server is nog steeds jouw taak.\nHet kan fouten maken. Vooral bij complexe businesslogica of domeinspecifieke regels die het nog niet eerder heeft gezien. De code die het genereert is een startpunt, geen eindproduct.\nHet werkt het best met expliciete instructies. \u0026ldquo;Voeg een health check endpoint toe\u0026rdquo; werkt beter dan \u0026ldquo;verbeter de API.\u0026rdquo; Hoe concreter je bent, hoe beter het resultaat.\nGrote solutions hebben even nodig. Heeft je solution 50+ projecten? Dan duurt de initiele scan wat langer. Dat is normaal. Dit hoeft maar een keer per sessie.\nProbeer het zelf npm install -g @anthropic-ai/claude-code cd jouw-dotnet-solution/ claude Voer /init uit. Vraag wat het ziet. Maak een kleine wijziging. Draai de tests. Dat is je eerste uur.\nDaarna heb je een gevoel voor wat Claude Code kan — en belangrijker, hoe het past in de manier waarop je nu al werkt. Het vervangt je vakkennis niet. Het is een tool die het saaie werk wegneemt, zodat jij je kunt richten op de interessante delen.\n","permalink":"https://renedekkers.nl/nl/posts/eerste-uur-met-claude-code-in-dotnet/","summary":"Je hebt Claude Code geinstalleerd. Je hebt een .NET solution open in je terminal. Wat nu? Een stap-voor-stap walkthrough van je eerste echte sessie.","title":"Je eerste uur met Claude Code in een .NET project"},{"content":"Het is maandagochtend. In Teams staat een bericht van de tester: \u0026ldquo;De registratie geeft een 500 sinds het laatste deployment.\u0026rdquo;\nJe opent de logging en ziet een NullReferenceException met een stacktrace die drie lagen diep gaat. Ergens in de validatielogica van de RegistrationService. Vrijdagmiddag is er een PR gemerged die het registratieproces uitbreidde met een bedrijfsnaam-veld. De tests waren groen. Maar in productie gaat het mis.\nNu begint het zoekwerk.\nDe oude manier Je opent de stacktrace en begint bestanden te openen. RegistrationService.cs — 200 regels. RegisterUserCommand.cs — het request model. RegisterUserValidator.cs — de FluentValidation rules. UserMappingProfile.cs — de AutoMapper config.\nJe leest, scrollt, vergelijkt. Na twintig minuten heb je een vermoeden. Je opent ChatGPT, kopieert de relevante stukken code erin — drie berichten, want het past niet in één keer — en vraagt: \u0026ldquo;Zie je wat hier misgaat?\u0026rdquo;\nChatGPT zegt: \u0026ldquo;Het lijkt erop dat er een null check mist. Probeer een null-coalescing operator toe te voegen.\u0026rdquo; Technisch niet fout, maar het is een pleister. De echte oorzaak zit ergens anders.\nNa een uur heb je het gevonden. Het nieuwe CompanyName-veld zit wel in het command, maar niet in de AutoMapper profile. De mapping faalt stil, de validator krijgt een onverwacht null-object, en gooit een exception.\nMet Claude Code Zelfde bug. Maar een andere aanpak.\nJe opent je terminal in het project en start Claude Code. Je plakt de stacktrace en zegt:\n\u0026ldquo;De registratie-endpoint geeft een 500 na het laatste deployment. Dit is de exception.\u0026rdquo;\nDat is alles. Geen bestanden kopiëren, geen context opbouwen. Claude Code pakt het vanaf hier op.\nHet leest zelf Claude Code volgt de stacktrace als een landkaart. Het opent RegistrationService.cs en ziet waar de exception vandaan komt. Het leest de RegisterUserValidator.cs en ziet dat de validator een property verwacht die null is. Het opent UserMappingProfile.cs en vindt het probleem: het nieuwe CompanyName-veld ontbreekt in de mapping.\nGeen generiek advies over null checks. De daadwerkelijke oorzaak, gevonden door de code te lezen — niet door te gissen.\nHet fixt en verifieert Claude Code stelt de fix voor: de mapping uitbreiden in UserMappingProfile.cs.\nCreateMap\u0026lt;RegisterUserCommand, User\u0026gt;() .ForMember(dest =\u0026gt; dest.Email, opt =\u0026gt; opt.MapFrom(src =\u0026gt; src.Email)) .ForMember(dest =\u0026gt; dest.Name, opt =\u0026gt; opt.MapFrom(src =\u0026gt; src.Name)) .ForMember(dest =\u0026gt; dest.CompanyName, opt =\u0026gt; opt.MapFrom(src =\u0026gt; src.CompanyName)); Na je akkoord past het de code aan en runt dotnet build. Compileert. Dan dotnet test. Alle tests groen — inclusief de bestaande registratietests die nu ook het nieuwe veld meenemen.\nHet is 09:15.\nWaarom dit sneller is Het verschil zit niet in het oplossen zelf. De fix is één regel. Het verschil zit in alles wat eraan voorafgaat.\nBij de oude manier ben je het grootste deel van je tijd kwijt aan context verzamelen. Bestanden openen, code lezen, verbanden leggen, relevante stukken kopiëren naar een chatvenster. Dat is het saaie, tijdrovende deel — en precies het deel waar Claude Code je van bevrijdt.\nClaude Code leest je codebase. Het volgt de stacktrace door je bestanden heen, van endpoint naar service naar mapping. Het hoeft niet te gissen welke bestanden relevant zijn, want het kan ze gewoon openen. En omdat het je hele project kent — zeker als je een goed CLAUDE.md-bestand hebt — begrijpt het ook hoe jullie dingen doen.\nWanneer het niet werkt Eerlijkheid: Claude Code is geen wondermiddel voor elke bug.\nBugs die hun oorzaak buiten de code hebben — een verkeerde environment variable, een database die vol zit, een DNS-wijziging die nog niet is doorgevoerd — daar kan Claude Code weinig mee. Het leest code, geen infrastructuur.\nEn sommige bugs zijn inherent lastig, ook voor AI. Race conditions, timing-gerelateerde problemen, bugs die alleen onder specifieke load optreden — die vereisen een ander soort onderzoek dan code lezen.\nDe vuistregel die ik hanteer: als de oorzaak in de code zit, kan Claude Code het vinden. En dat is veruit het merendeel van de bugs die je als developer tegenkomt.\nProbeer het zelf De volgende keer dat je een stacktrace in je scherm hebt, probeer dit:\nclaude Plak de exception en zeg wat er mis is. Niet meer dan dat. Laat Claude Code het zoekwerk doen.\nJe zult merken dat het frustrerendste deel van debuggen — het eindeloos openen van bestanden en bij elkaar puzzelen van context — simpelweg verdwijnt. Wat overblijft is het deel waar jij goed in bent: beoordelen of de fix klopt.\n","permalink":"https://renedekkers.nl/nl/posts/debuggen-met-claude-code/","summary":"De meeste tijd bij debuggen gaat op aan context verzamelen: bestanden openen, code lezen, verbanden leggen. Claude Code doet dat voor je.","title":"Debuggen met Claude Code — van stacktrace naar fix"},{"content":"In mijn vorige post schreef ik over vijf principes voor effectief samenwerken met AI. Het eerste principe — eerst denken, dan coderen — verdient een eigen verhaal.\nDe meeste developers openen Claude Code, typen een prompt, en laten het bouwen. Voor een klein utilitybestandje werkt dat prima. Maar zodra je een feature van enige omvang vraagt, krijg je code die technisch klopt maar niet past. Verkeerde patronen, andere naamgeving, een structuur die niemand in je team herkent. Je bent meer tijd kwijt aan corrigeren dan je hebt bespaard.\nHet probleem is niet dat de AI slecht codeert. Het probleem is dat je hem laat rennen voordat hij weet waar hij naartoe moet. Plan Mode draait dat om. In deze modus leest Claude Code eerst je codebase, analyseert de bestaande patronen, en stelt een aanpak voor — voordat het ook maar één regel code schrijft. Het verschil: je krijgt geen code die werkt, je krijgt code die past.\nElke keer dat een AI code schrijft, neemt hij tientallen beslissingen. Welk pattern gebruik ik? Maak ik een nieuwe tabel of breid ik een bestaande uit? Polling of events? Controller of Minimal API? Zonder plan zijn die keuzes onzichtbaar — ze zitten verstopt in de code die hij oplevert. Je ontdekt ze pas als je de pull request opent. En dan is bijsturen duur: code weggooien, opnieuw beginnen, of — erger — het maar laten staan.\nPlan Mode verandert niet of de AI aannames maakt. Het verandert wanneer je ze ziet. In een plan staan die aannames zwart op wit als voorstellen. \u0026ldquo;Ik wil een nieuwe tabel aanmaken.\u0026rdquo; \u0026ldquo;Ik gebruik een controller.\u0026rdquo; Dat zijn momenten waarop jij kunt zeggen: nee, wij doen dat anders. Bijsturen in een plan kost seconden. Bijsturen in code kost uren.\nZonder plan Stel: je team bouwt een bestelplatform in ASP.NET Core. Er komt een verzoek binnen: gebruikers willen meldingen krijgen als de status van hun order verandert. Je opent Claude Code en typt:\n\u0026ldquo;Bouw een notificatiesysteem voor statuswijzigingen van orders.\u0026rdquo;\nClaude Code gaat meteen aan de slag. Geen vragen, geen analyse — het levert werkende code op. Maar kijk eens wat het oplevert:\nHet maakt een standalone NotificationService aan met eigen logica — terwijl jullie MediatR gebruiken voor command/query separation en alles via handlers loopt. Het creëert een nieuwe Notifications-tabel met eigen migratie — terwijl jullie bestaande AuditLog al events bijhoudt en uitgebreid kan worden. Het implementeert een polling-mechanisme dat elke 30 seconden de database checkt — terwijl jullie een event-based architectuur hebben met IEventHandler\u0026lt;T\u0026gt;. En dan de structuur. Jullie hele API is gebouwd met Minimal APIs. Geen controllers, geen [Route]-attributen. Maar Claude levert dit:\n[ApiController] [Route(\u0026#34;api/[controller]\u0026#34;)] public class NotificationsController : ControllerBase { private readonly NotificationService _service; public NotificationsController(NotificationService service) { _service = service; } [HttpGet(\u0026#34;{userId}\u0026#34;)] public async Task\u0026lt;IActionResult\u0026gt; GetNotifications(int userId) { var notifications = await _service.PollForUpdates(userId); return Ok(notifications); } } Technisch correct. Compileert. Doet wat je vroeg. Maar het past in geen enkel opzicht bij je project. Een collega die dit in een pull request ziet, denkt dat het uit een ander project is gekopieerd.\nHet probleem is niet dat Claude het fout doet — het weet niet wat \u0026ldquo;goed\u0026rdquo; betekent in jouw project. Zonder plan mist het de context om de juiste keuzes te maken.\nMet plan Zelfde scenario, zelfde feature — maar een andere aanpak. Dit keer geef je Claude context en vraag je het om eerst na te denken.\nPlan Mode activeren Je opent Claude Code en geeft een prompt die richting geeft:\n\u0026ldquo;We willen een notificatiesysteem bij statuswijzigingen. Ga in plan mode en analyseer eerst hoe we events afhandelen in /Application/EventHandlers. Kijk naar onze bestaande architectuur voordat je iets voorstelt.\u0026rdquo;\nClaude Code schakelt naar Plan Mode — het leest en analyseert voordat het ook maar iets schrijft.\nExplore Claude duikt in je codebase en komt terug met bevindingen:\nIk heb de codebase geanalyseerd. Dit valt op:\nIn Application/EventHandlers/ vind ik een consistent IEventHandler\u0026lt;T\u0026gt;-patroon. Alle domeinevents worden via deze interface afgehandeld. MediatR wordt gebruikt voor command/query separation — commands gaan via IRequestHandler, queries via IRequestHandler\u0026lt;TRequest, TResponse\u0026gt;. In de data layer staat een AuditLog-entity die al events bijhoudt met velden als EntityId, Type en Message. Dit wordt project-breed gebruikt voor traceability. De kern: Claude begrijpt je patronen voordat het iets voorstelt. Het heeft gelezen, niet gegist.\nHet plan Op basis van die analyse komt Claude met een plan:\nNieuw OrderStatusChangedEvent dat past in het bestaande event-patroon Een NotificationHandler die IEventHandler\u0026lt;OrderStatusChangedEvent\u0026gt; implementeert De bestaande AuditLog-entity uitbreiden met notificatievelden — geen nieuwe tabel Een Minimal API-endpoint voor het ophalen van notificaties, via een MediatR query Geen nieuw systeem, maar een uitbreiding die past bij wat er al staat.\nHet resultaat Je keurt het plan goed. Claude implementeert.\nDe event handler:\npublic class NotificationHandler : IEventHandler\u0026lt;OrderStatusChangedEvent\u0026gt; { private readonly IMediator _mediator; public NotificationHandler(IMediator mediator) { _mediator = mediator; } public async Task Handle(OrderStatusChangedEvent @event) { await _mediator.Send(new CreateAuditLogCommand { EntityId = @event.OrderId, Type = \u0026#34;notification\u0026#34;, Message = $\u0026#34;Order status changed to {@event.NewStatus}\u0026#34; }); } } Het API-endpoint:\napp.MapGet(\u0026#34;/api/notifications/{userId}\u0026#34;, async (int userId, IMediator mediator) =\u0026gt; { var result = await mediator.Send(new GetNotificationsQuery(userId)); return Results.Ok(result); }) .WithName(\u0026#34;GetNotifications\u0026#34;) .WithTags(\u0026#34;Notifications\u0026#34;); Geen controller, geen polling, geen losstaande service. Dezelfde feature als in het vorige voorbeeld — maar nu gebouwd alsof het door je eigen team is geschreven.\nClaude draait dotnet build en dotnet test — alles groen.\nNiet voor alles Plan Mode is niet voor alles. Een bugfix met een duidelijke stacktrace, een typo, een config-wijziging — daar heb je geen plan voor nodig. Dat is overhead zonder meerwaarde.\nVuistregel: als je het resultaat al voor je ziet, heb je geen plan nodig. Als je twijfelt over de aanpak, wel.\nEerst denken De AI gaat altijd keuzes maken. De vraag is niet of, maar wanneer je ze ziet. In een plan lees je: \u0026ldquo;Ik maak een nieuwe NotificationService aan.\u0026rdquo; Dan kun je zeggen: nee, gebruik onze bestaande handlers. In code lees je datzelfde besluit pas terug als een complete implementatie die je moet weggooien.\nDe beste code is code die eruitziet alsof je team het zelf heeft geschreven. Plan Mode is hoe je daar komt — niet door meer te typen, maar door de aannames zichtbaar te maken voordat ze in je codebase terechtkomen.\nDe volgende keer dat je een feature bouwt, begin met: \u0026ldquo;Ga in plan mode en analyseer eerst hoe we dit doen in de bestaande codebase.\u0026rdquo; Je zult het verschil merken.\n","permalink":"https://renedekkers.nl/nl/posts/planning-mode-eerst-denken-dan-coderen/","summary":"De meeste developers typen meteen een prompt. Plan Mode dwingt je om eerst te denken. Het verschil: code die niet alleen werkt, maar past bij je project.","title":"Planning mode — eerst denken, dan coderen"},{"content":"Een recent onderzoek van METR zette me aan het denken. Ze lieten zestien ervaren open-source developers — gemiddeld vijf jaar ervaring met hun eigen codebase — werken met en zonder AI-tools. Het resultaat: met AI waren ze 19% tráger. Maar het opvallendste? Ze dáchten 20% sneller te zijn.\nOnderzoekers van de Universiteit van Tilburg vonden iets vergelijkbaars: AI helpt minder ervaren programmeurs sneller code schrijven, maar ervaren developers verschuiven naar reviewen en corrigeren — en worden per saldo niet productiever.\nDit is geen argument tegen AI-tools. Het is een argument voor een betere manier van samenwerken. Want het verschil tussen developers die er wél sneller van worden en developers die er niet veel aan hebben, zit niet in de tool. Het zit in de aanpak.\n1. Denk eerst, prompt daarna Het grootste anti-pattern dat ik zie — ook bij mezelf in het begin — is meteen code laten genereren. \u0026ldquo;Maak een notificatiesysteem.\u0026rdquo; En dan verbaasd zijn dat het resultaat technisch werkt, maar totaal niet past bij je architectuur.\nAddy Osmani, engineering lead bij Google, noemt zijn aanpak \u0026ldquo;waterfall in 15 minuten\u0026rdquo;: schrijf eerst een korte spec met de AI, beschrijf edge cases, en laat het daarna pas bouwen. De planning duurt een paar minuten, maar bespaart je een uur aan correcties.\nIn Claude Code betekent dit: begin in Plan Mode. Laat Claude je codebase lezen, je architectuur begrijpen, en een aanpak voorstellen vóórdat er één regel code wordt geschreven.\nZonder plan:\n\u0026ldquo;Bouw een notificatiesysteem voor statuswijzigingen.\u0026rdquo;\nClaude maakt een eigen NotificationService, een nieuwe tabel, en een polling-mechanisme. Technisch correct. Totaal niet hoe jullie het doen.\nMet plan:\n\u0026ldquo;We willen notificaties bij statuswijzigingen. Kijk eerst hoe we events afhandelen in /Application/EventHandlers. Maak een plan dat past bij onze bestaande architectuur.\u0026rdquo;\nClaude ziet dat jullie al een IEventHandler\u0026lt;T\u0026gt;-pattern hebben en stelt voor om daarop voort te bouwen. Geen nieuw systeem, maar een uitbreiding van wat er al staat.\n2. Geef context, niet alleen een opdracht Onderzoekers van UC San Diego en Cornell bestudeerden 99 professionele developers en observeerden er dertien in hun dagelijkse werk. Eén ding viel op: ervaren developers geven altijd bestandsnamen, functienamen en foutmeldingen mee. Ze laten de AI niet raden.\nHet verschil:\nVaag:\n\u0026ldquo;De build faalt. Fix het.\u0026rdquo;\nScherp:\n\u0026ldquo;De build faalt met deze error: [foutmelding]. Check de validatielogica in OrderValidator.cs. Fix de oorzaak, niet het symptoom. Run dotnet test na de fix.\u0026rdquo;\nIn de vorige post schreef ik over CLAUDE.md als structurele context — de dingen die Claude altijd moet weten. Maar even belangrijk is de ad-hoc context die je per vraag meegeeft. Hoe specifieker je bent over welke bestanden, welke patronen en welk eindresultaat je verwacht, hoe beter het resultaat.\n3. Stuur op richting, niet op elke regel Er zijn twee uitersten. Aan de ene kant: alles dicteren, elke regel code voorschrijven. Dan kun je het net zo goed zelf typen. Aan de andere kant: alles accepteren zonder te kijken. Dat is wat ze \u0026ldquo;vibe coding\u0026rdquo; noemen — en uit de Stack Overflow-enquête van 2025 blijkt dat 72% van professionele developers dat bewust níét doet.\nDe sweet spot zit ertussenin. Datzelfde UC San Diego-onderzoek laat zien: geen enkele ervaren developer liet AI volledig autonoom werken. Maar ze stuurden gemiddeld per twee stappen bij — niet per regel.\nDenk aan het aansturen van een collega. Je zegt niet: \u0026ldquo;Type op regel 14 een if-statement met deze exacte conditie.\u0026rdquo; Je zegt: \u0026ldquo;De validatie moet ook rekening houden met inactieve gebruikers. Kijk hoe we dat bij orders doen.\u0026rdquo; Richting geven, niet dicteren.\nAddy Osmani verwoordt het zo: \u0026ldquo;Every engineer is a manager now.\u0026rdquo; Je orkestreert en valideert. Je hoeft niet alles zelf te schrijven, maar je moet wel alles begrijpen.\n4. Weet wanneer je opnieuw begint Dit is misschien de belangrijkste les die ik heb geleerd: soms is bijsturen duurder dan opnieuw beginnen.\nDe vuistregel die ik hanteer: als je Claude twee keer corrigeert op hetzelfde punt, is je context vervuild met mislukte pogingen. Begin opnieuw. In Claude Code: /clear, revert naar je laatste goede commit, en schrijf een betere prompt waarin je meeneemt wat je de eerste keer hebt geleerd.\nJohn Lindquist van egghead.io zegt het simpel: \u0026ldquo;Starting over works every time.\u0026rdquo; En hij heeft gelijk. Een schone sessie met een scherpe prompt levert bijna altijd een beter resultaat dan een lange sessie vol correcties.\nDe signalen dat je beter opnieuw kunt beginnen:\nJe herhaalt dezelfde correctie voor de derde keer De antwoorden worden circulair De code wordt met elke iteratie complexer in plaats van simpeler Je merkt dat je gefrustreerd raakt Dat laatste is serieus. Frustratie is een signaal dat je aan het worstelen bent met de context, niet met het probleem.\n5. Vertrouw, maar verifieer altijd Een onderzoek van Sonar onder 1.100 developers leverde een veelzeggende paradox op: 96% vertrouwt AI-gegenereerde code niet volledig. Maar 48% checkt het niet altijd voor ze committen.\nDat is alsof je een collega niet vertrouwt, maar ook niet naar zijn code kijkt.\nAnthropic\u0026rsquo;s eigen documentatie is hier glashelder over: \u0026ldquo;Give Claude a way to verify its work. This is the single highest-leverage thing you can do.\u0026rdquo; Verifiëren is niet optioneel — het is het belangrijkste dat je kunt doen.\nConcreet in een .NET-project:\n\u0026ldquo;Implementeer de endpoint, schrijf tests voor het happy path en de belangrijkste edge case, en run dotnet test. Fix eventuele failures.\u0026rdquo;\nDoor verificatie onderdeel te maken van je prompt, bouw je een feedbackloop in. Claude schrijft code, test het zelf, en fixt wat niet werkt — voordat jij er überhaupt naar kijkt.\nKent Beck — de bedenker van Test-Driven Development — zegt dat TDD juist belangrijker wordt met AI, niet minder. Het is het vangnet dat ervoor zorgt dat snelheid niet ten koste gaat van kwaliteit.\nHet is een vaardigheid, geen knop Uit de Stack Overflow-enquête van 2025 blijkt dat 66% van developers meer tijd kwijt is aan het fixen van \u0026ldquo;bijna-goede\u0026rdquo; AI-code dan het zou kosten om het zelf te schrijven. Dat cijfer klinkt als een aanklacht tegen AI-tools. Maar ik denk dat het eerder een signaal is dat de meeste developers nog bezig zijn deze samenwerking te leren.\nEffectief samenwerken met AI is een vaardigheid die je ontwikkelt. Net als pair programming, code review, of het aansturen van een team. Het gaat niet om de perfecte prompt of de juiste tool. Het gaat om een manier van werken: plannen voordat je begint, context meegeven, bijsturen op het juiste moment, en altijd — altijd — het eindresultaat zelf beoordelen.\nBegin met één ding. De volgende keer dat je een prompt typt, stel jezelf eerst de vraag: weet Claude genoeg om dit goed te doen? Als het antwoord nee is, geef het wat het nodig heeft. Je zult het verschil merken.\n","permalink":"https://renedekkers.nl/nl/posts/effectief-samenwerken-met-ai/","summary":"Uit onderzoek blijkt dat ervaren developers niet automatisch sneller worden met AI — soms zelfs trager. Het verschil zit niet in de tool, maar in hoe je ermee samenwerkt.","title":"Hoe je effectief samenwerkt met AI — het is geen zoekmachine"},{"content":"Het probleem Je opent Claude Code in je .NET-project. Je vraagt: \u0026ldquo;Voeg een endpoint toe voor het ophalen van gebruikersnotificaties.\u0026rdquo;\nClaude Code gaat aan de slag. Maar het maakt een controller aan terwijl jullie Minimal API\u0026rsquo;s gebruiken. Het zet de connectionstring hardcoded neer terwijl jullie alles via appsettings.json doen. Het maakt een eigen NotificationService aan terwijl jullie al een generieke IEventHandler\u0026lt;T\u0026gt; hebben.\nTechnisch werkt het. Maar het past niet bij je project.\nJe corrigeert. Claude Code past aan. Volgende vraag, zelfde verhaal. Het weet niet hoe jullie dingen doen, want dat staat nergens beschreven — niet voor een AI althans.\nZoals John Lindquist (egghead.io) het treffend zegt: \u0026ldquo;Every time an AI starts, it has no memory, no idea of what\u0026rsquo;s going on in your application.\u0026rdquo; En dan proberen developers het bij te sturen met regels en correcties, maar ze vergeten het belangrijkste: de AI vertellen hoe hun applicatie in elkaar zit.\nDit is het frustrerende aan AI-tools zonder context. Ze zijn slim genoeg om code te schrijven, maar ze kennen je project niet. Elke sessie begin je weer bij nul.\nDe oplossing: CLAUDE.md CLAUDE.md is een Markdown-bestand in je project dat Claude Code automatisch inleest bij elke sessie. Het is het geheugen van je AI-assistent.\nGeen plugin, geen install, geen setup. Gewoon een .md-bestand met daarin de dingen die Claude Code moet weten over jouw project.\nJe maakt het aan met één commando:\nclaude /init Claude Code scant je project en genereert een eerste versie. Maar de echte waarde zit in wat jij er daarna aan toevoegt. Zie /init als een startpunt — niet als het eindresultaat.\nWat zet je erin? Geen roman. Geen volledige documentatie. Alleen de dingen die je anders steeds opnieuw zou moeten uitleggen.\nProjectstructuur en conventies:\n## Project - ASP.NET Core 8 Web API met Minimal API\u0026#39;s (geen controllers) - Entity Framework Core met SQL Server - Mediator pattern via MediatR - Alle configuratie via appsettings.json, nooit hardcoded Patronen die jullie gebruiken:\n## Conventies - Endpoints staan in /Endpoints, gegroepeerd per domein - Services implementeren altijd een interface - Validation via FluentValidation, niet via data annotations - Exceptions worden centraal afgehandeld via middleware Dingen die Claude Code niet moet doen:\n## Niet doen - Geen controllers aanmaken, we gebruiken Minimal API\u0026#39;s - Geen Console.WriteLine voor logging, gebruik ILogger\u0026lt;T\u0026gt; - Geen hardcoded connectionstrings - Geen business logic in endpoints Minstens zo belangrijk: wat je er niet in zet. Dingen die Claude al weet door je code te lezen hoeven er niet in. Standaard C#-conventies ook niet. En stijlregels die een linter kan afdwingen? Gebruik een linter, geen CLAUDE.md. Hoe minder ruis, hoe beter Claude de regels volgt die er wél toe doen.\nHet verschil Zonder CLAUDE.md vraag je om een endpoint en krijg je dit:\n[ApiController] [Route(\u0026#34;api/[controller]\u0026#34;)] public class NotificationsController : ControllerBase { private readonly string _connectionString = \u0026#34;Server=localhost;...\u0026#34;; [HttpGet] public async Task\u0026lt;IActionResult\u0026gt; GetNotifications() { ... } } Met CLAUDE.md vraag je om hetzelfde endpoint en krijg je dit:\napp.MapGet(\u0026#34;/api/notifications\u0026#34;, async (IMediator mediator) =\u0026gt; { var result = await mediator.Send(new GetNotificationsQuery()); return Results.Ok(result); }) .WithName(\u0026#34;GetNotifications\u0026#34;) .WithTags(\u0026#34;Notifications\u0026#34;); Geen controller. Geen hardcoded strings. Wel MediatR, wel Minimal API\u0026rsquo;s — precies zoals jullie het doen. Alsof een collega het geschreven heeft die weet hoe jullie werken.\nHetzelfde geldt voor refactoring, debugging, documentatie — alles wat Claude Code doet wordt beter als het weet hoe jouw project in elkaar zit.\nTeam vs. persoonlijk CLAUDE.md is niet één bestand. Het is een gelaagd systeem:\nCLAUDE.md in je project root is voor het hele team. Check het in via git, zodat iedereen dezelfde afspraken deelt. Dit is waar je architectuurkeuzes, conventies en \u0026ldquo;niet doen\u0026rdquo;-regels neerzet.\nCLAUDE.local.md is voor jou alleen. Dit bestand wordt automatisch aan .gitignore toegevoegd. Hier zet je je persoonlijke voorkeuren: jouw sandbox-URLs, jouw testdata, jouw manier van werken.\n~/.claude/CLAUDE.md in je home directory geldt voor al je projecten. Handig voor persoonlijke stijlvoorkeuren die je overal wilt.\nZo deel je teamafspraken via git, zonder dat je persoonlijke setup in de weg zit. En voor grotere projecten kun je regels zelfs opsplitsen in .claude/rules/ — aparte bestanden per onderwerp zoals code-style.md, testing.md of security.md, die allemaal automatisch worden ingeladen.\nClaude leert ook zelf CLAUDE.md is niet alleen iets dat jij schrijft. Claude Code heeft ook auto memory: het slaat zelf notities op over patronen die het ontdekt, debugging-inzichten en architectuurkeuzes. Deze worden bewaard in ~/.claude/projects/\u0026lt;project\u0026gt;/memory/ en automatisch geladen bij elke sessie.\nEn er is een handige snelkoppeling: begin een regel met # tijdens een sessie (bijvoorbeeld # gebruik altijd MUI componenten) en Claude slaat het direct op als geheugenregel.\nMaar het krachtigste is de feedback-loop. Als je Claude corrigeert, kun je zeggen: \u0026ldquo;Voeg dit toe aan CLAUDE.md zodat je het onthoudt.\u0026rdquo; Claude schrijft de regel dan zelf. Boris Cherny, de maker van Claude Code, raadt precies dit aan: \u0026ldquo;Invest in your CLAUDE.md file. After every correction, update it so you don\u0026rsquo;t make that mistake again. Claude is effective at writing rules for itself.\u0026rdquo;\nZo groeit je CLAUDE.md organisch mee met je project — zonder dat je er een documentatieproject van hoeft te maken.\nTips om het scherp te houden Houd het kort. CLAUDE.md is geen wiki. Richt op maximaal 300 regels. Hoe langer het bestand, hoe meer Claude belangrijke regels over het hoofd ziet. De officiële documentatie waarschuwt hier expliciet voor: als je CLAUDE.md te lang wordt, negeert Claude de helft. Vuistregel: als Claude iets al goed doet zonder de instructie, verwijder die instructie.\nWerk het bij als je iets corrigeert. Als je Claude Code voor de tweede keer moet vertellen dat jullie geen controllers gebruiken, zet het dan in CLAUDE.md — of vraag Claude het zelf toe te voegen. Dat hoef je het daarna nooit meer te zeggen.\nWees specifiek. \u0026ldquo;We volgen clean architecture\u0026rdquo; zegt niks. \u0026ldquo;Services staan in /Application/Services en implementeren altijd een interface uit /Application/Interfaces\u0026rdquo; zegt alles.\nGebruik imports voor grote projecten. CLAUDE.md ondersteunt @pad/naar/bestand om andere bestanden in te laden. Zo houd je het hoofdbestand lean zonder context te verliezen:\nZie @README.md voor projectoverzicht en @docs/architecture.md voor architectuur. Zelf beginnen? Open je terminal in je project:\nclaude /init Kijk wat Claude Code genereert, pas het aan met jullie eigen conventies, en merk het verschil bij je volgende vraag.\nJa, meer context betekent meer tokens — en dat kost iets meer. Maar de tijd die je bespaart door niet steeds dezelfde correcties te doen weegt daar ruimschoots tegenop.\nJe AI is zo goed als de context die je het geeft. CLAUDE.md is die context.\n","permalink":"https://renedekkers.nl/nl/posts/claude-md-geheugen-van-je-ai/","summary":"Elke sessie begint Claude Code bij nul. Met CLAUDE.md geef je het geheugen: je conventies, patronen en architectuur. Geen herhaalde correcties meer.","title":"Stop met steeds dezelfde correcties — geef je AI geheugen"},{"content":"Een dag uit het leven van een developer\n09:00 — De bug Het is maandagochtend. In Teams staat een bericht van de tester: \u0026ldquo;De gebruikersregistratie geeft een 500 na het laatste deployment.\u0026rdquo;\nVroeger open je ChatGPT. Je kopieert de RegistrationService.cs erin. En de UserRepository.cs, want die hangt eraan. En het request-model, want anders snapt het de context niet. Drie berichten verder vraag je: \u0026ldquo;Kun je zien wat er mis is?\u0026rdquo; ChatGPT geeft een suggestie. Je kopieert het terug, past het aan, runt de tests handmatig. Kost je een uur.\nNu open je je terminal in het project en type je claude. Je zegt:\n\u0026ldquo;De registratie-endpoint geeft een 500 na het laatste deployment. Dit is de exception:\u0026rdquo;\nJe plakt de stacktrace erbij. Claude Code pakt het vanaf daar op — het leest de relevante bestanden zelf, met de exception als startpunt.\nClaude Code leest de relevante bestanden zelf. Het vindt de fout: een null-reference in de nieuwe validatielogica. Het past de code aan en runt dotnet test. Alle tests groen. Klaar. Het is 09:20.\n11:00 — De brainstorm Er moet een notificatiesysteem bij. Gebruikers willen meldingen krijgen bij statuswijzigingen. Je hebt een idee, maar je wilt even sparren over de aanpak.\nVroeger open je ChatGPT en typ je: \u0026ldquo;Hoe zou je een notificatiesysteem opzetten in een ASP.NET Core-applicatie?\u0026rdquo; Je krijgt een generiek antwoord met voorbeeldcode die niks te maken heeft met jouw project. SignalR-voorbeeld met een NotificationHub die je helemaal moet omschrijven naar jullie structuur.\nNu zeg je tegen Claude Code:\n\u0026ldquo;We willen een notificatiesysteem toevoegen. Gebruikers moeten meldingen krijgen bij statuswijzigingen. Hoe zou dat passen in onze huidige architectuur?\u0026rdquo;\nClaude Code kent je codebase. Het ziet dat jullie al een event-based pattern gebruiken, dat er al een IEventHandler-interface is, en stelt voor om daarop voort te bouwen. Geen generiek verhaal, maar een voorstel dat past bij wat er al staat.\n14:00 — De refactor Er is een service die te groot is geworden. Iedereen kent hem: OrderProcessingService.cs, 800 regels, doet te veel. Het staat al weken op de backlog om die op te splitsen.\nVroeger zou je er een middag voor uittrekken. Handmatig door de code gaan, uitzoeken welke methods bij elkaar horen, nieuwe classes aanmaken, alles doorverbinden, hopen dat je niks breekt.\nNu zeg je:\n\u0026ldquo;OrderProcessingService is te groot. Kun je analyseren welke verantwoordelijkheden erin zitten en een voorstel doen om het op te splitsen?\u0026rdquo;\nClaude Code leest de service, identificeert drie duidelijke verantwoordelijkheden — validatie, prijsberekening en statusbeheer — en stelt een opsplitsing voor. Na je goedkeuring voert het de refactor door, verplaatst de methods, past de dependency injection aan in Program.cs, en runt dotnet build en dotnet test om te checken dat alles nog werkt.\n16:30 — De documentatie Je wilt even vastleggen hoe het notificatiesysteem werkt, voor de rest van het team.\nVroeger stel je dit uit tot volgende week. Of de week daarna. Of nooit.\nNu zeg je:\n\u0026ldquo;Schrijf een korte technische beschrijving van het notificatiesysteem zoals we het net hebben opgezet.\u0026rdquo;\nClaude Code kent de code die het net heeft geholpen bouwen en schrijft een beknopte beschrijving die klopt met de werkelijkheid. Geen generiek verhaal, maar documentatie die je zo in de wiki kunt zetten.\n17:00 — Einde dag Vier taken. Bug gefixt, feature ontworpen, legacy code gerefactord, documentatie geschreven. Niet omdat je harder hebt gewerkt, maar omdat je minder tijd kwijt was aan het aanleveren van context, het copy-pasten van code, en het handmatig vertalen van generieke antwoorden naar jouw situatie.\nDat is het verschil tussen ChatGPT en Claude Code. ChatGPT is een slimme assistent in een ander tabblad. Claude Code is een collega die in je project zit.\nZelf beginnen? npm install -g @anthropic-ai/claude-code Open je terminal in je project, type claude, en doe /init. Probeer het een week. Ik denk dat je niet meer terug wilt.\n","permalink":"https://renedekkers.nl/nl/posts/van-chatgpt-naar-claude-code/","summary":"Een dag uit het leven van een developer. Vier scenario\u0026rsquo;s die laten zien hoe Claude Code je workflow verandert ten opzichte van ChatGPT.","title":"Van ChatGPT naar Claude Code: wat is het verschil?"},{"content":"Ik bouw al lang software. Van VB6 tot .NET, van monolithen tot microservices, van on-premise naar de cloud. In al die jaren heb ik veel trends zien komen en gaan. De meeste waren ruis. Sommige veranderden alles.\nAI in software development is zo\u0026rsquo;n moment.\nNiet de hype, maar de praktijk Het internet staat vol met meningen over AI. Iedereen heeft er een hot take over. Maar als ik zoek naar eerlijke, praktische verhalen van developers die AI dagelijks inzetten — niet als speeltje, maar als integraal onderdeel van hun workflow — dan vind ik opvallend weinig.\nDat is precies de reden dat ik deze blog ben begonnen.\nWat ik dagelijks zie Sinds ik met een volledige AI-workflow werk, is mijn manier van software bouwen fundamenteel veranderd:\nBugs die ik voorheen in een uur oploste, zijn nu in twintig minuten gefixt — niet omdat ik sneller type, maar omdat ik minder tijd kwijt ben aan het handmatig bij elkaar zoeken van context Refactoring is geen dagtaak meer — AI leest de code, begrijpt de structuur, en voert de wijzigingen door terwijl ik meekijk en bijstuur Documentatie schrijft zichzelf — als bijproduct van het ontwikkelproces, niet als verplicht nummertje achteraf Architectuurbeslissingen neem ik in dialoog — met een tool die mijn hele codebase kent en daarop kan voortbouwen Ik lever meer in minder tijd — en niet door harder te werken, maar door slimmer samen te werken Dit klinkt misschien als een verkooppraatje. Dat snap ik. Maar het is gewoon wat ik elke dag ervaar.\nWat je hier kunt verwachten Op deze blog deel ik die ervaringen. Eerlijk, concreet, vanuit de praktijk. Geen abstracte toekomstvisies, maar verhalen over hoe het er vandaag aan toe gaat als je AI serieus inzet in je development workflow.\nSoms gaat het over tools. Soms over aanpak. Soms over waar het misgaat — want dat gebeurt ook.\nAls je developer bent en je afvraagt of AI je werk echt beter kan maken: blijf lezen. Ik laat het je zien.\n","permalink":"https://renedekkers.nl/nl/posts/waarom-deze-blog/","summary":"AI verandert mijn dagelijkse werk als developer fundamenteel. Dit is waarom ik daarover ga schrijven.","title":"Waarom deze blog?"},{"content":"Je bericht is verstuurd. Ik neem zo snel mogelijk contact met je op.\nTerug naar home\n","permalink":"https://renedekkers.nl/nl/bedankt/","summary":"\u003cp\u003eJe bericht is verstuurd. Ik neem zo snel mogelijk contact met je op.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/nl/\"\u003eTerug naar home\u003c/a\u003e\u003c/p\u003e","title":"Bedankt!"},{"content":"Wil je sparren over AI in software development, een vraag stellen, of gewoon even hallo zeggen? Stuur me een bericht via het formulier hieronder.\n","permalink":"https://renedekkers.nl/nl/contact/","summary":"\u003cp\u003eWil je sparren over AI in software development, een vraag stellen, of gewoon even hallo zeggen? Stuur me een bericht via het formulier hieronder.\u003c/p\u003e","title":"Contact"},{"content":"Over mij Ik ben René Dekkers — software developer. Ik bouw dagelijks software met een volledige AI-workflow. Claude Code is mijn vaste collega, en AI zit verweven in elke stap van mijn ontwikkelproces: van architectuur en code tot testing en documentatie.\nMijn AI-workflow Waar de meeste developers AI nog gebruiken als een slim zoekvenster, werk ik met een geïntegreerde aanpak:\nClaude Code als pair programmer — niet in een apart tabblad, maar direct in mijn terminal, met volledige toegang tot de codebase AI-gestuurde architectuur — nieuwe features ontwerp ik in dialoog met AI, die mijn bestaande patronen kent en daarop voortbouwt Geautomatiseerd testen en refactoren — AI leest de code, identificeert problemen, voert wijzigingen door en valideert het resultaat Documentatie als bijproduct — geen apart klusje meer, maar iets dat direct ontstaat uit het ontwikkelproces Het resultaat: ik lever in uren wat voorheen dagen kostte. Niet door harder te werken, maar door de juiste tools op de juiste manier in te zetten.\nAchtergrond Ruime ervaring met .NET, C# en cloud-architecturen. Ik heb enterprise-applicaties, microservices en schaalbare oplossingen gebouwd voor uiteenlopende organisaties. Die ervaring maakt het verschil — ik weet niet alleen hóe AI-tools werken, maar ook wanneer ze waarde toevoegen en wanneer niet.\nWaarover ik schrijf Op deze blog deel ik eerlijke ervaringen uit de praktijk. Geen hype, maar concrete verhalen:\nHoe ik AI inzet in mijn dagelijkse workflow — stap voor stap De echte impact van Claude Code, GitHub Copilot en ChatGPT Waar AI briljant is en waar het tekortschiet Lessen voor developers die serieus aan de slag willen met AI Contact Wil je sparren over AI in software development, of gewoon even hallo zeggen? Stuur me een bericht of vind me op LinkedIn.\n","permalink":"https://renedekkers.nl/nl/over/","summary":"\u003ch2 id=\"over-mij\"\u003eOver mij\u003c/h2\u003e\n\u003cp\u003eIk ben René Dekkers — software developer. Ik bouw dagelijks software met een volledige AI-workflow. Claude Code is mijn vaste collega, en AI zit verweven in elke stap van mijn ontwikkelproces: van architectuur en code tot testing en documentatie.\u003c/p\u003e\n\u003ch2 id=\"mijn-ai-workflow\"\u003eMijn AI-workflow\u003c/h2\u003e\n\u003cp\u003eWaar de meeste developers AI nog gebruiken als een slim zoekvenster, werk ik met een geïntegreerde aanpak:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eClaude Code als pair programmer\u003c/strong\u003e — niet in een apart tabblad, maar direct in mijn terminal, met volledige toegang tot de codebase\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAI-gestuurde architectuur\u003c/strong\u003e — nieuwe features ontwerp ik in dialoog met AI, die mijn bestaande patronen kent en daarop voortbouwt\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eGeautomatiseerd testen en refactoren\u003c/strong\u003e — AI leest de code, identificeert problemen, voert wijzigingen door en valideert het resultaat\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDocumentatie als bijproduct\u003c/strong\u003e — geen apart klusje meer, maar iets dat direct ontstaat uit het ontwikkelproces\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eHet resultaat: ik lever in uren wat voorheen dagen kostte. Niet door harder te werken, maar door de juiste tools op de juiste manier in te zetten.\u003c/p\u003e","title":"Over"},{"content":"Benieuwd hoe AI jouw development-workflow kan verbeteren? Plan hieronder een vrijblijvend kennismakingsgesprek van 30 minuten. We bespreken je situatie en kijken waar de kansen liggen.\n","permalink":"https://renedekkers.nl/nl/plan-een-gesprek/","summary":"\u003cp\u003eBenieuwd hoe AI jouw development-workflow kan verbeteren? Plan hieronder een vrijblijvend kennismakingsgesprek van 30 minuten. We bespreken je situatie en kijken waar de kansen liggen.\u003c/p\u003e","title":"Plan een gesprek"}]