Antimönster du bör undvika i din kod

Varje utvecklare vill skriva strukturerad, helt enkelt planerad och snyggt kommenterad kod. Det finns till och med en myriad av designmönster som ger oss tydliga regler att följa och en ram att tänka på.

Men vi kan fortfarande hitta antimönster i programvara som skrevs någon gång, eller som skrevs för snabbt.

Ett ofarligt grundläggande hack för att snabbt lösa ett problem kan skapa ett prejudikat i din kodbas. Den kan kopieras över flera platser och förvandlas till ett antimönster du behöver ta itu med.

Så vad är ett antimönster?

I programvara är anti-mönster en term som beskriver hur man INTE ska lösa återkommande problem i din kod. Antimönster anses vara dålig programvarudesign och är vanligtvis ineffektiva eller obskyra korrigeringar.  

De lägger i allmänhet också till "teknisk skuld" - vilket är koden du måste komma tillbaka och fixa ordentligt senare.

De sex antimönster jag kommer att diskutera i den här artikeln är Spaghetti Code , Golden Hammer , Boat Anchor , Dead Code , Proliferation of Code och the God Object .

Spaghettikod

Spaghetti Code är det mest kända antimönstret. Det är kod med liten till noll struktur.

Ingenting är modulerat. Det finns slumpmässiga filer ströda i slumpmässiga kataloger. Hela flödet är svårt att följa och är helt trassligt ihop (som spagetti).

Normalt är detta en fråga där någon inte noggrant har funderat över flödet i sitt program i förväg och bara börjat koda.

Vad gör den?! Jag kan inte följa detta

image.png

Det här är inte bara en mardröm för underhåll, utan det gör det omöjligt att lägga till ny funktionalitet.

Du kommer hela tiden att bryta saker, inte förstå omfattningen av dina ändringar eller ge några exakta uppskattningar för ditt arbete eftersom det är omöjligt att förutse de otaliga frågor som dyker upp när du gör sådan arkeologi / gissningar.

Du kan läsa mer här om Spaghettikodens antimönster.

Golden Hammer

"Jag antar att det är frestande, om det enda verktyget du har är en hammare, att behandla allt som om det vore en spik." Abraham Maslow

Föreställ dig ett scenario med mig: ditt team är mycket, mycket kompetent i den helt nya Hammer-arkitekturen. Det har fungerat fantastiskt för alla dina tidigare problemuppgifter. Du är världens ledande Hammer-arkitekturteam.

Men nu, på något sätt, hamnar allt alltid med den här arkitekturen. En platt skruv? Hammare. Phillips skruv? Hammare. Behöver du en insexnyckel? Nej du gör det inte, hammar det.

Du börjar att tillämpa ett arkitektoniskt förhållningssätt som inte riktigt passar det du behöver, men får jobbet gjort. Du är över beroende av ett mönster och behöver lära dig det bästa verktyget för det bästa jobbet.

Hela ditt program kan sluta ta en seriös prestationshit eftersom du försöker rama en kvadrat i en cirkelform. Du vet att det tar dubbelt så lång tid att koda upp och att köra ett program med hjälp av hammararkitekturen för detta problem, men det är lättare och det är vad du är bekväm med.

Det är inte särskilt förutsägbart. Olika språk har gemensamma lösningar på problemen de möter och deras egna standarder. Du kan inte tillämpa varje enskild regel som fungerade bra för dig på ett språk till nästa, utan problem.

Försumma inte konsekvent lärande i din karriär. Välj rätt språk för ditt problem. Tänk på arkitekturen och tryck ut din komfortzon. Undersök och undersök nya verktyg och nya sätt att närma dig de problem du möter.

Du kan läsa mer här om Golden Hammer antimönster.

Båtankare

The Boat Anchor antimönster är där programmerare lämnar kod i kodbasen eftersom de kan behöva den senare.

De kodade något utifrån specifikationen och det behövs inte ännu, men de är säkra på att de kommer att göra nästa månad. Så de vill inte ta bort det. Skicka det till produktion och senare när de behöver det kan de snabbt få det att fungera.

Men detta orsakar underhålls mardrömmar i kodbasen som innehåller all den föråldrade koden. Den stora frågan är att deras kollegor kommer att ha svårt att räkna ut vilken kod som är föråldrad och inte förändrar flödet, jämfört med koden som gör det.

Föreställ dig att du har en snabb fix och försöker desperat ta reda på vad som är ansvarigt för att skicka kunders kortuppgifter till API: et för att ta ut pengar från sin bank. Du kan slösa tid på att läsa och felsöka föråldrad kod utan att inse att du inte ens är på rätt plats i kodbasen.

