Prompt engineering til kode, tests og forklaringer
Hvad er prompt engineering i praksis?
Prompt engineering er metoden til at beskrive en opgave så præcist, at en AI-model kan levere kode, tests eller forklaringer, du faktisk kan bruge. Du styrer opgave, kontekst, begrænsninger og format, og du justerer prompten iterativt, indtil output passer ind i dit udviklerworkflow.
Et prompt er kort sagt den besked, du sender til en model som ChatGPT, Claude, Gemini eller GitHub Copilot. Når du arbejder som udvikler, er det ikke nok at skrive “lav en funktion i Python”. Du skal forklare:
- Hvad problemet er (opgave)
- Hvor koden skal leve (kontekst, sprog, version, framework)
- Hvad der er acceptabelt output (acceptkriterier)
- Hvordan output skal struktureres (format)
- Hvad modellen ikke må gøre (begrænsninger)
Prompt engineering handler derfor ikke om at finde “magiske sætninger”, men om at arbejde systematisk: du beskriver opgaven, får et bud, tester det, justerer prompten og får et bedre bud. I en udviklerkontekst hænger det typisk sammen i et flow:
- Specifikation af problem eller feature
- Kodeudkast
- Tests og edge cases
- Kodeforklaring og dokumentation
- Debugging og revision
Hvis du vil have den helt grundlæggende introduktion til prompts, kan du evt. læse hvad et prompt er først. Her fokuserer vi på, hvordan du bruger prompt engineering målrettet til kode, tests og forklaringer.
Sådan opbygger du en stærk prompt til kode, tests og forklaringer
En god coding-prompt fortæller AI’en, hvad opgaven er, hvilken kode og hvilket miljø den arbejder i, hvad den ikke må ændre, hvilke kriterier løsningen skal opfylde, og hvordan resultatet skal formateres. Jo tydeligere du er på de punkter, jo mindre gætteri fra modellens side.
En simpel model, der virker godt til udviklingsopgaver, er:
- Kontekst – hvor lever koden, hvilket sprog, hvilken version, hvilket framework?
- Opgave – hvad skal der ændres, laves eller forklares?
- Input – relevant kode, fejlmeddelelser, dataeksempler.
- Begrænsninger – hvad må ikke brydes eller ændres?
- Acceptkriterier – hvornår er løsningen god nok?
- Outputformat – hvordan skal svaret struktureres?
- Validering – hvordan vil du tjekke, at svaret virker?
Her er en kompakt tabel med eksempler:
| Element | Hvad det indeholder | Eksempel (kode) | Eksempel (tests) | Eksempel (forklaring) |
|---|---|---|---|---|
| Kontekst | Sprog, version, framework, fil | “TypeScript, React 18, funktionel komponent” | “Node 20, Jest 29, eksisterende API” | “Python 3.11 script til data-cleaning” |
| Opgave | Hvad der skal laves | “Tilføj inputvalidering på denne handler” | “Skriv unit tests til funktionen below” | “Forklar hvordan algoritmen virker” |
| Input | Kode, fejl, data | Indsæt funktion eller filuddrag | Indsæt funktion og kort funktionsbeskrivelse | Indsæt kodeblok der skal forklares |
| Begrænsninger | Regler og no-go’s | “Må ikke ændre public API” | “Brug kun Jest, ingen ekstra libs” | “Ingen omskrivning af koden, kun forklaring” |
| Acceptkriterier | Hvornår er løsningen ok? | “Håndterer tom input og null” | “Dækker happy path + edge cases” | “Kan læses af en 1.års-studerende” |
| Outputformat | Struktur på svaret | “Kun én komplet funktion i en “`ts“` blok” | “Testfil i “`js“` med Jest-tests” | “Punktliste + kort resumé” |
Eksempel: generel vs. struktureret prompt
Dårlig prompt:
“Lav en funktion der validerer e-mails i JavaScript.”
Bedre prompt med modellet her:
“Du er senior JavaScript-udvikler.
Kontekst: Jeg bygger et lille Express-API i Node 20. Jeg vil validere brugeres e-mail, før jeg gemmer den i databasen.
Opgave: Skriv en ren, testbar funktion
isValidEmail(email: string): boolean.Begrænsninger: Brug kun standard JS/TS, ingen eksterne biblioteker. Undgå alt for aggressive regex, men fang tydelige fejl (manglende @, ingen domain, whitespace).
Acceptkriterier: Funktionen skal returnere
truefor typiske gyldige adresser ogfalsefor tydeligt ugyldige. Den må ikke kaste exceptions ved normale string inputs.Outputformat: Svar kun med én komplet TypeScript-funktion i en “`ts“` kodeblok, ingen forklarende tekst.”
Allerede her har du langt bedre kontrol over, hvad du får tilbage, og du gør det nemmere at køre og teste koden bagefter.
Sådan skriver du prompts, der giver bedre kode
For at få bedre kode skal du give AI’en det rigtige sprog og version, relevant eksisterende kode, klare krav, begrænsninger og et tydeligt outputformat, fx “én funktion”, “diff” eller “patch”. Jo mere du tænker som i et godt issue eller PR, jo bedre kode får du.
Nøgler til gode kodeprompts
Gode prompts til kodegenerering gør tre ting:
- Fastlægger konteksten – sprog, version, framework, fil.
- Afgrænser ændringen – hvad må ændres, og hvad skal forblive urørt?
- Definerer outputformat – hvordan du vil modtage koden.
Elementer, der næsten altid bør være med:
- Sprog og version: “Python 3.11”, “TypeScript i et React 18-projekt”.
- Miljø: backend API, CLI-tool, frontend-komponent, serverless-funktion.
- Eksisterende kode: det relevante udsnit, ikke hele repoet.
- Data-kontrakt: typer for input og output, fx
(req: Request, res: Response). - Begrænsninger: ingen breaking changes, ingen nye dependencies, ingen ændring af public API.
- Format: fuld fil, kun funktion, kun diff, kun patch.
Før/efter: fra “skriv noget kode” til brugbart patch
Før: vag kodeprompt
“Skriv et endpoint i Express, der opretter en bruger.”
Du risikerer her:
- Forkerte versioner af Express
- Anderledes error-håndtering end resten af projektet
- Ny struktur, der ikke matcher eksisterende routere
Efter: fokuseret kodeprompt
“Du er erfaren Node/Express-udvikler.
Kontekst: Jeg har et eksisterende Express API i Node 20. Vi bruger async/await og central error middleware. Her er den nuværende routerfil og userService:
“`ts // userRouter.ts (uddrag) router.get(‘/users/:id’, getUserById) // userService.ts (uddrag) export async function createUser(data: CreateUserInput) { /* … */ } “`
Opgave: Tilføj et POST
/usersendpoint iuserRouter.ts, der:
- Læser
nameogreq.body- Validerer at
- Kalder
createUserog returnerer 201 med JSON-responsenBegrænsninger: Du må kun ændre i
userRouter.ts. Brug eksisterende error-håndtering (kast errors i stedet for at sende responses manuelt).Acceptkriterier: Endpointet håndterer både succes og valideringsfejl for manglende e-mail.
Outputformat: Svar kun med den opdaterede
userRouter.tsfil i en “`ts“` kodeblok, uden forklaringer.”
Nu får du kode, der både passer ind og er nemmere at smide i en PR.
Promptflow til kode: fra specifikation til revision
I praksis fungerer det bedst at bruge AI i flere små skridt frem for én stor “lav alt”-prompt:
- Specifikation: Bed først modellen om at hjælpe med at formulere kravene. “Hjælp mig med at skrive en kort specifikation til…”
- Kodeudkast: Brug specifikationen som input og få et første udkast.
- Tests: Bed om relevante unit tests og edge cases (mere om det om lidt).
- Forklaring: Få en kort forklaring eller et PR-resumé, du kan bruge i review.
- Revision: Efter at have kørt kode og tests, prompt igen: “Opdater funktionen, så den også håndterer X, uden at ændre public API.”
Hvis du bruger værktøjer som Copilot i din editor, er principperne de samme, bare i mindre bidder. Vil du dykke mere ned i værktøjerne, kan du fx kigge på guiden til Microsoft Copilot eller ChatGPT på dansk for udviklere.
Sådan beder du AI om at skrive tests, der faktisk er brugbare
For at få brugbare tests skal du give AI funktionens kontrakt, de vigtigste fejltilstande og det testframework, du bruger, og derefter eksplicit bede om happy path, edge cases og negative tests. Ellers ender du ofte med trivielle tests, der mest kopierer implementationen.
Giv modellen funktionens kontrakt først
En typisk faldgrube er at skrive:
“Skriv unit tests til denne funktion i Jest:”
“`js function add(a, b) { return a + b } “`
Det giver ofte flade tests uden edge cases. Tilføj i stedet kontrakt og forventninger:
- Hvad funktionen gør i ord
- Gyldige og ugyldige input
- Forventede fejltilstande
- Eventuelle sideeffekter
Eksempel på testprompt med strategi
“Du er erfaren JavaScript-udvikler med fokus på test.
Kontekst: Vi bruger Jest 29 til unit tests i et Node 20-projekt.
Funktion (implementation):
“`js export function parsePort(value) { const n = Number(value) if (!Number.isInteger(n) || n <= 0 || n > 65535) { throw new Error(‘Invalid port’) } return n } “`
Kontrakt:
parsePortmodtager et tal eller en string og returnerer et heltal i intervallet 1-65535. Hvis værdien ikke er et heltal i det interval, kaster den"Invalid port".Opgave: Skriv Jest unit tests, der dækker:
- Happy path (gyldige porte, fx 80, ‘3000’)
- Boundary cases (0, 1, 65535, 65536)
- Negative tests (NaN, float, tom string, null, undefined, ‘abc’)
Begrænsninger: Brug ikke test.each. Tests skal være eksplicitte, så de er lette at læse for juniorer.
Outputformat: Svar med en komplet Jest testfil i en “`js“` kodeblok.”
Bed om både testliste og implementation
Hvis du vil have mere kontrol, kan du dele processen i to prompts:
- “Lav først kun en liste over testcases i tabel-form (input, forventet resultat/fejl, case-type).”
- “Skriv derefter Jest-tests baseret på tabellen.”
Det gør det nemmere for dig at gennemgå testdækningen, før du får genereret selve testkoden.
Lille rubric: Tjek om AI-tests er gode nok
Når du læser AI-genererede tests, kan du groft score dem ud fra disse spørgsmål:
- Happy path: Er der mindst én test, der dækker det normale, forventede forløb?
- Edge cases: Er grænser og særlige tilfælde testet (0, max-værdi, tom liste, osv.)?
- Fejltilstande: Bliver exceptions og fejlscenarier testet?
- Inputtyper: Er relevante typer dækket (string vs. number vs. null)?
- Uafhængighed: Er tests uafhængige af hinanden og uden unødige mocks?
- Læselighed: Kan en kollega hurtigt se, hvad der testes, og hvorfor?
Hvis 2-3 af de punkter halter, skal du justere din prompt og bede modellen forbedre testsættet, fx: “Udvid testsættet med flere edge cases omkring grænserne 1 og 65535.”
Vil du videre med teststrategi og praksis, kan du fx kigge i kategorien test og kvalitet eller artikler som om at få tests kørt konsekvent.
Sådan bruger du prompts til debugging og refaktorering
God debugging med AI kræver, at du giver den relevante kode, den præcise fejlmeddelelse, hvordan fejlen kan genskabes, og hvad den forventede adfærd er, samt at du beder om den mindst mulige ændring. Ellers gætter modellen og risikerer at omskrive for meget.
Debugging-prompt: giv problemet som en mini-bugreport
Strukturer din debugging-prompt som en kompakt bugreport:
- Miljø: sprog, version, framework, evt. OS.
- Kodeudsnit: kun det relevante stykke (funktion, komponent, query).
- Fejlmeddelelse: fuld tekst + stack trace-udsnit.
- Reproduktion: hvad du gør for at udløse fejlen.
- Forventet vs. faktisk adfærd.
- Begrænsning: fx “mindst mulige ændring”.
Eksempel:
“Du er erfaren React-udvikler.
Miljø: React 18, TypeScript, Vite. Vi får en runtime-fejl, når komponenten mountes.
Kode:
“`tsx function UserList({ users }: { users: User[] }) { const [selectedId, setSelectedId] = useState(users[0].id) // … } “`
Fejlmeddelelse:
“`text Cannot read properties of undefined (reading ‘id’) “`
Reproduktion: Fejlen sker, når
userser en tom liste.Forventet: Komponenten bør rendere en tom tilstand uden fejl, hvis listen er tom.
Opgave: Forklar den sandsynlige årsag til fejlen, og foreslå én mindst mulig ændring, der håndterer tomme lister korrekt.
Begrænsning: Du må kun ændre
UserList-komponenten.Outputformat: Først en kort forklaring i 2-3 linjer, derefter den opdaterede komponent i en “`tsx“` kodeblok.”
Refaktorering-prompt: bevar adfærd, forbedr struktur
Til refaktorering er nøglen at holde adfærd fast, men give modellen frihed til at forbedre struktur:
- Beskriv formålet med koden
- Beskriv, hvad der må ændres (navne, struktur, helper-funktioner)
- Beskriv, hvad der ikke må ændres (public API, returværdier, sideeffekter)
- Bed om tests først eller samtidig, så du kan verificere adfærden
Eksempel:
“Refaktorer følgende TypeScript-funktion, så den bliver kortere og lettere at læse:
“`ts export function calculateInvoiceTotal(items: Item[], discount?: number) { // … 40 linjers blandet logik … } “`
Bevar:
- Funktionsnavn og signatur
- Samme returtype og rounding-regler
Forbedr:
- Udtræk helper-funktioner hvor det giver mening
- Fjern duplikeret logik
Outputformat: Først en kort punktopstilling over ændringerne, derefter den refaktorerede funktion i en “`ts“` blok. Lav ingen ændringer i API’et.”
Hvis du vil arbejde mere struktureret med debugging generelt, kan artiklen om systematisk debugging være et godt supplement til dine prompts.
Hvornår skal du bruge debugging-, refaktorering- eller forklaringsprompt?
- Brug debugging-prompt, når du har en konkret fejlmeddelelse eller et reproducerbart problem.
- Brug refaktorering-prompt, når koden virker, men er svær at læse eller vedligeholde.
- Brug forklaringsprompt, når du (eller en kollega) skal forstå, hvad koden gør, før I ændrer den.
Sådan får du AI til at forklare kode klart og i det rigtige niveau
Hvis du vil have brugbare forklaringer, skal du altid angive målgruppe, ønsket detaljeniveau, fokusområde og format. Ellers gætter modellen selv, og du ender ofte med enten alt for overfladiske eller alt for lange svar.
Tre niveauer for kodeforklaring
Forestil dig denne korte funktion:
“`py def normalize_scores(scores: list[float]) -> list[float]: if not scores: return [] max_score = max(scores) return [s / max_score for s in scores] “`
Du kan bede om forklaring på mindst tre måder:
- Begynder-venlig – til studerende eller nye kolleger.
- Team-niveau – kort, fokuseret på designvalg.
- Dokumentation – mere formel, evt. docstring/README.
Eksempler på prompts til samme kode
Begynder-venlig forklaring:
“Forklar følgende Python-funktion til en førsteårs-studerende, der lige er begyndt på Python:
“`py # … funktionen her … “`
Fokus: Hvad sker der linje for linje, og hvad betyder
max()og list comprehension?Format: Kort tekst med simple ord, ingen matematiske symboler. Max 10 linjer tekst.”
Team-forklaring (code review niveau):
“Giv en kort forklaring af, hvad denne funktion gør, som jeg kan bruge i en pull request-beskrivelse. Fokusér på:
- Formålet med funktionen
- Hvordan tomme lister håndteres
- Eventuelle edge cases eller begrænsninger
Format: 3-5 punktopstillinger. Ingen linje-for-linje forklaring.”
Dokumentation:
“Skriv en kort Python docstring til denne funktion, velegnet til et internt dataanalyse-bibliotek. Inkludér:
- Hvad funktionen gør
- Parametre og returværdi
- Hvordan tom input håndteres
Format: Standard Python docstring i en “`py“` blok.”
Mini-matrix: tilpas forklaringen
| Målgruppe | Detaljeniveau | God prompt-tilpasning |
|---|---|---|
| Begynder | Højt (linje-for-linje) | “Forklar linje for linje med simple ord, uden at antage kendskab til X” |
| Teamkolleger | Mellem | “Kort opsummering, fokus på designvalg og trade-offs” |
| Stakeholders | Lavt | “Beskriv med 3 sætninger på ikke-teknisk engelsk, hvad funktionen hjælper med” |
| Dokumentation | Struktureret | “Skriv en API-dokumentationssektion med parametre, returtype og eksempel” |
Hvis du vil arbejde videre med dok-fokuserede prompts, kan guiden om at skrive en README der bliver læst give god inspiration til, hvilke informationer der faktisk er nyttige.
Hvilken prompt-teknik skal du bruge: zero-shot, few-shot eller system prompt?
Brug zero-shot til simple, velkendte opgaver, few-shot når format eller stil skal rammes præcist, og system prompts når du vil have konsistent adfærd over flere beskeder eller længere tid. Kombinér det med gode opfølgningsprompts i stedet for at jagte én perfekt formulering.
Zero-shot: når opgaven er simpel og veldefineret
Zero-shot betyder, at du ikke giver eksempler, kun instruktion. Det er fint til:
- Små helper-funktioner
- Enkeltstående forklaringer
- Korte, fokuserede ændringer i kode
Eksempel: “Skriv en ren Python-funktion, der…” kombineret med klar kontrakt og outputformat.
Few-shot: når format og stil er vigtige
Few-shot betyder, at du viser 1-3 eksempler på det output, du vil have. Det er særlig nyttigt til:
- Tests i et bestemt mønster
- Commit-beskeder
- PR-resuméer
- Specifikke kode-stilarter
Eksempel (testformat):
“Her er to eksempler på, hvordan vi skriver Jest-tests i dette projekt:
“`js // Example 1 it(‘returns 200 when user exists’, async () => { /* … */ }) // Example 2 it(‘returns 404 when user is missing’, async () => { /* … */ }) “`
Skriv nu nye tests til funktionen nedenfor i samme stil: …”
System prompt: når du vil have en stabil “virtuelt teammedlem”
I værktøjer der understøtter det, kan du angive en system prompt (system message) der beskriver AI’ens rolle og generelle regler. Det er nyttigt, når:
- Du vil have konsistens over længere tid (samme stil på tests, forklaringer osv.)
- Du kører gentagne opgaver (fx review af PR-beskrivelser eller generering af boilerplate)
Eksempel på system prompt til et “virtuelt reviewer” setup:
“Du er en erfaren full stack-udvikler. Når du bliver bedt om at hjælpe med kode:
- Antag TypeScript som standard
- Bevar eksisterende public API, medmindre andet er angivet
- Foreslå altid mindst én forbedring af læsbarhed
- Hvis du er usikker, så sig det eksplicit i stedet for at gætte”
Kombinér system prompt, few-shot og gode opfølgningsprompts, og du får et mere stabilt “AI-teammedlem” i stedet for tilfældige engangssvar. Hvis du vil se, hvordan forskellige modeller håndterer kodeopgaver, kan du fx se på sammenligningen af Codex og Claude Code eller ChatGPT vs. Gemini.
Sådan vurderer du, om et AI-svar er godt nok
Et AI-svar til kode er først godt nok, når du kan køre det, teste det, forstå det og begrænse dets effekt. Koden må ikke bare se plausibel ud – den skal være korrekt, testbar, læsbar og sikker i din kontekst.
Output-checkliste til kode og tests
Brug denne korte rubric, når du gennemgår AI-genereret kode:
- Korrekthed: Matcher koden specifikationen? Bruges de rigtige typer, argumenter og returværdier?
- Testbarhed: Kan du skrive eller køre unit tests mod koden uden at ændre alt for meget?
- Dækning af edge cases: Tænker koden over tomme lister, null/undefined, grænseværdier, timeouts osv.?
- Afhængigheder: Introducerer svaret nye pakker eller frameworks, du ikke vil bruge?
- Læselighed: Kan en kollega forstå logikken uden at åbne AI-chatten?
- Sikkerhed: Ser du oplagte sikkerhedsproblemer (SQL-injection, usanitiserede inputs, nøgle-læk osv.)?
Du kan sagtens bruge AI til at hjælpe med kvalitetstjekket, fx:
“Gennemgå følgende funktion kritisk. Peg på potentielle edge cases, sikkerhedsrisici og performance-problemer. Svar i punktopstilling.”
Prompt-review: er det prompten eller svaret, der er problemet?
Når output er dårligt, er det fristende bare at konkludere “AI virker ikke”. Men ofte er det prompten:
- Var opgaven for bred? (“Lav et bookingsystem”)
- Manglede du kontekst? (sprog, version, framework)
- Sagde du tydeligt, hvad du ikke ville have? (nye dependencies, breaking changes)
- Definerede du outputformat? (funktion, diff, tests, docstring)
I stedet for at starte forfra, kan du ofte forbedre svaret med en fokuseret opfølgning:
“Reducer ændringerne til mindst mulige patch, der kun berører funktionen X, og bevar den eksisterende signatur.”
“Udvid testsættet med flere edge cases omkring tom input og max-værdi.”
Brug AI-kode klogt – ikke blindt
AI kan spare dig tid, men det erstatter ikke din egen forståelse. Hvis du bare copy/paster kode uden at læse og teste den, snyder du mest dig selv fagligt og risikerer at bygge fejl ind i projektet. Der er en god gennemgang af den faldgrube i artiklen “Stop med bare at copy-paste AI-kode”.
Som tommelfingerregel:
- Brug AI som en hurtig sparringspartner, ikke en autoritet.
- Test altid koden i dit eget miljø.
- Lav mindst et hurtigt code review af AI-forslag, som om en junior-kollega havde skrevet det.
- Smid aldrig følsomme data, hemmelige nøgler eller fortrolig kode ukritisk ind i en ekstern model.
Næste skridt
Hvis du vil bygge videre på det her, er oplagte næste skridt:
- Øv dig i små, konkrete prompts til dine daglige opgaver.
- Læs mere om AI-prompts der virker til kodning, debugging og læring.
- Sæt tid af til at teste forskellen på zero-shot og few-shot på den samme opgave.
- Overvej, hvordan prompts kan blive en fast del af din workflow-kæde: specifikation → kode → tests → forklaring → review.
Jo mere bevidst du arbejder med prompt engineering, jo mere får du faktisk ud af AI i din hverdag som udvikler.









Send kommentar
Du skal være logget ind for at skrive en kommentar.