Defensief programmeren: is het paranoia of slimme codering?

Waarom defensief programmeren de beste manier is voor robuuste codering

Defensieve programmering is de enige echte programmering.

Afbeeldingscredits: Pixabay.com

Defensief programmeren is wanneer een programmeur op problemen anticipeert en code schrijft om hiermee om te gaan.

Het is alsof je in de toekomst een auto-ongeluk ziet ... en kalm blijven omdat je je ervoor hebt verzekerd.

Dat gezegd hebbende, het hele punt van defensief programmeren is beschermen tegen fouten die je niet verwacht. Een defensieve programmeur let op problemen, om het te vermijden voordat het echte problemen kan veroorzaken. Het idee is niet om code te schrijven die nooit faalt. Dat is een utopische droom. Het idee om de code prachtig te laten mislukken in geval van een onverwacht probleem. Prachtig mislukken kan een van de volgende dingen betekenen.

· Mislukt vroeg: uw code moet ervoor zorgen dat alle belangrijke bewerkingen van tevoren worden beëindigd, vooral als deze rekenkundig duur zijn of gegevens onomkeerbaar kunnen beïnvloeden.

· Faalveilig: in geval van een storing moet uw code ervoor zorgen dat alle vergrendelingen worden opgeheven en geen nieuwe worden verkregen, geen bestanden worden geschreven, enzovoort.

· Mislukt duidelijk: als er iets kapot is, moet dit een zeer duidelijke foutmelding en beschrijving opleveren, waarmee het ondersteuningsteam de fout kan oplossen.

OK. Je zou hier ruzie kunnen maken.

Er zijn geen problemen in het heden. Mijn code werkt prachtig. Waarom zou ik tijd en moeite investeren in een 'toekomstverwacht' probleem? We hebben tenslotte herhaaldelijk geleerd om "You Gonna Need It" (YAGNI) te gebruiken. En u bent een professionele programmeur en geen hobbyist die de code naar believen kan blijven toevoegen.

De sleutel hier is pragmatisme.

Andrew Hunt beschrijft in zijn boek Pragmatic Programmer defensief programmeren als "Pragmatic Paranoia".

Bescherm uw code tegen fouten van anderen en uw eigen fouten. Bevestig in geval van twijfel. Controleer op gegevensconsistentie en integriteit. Je kunt niet voor elke fout testen, dus gebruik beweringen en uitzonderingshandlers voor dingen die 'niet kunnen gebeuren'.

Healthy Paranoid Programming is het juiste soort programmeren. Maar paranoia kan te ver worden doorgevoerd. De sleutel is het juiste evenwicht vinden.

En hier zijn enkele manieren om defensief te programmeren.

Vraag jezelf: als dit mislukt?

Elke regel code doet iets, dus het eerste verdedigingspunt is jezelf afvragen of de code faalt, wat dan.

Overweeg bijvoorbeeld de volgende niet-compatibele code.

GEVAL SY-INDEX. // Niet-conform; ontbreekt WANNEER ANDERE clausule
WANNEER EEN.
SCHRIJF ‘One’.
WANNEER 2.
SCHRIJF ‘Twee’.
ENDCASE.

Hier kunnen we de volgende vragen stellen.

Wat gebeurt er als sy-index niet 1 is.

Wat gebeurt er als sy-index niet 2 is.

Om dit probleem op te lossen, voegen we een ANDERE verklaring toe.

GEVAL SY-INDEX.
WANNEER EEN.
SCHRIJF ‘One’.
WANNEER 2.
SCHRIJF ‘Twee’.
WANNEER ANDEREN. // Conform
SCHRIJF ‘Onverwacht resultaat’
ENDCASE.

Gemakkelijk. Is het niet?

Het is dit "wat als" -denken dat goede programmeurs scheidt van degenen die code schrijven en hopen dat het nooit faalt. "Nooit" komt altijd sneller dan verwacht, en tegen die tijd is de code begraven in een lang vergeten deel van het programma, waarbij de foutmeldingen geen indicatie geven van waar het probleem is en hoe het kan worden opgelost.

Het mooie van deze defensieve programmeertechniek is dat het bijna geen tijd kost om uitgebreide typecontrole aan je code toe te voegen. U bent niet "over codering". U bent gewoon uw code aan het 'beveiligen'.

Controleer zorgvuldig de grensvoorwaarden.

