Torbjorn Zetterlund

Wed 06 2021
Image

Efter att ha läst “Clean Code” lärde jag mig detta

by bernt & torsten

Jag har läst klart koden av farbror Bob. När jag gillar att skriva ner allt jag läser, tänkte jag att det skulle vara en bra idé att sammanfatta boken och skapa en praktisk lekbok.

Självklart är denna lista inte fullständig. Det kommer förmodligen att utvecklas och förändras på många sätt, när jag upplever och upptäcker fler kodningsstilar, scheman och synpunkter.

Eftersom en ren kod bör läggas ut kommer denna artikel att beskrivas på samma sätt. Först kommer jag att täcka makroprinciperna – de övergripande idéerna och begreppen ren kod.

Ren metod för kodning

Utvecklare skriver kod för sitt yrke, de läser också en hel del. Faktum är att de flesta utvecklare läser mer än den faktiska kodningen.

Hur många gånger har du läst koden du just skrivit? Uppenbarligen är förhållandet för läsning än för verklig kodning.

Om det mesta av vår tid går åt till att förstå och läsa kod, är det bättre att se till att när vi faktiskt kodar, kommer det att vara otvetydigt för alla våra kamrater.

Eftersom du förmodligen kommer att bli nästa person att läsa koden du skriver, se till att du kommer att förstå den även om två månader. Jag är säker på att nästan varje programmerare (åtminstone jag) har skrivit kod klockan 03.00 bara för att vakna nästa dag och spendera timmar på att försöka förstå vad som händer.

Att se till att din kod är tydlig och ren kommer att minimera WTF -ögonblicken, förbättra din produktivitet och ha långsiktiga resultat. Gör dig själv en tjänst och börja skriva ren kod.

Det finns fler parametrar att koda än “fungerar” kontra “fungerar inte”

Koden ska inte mätas enbart på “fungerar” eller “fungerar inte”. Naturligtvis är kod som inte fungerar inte bra – för den fungerar inte.

Men kod som fungerar är inte nödvändigtvis bra, och det är en mycket viktig skillnad att göra. När vår kod visar sig fungera är vårt jobb som utvecklare inte klart.

En professionell utvecklare ser till att efter att koden klarat alla tester är den också tydlig, ren och underhållbar. De faktiska principerna för hur man skriver ren kod kommer att följa, men för närvarande får vi inte bara bedöma koden utifrån dess funktionsförmåga.

Det finns fler parametrar och vägledande principer för vad som är en välskriven kodbit.

Bättre klar än smart

Som mjukvaruutvecklare ägnar vi oss åt problemlösning, kreativitet och intensivt tänkande. Många gånger, när vi stöter på komplexa problem, kan vår kod visa sig vara “smart”- som i att göra mycket smarta saker som bara den som har skrivit koden kan förstå vad det är som händer.

Det är ett oönskat resultat, även när problemet är svårt att lösa.

Sann behärskning och professionalism är att lösa ett komplext problem och visa upp lösningen rent och tydligt så att andra människor kommer att kunna granska koden och vara delaktig i den. Det är inte för att vi behöver vara trevliga mot våra kollegor och dela med sig av det roliga (även om det också är en bra anledning), snarare än om du är den enda som förstår koden kommer det att skada dig på lång sikt.

Dina kollegor kommer inte bara att ha svårt att återskapa koden, det kommer också att begränsa projektet och göra det långsammare – och du kommer att vara knuten till en kod för alltid tills du dör eller lämnar företaget.
Vi måste se till att lösningen verkar tydlig och enkel för alla som läser den koden efter att vi har löst ett problem.

Förväntan och tillit är avgörande

När någon läser din kod finns det en process för att bygga upp förtroende mellan dig och läsaren. Tillit förmedlas i princip till förtroende. Läsaren har en viss tillit till koden du har skrivit, och när förtroendet är högt kan projektet gå snabbare.

Jag hoppas att det är klart nu att läsaren också är ditt framtida jag. Om du inte litar på din egen kod, lycka till.

Så hur kan vi bygga upp förtroende och förtroende hos läsaren? Genom att låta dem förutse korrekt vad som kommer att hända. När läsaren förstår koden, vet vad koden ska göra och korrekt förutser vad som kommer att hända, byggs förtroende upp mellan sidorna.

Förhindra överraskningar, oklarhet och tvetydig kod som kan förvirra läsaren. Om en funktion eller en klass förväntas utföra en viss handling, men läsaren blir förvånad och andra handlingar görs – förtroendet skakas och läsaren kan inte lita på programmeraren längre, hur skulle de kunna göra det? De hade fel på denna kod, och nu kommer de att vara i ett konstant gissnings läge under hela recensionen.

