Edsger Dijkstras idéer om strukturerad programmering förklarar varför disciplinerad, enkel kod förblir korrekt och underhållbar när team, funktioner och system växer.

Mjukvara fallerar sällan för att den inte går att skriva. Den fallerar för att, ett år senare, kan ingen ändra den säkert.
När kodbaser växer börjar varje “liten” ändring få ringeffekter: en bugfix bryter en avlägsen funktion, ett nytt krav tvingar omskrivningar, och en enkel refaktor blir en vecka av noggrann koordinering. Det svåra är inte att lägga till kod—det är att behålla förutsägbart beteende medan allt runt omkring förändras.
Edsger Dijkstra hävdade att korrekthet och enkelhet bör vara första prioritet, inte något trevligt att ha. Vinsten är inte akademisk. När ett system är lättare att resonera om spenderar team mindre tid på akuta problem och mer tid på att bygga.
När folk säger att mjukvara måste “skala” menar de ofta prestanda. Dijkstras poäng är annorlunda: komplexitet skalar också.
Skalning visar sig som:
Strukturerad programmering handlar inte om att vara strikt för striktens skull. Det handlar om att välja kontrollflöde och uppdelning som gör det lätt att svara på två frågor:
När beteendet är förutsägbart blir förändring rutin istället för riskabelt. Det är därför Dijkstra fortfarande är relevant: hans disciplin riktar in sig på den verkliga flaskhalsen i växande mjukvara—att förstå den tillräckligt väl för att förbättra den.
Edsger W. Dijkstra (1930–2002) var en nederländsk datavetare som formade hur programmerare tänker kring att bygga tillförlitlig mjukvara. Han arbetade med tidiga operativsystem, bidrog till algoritmer (inklusive den kortaste-vägen-algoritmen som bär hans namn) och—viktigast för vardagsutvecklare—drev idén att programmering borde vara något vi kan resonera om, inte bara något vi provar tills det verkar fungera.
Dijkstra brydde sig mindre om huruvida ett program kunde fås att producera rätt output för några exempel, och mer om huruvida vi kunde förklara varför det är korrekt för de fall som är viktiga.
Om du kan ange vad en kodbit ska göra, bör du kunna argumentera (steg för steg) att den verkligen gör det. Denna inställning leder naturligt till kod som är lättare att följa, enklare att granska och mindre beroende av hjältedebuggning.
En del av Dijkstras skrivande kan kännas kompromisslöst. Han kritiserade “clever” tricks, slarvigt kontrollflöde och kodningsvanor som gör resonemang svårt. Strängheten handlar inte om stilpoliseri; den handlar om att minska tvetydighet. När kodens mening är klar, spenderar du mindre tid på att debattera intentioner och mer tid på att validera beteende.
Strukturerad programmering är praxis att bygga program från en liten uppsättning tydliga kontrollstrukturer—sekvens, selektion (if/else) och iteration (loopar)—istället för trassliga hopp i flödet. Målet är enkelt: gör vägen genom programmet begriplig så att du kan förklara, underhålla och ändra den med förtroende.
Folk beskriver ofta mjukvarukvalitet som “snabb”, “vacker” eller “funktionsrik”. Användare upplever korrekthet annorlunda: som tyst förtroende att appen inte kommer att överraska dem. När korrekthet finns märker ingen den. När den saknas slutar allt annat att spela roll.
“Det fungerar nu” betyder oftast att du provade några vägar och fick förväntat resultat. “Det fortsätter fungera” betyder att det beter sig som avsett över tid, kantfall och förändringar—efter refaktorer, nya integrationer, högre belastning och nya teammedlemmar som rör koden.
En funktion kan “fungera nu” men ändå vara skör:
Korrekthet handlar om att ta bort dessa dolda antaganden—eller göra dem explicita.
Ett litet fel förblir sällan litet när mjukvaran växer. Ett felaktigt tillstånd, en off-by-one-gräns eller en otydlig felhantering kopieras till nya moduler, kapslas in av andra tjänster, cachas, återförsökas eller “löses runt”. Med tiden slutar team fråga “vad är sant?” och börjar fråga “vad brukar hända?” Det är då incidenthantering blir arkeologi.
Multiplikatorn är beroenden: ett litet fel leder till många nedströmsfel, var och en med sina egna partiella fixar.
Tydlig kod förbättrar korrekthet eftersom den förbättrar kommunikation:
Korrekthet betyder: för de indata och situationer vi säger att vi stödjer, producerar systemet konsekvent de utfall vi lovar—och misslyckas på förutsägbara, förklarbara sätt när det inte kan.
Enkelhet handlar inte om att göra kod ”söt”, minimal eller smart. Det handlar om att göra beteende lätt att förutsäga, förklara och ändra utan rädsla. Dijkstra värderade enkelhet eftersom det förbättrar vår förmåga att resonera om program—speciellt när kodbasen och teamet växer.
Enkel kod håller ett litet antal idéer i rörelse samtidigt: klart dataflöde, klart kontrollflöde och tydliga ansvar. Den tvingar inte läsaren att simulera många alternativa vägar i huvudet.
Enkelhet är inte:
Många system blir svåra att förändra inte för att domänen är komplex, utan för att vi introducerar oavsiktlig komplexitet: flaggor som interagerar på oväntade sätt, specialpatcher som aldrig tas bort och lager som mest finns för att arbeta runt tidigare beslut.
Varje extra undantag är en skatt på förståelse. Kostnaden visar sig senare, när någon försöker fixa en bugg och upptäcker att en ändring i ett område subtilt bryter flera andra.
När en design är enkel kommer framsteg genom stadigt arbete: granskbara ändringar, mindre diffar och färre nödfixar. Team behöver inte “hjälteutvecklare” som minns varje historiskt kantfall eller kan felsöka under press kl. 02:00. I stället stödjer systemet normala människors uppmärksamhetsspann.
Ett praktiskt test: om du fortsätter lägga till undantag (“om inte…”, “utom när…”, “bara för den här kunden…”) samlar du antagligen oavsiktlig komplexitet. Föredra lösningar som minskar branching i beteendet—en konsekvent regel slår fem specialfall, även om den konsekventa regeln är något mer generell än du först föreställde dig.
Strukturerad programmering är en enkel idé med stora konsekvenser: skriv kod så att dess exekveringsväg är lätt att följa. I klarspråk kan de flesta program byggas från tre byggstenar—sekvens, selektion och repetition—utan att förlita sig på trassliga hopp.
if/else, switch).for, while).När kontrollflödet är byggt från dessa strukturer kan du oftast förklara vad programmet gör genom att läsa uppifrån och ner, utan att behöva “teleportera” runt i filen.
Innan strukturerad programmering blev norm fanns det mycket kod som förlitade sig på godtyckliga hopp (klassisk goto-stil). Problemet var inte att hopp alltid är onda; problemet är att obegränsat hopp skapar exekveringsvägar som är svåra att förutsäga. Du ställer frågor som “Hur hamnade vi här?” och “Vilket tillstånd har den här variabeln?”—och koden svarar inte tydligt.
Tydligt kontrollflöde hjälper människor att bygga en korrekt mental modell. Den modellen förlitar du dig på när du felsöker, granskar en pull request eller ändrar beteende under tidspress.
När struktur är konsekvent blir modifiering säkrare: du kan ändra en gren utan att påverka en annan, eller refaktorera en loop utan att missa en dold utgångsväg. Läsbarhet är inte bara estetik—det är grunden för att ändra beteende tryggt utan att förstöra det som redan fungerar.
Dijkstra förespråkade en enkel idé: om du kan förklara varför din kod är korrekt kan du ändra den med mindre rädsla. Tre små resonemangsverktyg gör det praktiskt—utan att förvandla ditt team till matematiker.
En invariant är ett faktum som förblir sant medan en kodbit körs, särskilt inne i en loop.
Exempel: du summerar priser i en varukorg. En användbar invariant är: “total equals the sum of all items processed so far.” Om det förblir sant i varje steg är resultatet pålitligt när loopen slutar.
Invariants är kraftfulla eftersom de fokuserar din uppmärksamhet på vad som aldrig får gå sönder, inte bara vad som bör hända härnäst.
En precondition är vad som måste vara sant innan en funktion körs. En postcondition är vad funktionen garanterar efter att den slutförts.
Vardagliga exempel:
I kod kan en precondition vara “inmatningslistan är sorterad” och postcondition “utdata är sorterad och innehåller samma element plus det insatta.”
När du skriver ner dem (även informellt) blir designen skarpare: du bestämmer vad en funktion förväntar sig och lovar, och du gör den naturligt mindre och mer fokuserad.
I granskningar skiftar debatten bort från stil (“jag skulle skriva det annorlunda”) till korrekthet (“behåller den här koden invariantet?” “Håller vi preconditionen eller dokumenterar vi den?”).
Du behöver inga formella bevis för att få nytta. Välj den loop som är mest buggig eller den knepigaste tillståndsuppdateringen och lägg till en enradig invariantkommentar ovanför den. När någon redigerar koden senare fungerar kommentaren som ett räcke: om en förändring bryter detta faktum är koden inte längre säker.
Testning och resonemang strävar efter samma mål—mjukvara som beter sig som avsett—men de fungerar väldigt olika. Tester upptäcker problem genom att prova exempel. Resonemang förhindrar hela kategorier av problem genom att göra logiken explicit och kontrollerbar.
Tester är ett praktiskt säkerhetsnät. De fångar regressioner, verifierar verkliga scenarier och dokumenterar förväntat beteende så hela teamet kan köra det.
Men tester kan bara visa att buggar finns, inte att de inte finns. Inget testsuite täcker varje input, varje timingvariation eller varje interaktion mellan funktioner. Många “fungerar på min maskin”-fel kommer från otestade kombinationer: ett sällsynt input, en särskild ordning av operationer eller ett subtilt tillstånd som bara uppstår efter flera steg.
Resonemang handlar om att bevisa egenskaper i koden: “den här loopen terminerar alltid”, “den här variabeln är aldrig negativ”, “den här funktionen returnerar aldrig ett ogiltigt objekt.” När det görs väl utesluter det hela kategorier av fel—särskilt kring gränser och kantfall.
Begränsningen är ansträngning och omfattning. Fullständiga formella bevis för en hel produkt är sällan ekonomiska. Resonemang fungerar bäst när det tillämpas selektivt: kärnalgoritmer, säkerhetskritiska flöden, betalningslogik och concurrency.
Använd tester brett och applicera djupare resonemang där fel är dyra.
En praktisk brygga mellan de två är att göra avsikten exekverbar:
Dessa tekniker ersätter inte tester—de drar åt nätet. De förvandlar vaga förväntningar till kontrollerbara regler, vilket gör buggar svårare att skriva och enklare att diagnostisera.
“Clever” kod känns ofta som en vinst i stunden: färre rader, en snygg trick, en one-liner som får dig att känna dig smart. Problemet är att cleverness inte skalar över tid eller över människor. Sex månader senare glömmer författaren tricket. En ny kollega läser koden bokstavligt, missar det dolda antagandet och ändrar det på ett sätt som bryter beteendet. Det är “cleverness debt”: kortsiktig snabbhet köpt med långsiktig förvirring.
Dijkstras poäng var inte “skriv tråkig kod” som stilpreferens—det var att disciplinerade begränsningar gör program lättare att resonera om. På ett team minskar begränsningar också beslutsutmattning. Om alla redan känner till default (hur man namnger saker, hur man strukturerar funktioner, vad “klart” innebär) slipper ni återuppliva grunderna i varje pull request. Den tiden går tillbaka till produktarbete.
Disciplin syns i rutinpraxis:
Några konkreta vanor förhindrar att cleverness debt samlas:
calculate_total() framför do_it()).Disciplin handlar inte om perfektion—det handlar om att göra nästa ändring förutsägbar.
Modularitet är inte bara “dela upp kod i filer.” Det är att isolera beslut bakom tydliga gränser, så resten av systemet inte behöver veta (eller bry sig om) interna detaljer. En modul gömmer de stökiga delarna—datastrukturer, kantfall, prestandatrick—och exponerar en liten, stabil yta.
När en ändringsförfrågan kommer är det idealiska: en modul ändras, och allt annat förblir orört. Det är den praktiska betydelsen av “håll förändring lokal.” Gränser förhindrar oavsiktlig koppling—där en uppdatering tyst bryter tre andra eftersom de delade antaganden.
En bra gräns gör också resonemang enklare. Om du kan ange vad en modul garanterar kan du resonera om det större programmet utan att läsa om dess hela implementation varje gång.
Ett gränssnitt är ett löfte: “Givet dessa indata kommer jag producera dessa utdata och upprätthålla dessa regler.” När löftet är tydligt kan team arbeta parallellt:
Det handlar inte om byråkrati—det handlar om att skapa säkra samordningspunkter i en växande kodbas.
Du behöver ingen stor arkitekturöversyn för att förbättra modularitet. Prova dessa lättviktiga kontroller:
Välritade gränser förvandlar “förändring” från ett systemomfattande evenemang till en lokal redigering.
När mjukvara är liten kan du “hålla allt i huvudet.” I skala slutar det vara sant—och felsätten blir bekanta.
Vanliga symptom ser ut såhär:
Dijkstras huvudantagande var att människor är flaskhalsen. Klart kontrollflöde, små välbestämda enheter och kod du kan resonera om är inte estetiska val—de är kapacitetsmultiplikatorer.
I en stor kodbas fungerar struktur som kompression för förståelse. Om funktioner har explicita in- och utdata, moduler har gränser du kan namnge, och “happy path” inte är ihoptrasslad med varje kantfall, spenderar utvecklare mindre tid på att återskapa avsikt och mer tid på att göra avsiktliga ändringar.
När team växer ökar kommunikationskostnader snabbare än kodmängd. Disciplinerad, läsbar kod minskar mängden tribal knowledge som krävs för att bidra säkert.
Det syns direkt i onboarding: nya ingenjörer kan följa förutsägbara mönster, lära sig en liten mängd konventioner och göra ändringar utan en lång rundtur i “gotchas.” Koden lär ut systemet.
Under en incident är tid knapp och förtroende skört. Kod skriven med explicita antaganden (preconditions), meningsfulla kontroller och enkelt kontrollflöde är lättare att spåra under press.
Lika viktigt är att disciplinerade ändringar är enklare att rulla tillbaka. Mindre, lokala redigeringar med tydliga gränser minskar risken att en rollback triggar nya fel. Resultatet är inte perfektion—det är färre överraskningar, snabbare återställning och ett system som förblir underhållbart allteftersom år och bidragsgivare ackumuleras.
Dijkstras poäng var inte “skriv kod på det gamla sättet.” Det var “skriv kod du kan förklara.” Du kan anta den inställningen utan att förvandla varje funktion till en formell bevisövning.
Börja med val som gör resonemang billigt:
Ett bra tumregel: om du inte kan sammanfatta vad en funktion garanterar i en mening gör den förmodligen för mycket.
Du behöver ingen stor refaktorering. Lägg till struktur vid skarvarna:
isEligibleForRefund).Dessa uppgraderingar är inkrementella: de minskar kognitiv belastning för nästa ändring.
När du granskar (eller skriver) en ändring, fråga:
Om granskare inte snabbt kan svara signalerar koden dolda beroenden.
Kommentarer som upprepar koden blir snabbt inaktuella. Skriv istället varför koden är korrekt: nyckelantaganden, kantfall ni skyddar mot och vad som skulle gå sönder om antagandena ändras. En kort notis som “Invariant: total equals sum of processed items” kan vara mer värdefull än en rad med förklaring.
Om du vill ha en lätt plats för att fånga dessa vanor, samla dem i en delad checklista.
Moderna team använder i allt högre grad AI för att snabba upp leverans. Risken är välkänd: snabbhet idag kan bli förvirring imorgon om den genererade koden är svår att förklara.
Ett Dijkstra-vänligt sätt att använda AI är att behandla det som en accelerator för strukturerat tänkande, inte som en ersättning. Till exempel, när du bygger i Koder.ai kan du behålla “resonemang först”-vanorna genom att göra dina prompts och granskningssteg explicita:
Även om du slutligen exporterar källkoden och kör den någon annanstans gäller samma princip: genererad kod bör vara kod du kan förklara.
Det här är en lättviktig “Dijkstra-vänlig” checklista du kan använda under granskningar, refaktorer eller innan merge. Det handlar inte om att skriva bevis hela dagen—det handlar om att göra korrekthet och klarhet till standard.
total equals sum of processed items” förhindrar subtila buggar.Välj en rörig modul och strukturera om kontrollflödet först:
Lägg sedan till några fokuserade tester runt de nya gränserna. Om du vill ha fler mönster som detta, läs relaterade inlägg.
Därför att när kodbaser växer blir huvudproblemet att förstå systemet—inte att skriva det. Dijkstras fokus på förutsägbart kontrollflöde, tydliga kontrakt och korrekthet minskar risken att en “liten ändring” orsakar överraskande beteenden någon annanstans, vilket är precis det som bromsar team över tid.
I det här sammanhanget handlar “skala” mindre om prestanda och mer om att komplexitet multipliceras:
Dessa krafter gör resonemang och förutsägbarhet viktigare än ”cleverness”.
Strukturerad programmering förespråkar en liten uppsättning tydliga kontrollstrukturer:
if/else, switch)for, while)Målet är inte stelhet—det är att göra exekveringsvägar lätta att följa så att du kan förklara beteendet, granska ändringar och felsöka utan att behöva “teleportera” genom koden.
Problemet är obegränsade hopp som skapar svårförutsägbara vägar och oklart tillstånd. När kontrollflödet trasslar sig bortslösar utvecklare tid på att besvara grundläggande frågor som “Hur kom vi hit?” och “Vilka värden är giltiga nu?”.
Moderna motsvarigheter är djupt nästlade grenar, spridda tidiga avbrott och implicita tillståndsändringar som gör beteendet svårt att spåra.
Korrekthet är den tysta funktion användarna förlitar sig på: systemet gör konsekvent det det lovar och misslyckas på förutsägbara, förklarbara sätt när det inte kan uppfylla löftet. Det är skillnaden mellan “det fungerar i några exempel” och “det fortsätter fungera efter refaktorer, integrationer och kantfall”.
För att beroenden förstärker fel. Ett litet fel i tillstånd eller gräns kopieras, cachas, återförsökas eller ”arbetas runt” i andra moduler och tjänster. Med tiden slutar team fråga “vad är sant?” och börjar fråga “vad händer vanligtvis?”, vilket gör incidenter svårare och ändringar mer riskfyllda.
Enkelhet handlar om få idéer i rörelse samtidigt: tydligt ansvar, tydligt dataflöde och minimalt med specialfall. Det handlar inte om färre kodrader eller smarta one-liners.
Ett bra test är om beteendet förblir lätt att förutsäga när kraven ändras. Om varje nytt fall lägger till ett “om inte…” så samlar du oavsiktlig komplexitet.
En invariant är ett faktum som måste förbli sant under en loop eller tillståndsövergång. Ett lätt sätt att använda den:
total equals the sum of processed items”)Det gör senare ändringar säkrare eftersom nästa person vet vad som inte får brytas.
Tester hittar buggar genom att köra exempel; resonemang förhindrar hela kategorier av buggar genom att göra logiken explicit. Tester kan inte bevisa frånvaro av defekter eftersom de inte täcker alla indata eller tidsvariationer. Resonemang är särskilt värdefullt för områden där fel är kostsamma (pengar, säkerhet, concurrency).
En praktisk blandning är: breda tester + riktade assertioner + tydliga preconditions/postconditions kring kritisk logik.
Börja med små, upprepbara åtgärder som minskar kognitiv belastning:
Detta är inkrementella “strukturuppgraderingar” som gör nästa ändring billigare utan omstart.