De allereerste controle is om te bepalen of u een randvoorwaarde nodig hebt, omdat lussen immers duur zijn.

Grenzen (of rand) voorwaarden zijn waar alle actie gebeurt. Lussen van 0 tot 100 en luswaarden 1 tot en met 98 zijn vrijwel hetzelfde (voorwaardelijke uitzonderingen in de code natuurlijk). Maar lus 0 is waar de code de lus binnengaat en initialisatiecondities worden ingesteld (en mogelijk verkeerd ingesteld). Evenzo, de laatste lus is waar dingen vertrekken, en wat de lus ook deed met waarden, stopt.

Een lus met maximaal één iteratie is gelijk aan het gebruik van een IF-instructie om een ​​stuk code voorwaardelijk uit te voeren. Geen enkele ontwikkelaar zou zo'n gebruik van een lus-instructie kunnen verwachten. Als de oorspronkelijke bedoeling van de auteur echt was om voorwaardelijk één stuk code uit te voeren, moet een IF-instructie worden gebruikt.

Overweeg de volgende niet-conforme en compatibele code. In dit geval hebben we helemaal geen lus nodig. Een simpele IF is voldoende.

            Voorbeeld van niet-conforme code
DATA rest TYPE i.
DOE 20 MAAL.
rest = sy-index MOD 2.
cl_demo_output => write_text ().
UITGANG. “Niet-conform, loop wordt slechts eenmaal uitgevoerd. We kunnen IF gebruiken
ENDDO.
          Compliant codevoorbeeld
DATA rest TYPE i.
DOE 20 MAAL.
rest = sy-index MOD 2.
cl_demo_output => write_text ().
ENDDO.

Onthoud altijd dat foutopsporingslussen altijd aan het begin en aan het einde het meeste werk met zich meebrengen, zorg ervoor dat wat erin gaat en wat eruit komt correct is. Dus als u eenmaal duidelijk bent met de randvoorwaarden, kan er niets anders misgaan met uw code.

Gebruik TDD (Test Driven Development)

Het fundamentele idee van TDD is "eerst schrijfeenheid testen, schrijf dan de code, dan refactor, herhaal dan."

Eenheidstests zijn geautomatiseerde tests die controleren of functies naar verwachting werken. Je allereerste unit-test zou moeten mislukken, omdat deze is geschreven voordat je zelfs een codebasis hebt.

Je voegt een beetje toe aan de testcasecode. Je voegt een beetje toe aan de productiecode. De twee codestromen groeien gelijktijdig uit tot complementaire componenten. De tests passen bij de productiecode zoals een antilichaam bij een antigeen past.

Het probleem met het testen van code is dat je die code moet isoleren. Het is vaak moeilijk om een ​​functie te testen als die functie andere functies aanroept. Om die test te schrijven, moet je een manier bedenken om de functie van alle anderen te ontkoppelen. Met andere woorden, de noodzaak om eerst te testen dwingt je na te denken over goed ontwerp.

Dit zorgt voor een beter, ontkoppeld ontwerp waarin u betere controle hebt over dingen naarmate de code zich ontwikkelt.

Tijdens het vooraf schrijven van testcases kan het in eerste instantie tijd kosten, maar dit biedt veel voordelen. Ontwikkelaars geven toe dat ze voorheen coderegels schreven, zich realiseerden dat hun oplossingen irrelevant waren en vervolgens opnieuw begonnen met coderen.

In tegenstelling tot verouderde coderingspraktijken, stelt TDD ontwikkelaars in staat om terug te gaan naar de tekentafel en zich te concentreren op het vooraf ontwerpen van een lichtgewicht, flexibele architectuur.

En alleen al het vooraf schrijven van testcases voorkomt bugs die later kunnen opduiken, wat tijd, moeite en brandend maagzuur bespaart.

Schrijf altijd geoptimaliseerde code.

Sommige programma's (en programmeurs) houden veel van bronnen. Maar gebruik waar mogelijk het minimum. En om het minimum te gebruiken, moet uw code zo optimaal mogelijk zijn.

Gewoonlijk is een zekere manier om te optimaliseren de optimalisatie die de compiler biedt ingebouwd.

Compileroptimalisaties verbeteren meestal de looptijd van enkele procenten tot een factor 2. Soms kan het ook het product vertragen, dus meet voorzichtig voordat u het laatste gesprek neemt. Moderne compilers doen het in dit opzicht echter voldoende goed omdat ze veel van de noodzaak voor kleinschalige veranderingen door programmeurs overbodig maken.