Ren kod sker inte direkt, av sig själv

Nu när vi har lagt fram varför ren kod är avgörande för vår framgång som utvecklare, står vi inför den ödesdigra frågan:

Hur skriver jag ren kod?

Tja, det händer säkert inte av sig själv. det kräver övning, kontinuerliga iterationer och refactoring, och ständigt följa principerna för ren kod. Farbror Bob nämner tydligt att även han inte skriver ren kod första gången. varje kod kod undersöks efter att den har skrivits och refaktorerats för att följa principerna för ren kod.

Tidlösa principer

Separata bekymmer

Varje avsnitt i koden bör behandla ett visst problem med programvaran. Att skriva funktioner eller klasser som innebär distinkta problem är inte ett exempel på ren kod. Håll istället affärslogiken åtskild. Oavsett om vi pratar i termer av moduler, klasser, funktioner – var och en i sin egen abstraktionsnivå.

Ändå är principen enkel: Vad är logiken jag skriver nu? Vad är det största problemet här? Har jag andra problem som genomförs? Om du gör det, separera dem.

Koden är som en artikel

En artikel, eller en blogg, har en tydlig struktur. Huvudämnet är högst upp, och när vi fortsätter med artikeln kan vi märka att huvudämnet är uppdelat i underdelar, och inom dem har vi mer information om varje del.

Koden bör följa samma princip.

I början kommer vi att ha det största bekymret som koden handlar om. När vi utvecklas kommer funktionen och klasserna att innehålla mer detaljer om genomförandet och hålla en tydlig separering av oro-precis som varje blogg har en underrubrik och ett stycke som är nära relaterat till det.

Varje kod bör delas in i klasser och funktioner som behandlar ett specifikt problem, och när vi fortsätter inom dem hanterar de bara det problemet

Scoutregeln

När jag var på scouterna var jag instruktör för barn med särskilda behov. Vår vägledande princip när vi åkte på utflykter var “lämna platsen renare än du hittade den”.

Uppenbarligen känner farbror Bob i sin bok ungefär samma sak om kod – Lämna alltid koden bättre än du hittade den. Som diskuterats tidigare är ren kod en process. Den består av kontinuerlig förbättring och utveckling, av dig och av andra i organisationen.

Lämna därför koden bättre än du hittade den. Dåliga variabelnamn, värdelösa kommentarer, kommenterad kod – ju längre den stannar desto luktigare blir den. Var proaktiv. Var en scoutpojke.

Ren, återanvändbar och underhållbar

Se alltid till att din kod följer dessa riktlinjer. Genom att se till att det gör det, kryssar du av de flesta av bokens principer. Ren kod är kod som har en bra struktur, är tydlig, väl namngiven och inte innehåller överraskningar.

Återanvändbar kod tvingar dig att separera problem, behålla rätt abstraktionsnivå och hindrar dig från att skriva kod som är logiskt beroende av andra avsnitt (mer om det senare).

Underhållbar kod tillåter andra att bidra och förstå vad som händer. Det tar hänsyn till att programvara omvandlas med tiden. Förutse därför ändringar och se till att koden blir lätt att återskapa om ändringar sker.

Aldrig duplicera

Dubblering innebär att koden inte är ren. Följ DRY -principen – upprepa inte dig själv. Dubbleringskrik för en funktion, klass, komponent- vad som helst! duplicera bara inte!

När du befinner dig med minsta ctrl+c och ctrl+v -sekvens, fråga dig själv intuitivt om det finns ett bättre alternativ. För det mesta blir svaret ett definitivt JA.

Undvik logiskt beroende

Logiskt beroende innebär att en kodbit förlitar sig på en separat kodbit för att innehålla viss affärslogik, förutsatt att koden inte ändras och därför är implementeringen bra.

När vi skriver kod måste vi se till att koden är “på egen hand” – att vi har rätt abstraktionsnivå, att koden inte känner till information som den inte ska veta (baserat på dess nivå av abstraktion och oro) och att om koden omstruktureras kommer vi inte att få ovanligt många biverkningar även i andra funktioner och klasser.

Namngivning

Välj beskrivande och entydiga namn

Namn bör beskriva vad som är gjort eller innehåller. En del av att vara tydlig är att inte låta någon annan tolkning av namnet och rikta läsaren till endast en möjlig slutsats.

Lägg märke till att exemplet nedan inte kräver någon kommentar eller förklaring av något slag. den är beskrivande och leder bara till en slutsats.

