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.
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.
abc— matchar den bokstavliga strängen “abc”.missing|semester— matchar strängen “missing” eller strängen “semester”.\d{4}-\d{2}-\d{2}— matchar datum i formatet YYYY-MM-DD, till exempel “2026-01-14”. Utöver att säkerställa att strängen består av fyra siffror, ett bindestreck, två siffror, ett bindestreck och två siffror validerar det inte själva datumet, så “2026-01-99” matchar också detta regex-mönster..+@.+— matchar e-postadresser, alltså strängar som innehåller text, sedan ett “@” och sedan mer text. Detta gör bara en mycket grundläggande validering och matchar strängar som “nonsense@@@email”. Ett regex som matchar e-postadresser utan falska positiva eller negativa finns, men är opraktiskt.
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:
abcmatchar den bokstavliga strängen när tecknen inte har särskild betydelse (i det här exemplet “abc”).matchar ett valfritt enskilt tecken[abc]matchar ett enskilt tecken som finns inom hakparenteserna (i det här exemplet “a”, “b” eller “c”)[^abc]matchar ett enskilt tecken utom dem som finns inom hakparenteserna (t.ex. “d”)[a-f]matchar ett enskilt tecken inom intervallet i hakparenteserna (t.ex. “c”, men inte “q”)a|bmatchar något av mönstren (t.ex. “a” eller “b”)\dmatchar valfri siffra (t.ex. “3”)\wmatchar valfritt ordtecken (t.ex. “x”)\bmatchar en ord-_gräns_ (t.ex. i strängen “missing semester”, precis före “m”, precis efter “g”, precis före “s” och precis efter “r”)(...)matchar en grupp i ett mönster...?matchar noll eller en av ett mönster, till exempelwords?för att matcha “word” eller “words”...*matchar valfritt antal av ett mönster, till exempel.*för att matcha valfritt antal av valfritt tecken...+matchar en eller flera av ett mönster, till exempel\d+för att matcha ett antal siffror större än noll...{N}matchar exakt N av ett mönster, till exempel\d{4}för 4 siffror\.matchar ett bokstavligt “.”\\matchar ett bokstavligt “\”^matchar början av raden$matchar slutet av raden
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
- 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.
- 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?
- 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.
- Prova att skriva ett regex-mönster och använd kommandoradsverktyget
grepkommandoradsverktyg för att hitta förekomster avsubprocess.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? - Ö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. - Skriv ett regex för att ur JSON-strukturer av formen
{"name": "Alyssa P. Hacker", "college": "MIT"}fånga namnet (t.ex.Alyssa P. Hackeri detta exempel). Tips: i ditt första försök kan du råka skriva ett regex som extraherarAlyssa P. Hacker", "college": "MIT. Läs om giriga kvantifierare i Python regex-dokumentationen för att förstå hur du fixar det.- Få regex-mönstret att fungera även när namnet innehåller tecknet
"(dubbla citattecken kan escapas i JSON med\"). - Vi rekommenderar inte att använda reguljära uttryck för avancerade parsningsproblem i praktiken.
- Ta reda på hur du använder ditt programmeringsspråks JSON-parser för denna uppgift.
- Skriv ett kommandoradsprogram som tar en JSON-struktur av formen ovan på stdin och skriver ut namnet på stdout.
- Du behöver sannolikt bara några få rader kod.
- I Python kan du göra det enkelt på en enda rad kod utöver
import json.
- Få regex-mönstret att fungera även när namnet innehåller tecknet
Licensed under CC BY-NC-SA.