Naar de inhoud
maarten.
Alle posts

AI-code in productie: review, beveiliging en onderhoud

Maarten Soetens 12 min lezen

AI-gegenereerde code kan snel een werkende oplossing opleveren, maar werkende code is niet automatisch veilig, correct of onderhoudbaar. Lees hoe je de risico’s beoordeelt, welke controles je automatiseert en waar menselijke beoordeling noodzakelijk blijft.

AI-gegenereerde code reviewen op gedrag, niet alleen op stijl

Een code review van gegenereerde code begint bij de vraag welk gedrag de wijziging moet opleveren. Een nette structuur, herkenbare naamgeving en succesvolle compilatie zeggen weinig over de juistheid van de oplossing. Vergelijk de code met de functionele eis, de relevante architectuur en bestaande patronen in de repository. Controleer welke bestanden zijn aangepast, of er onverwachte wijzigingen zijn en of de implementatie meer doet dan gevraagd.

Bekijk vervolgens de gegevensstroom: waar komt invoer binnen, welke bewerkingen vinden plaats en waar worden resultaten opgeslagen of getoond? Een gegenereerde functie kan lokaal logisch lijken, maar elders onbedoeld toestandsbeheer, foutafhandeling of validatie omzeilen. Let ook op impliciete keuzes, zoals standaardwaarden, tijdzones, afronding en gedrag bij lege of ontbrekende waarden. Juist die details veroorzaken verschillen tussen een demo en productiegedrag.

Een diff die klein en begrijpelijk blijft, is makkelijker te beoordelen dan een brede wijziging met gemengde refactors. Splits waar mogelijk functionele aanpassingen, dependency-updates en formattering van elkaar. Reviewers kunnen dan gerichter redeneren over oorzaak en effect. Gebruik automatische checks voor formattering en bekende patronen, maar behandel die als filter: ze verkleinen de zoekruimte, niet de verantwoordelijkheid om vast te stellen of de code het bedoelde systeemgedrag implementeert.

Kwetsbaarheden opsporen in AI-gegenereerde code

Beoordeel beveiliging vanuit de context van de toepassing, niet vanuit de losse functie. Breng eerst vertrouwensgrenzen in kaart: welke invoer komt van gebruikers, externe API’s, bestanden of interne services, en welke onderdelen mogen die invoer vertrouwen? Controleer vervolgens of validatie plaatsvindt op de juiste grens. Validatie in de browser vervangt bijvoorbeeld geen servervalidatie, en een typecontrole voorkomt niet automatisch injectie in een SQL-query of shellcommando.

Let specifiek op authenticatie, autorisatie en geheimen. Een gegenereerde route kan een geldige gebruiker controleren zonder te controleren of die gebruiker toegang heeft tot het opgevraagde object. Controleer of machtigingen per actie en per resource worden afgedwongen. Zoek ook naar tokens, wachtwoorden en sleutels in broncode, logs en foutmeldingen. Een algemene foutmelding voor de gebruiker mag intern voldoende context bevatten voor onderzoek, maar geen gevoelige gegevens blootleggen.

Statische analyse, dependency-scans en secret-scans zijn nuttig om bekende risico’s vroeg te signaleren. Ze missen echter fouten in bedrijfslogica en kunnen meldingen produceren die beoordeling nodig hebben. Test daarom misbruikscenario’s: ongeldige invoer, herhaalde verzoeken, ontbrekende rechten, verlopen sessies en ongebruikelijke combinaties van parameters. De relevante diepgang hangt af van blootstelling en impact. Code die publiek toegankelijke gegevens verwerkt vraagt een andere beoordeling dan code die accounts, betalingen of persoonsgegevens kan wijzigen.

Foutieve aannames in gegenereerde code herkennen

Een taalmodel vult ontbrekende context aan met aannemelijke patronen. Dat maakt code vaak leesbaar, maar kan verborgen aannames introduceren over het datamodel, de API of de bedrijfsregels. Controleer daarom of gebruikte velden en statuswaarden echt bestaan, of een externe dienst het veronderstelde formaat levert en of foutcodes dezelfde betekenis hebben als de code aanneemt. Documentatie en bestaande implementaties zijn daarbij betrouwbaarder dan een overtuigende toelichting bij de gegenereerde code.

Besteed aandacht aan randgevallen die in een standaardvoorbeeld ontbreken. Wat gebeurt er bij dubbele verzoeken, gelijktijdige updates, een lege collectie of een gedeeltelijk mislukte transactie? Een functie kan bij één record correct lijken, maar bij grotere aantallen onjuiste resultaten opleveren. Bij datums, valuta en eenheden zijn impliciete conversies riskant: lokale tijd en UTC, decimalen en gehele centen, of inclusieve en exclusieve grenzen moeten expliciet overeenkomen met het domein.