Det sista problemet är att föråldrad kod förlänger din byggtid och du kan blanda ihop fungerande och föråldrad kod. Du kan till och med börja oavsiktligt "slå på" i produktionen.

Nu kan du förmodligen se varför det kallas båtankringsmönster - det är tungt att bära (lägger till teknisk skuld) men gör ingenting (bokstavligen, koden tjänar inget syfte, det fungerar inte).

Du kan läsa mer här om Boat ankare antimönster.

Död kod

Har du någonsin varit tvungen att titta på kod skriven av någon som inte arbetar längre i ditt företag? Det finns en funktion som inte ser ut som den gör någonting. Men det kallas från överallt! Du frågar runt och ingen annan är helt säker på vad den gör, men alla är för oroliga för att ta bort det.

Ibland kan du se vad den gör, men sammanhanget saknas. Du kan läsa och förstå flödet, men varför? Det ser inte ut som om vi behöver nå den slutpunkten längre. Svaret är alltid samma svar för alla olika användare.

Detta beskrivs vanligtvis som det döda kodens antimönster. När du inte kan se vad som är den "faktiska" koden som behövs för flödet och framgångsrikt genomförande av ditt program, mot vad som bara behövdes för 3 år sedan, och inte nu.

Detta speciella antimönster är vanligare i bevis på koncept eller forskningskod som hamnade i produktion.

En gång vid ett tekniskt möte träffade jag en kille som hade just detta problem. Han hade massor av död kod, som han visste var död, och mycket han misstänkte var död. Men han kunde inte få tillstånd från ledningen att någonsin ta bort all död kod.

Han hänvisade till sitt tillvägagångssätt som Monkey-testning, där han började kommentera och stänga av saker för att se vad som sprängde i produktionen. Kanske lite för riskabelt!

If you don't fancy Monkey testing your production app, try to frame technical debt to management as "technical risk" to better explain why you think it's so important to tidy up.

Or even write down everything your particular module/section does you want to re-write, and take an iterative approach to remove piece by piece the dead code. Checking every time you haven't broken anything.

You don't have to drop a huge rewrite with thousands of changes. But you will either understand why it's so crucial and document why it's needed, or delete the dead code as you desired.

You can read more here about the Dead code anti-pattern.

Proliferation of Code

Objects or modules regularly communicate with others. If you have a clean, modularised codebase you often will need to call into other separate modules and call new functions.

The Proliferation of Code anti-pattern is when you have objects in your codebase that only exist to invoke another more important object. Its purpose is only as a middleman.

This adds an unnecessary level of abstraction (adds something that you have to remember) and serves no purpose, other than to confuse people who need to understand the flow and execution of your codebase.

A simple fix here is to just remove it. Move the responsibility of invoking the object you really want to the calling object.

You can read more here about the Proliferation of Code anti-pattern.

God Object

If everywhere in your codebase needs access to one object, it might be a God object.

God objects do too much. They are responsible for the user id, the transaction id, the customer's first and last name, the total sum of the transaction, the item/s the user is purchasing...you get the picture.

It is sometimes called the Swiss Army Knife anti-pattern because you only really need it to cut some twine, but it also can be a nail file, saw, pair of tweezers, scissors, bottle opener and a cork screw too.

In this instance you need to separate out and modularise your code better.

Programmers often compare this problem to asking for a banana, but receiving a gorilla holding a banana. You got what you asked for, but more than what you need.

The SOLID principles explicitly discuss this in object orientated languages, to help us model our software better (if you don't know what the SOLID principles are, you can read this article).

The S in the acronym stands for Single Responsibility - every class/module/function should have responsibility over one part of the system, not multiple.

You can see this problem over and over again, how about the below interface?

interface Animal { numOfLegs: string; weight: number; engine: string; model: string; sound: string; claws: boolean; wingspan: string; customerId: string; } 

Can you see by even just briefly scanning this interface that the responsibility of this is far too broad, and needs refactoring? Whatever implements this has the potential to be a God object.

How about this?

 interface Animal { numOfLegs: string; weight: number; sound: string; claws: boolean; } interface Car { engine: string; model: string; } interface Bird { wingspan: string; } interface Transaction { customerId: string; } 

Interface segregation will keep your code clear about where the responsibilities lie, and stop forcing classes that only need wingspan to also implement the engine, customerId and model  and so on.

Du kan läsa mer här om Guds objekt mot mönster.

Slutsats

I varje stor kodbas finns det en konstant balans mellan att hantera teknisk skuld, starta ny utveckling och att hantera en kö med buggar för din produkt.

Jag hoppas att den här artikeln har gett dig ett öga för att upptäcka när du kanske går ner i kaninhålet i ett antimönster och några verktyg för att lösa det rent.

Jag delar mitt skrivande på Twitter om du gillade den här artikeln och vill se mer.