Naast de standaard compileroptimalisaties zijn er verschillende andere afstemmingstechnieken die kunnen worden gebruikt.

Verzamel algemene subexpressies.

Als een dure berekening op meerdere plaatsen plaatsvindt, is het beter om op één plaats te berekenen en het resultaat te onthouden. Zet dergelijke berekeningen niet in een lus tenzij vereist.

Vervang dure bewerkingen door goedkope.

Stringmanipulatie is waarschijnlijk een van de meest voorkomende bewerkingen in elk programma. Het kan echter een dure operatie zijn als het verkeerd wordt gedaan. Evenzo kunt u in sommige gevallen de prestaties verbeteren door vermenigvuldiging te vervangen door een reeks shift-bewerkingen. Zelfs waar dit effectief is (en het is niet altijd) produceert het zeer verwarrende code. Neem dus de beslissing, rekening houdend met de leesbaarheid van code.

Loops elimineren.

Lussen zijn meestal overhead. Probeer lussen waar mogelijk te vermijden als iteraties niet veel zijn.

Cache vaak gebruikte waarden.

Caching maakt gebruik van de locatie, de neiging van programma's en mensen om recent gebruikte gegevens opnieuw te gebruiken. Het cachen van alleen het meest gebruikte teken of gegevens verbetert de prestaties van het programma aanzienlijk.

Herschrijf in een taal van een lager niveau.

Dit zou het laatste redmiddel moeten zijn. Talen op lager niveau zijn meestal efficiënter, hoewel ze tijdrovend zijn vanuit het oogpunt van de programmeur. Af en toe krijgen we aanzienlijke verbeteringen door cruciale code in lagere talen te herschrijven, maar dit gaat ten koste van verminderde draagbaarheid en onderhoud wordt erg moeilijk. Dus neem de beslissing zorgvuldig.

Onthoud in optimalisatie dat selectie misschien 90% van het spel is. Het is de moeite waard om de tijd te nemen om te beslissen wat u doet en om het goed te doen. Natuurlijk: dat is ook waar de zwarte magie ligt!

En tot slot, vertrouw niemand.

“Er zijn bekende bekende; er zijn dingen waarvan we weten dat we ze kennen, 'zei Donald Rumsfeld, de minister van Defensie tijdens de tweede regering-Bush, ooit op een persconferentie. “We weten ook dat er bekende onbekenden zijn; dat wil zeggen dat we weten dat er dingen zijn die we niet weten. Maar er zijn ook onbekende onbekenden - degenen die we niet kennen, weten we niet. "

Rumsfeld had het over de oorlog in Irak, maar hetzelfde geldt ook voor gegevens. Kort gezegd betekent dit dat alle gegevens worden gecontroleerd waarover u geen volledige controle hebt.

Het is duidelijk dat gebruikersgegevens altijd verdacht zijn. Gebruikers kunnen heel goed begrijpen wat jij denkt dat kristalhelder is. Probeer te anticiperen op problemen en verifieer of ruim alles op dat binnenkomt.

Programma-instellingen gegevens zijn ook gevoelig voor fouten. Vroeger waren INI-bestanden een veelgebruikte manier om programma-instellingen op te slaan. Omdat het een tekstbestand was, hadden veel mensen de gewoonte om ze handmatig met een teksteditor te bewerken en mogelijk (waarschijnlijk) de waarden te verknallen. Registergegevens, databasebestanden - iemand kan en zal ze ooit aanpassen, dus het loont om zelfs die dingen te verifiëren.

Kortom, de binnenkomende gegevens moeten schoon zijn als u enige hoop hebt dat uw code doet wat deze moet doen. Als je ooit de uitdrukking 'Garbage in, Garbage Out' hebt gehoord, is dit waar het vandaan komt.

Zoals Edward Demming terecht heeft gezegd.

“In God vertrouwen we. Alle anderen moeten gegevens meebrengen. '
Over de auteur-:
Ravi Rajan is een wereldwijde IT-programmamanager gevestigd in Mumbai, India. Hij is ook een fervent blogger, Haiku-poëzieschrijver, archeologieliefhebber en geschiedenismaniak. Maak contact met Ravi op LinkedIn, Medium en Twitter.