Kodkvalitet

Det finns många verktyg och tekniker som hjälper utvecklare att skriva kod av hög kvalitet. I den här föreläsningen går vi igenom:

Som bonusämne går vi också igenom reguljära uttryck, ett tvärgående ämne som används inom kodkvalitet (t.ex. för att köra en delmängd tester som matchar ett mönster) och i andra områden som IDE:er (t.ex. för sök och ersätt).

Många av dessa verktyg är språkspecifika (t.ex. lintnings-/formateringsverktyget Ruff för Python). I vissa fall stöder verktyg flera språk (t.ex. kodformatteraren Prettier). Koncepten är däremot nästan universella — du kan hitta kodformatterare, linters, testbibliotek och så vidare för vilket programmeringsspråk som helst.

Formatering

Automatiska kodformatterare snyggar upp ytsyntaxen automatiskt. På så sätt kan du fokusera på djupare och mer utmanande problem, medan formatteringsverktyget hanterar vardagsdetaljer som konsekvent användning av ' kontra " i strängar, mellanslag runt binära operatorer (x + y i stället för x+y), sorterade import-satser och att undvika för långa rader. En stor fördel med kodformatterare är att de standardiserar kodstilen för alla utvecklare som arbetar i kodbasen.

Vissa verktyg, som Prettier, är mycket konfigurerbara, och du bör versionshantera konfigurationsfilen i versionshantering för projektet. Andra verktyg, som Black och gofmt, har begränsad eller ingen konfigurerbarhet för att minska bikeshedding.

Du kan sätta upp integrering i IDE:n med din kodformatterare, så att koden formatteras automatiskt medan du skriver eller när du sparar en fil. Du kan också lägga till en EditorConfig-fil i projektet, som kommunicerar projektnivåinställningar till IDE:n, till exempel indenteringsstorlek per filtyp.

Lintning

Linters kör statisk analys (analyserar din kod utan att köra den) för att hitta antipatterns och potentiella problem i koden. Dessa verktyg går djupare än autoformatters och tittar bortom ytsyntax. Hur djup analysen är varierar mellan verktyg.

Linters kommer med listor av regler, med förinställningar som kan konfigureras på projektnivå. Vissa lintregler ger falska positiva resultat, så du kan stänga av dem per fil eller per rad.

Bra linters har inbyggd hjälp eller dokumentation som förklarar varje lintregel — vad regeln letar efter, varför det är dåligt och vad som är ett bättre alternativ för kodmönstret. Se till exempel dokumentationen för regeln SIM102 i Ruff, som fångar onödigt nästlade if-satser i Python-kod.

Vissa linters kan inte bara flagga problem utan också automatiskt fixa vissa problem åt dig.

Utöver språkspecifika linters kan ett annat verktyg som kan vara användbart vara semgrep, ett “semantiskt grep”-verktyg som arbetar på AST-nivå (i stället för teckennivå, som grep) och stöder många språk. Du kan använda semgrep för att enkelt skriva egna lintregler för dina projekt. Om du till exempel vill förhindra farlig användning av subprocess.Popen(..., shell=True) i Python kan du hitta det kodmönstret med:

semgrep -l python -e "subprocess.Popen(..., shell=True, ...)"

Testning

Programvarutestning är en standardteknik för att öka din tillit till att koden är korrekt. Du skriver kod, och sedan skriver du kod som kör den kod du skrev och kastar ett fel om koden inte fungerar som förväntat.

Du kan skriva tester för kodblock på olika granularitetsnivåer: enhetstester för enskilda funktioner, integrationstester för samspel mellan moduler eller tjänster och funktionella tester för end-to-end-scenarier. Du kan arbeta med testdriven utveckling, där du skriver tester innan du skriver implementationen. När du hittar programfel i koden kan du skriva regressionstester så att du fångar om funktionaliteten går sönder i framtiden. Du kan skriva property-baserade tester, introducerade i QuickCheck i Haskell och implementerade i många bibliotek, som Hypothesis för Python. Vilken teststrategi som passar beror på projektet, och du kommer sannolikt att använda en kombination.

Om programmet har externa beroenden som en databas eller ett webb-API kan det vara hjälpsamt att mocka dessa beroenden i testerna i stället för att låta koden interagera med tredjepartsberoenden vid testkörning.

Kodtäckning

Kodtäckning är ett mått som du kan använda för att mäta hur bra dina tester är. Kodtäckning tittar på vilka rader i koden som exekveras när testerna körs, så att du kan säkerställa att du täcker alla kodvägar. Verktyg för kodtäckning kan visa täckning rad för rad för att hjälpa dig skriva tester. Tjänster som Codecov erbjuder webbgränssnitt för att följa och visa kodtäckning över projektets historik.

Som alla mätetal är kodtäckning inte perfekt, så överoptimera inte för täckning utan fokusera på att skriva högkvalitativa tester.

Pre-commit-hookar

