AI som makker

Fra bug til end-to-end-test: sådan klarede GitHub Copilot vores træningsforløb

Marcus de Chiffre
DevOps Engineer, cVation

Kan man bruge AI til at komme sikkert gennem cVations træningsmateriale i C#, Angular og Azure, når man har begrænset kendskab til C# og Angular? Det satte jeg mig for at undersøge.

Kort svar: Ja og nej. Med klare rammer og god kontekst kan AI hjælpe dig meget af vejen. Uden rammer bliver det hurtigt… kreativt.

Jeg er DevOps Engineer og mit primære fokus er cloud-infrastruktur. Jeg kan godt finde rundt i .NET-løsninger men er ikke applikationsudvikler til daglig. Netop derfor var eksperimentet interessant: Kan Copilot hjælpe mig sikkert igennem på et niveau vi i cVation kan stå inde for?

Hvorfor overhovedet prøve?

Det er nemmere end nogensinde at generere kode med AI. GitHub Copilot integrerer direkte i IDE'en og kan foreslå, skrive og ændre kode ud fra dine instruktioner. I cVation har vi løbende fulgt udviklingen af AI-værktøjer til kodegenerering (se f.eks. vores bloginlæg om at generere kode med GPT-3 fra 2022). Med lanceringen af GitHub Copilot’s agent mode besluttede vi at tiden var inde til at lave en struktureret test af hvor langt AI-assisteret udvikling kan nå.

De sprogmodeller der er tilgængelige i GitHub Copilot er især gode til at skabe kode fra bunden med populære open source-frameworks. Men projekter i produktionsskala har ofte stramme kvalitetskrav og bygger på proprietære platforme som AI'en ikke kender.

Spørgsmålet var: Fungerer det stadig når AI'en skal følge vores regler og bruge vores værktøjer?

Til at besvare dette havde vi en oplagt test. I cVation har vi nemlig en demo-applikation, kaldet Cake Planner, som nye ansatte kan bruge til at øve sig på. Gennem en række opgaver bliver man introduceret til cVations Platform til cloud-applikationer, CADD, og kodeprincipper og standarder, som Test Driven Development.

cVations Platform er vores specialiserede framework til udvikling af Azure-applikationer med indbyggede mønstre for sikkerhed, logging og datahåndtering - præcis den slags proprietære værktøjer som AI'en ikke kender på forhånd.

Rammerne for kvalitet

For at måle kvaliteten satte vi følgende rammer for forsøget:

  • Alle kodeændringer skulle ske via prompts til Copilot i agent mode

  • Agenten var Claude Sonnet 4 (del af Copilot for Business)

  • Debugging og test skulle styres via prompts så vidt muligt, men jeg måtte godt hjælpe til, f.eks. ved at starte løsningen lokalt

  • Brian, en af vores mest erfarne arkitekter, skulle reviewe og først godkende når kvaliteten var i orden

Med reglerne på plads gik vi i gang.

De tidlige sejre

Vi startede med en klassiker: find en bug, skriv tests og fiks problemet. Copilot leverede et fint sammendrag af opgaven og kastede sig ud i det. Selve buggen handlede om håndtering af weekenddage. Vi fik reproduceret fejlen, skrev tests og rettede. Vi ventede med spænding på Brians tilbagemelding:

"Det er ikke helt off, men der mangler tests af søndage. Brug konkrete datoer i testen – ikke udregning af weekenddage ud fra en mærkelig startdato.

Resten er faktisk OK."

Da søndagen blev hardcodet, var opgaven i hus.

Vi udvidede også med et historik-endpoint og en frontendside til forgangne kagedage. Backenden gled igennem uden anmærkninger og frontenden manglede kun en test for visning af data. God energi, god fremdrift.

Reality check

Så kom opgaven der skiller fårene fra bukkene: Man skal opdatere antal ansatte på arbejde for en given dato og gemme det i databasen. Her var der ikke en lige så tydelig skabelon at følge.

Copilot gik i gang, men opfandt dele af løsningen selv i stedet for at bruge cVations Platforms indbyggede redskaber. Vi fik en implementering der både var overkompliceret og forkert. Der kunne opstå duplikerede elementer for samme dato og race conditions blev ikke håndteret selvom Platformen har en UpsertAsync-metode til netop det. Brian var ikke tilfreds. Vores pull request var spækket med kommentarer, men hver gang jeg prøvede at lede Copilot mod de rigtige mønstre, holdt den fast i hjemmestrikkede løsninger.

Her viste det sig tydeligt at sprogmodeller er trænet på offentligt tilgængeligt kode og open source-frameworks. Så snart vi bevægede os ind i vores egen kodebase, blev AI'en usikker og faldt tilbage på generiske mønstre.

Det oplagte træk var at give Copilot adgang til mere kontekst. Jeg klonede Platform-koden lokalt og lod Copilot læse kildekode og dokumentation. Med den rette kontekst fandt den hurtigt de rigtige metoder og fik løsningen på plads.

Møde med Brian

Midt i det hele havde jeg et møde med Brian. Vi snakkede om hvordan det gik.

Det var klart for os at der er sket en enorm udvikling den seneste tid i forhold til den kodekvalitet der bliver genereret, men det betyder ikke at det er fejlfrit.

At få noget der er robust, læsbart og konsistent kræver domæneforståelse og standarder.

Det tog mig ofte mange forsøg at få det som Brian ville have det. Han sammenlignede mig med vores nystartede kollega som også sad med Cake Planner, dog uden AI-hjælpemidler.

Dommen var tør, men præcis:

"Hun klarer opgaverne hurtigere end dig."

Det var jo helst ikke sådan det skulle være.

Da Brian udtalte sin næste dom, priste jeg mig lykkelig over at jeg havde spillet med åbne kort fra starten:

"Hvis jeg ikke havde vidst at det var AI-genereret, ville jeg nok have taget en snak med ledelsen om din præstation."

Av. Det blev klart for mig at forsøget lige så meget handlede om at forstå hvordan man bruger et AI-værktøj som hvad et AI-værktøj kan.

Tilbage på sporet

Med Brians feedback og de rigtige mønstre i bagagen kom vi videre. Den næste opgave handlede om at integrere forskellige komponenter i løsningen med Azure Functions og Azure Service Bus. Den blev løst med mindre justeringer og stylingkommentarer.

Så ramte vi end-to-end-testen.

Herre jemini!

Copilot ville først bare kopiere de unit tests vi allerede havde og kalde det en end-to-end-test. Jeg måtte forklare at ideen var at teste integrationen og den asynkrone beskedkæde.

Copilot var som altid klar:

You're absolutely right! Let me reconsider the approach for Task 6. The key insight from the task description is that end-to-end tests should test integration between services and external resources that can't be easily covered by unit tests.

Efter en frisk start og meget præcise instruktioner fik vi en test der ventede 30 sekunder på beskeden - hvorefter den fejlede hver gang. Beskederne blev nemlig kun sendt hvert minut. Da vi justerede til ventetiden 90 sekunder blev testen stabil. En lille detalje, en stor forskel.

Testen virkede godt nok nu, men det kendte mønster viste sig igen: Copilot havde svært ved at finde frem til Platformens indbyggede værktøjer og foretrak at bygge funktionalitet fra bunden. Selv med adgang til kildekoden krævede det flere iterationer før vi nåede et resultat som levede op til vores standarder.

Til sidst skulle vi bygge en ny service fra bunden og integrere med den. Platformen har et script til at lave en ny service, og man bliver bedt om at bruge det i opgavebeskrivelsen. Atter en gang så vi tendensen til at gå uden om Platformens indbyggede funktionalitet og bygge servicen fra bunden. Jeg ledte den i den rigtige retning, og efter et par runder selvanalyse fra Copilot og styring fra min side landede vi en løsning der var fornuftig og gennemskuelig.

Brian havde en del bemærkninger, men de fleste af dem var heldigvis nemme at adressere og efter endnu et par iterationer nåede vi i mål.

Hvad lærte jeg?

Når Copilot rammer plet er det fordi:

  • Rammen er tydelig og succeskriterierne klare

  • Den relevante kontekst er defineret

  • Den har eksempler at gå ud fra

Når den misser er det oftest fordi:

  • Den får for lidt eller forkert kontekst og gætter sig frem

  • Den skriver funktionalitet fra bunden i stedet for at bygge på eksisterende funktionalitet

  • Den laver tests der ikke måler det vigtigste eller som bygger på mangelfuld logik

Et centralt trick var at bruge en instruktionsfil der altid kom med i konteksten. Når jeg gav Copilot adgang til den rigtige dokumentation og beskrev de basale principper den skulle følge, steg kvaliteten mærkbart. Men selv med disse forbedringer blev det tydeligt at der stadig er væsentlige begrænsninger.

AI er ikke en genvej men en forstærker

Min GitHub Copilot og jeg kom helskindede igennem forløbet. Vi fik fart på når vi holdt os til mønstre fra cVations Platform, gav tydelig kontekst og definerede klare succeskriterier. Vi faldt i når jeg slap tøjlerne og lod AI'en finde egne veje.

  • Giv Copilot konkret kontekst: filer, API'er, domænemodeller, succeskriterier

  • Brug en instruktionsfil til at fastholde projektets standarder

  • Review AI-kode, som var den skrevet af en junior: navngivning, simpelhed, fejlhåndtering, concurrency

  • Sørg som altid for at have automatiserede checks som statisk kodeanalyse for at fange potentielle fejl tidligt

  • Mål succes på godkendte PR'er og stabil drift – ikke på linjer kode

AI-assisteret udvikling forstærker den retning og de standarder du sætter. Den skaber ikke struktur eller disciplin for dig. Det er nødvendigt at vide hvad man vil have, så man kan guide AI'en igennem flere iterationer indtil man opnår det. Uden stram styring får man kode med bugs der er svær at vedligeholde. Udvikleren skal altså kunne vurdere kvalitet, arkitektur og sikkerhed - AI'en er et kraftfuldt værktøj, men kræver erfaren styring.

Definerer du mål, kontekst og standarder er Copilot en dygtig medspiller. Lader du den arbejde uden retning finder den på egne mønstre - og bager efter sin egen kageopskrift.

En sidste opgave

Nu var der kun én opgave tilbage til GitHub Copilot:


Skriv et længere blogindlæg på dansk baseret på eksperimentet med at løse cVations Cake Planner-træningsopgaver.

Målgruppe: it-professionelle og deres ledere.

Tone: let, underholdende og professionel. Vær personlig, men præcis.

Struktur:

  • Titel og kort intro (Kan AI hjælpe?)

  • Hvorfor overhovedet prøve? (motivation, kvalitet af genereret kode)

  • Rammerne for kvalitet (regler for eksperimentet)

  • De tidlige sejre (bug, søndagstest med konkret dato, historik-endpoint, frontend)

  • Reality check (databaseopdatering, risiko for duplikater, race conditions, brug af Platformens UpsertAsync)

  • Møde med Brian (midtvejsevaluering inkl. citat om nystartet kollega og åbenhed om AI-brug)

  • Tilbage på sporet (Service Bus, Functions, E2E-test med 90 sek. polling pga. minut-frekvens på beskeder, bygge ny service)

  • Hvad skete der egentlig? (analyse: hvornår AI rammer/misser)

  • AI er ikke en genvej men en forstærker (konklusion, praktiske anbefalinger i punktopstilling)

Indholdskrav og eksempler:

  • Anekdoter: hardcoded søndag som konkret dato i test, 90 sekunders polling fordi beskeder kun sendes hvert minut, Copilot opfinder funktionalitet i stedet for at bruge Platform-metoder, race conditions håndteret med UpsertAsync.

  • Praktiske anbefalinger: kontekst, brug Platformens metoder før custom, instruktionsfil, test det væsentlige, review AI-kode som junior.

Sprog/format:

  • Dansk uden startkomma

  • Korte, klare sætninger