// BAD
let days;// elapsed time in days 
//CLEAN 
let daysSinceCreation;
let daysSinceUpdate;
let ageInDays;
let elapsedDays;

Gör meningsfulla skillnader

Det är viktigt att ha namn som skiljer sig åt i sammanhang och struktur. Av uppenbara skäl vill vi inte förväxla sammanhanget och innebörden av en variabel/funktion med den andra.

De får dock inte ha ett mycket liknande namn också.

// Not distincting context 
getActiveAccount();
getActiveAccountInfo();
getActiveAccountData();

I exemplet ovan är det inte säkert vad skillnaden är mellan konto, kontoinformation och kontodata. De säger i stort sett samma sak. Vad är data? Vad är informationen? Vad är konto om inte kontodata och hur är de olika? Så många frågor från dessa tvetydiga namn.

// Not disticnting structure
getActiveAccount()
getActiveAccounts()
getActivityAccount()
//With distinction 
getActiveAccount()
getManyActiveAccounts()
getAccountActivity()

Autofullständighetsfunktionen i alla kodredigerare bör också beaktas. Att ha mycket liknande namn är benäget för autosuggestionsfel, felaktig läsning och följaktligen – felaktiga funktioner kallas.

Använd sökbara namn

Medan vi namnger variabler i vår kod måste vi se till att de inte är kodade, för korta (1–2 bokstäver) och inte för vanliga – beroende på deras betydelse och omfattning.

I exemplet nedan är appen en kurshanterings plattform för studenter.

//Bad examples
const max=7;
const students=7;
//Clean example
const MAX_CLASSES_PER_STUDENT=7;

Jag tror inte att du kommer att ha några problem med att hitta den variabeln, medan max och studenter med stor sannolikhet är vanliga ord i den här appen

Inga magiska värden

Magiska värden är värden som “magiskt” visas i koden och ska förmedla någon mening, men ingen kan säga det eftersom de bara är siffror/strängar.

Undvik att stänka magiska värden i koden till varje pris. Se till att tilldela en variabel till dessa värden för tydlighet och enkel refaktorering.

//BAD 
if(val === 38) console.log('congratulations!')
//CLEAN
const JACKPOT=38;
if(val === JACKPOT) console.log('congratulations!')

Avslöja inte genomförandet

Hur vi implementerar kod skiljer sig från tid till annan och förändras helt och hållet. Att binda ett visst sätt att implementera är begränsande och kommer faktiskt att bli föråldrat när tiden går.

//BAD 
spliceProductFromArr()
let productArr=[]; 
//CLEAN 
removeProductById()
let products=[];

Akta dig för namngivnings konventioner

Om vi ​​utvecklar en app som främst är relaterad till bilar, vad blir vår namngivningskonvention? Bil? Fordon? Bil? Oavsett vad det är, se till att bilen är konsekvent.

Det valda namnet bör refereras till i funktionerna, variablerna, tjänsterna, klasserna – när det behövs. Ändå är synonymer väldigt förvirrande, så välj ett ord med ett ord och håll fast vid det.

Undvik mental kartläggning

Variabler som kräver att läsaren (och kodaren) kommer ihåg deras betydelse är i allmänhet dåliga variabelnamn. Det enda undantaget som jag tänker på är i och j som är iteratorer i en loop.

Förutom det bör namn återspegla verkligheten, vara konsekventa och beskrivande nog för att undvika mental kartläggning eller memorering av något slag.

Funktioner

Ska vara liten

Små funktioner hjälper dig att berätta historien korrekt. De lägger till ordning, separering av bekymmer, korrekt abstraktion och korrekt struktur till din kod.

Om din funktion inte är liten följer den förmodligen inte andra viktiga principer som vi snart kommer att upptäcka. Små funktioner, när de heter rätt, gör koden tydlig, lätt att felsöka och enkel att följa.
Hur liten? Du kanske undrar. Tja, enligt författaren till Clean Code, Uncle Bob, ska en funktion aldrig vara mer än 20 rader kod.

Om det är, enligt Bob, kan du förmodligen dela upp det i en annan funktion. När det gäller det exakta antalet är jag säker på att det bara är en tumregel eller en röd flagga för att hålla oss uppmärksamma om vi skriver stora funktioner.

”Den första regeln för funktioner är att de måste vara små. Den andra regeln är att de måste vara mindre än så. ”

Gör en sak

Funktioner bör göra en sak, och bara en sak. Ibland är det mycket svårt att se till att funktionen bara gör en sak, men det är en viktig princip att se till att vi inte gör flera saker samtidigt. Bobs styre? Om det gör mer än en sak har du förmodligen 2 funktioner, inte en.