Git pre-commit-hookar, som blir enklare med ramverket pre-commit, kör automatiskt användarspecificerad kod före varje Git-incheckning. Projekt använder ofta pre-commit-hookar för att köra formatterare och linters, och ibland tester, automatiskt före varje incheckning för att säkerställa att kod i incheckningen följer projektets kodstil och är fri från vissa typer av problem.

Kontinuerlig integration

Tjänster för kontinuerlig integration (CI), som GitHub Actions, kan köra skript åt dig varje gång du pushar kod (eller vid varje ändringsförfrågan (PR), eller enligt schema). Utvecklare använder ofta CI-tjänster för att köra kodkvalitetsverktyg, inklusive formatterare, linters och tester. För kompilerade språk kan du säkerställa att koden kompilerar, och för statiskt typade språk kan du säkerställa att den typkontrollerar. Att köra CI vid varje push med nya incheckningar kan fånga fel som förs in i huvudversionen av koden. Att köra vid ändringsförfrågningar kan fånga problem i bidrag från andra. Att köra enligt schema kan fånga problem med externa beroenden (t.ex. när en utvecklare av misstag släpper en brytande ändring som semver-kompatibel).

Eftersom CI-skript körs separat från utvecklarnas datorer kan du enkelt köra långkörande jobb där. Det kan till exempel användas för att köra en test-_matris_ över olika operativsystem och versionskombinationer av programmeringsspråk för att säkerställa att programvaran fungerar korrekt på alla.

Generellt ska skriptet som körs i CI inte direkt ändra koden. Det kör verktyg i “check-only”-läge i stället för “fix”-läge, så till exempel formatteraren höjer ett fel när koden inte följer formatet.

Kodförråd innehåller ofta statusmärken i README, som visar CI-status och annan information som kodtäckning. Nedan är Missing Semesters nuvarande byggstatus.

Build Status Links Status

Vår länkkontroll, som använder GitHub Action proof-html, misslyckas ofta, vanligtvis på grund av problem på tredjepartswebbplatser. Trots det har den hjälpt oss att hitta och fixa många brutna länkar (ibland på grund av stavfel, oftast för att webbplatser flyttar innehåll utan att lägga till omdirigeringar eller för att webbplatser försvinner).

Ett bra sätt att lära sig detaljerna i CI-tjänster, formatterare, linters och testbibliotek är att lära genom exempel. Hitta högkvalitativa öppen källkod-projekt på GitHub — ju mer de liknar ditt projekt i språk, domän, storlek, omfattning och så vidare, desto bättre — och studera deras pyproject.toml, .github/workflows/, DEVELOPMENT.md och andra relevanta filer.

Kontinuerlig driftsättning

Kontinuerlig driftsättning använder CI-infrastruktur för att faktiskt driftsätta ändringar. Till exempel använder Missing Semesters kodförråd kontinuerlig driftsättning till GitHub Pages, så att webbplatsen byggs och driftsätts automatiskt när vi git pushar uppdaterade föreläsningsanteckningar. Du kan bygga andra typer av artefakter i CI, till exempel binärer för applikationer eller Docker-avbilder för tjänster.

Kommandokörningar

Kommandokörningar som just förenklar uppgiften att köra kommandon i projektets kontext. När du bygger upp infrastruktur för kodkvalitet i projektet vill du inte att utvecklarna ska behöva memorera kommandon som uv run ruff check --fix. Med ett sådant verktyg kan detta bli just lint, och du kan ha motsvarande kommandon som just format, just typecheck och så vidare för alla olika verktyg som en utvecklare kan vilja köra i projektet.

Vissa språkspecifika projekt- eller pakethanterare har inbyggt stöd för sådan funktionalitet, vilket betyder att du inte behöver använda ett språkagnostiskt verktyg som just. Till exempel stöder scripts-sektionen i en package.json för npm (Node.js) och sektionerna tool.hatch.envs.*.scripts i en pyproject.toml för Hatch (Python) detta.

Reguljära uttryck

Reguljära uttryck, ofta förkortat “regex”, är ett språk för att representera mängder av strängar. Regex-mönster används ofta för mönstermatchning i olika sammanhang, till exempel kommandoradsverktyg och IDE:er. Till exempel stöder ag regex-mönster för sökning i hela kodbasen (t.ex. ag "import .* as .*" hittar alla omdöpta importer i Python), och go test stöder alternativet -run [regexp] för att välja en delmängd av tester. Dessutom har programmeringsspråk inbyggt stöd eller tredjepartsbibliotek för reguljära uttryck, så du kan använda regex för funktioner som mönstermatchning, validering och parsning.

För att bygga intuition följer här några exempel på regex-mönster. I den här föreläsningen använder vi Python-syntax för regex. Det finns många regex-varianter med små skillnader mellan dem, särskilt i mer avancerad funktionalitet. Du kan använda en webbaserad testare som regex101 för att utveckla och felsöka reguljära uttryck.

Regex-syntax

Du hittar en omfattande guide till regex-syntax i den här dokumentationen (eller i någon av många andra resurser på nätet). Här är några grundläggande byggstenar:

Capture groups och referenser