Leg belangrijke aannames vast als tests of expliciete validaties. Als een externe API bijvoorbeeld altijd een uniek ID zou moeten teruggeven, moet de code bepalen wat er gebeurt wanneer dat veld ontbreekt of dubbel voorkomt. Vraag bij twijfel niet alleen of de implementatie compileert, maar welke waarneming de aanname bevestigt. Een gerichte test tegen een representatieve respons of een controle in de bronspecificatie kan een geloofwaardige maar onjuiste implementatie blootleggen voordat die afhankelijk wordt van productiegegevens.

Nieuwe dependencies en risico’s in de softwareketen beoordelen

AI-gegenereerde code kan een package voorstellen dat niet nodig is, een verkeerd gelijkende naam heeft of niet aansluit op het beheerbeleid van een project. Controleer vóór installatie de exacte pakketnaam, de officiële bron, de onderhoudsactiviteit en de licentie. Kijk ook naar transitieve dependencies: een kleine directe library kan een grote keten van indirecte code toevoegen. Elke dependency vergroot het aanvalsoppervlak en brengt onderhoud mee, ook wanneer de implementatie maar enkele functies gebruikt.

Beoordeel of bestaande platformbibliotheken of interne componenten hetzelfde probleem al oplossen. Een extra package kan aantrekkelijk zijn omdat het voorbeeldcode verkort, maar de afweging verandert wanneer het project al een gevalideerde HTTP-client, parser of cryptografische library gebruikt. Implementeer cryptografie niet zelf op basis van gegenereerde voorbeelden; kies een onderhouden implementatie en controleer of de configuratie aansluit op de beveiligingseisen. Pin versies volgens het beleid van de repository en houd lockfiles consistent, zodat builds reproduceerbaar blijven.

Automatische scanners signaleren bekende kwetsbaarheden en verouderde versies, maar beoordelen niet of een package noodzakelijk of betrouwbaar is. Let daarom ook op onverwachte installatiescripts, onduidelijk eigenaarschap en packages met weinig gebruikers of activiteit. Een kwetsbaarheidsscore alleen vertelt niet of de betreffende code bereikbaar is in de toepassing; andersom maakt beperkte bereikbaarheid een onnodige dependency niet vanzelf acceptabel. Leg vast waarom een dependency wordt toegevoegd en wie verantwoordelijk is voor updates en meldingen.

Tests gebruiken om gegenereerde code inhoudelijk te toetsen

Tests die samen met de implementatie zijn gegenereerd, kunnen dezelfde verkeerde aanname bevatten als de code zelf. Een test die alleen het gewenste voorbeeld bevestigt, bewijst niet dat de oplossing bestand is tegen afwijkende invoer of veranderende toestand. Schrijf of beoordeel tests vanuit onafhankelijk beschreven verwachtingen: contracten, domeinregels en bekende fouten. Gebruik voorbeelden die normale werking, grenzen en afwijzingen afdekken, in plaats van uitsluitend de happy path.

Unit-tests zijn geschikt voor geïsoleerde logica, maar testen niet vanzelf databasegedrag, configuratie of integratie met externe diensten. Voeg waar nodig contracttests of integratietests toe die controleren of invoer en respons aansluiten op echte interfaces. Voor beveiligingsgevoelige functies zijn negatieve tests belangrijk: onbevoegde gebruikers, ongeldige tokens, onvolledige objecten en pogingen om gegevens van een andere gebruiker op te vragen. Controleer bovendien dat transacties bij fouten geen gedeeltelijke wijzigingen achterlaten.

Let op tests die afhankelijk zijn van interne details, zoals exacte aanroepen of implementatiestructuur, terwijl het relevante contract ongetest blijft. Zulke tests kunnen refactors blokkeren zonder regressies te voorkomen. Mutatietesten of gerichte handmatige aanpassingen kunnen laten zien of assertions daadwerkelijk falen wanneer gedrag verandert. Testdekking is een meetpunt, geen kwaliteitsbewijs: een hoge dekking kan samengaan met zwakke assertions. Beoordeel daarom wat een test zou ontdekken en welke risico’s buiten de testomgeving blijven.

Onderhoudbaarheid en architectuur toetsen voordat code blijft

Gegenereerde code kan een taak oplossen door bestaande architectuur te omzeilen. Een nieuwe databaseverbinding in een controller, een tweede validatielaag of een parallelle helper lijkt lokaal efficiënt, maar maakt verantwoordelijkheden minder duidelijk. Vergelijk de wijziging met de grenzen die het project al hanteert: waar hoort domeinlogica, hoe worden afhankelijkheden geïnjecteerd en welke component beheert foutafhandeling? Afwijken kan terecht zijn, maar moet een bewuste keuze zijn in plaats van een gevolg van ontbrekende context.