“Funktioner borde göra en sak. De borde göra det bra. De borde bara göra det”

Bör namnges korrekt

Alla funktioner gör åtgärder. det är syftet med dem – att fungera. Därför, när de heter funktioner, bör de ha verb som innebär vad de gör.

få, ställ in, spara, ta bort, ladda … du förstår kärnan i det, eller hur?

Ett vanligt undantag, som är allmänt känt, är dock ordet – att kontrollera om det är ett booleskt tillstånd.

Konventionen är att funktionen ska returnera en boolean angående tillståndet (inga överraskningar med fel förväntan). Så ett ogiltigt lösenord (lösenord) förväntas returnera en booleskt.

Bör minimera argument

Helst, ju mindre argument desto bättre. Men om vi vill göra våra funktioner återanvändbara och oberoende av någon annan affärslogik kan vissa argument behövas.

Därför är upp till 2 argument bra, 3 bör undvikas, och utöver det bjuds hårda sonderingar. Tanken bakom denna princip är att funktioner ändå är väldigt små, så om du behöver 3 argument som är avgörande för affärslogiken har troligen avkopplingen av den stora funktionen gjorts dåligt.

Betydelsen är att logiken är bunden för starkt, och kanske är separation i en annan punkt i koden bättre.

Skapa inte biverkningar

Funktioner bör inte ha några biverkningar eller konsekvenser för andra processer, utom dess huvudansvar. Som “Gör en sak” -regeln antyder, bör den inte göra något annat än den avsedda uppgiften att utföra.

Detta hjälper till att förhindra överraskningar, det är lättare att felsöka och hålla reda på exakt vad som gör vad.

Det rena sättet att skriva funktioner

Låt oss erkänna det. Det är mycket svårt att skriva funktioner som följer dessa principer vid vårt första försök. Vi kommer sannolikt att tänka om och göra mycket mer planering än vi behöver för att få det på vårt första försök.

Men sanningen är att den inte ska vara perfekt första gången vi skriver funktionen. Precis som i skrift har kodningen sina lager, utkast, förfiningar (refactoring) och utveckling tills den är skarp och blank. Farbror Bob uttrycker det vältaligt:

”Skriva mjukvara är som alla andra typer av skrivande. När du skriver ett papper eller en artikel får du ner tankarna först, sedan masserar du det tills det läser bra. Det första utkastet kan vara klumpigt och oorganiserat, gör du ord på det och omstrukturerar det och förfinar det tills det läser som du vill att det ska läsa ”

Klasser

Ska vara liten

Den första regeln i klasserna är att de måste vara små. Den andra regeln är att de måste vara mindre än så. Låter bekant? Klasserna bör följa samma princip som funktioner – gör dem så små du kan.

Det finns dock en liten skillnad – klasser kan göra mer än en sak. De kommer förmodligen att göra mer än en sak för det mesta. Med detta sagt måste de följa principen om ett enda ansvar

Principen om ett enda ansvar

Klasserna bör ha ett ansvar, och inget mer än det. Det är här som separationen av oro är ett verkligt problem … som utvecklare måste vi se till att klasserna bara har ett ansvar för att skapa små och rena klasser.

Ansvar är en term som i detta sammanhang innebär en anledning att ändra. Det ska bara finnas en anledning till att klassen förändras. om flera skäl kan få klassen att förändras betyder det att klassen har för många ansvar. Detta är ett kortfattat sätt att se till att klassen har ett enda ansvar.

Sammanhållning

Hur väl är logiken i en klass “limmad” ihop och har anslutningspunkter i den? Hur väl implementeras metoderna med logiken och klassen och är de på rätt plats?

En bra indikator som kan hjälpa till att svara på dessa frågor är antalet instansvariabler i en klass. Helst skulle vi föredra att minska antalet instansvariabler och använda var och en av dem i klassens metoder. Det är sammanhållning i en klass.

Metoderna är starkt relaterade till klassen, de använder instansvariablerna och logiken odlas och används av alla metoder. Detta är en stark indikator på att alla metoder och problem finns på rätt plats.

”Strategin att hålla funktioner små och hålla parameterlistor korta kan ibland leda till en spridning av instans variabler som används av en delmängd av metoder. När detta händer betyder det nästan alltid att det finns minst en annan klass som försöker komma ur den större klassen. Du bör försöka separera variablerna och metoderna i två eller flera klasser så att de nya klasserna är mer sammanhängande ”