Om du använder regex-grupper (...) kan du referera till delmängder av matchningen för extrahering eller sök-och-ersätt. För att till exempel extrahera bara månaden från ett datum i stil med YYYY-MM-DD kan du använda följande Python-kod:

>>> import re
>>> re.match(r"\d{4}-(\d{2})-\d{2}", "2026-01-14").group(1)
'01'

I din textredigerare kan du använda referenser till fångstgrupper i ersättningsmönster. Syntaxen kan variera mellan IDE:er. I VS Code kan du till exempel använda variabler som $1, $2 och så vidare, och i Vim kan du använda \1, \2 och så vidare för att referera till grupper.

Begränsningar

Reguljära språk är kraftfulla men begränsade. Det finns klasser av strängar som inte kan uttryckas med standardregex (t.ex. är det inte möjligt att skriva ett reguljärt uttryck som matchar mängden strängar {a^n b^n | n ≥ 0}, alltså mängden strängar med ett antal “a” följt av samma antal “b”, och mer praktiskt sett är språk som HTML inte reguljära språk). I praktiken stöder moderna regex-motorer funktioner som lookahead och backreferences som utökar stödet bortom reguljära språk, och de är extremt användbara i praktiken, men det är viktigt att veta att de fortfarande är begränsade i uttryckskraft. För mer avancerade språk kan du behöva använda en kraftfullare typ av parser (se till exempel pyparsing, en PEG-parser).

Lära sig regex

Vi rekommenderar att du lär dig grunderna (det vi har täckt i den här föreläsningen) och sedan tittar i regex-referenser när du behöver dem, i stället för att memorera hela språket.

Samtalsbaserade AI-verktyg kan vara effektiva för att hjälpa dig skapa regex-mönster. Prova till exempel att fråga din favorit-LLM med följande fråga:

Write a Python-style regex pattern that matches the requested path from log lines from Nginx.
Here is an example log line:

169.254.1.1 - - [09/Jan/2026:21:28:51 +0000] "GET /feed.xml HTTP/2.0" 200 2995 "-" "python-requests/2.32.3"

Övningar

  1. Konfigurera en formatterare, en linter och pre-commit-hooks för ett projekt du arbetar med. Om du har många fel bör autoformattering ta hand om formateringsfelen. För linterfelen kan du prova att använda en AI-agent för att fixa alla linterfel. Se till att AI-agenten kan köra lintern och observera resultaten, så att den kan arbeta iterativt för att fixa alla problem. Granska resultaten noga för att säkerställa att AI inte förstör din kod.
  2. Lär dig ett testbibliotek för ett språk du kan och skriv ett enhetstest för ett projekt du arbetar med. Kör ett verktyg för kodtäckning, generera en HTML-formaterad täckningsrapport och studera resultatet. Kan du hitta raderna som täcks? Din kodtäckning blir sannolikt väldigt låg. Prova att manuellt skriva några tester för att förbättra den. Prova att använda en AI-agent för att förbättra täckningen. Se till att kodagenten kan köra tester med täckning och producera en rad-för-rad-rapport, så att den vet var den ska fokusera. Är de AI-genererade testerna faktiskt bra?
  3. Sätt upp kontinuerlig integration som körs vid varje push för ett projekt du arbetar med. Låt CI köra formatering, lintning och tester. Bryt din kod med flit (t.ex. genom att introducera en linteröverträdelse), och säkerställ att CI fångar det.
  4. Prova att skriva ett regex-mönster och använd kommandoradsverktyget grep kommandoradsverktyg för att hitta förekomster av subprocess.Popen(..., shell=True) i din kod. Försök sedan att “bryta” regex-mönstret. Matchar semgrep fortfarande korrekt den farliga kod som gör att ditt grep-anrop missar?
  5. Öva regex-sök-och-ersätt i din IDE eller textredigerare genom att ersätta - Markdown-punktlistemarkörer med * i dessa föreläsningsanteckningar. Observera att det vore fel att bara ersätta alla “-“ i filen, eftersom tecknet används i många sammanhang som inte är punktlistemarkörer.
  6. Skriv ett regex för att ur JSON-strukturer av formen {"name": "Alyssa P. Hacker", "college": "MIT"} fånga namnet (t.ex. Alyssa P. Hacker i detta exempel). Tips: i ditt första försök kan du råka skriva ett regex som extraherar Alyssa P. Hacker", "college": "MIT. Läs om giriga kvantifierare i Python regex-dokumentationen för att förstå hur du fixar det.
    1. Få regex-mönstret att fungera även när namnet innehåller tecknet " (dubbla citattecken kan escapas i JSON med \").
    2. Vi rekommenderar inte att använda reguljära uttryck för avancerade parsningsproblem i praktiken.
    3. Ta reda på hur du använder ditt programmeringsspråks JSON-parser för denna uppgift.
    4. Skriv ett kommandoradsprogram som tar en JSON-struktur av formen ovan på stdin och skriver ut namnet på stdout.
    5. Du behöver sannolikt bara några få rader kod.
    6. I Python kan du göra det enkelt på en enda rad kod utöver import json.

Edit this page.

Licensed under CC BY-NC-SA.