Beoordeel de code op leesbaarheid voor de volgende wijziging. Zijn namen gekoppeld aan het domein, zijn foutcondities expliciet en is de controleflow te volgen zonder lange ketens van neveneffecten? Let op dubbele logica, brede uitzonderingsblokken en generieke hulpfuncties die moeilijk te begrijpen zijn. Een compacte oplossing is niet automatisch onderhoudbaar; abstraheren voordat er meerdere concrete gebruikssituaties zijn kan juist onnodige complexiteit toevoegen. Kies abstrahering op basis van herhaald gedrag en duidelijke verantwoordelijkheid.

De onderhoudslast omvat ook observability en operationeel gedrag. Zijn relevante fouten herkenbaar in logs zonder persoonsgegevens of geheimen te registreren? Zijn time-outs, retries en limieten passend bij de externe dienst? Onbegrensde retries kunnen een storing verergeren, terwijl het ontbreken van time-outs processen kan laten vastlopen. Houd rekening met compatibiliteit van configuratie en datamigraties. Een diff kan syntactisch eenvoudig zijn en toch een wijziging in opslagformaat of publieke API verbergen die andere onderdelen raakt.

Privacy, licenties en herkomst van gegenereerde code controleren

De beoordeling van AI-code stopt niet bij technische werking. Controleer welke gegevens tijdens het genereren zijn gedeeld. Broncode kan API-sleutels, persoonsgegevens, interne URL’s of vertrouwelijke bedrijfsregels bevatten, ook wanneer die niet direct als geheim herkenbaar zijn. Volg het beleid van de organisatie voor modellen, invoer en bewaartermijnen. Verwijder gevoelige gegevens vóór gebruik van externe diensten en ga na of prompts of context worden opgeslagen of gebruikt voor verdere verwerking.

Beoordeel daarnaast de herkomst van voorgestelde code en tekst. Een model kan patronen reproduceren die lijken op bestaande open-sourcecode, maar doorgaans is niet vanzelf duidelijk uit welke bron een fragment afkomstig is. Controleer licentievoorwaarden voor code die letterlijk is overgenomen uit documentatie, repositories of voorbeelden. De juridische beoordeling hangt af van de herkomst, de licentie en de manier waarop code wordt verspreid; een gegenereerd antwoord maakt die vragen niet automatisch irrelevant.

Houd bij codewijzigingen voldoende context bij om later te kunnen reconstrueren waarom een oplossing is gekozen: de relevante eis, ontwerpbeslissing, tests en eventuele externe bron. Dat betekent niet dat iedere prompt in versiebeheer moet belanden. Prompts kunnen vertrouwelijke informatie bevatten en zijn vaak geen goede technische specificatie. Een nauwkeurige wijzigingsbeschrijving en controleerbare tests zijn doorgaans bruikbaarder voor toekomstige reviewers. Stel ook vast wie bevoegd is om de code te accepteren en welke gegevens in reviewtools of tickets zichtbaar worden.

Menselijke beoordeling en risicogestuurde controle organiseren

Niet elke gegenereerde wijziging vraagt dezelfde reviewdiepte. Een aanpassing aan een interne formatteringsregel heeft een ander risicoprofiel dan wijzigingen aan authenticatie, autorisatie, betalingen of gegevensverwijdering. Classificeer wijzigingen op basis van impact, blootstelling en herstelbaarheid. Gebruik dat onderscheid om te bepalen welke tests, reviewers en beveiligingscontroles nodig zijn. Een klein aantal gewijzigde regels kan een kritieke bevoegdheidscontrole veranderen; diffgrootte alleen is daarom geen betrouwbare maat voor risico.

Maak zichtbaar welke code gegenereerd is en welke context de reviewer nodig heeft: de beoogde werking, relevante constraints, afhankelijkheden en resultaten van controles. De reviewer moet de wijziging zelfstandig kunnen beoordelen, niet alleen de uitleg van het model overnemen. Bij hoog risico hoort een reviewer met domein- of beveiligingskennis te toetsen of alternatieve scenario’s zijn onderzocht. Automatische goedkeuring op basis van testsucces is ongeschikt wanneer de tests de beleidskeuze of bedrijfsregel zelf niet vastleggen.

Leg vast welke controles verplicht zijn en wat er gebeurt bij een mislukte scan, onduidelijke waarschuwing of ontbrekende test. Waarschuwingen die structureel worden genegeerd verliezen hun waarde; groepeer daarom bevindingen op ernst en wijs uitzonderingen toe aan een eigenaar met een onderbouwing. Na ingebruikname kunnen logs, foutpercentages en beveiligingsmeldingen aannames toetsen die vooraf niet zichtbaar waren. Die signalen zijn geen vervanging voor review, maar geven aanleiding om concrete codepaden, afhankelijkheden en tests opnieuw te beoordelen.

Veelgestelde vragen

Wie is verantwoordelijk voor AI-gegenereerde code die in productie staat?

De organisatie die de code accepteert en inzet, blijft verantwoordelijk voor het functioneren en beheren ervan; die verantwoordelijkheid kan niet aan een AI-model worden overgedragen. Maak daarom duidelijk wie de wijziging inhoudelijk heeft beoordeeld en wie na livegang aanspreekbaar is op incidenten en onderhoud. De precieze juridische verantwoordelijkheden hangen af van de situatie, contracten en toepasselijke regels.

  • Wijs een menselijke eigenaar aan voor de wijziging.
  • Leg vast wie incidenten opvolgt en wie de code kan aanpassen of terugdraaien.
  • Volg het interne beleid voor goedkeuring van wijzigingen.

Moet je in een pull request vermelden dat code door AI is gegenereerd?

Vermeld AI-gebruik in een pull request wanneer het interne beleid dat vraagt of wanneer die informatie reviewers helpt de wijziging goed te beoordelen. Een label is geen vervanging voor uitleg over het gedrag, de ontwerpkeuzes en de tests. Het kan reviewers wel aanleiding geven om extra aandacht te besteden aan aannames of wijzigingen die de auteur niet zelfstandig kan toelichten.

  • Beschrijf welk deel met AI-hulp tot stand kwam als dat relevant is.
  • Leg de functionele bedoeling en belangrijke keuzes uit.
  • Deel geen vertrouwelijke prompts of gevoelige context zonder toestemming.

Hoe neem je controles voor AI-gegenereerde code op in een CI/CD-pipeline?

Neem AI-gegenereerde code op in dezelfde CI/CD-controles als andere code en laat risicovolle wijzigingen pas door wanneer de vereiste controles slagen. Stel per type wijziging vast welke automatische tests en goedkeuringen verplicht zijn; een aparte pipeline voor AI-code is niet automatisch nodig. Automatisering kan fouten vroeg signaleren, maar kan niet bepalen of de oplossing inhoudelijk klopt of past bij de bedrijfsregels.

  • Voer linting, tests en dependency- en secret-scans uit bij iedere relevante wijziging.
  • Laat gevoelige onderdelen extra goedkeuren door passende reviewers.
  • Blokkeer samenvoegen bij mislukte verplichte checks en registreer uitzonderingen.

Wanneer kun je AI-gegenereerde code beter herschrijven dan verder reviewen?

Herschrijf AI-gegenereerde code wanneer het goedkoper en veiliger is om een kleine, begrijpelijke oplossing opnieuw te maken dan om een onduidelijke of architectonisch afwijkende implementatie te corrigeren. Dat kan bijvoorbeeld het geval zijn als de code moeilijk te verklaren is, meerdere onnodige lagen introduceert of steunt op aannames die niet betrouwbaar zijn te toetsen. Baseer de keuze op risico, wijzigingsomvang en onderhoudbaarheid, niet alleen op het feit dat AI is gebruikt.

  • Vraag de auteur om de werking en belangrijkste keuzes uit te leggen.
  • Vergelijk de kosten van aanpassen met die van een gecontroleerde herimplementatie.
  • Behoud alleen onderdelen waarvan gedrag en herkomst voldoende duidelijk zijn.

Hoe monitor je AI-gegenereerde code nadat die in productie is gezet?

Monitor de code na livegang met meetpunten die aansluiten op het verwachte gedrag en de risico’s van de wijziging. Een geslaagde test of deployment bewijst niet dat de code zich onder echte belasting, met echte gegevens en in combinatie met andere systemen correct gedraagt. Vergelijk daarom relevante foutpercentages, responstijden en bedrijfsuitkomsten met de situatie vóór de wijziging en spreek vooraf af wanneer ingrijpen nodig is.

  • Gebruik waar passend een gefaseerde uitrol of featureflag.
  • Stel meldingen in voor onverwachte fouten of afwijkende bedrijfsresultaten.
  • Leg een terugvalplan vast en controleer na uitrol of de verwachte werking zichtbaar is.
Portret van Maarten

Maarten

Freelance developer in Nijmegen

Even kennismaken?

Vertel kort wat er speelt. Dan hoor je wat er kan, wat ik anders zou doen en waar AI bij jou wél en niet iets toevoegt. Vrijblijvend.

[email protected]
Het kantoor in Nijmegen
© 2026 maarten.online Sitemap Privacy Algemene voorwaarden
Het kantoor in Nijmegen

Maarten.

Freelance developer in Nijmegen. Liever direct contact? Dat kan ook.

Kennismaken

Laat je gegevens achter, dan kijken we of het klikt. Vrijblijvend en zonder verkooppraat.

Maarten

Stuur een bericht via WhatsApp

Hoi! Waar kan ik je mee helpen?

nu