Allmänna begrepp för ren kod

Den bästa kommentaren är kommentaren du inte skrev

Kommentarer i kod, exklusive tekniska aspekter, ska mildras så mycket som möjligt. De är svåra att underhålla, de brukar försöka täcka upp till kod som inte är tillräckligt ren.

Om du känner att du behöver skriva en kommentar som förklarar vad koden gör, bör det lyfta en röd flagga och du bör granska din kod och se till att den är tillräckligt ren.

Vi syftar inte på kommentarer som ingår i bibliotekets dokumentation eller tekniska kommentarer om licensiering.

Kommentarer bör mildras. Den ledande principen är att om du behöver förklara vad koden gör, är koden inte tillräckligt tydlig. Koden bör förklara sig själv. Det bör vara självförklarande för andra kamrater att granska.

Förstå algoritmen

Att förstå algoritmen är ett avgörande steg för att skriva ren kod.

Utöver de uppenbara anledningarna till att vi behöver förstå algoritmen kan man bara skriva ren kod om han förstår den på riktigt.

Om vi ​​lägger tid på att lära oss algoritmen och se till att vi känner till dess krångel, kommer vi att kunna dekonstruera den i mindre delar.

Därifrån är vägen till att skriva ren kod ganska enkel
vår förmåga att skriva ren kod härrör från vår förståelse av algoritmen och väljer de exakta punkterna för att dekonstruera den till mindre delar

Akta dig för abstraktionsnivåer

En abstraktionsnivå är ett viktigt koncept att tänka på när man planerar och skriver kod. Medan vi skriver en funktion överväger vi var den står i lösningens stora schema.

Är det en funktion på hög nivå som är nära besläktad med algoritmen? Eller är det en funktion som är ansvarig för ett nyttighetssyfte (som att analysera en kommandorad, kontrollera villkor, utföra vissa beräkningar)?

Abstraktionsnivåprincipen är ett annat sätt att se till att vi håller alla våra funktioner och klasser på plats och att våra funktioner bara gör en sak. Om en funktion blandar abstraktionsnivåer gör den definitivt mer än en sak.

Att tänka i abstraktion är ett verktyg för att hjälpa oss att organisera strukturerna på funktionerna bättre och planera hur hela pusslet sätts ihop.

För att bli praktisk kommer vi alltid att börja med de högre nivåerna som ligger på den högsta abstraktionsnivån. Varje funktion ska bara sjunka en nivå under medan den kallar den andra funktionen.

Som vi redan har täckt kommer de detaljerade detaljerna i algoritmen och den specifika implementeringen att avslöjas längre ner i källkoden.

Den ledande frågan är vad jag bryr mig om i denna logik? Naturligtvis bryr vi oss om varje rad. Men vad är den absolut viktigaste delen av algoritmen, där huvudlogiken finns? Det är den högsta abstraktionsnivån.

Orkestrering av hela logiken. Tvättkontroll, felhantering, analys, beräkningar – de är på en annan abstraktionsnivå. De bör extraheras till olika funktioner, var och en har sin egen abstraktionsnivå.

Tänk på följande kod:

//Mongoose call to create a product
async function createProduct(productToSave) {
  try {
    const product = new Product(productToSave)
    return await product.save()
  } catch (e) {
    _handleError('failed to create product in db', e)
  }
}

Märkte du _handleError -funktionen? Förutom att peka exakt vad som var felet, har funktionen createProduct ingen aning om hur felet hanteras. Det går bara längs med felet och låter en annan funktion på en lägre abstraktionsnivå.

Varför är det så? För det är inte dess ansvar. Funktionen får en produkt och returnerar en sparad produkt från DB. Fel? bra. Inte dess ansvar. Den aktiverar _handleError -funktionen för att göra exakt vad titeln avslutar: Att hantera fel.

function _handleError(msg, e = 'initiated') {    
      logger.error(msg, e)    throw Error(msg, e)
}

Har funktionen ovan någon aning om vad logen gör med felet? Inte alls.

Som vi förmodligen har förstått är det inte sitt jobb att logga felet. Dess uppgift är att hantera felet – ring de relevanta funktionerna på nästa lägre abstraktionsnivå och vidarebefordra informationen

Slutord: Var konsekvent

Avslutningsvis tror jag att detta är ett recept på ren kod och att vara en bra programmerare. Alla har sin egen stil, unika synvinkel och ett annat sätt att lösa problem.

Att vara konsekvent med alla principer som nämns i denna artikel är därför nyckeln till att kunna skriva underhållbar, återanvändbar, tydlig och ren kod.

Share: