Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

16
voti

Come scrivere codice ingestibile

In questo post, darò una serie ( limitata per altro ) di indicazioni sullo sviluppo del codice che se applicate, lo renderanno impossibile da gestire da qualsiasi persona. Nella loro pedissequa applicazione il codice manipolato diventerà ben presto inutilizzabile anche allo stesso autore.



Diciamo subito che è un post, dai tratti spiccatamente goliardici, ed è ripreso da una pagina web ( link ) scritta in modo magistrale da un esperto softwarista d’oltreoceano. L’intento esplicito dell’autore, rivolto ai soli softwareisiti, ( java per altro ) è quello di contrastare il più possibile l’eventuale scelta aziendale di silurarti. Ne riprenderò le struttura con qualche personale aggiunta e modifica, in modo da integrarli in alcune parti a mio avviso mancanti. Per essere dei veri cani nella programmazione ci vuole metodo e oggi impareremo come.

In verità però c’è da dire anche che, se i presupposti del post sono delle poco etiche rivalse prototerrostiche aziendali, questa panoramica di indicazioni, sono comunque un ausilio per chi ha intenzione di intraprendere la carriera da programmatore. Basta infatti mettere una negazione ad ogni post e otterremo esattamente l’opposto di quello che il nostro amico voleva ottenere, a voi la scelta.
Durante lo sviluppo del codice, il programmatore serio, deve pensare anche quando dovrà gestire il software nei tempi futuri. Questo perchè lo sviluppo di un qualsiasi software non è mai un progetto a tempo determinato. Per tanti motivi, saranno applicate eccezioni progettuali che necessiteranno l'integrazione di nuove sezioni di codice o ci saranno comunque attività di manutenzione che sono inevitabilmente necessarie. Noi dovremmo fare in modo di rendere il codice non mantenibile, rendendo il più difficile possibile qualsiasi postumo tentativo di modifica.
Ma prima di parlare delle tecniche di non manutenibilità, parliamo un attimo di cosa vuol dire la parola mantenibilità. Per fare questo su web ho trovato questa rigorosa definizione:


Mantenibilità  
La mantenibilità è una misura della facilità con la quale 
un sistema può essere riparato una volta manifestatosi 
il malfunzionamento. In maniera più  specifica, la mantenibilità 
è la probabilità M(t) che il   sistema malfunzionante  possa 
essere riportato al suo corretto funzionamento entro il periodo t. 
Essa  è  strettamente correlata con la disponibilità 
poiché tanto più è breve l'intervallo di ripristino del corretto 
funzionamento, tanto più elevata sarà la  probabilità di trovare 
il sistema funzionante ad un dato istante  temporale.  
Per il valore estremo M(0) = 1.0, il sistema in oggetto sarà
sempre disponibile.


Ora per rendere codice ingestibile bisogna pensare come il futuro programmatore tenterà di approcciarsi alle migliaia di righe di codice che hai scritto. In pratica devi essere malvagiamente empatico. Si parte dalla premessa che, se tu fossi lo sventurato successore di qualche prolifico softwareista, non potrai sicuramente leggerti tutti i moduli sorgente cercando, magari di capirne il "senso" trascrivendone i diagrammi di flusso. L’unica cosa che potrai fare è quella di interpretare le sezioni di codice che ti servono per risolvere il problema contingente. Così il nostro target sarà quello di diminuire il più possibile il grado di comprensione del nostro codice o per esserè più rigorosi tenteremo di azzerare l’indice di mantenibilità


Indice

Nomenclatura delle variabili

Quidquid latine dictum sit, altum sonatur. - ( qualsiasi cosa detta in latino, sembrerà importante )

Le variabili vengono utilizzate nel 99% del nostro codice. La gestione e manipolazione delle variabili aumenta o diminuisce il grado di comprensione del software. In questa sezione consiederemo nel dettaglio come dare un nome specifico alle variabili.

Nomenclatura onomastica

Prendete un libro di nomi per bambini. Se sono stranieri è ancora meglio. Cercate quelli più caratteristici e applicateli alle variabili specifiche del vostro programma. Fred per esempio è un nome di variabile perfetto. Ci sono altri nomi particolarmente belli e facili da scrivere perchè sono sequenze di tasti come ad esempio asdf e qwerty e che vengono usati come nick dei forum...

Minimalista

Dichiarate le variabili con singole lettere x, y, vanno bene anche le accoppiate xy xz vanno benissimo anche i dittonghi ia ie etc. etc.

Scrittura erronea creativa

In questo caso le variabili vanno dichiarate con errori sintattici come, ad esempio, Idnice, Miusra o ad esempio gms o comnado. Possono essere chiamate anche dichiarazioni da refuso ( un mio collega ha scritto un modulo gms.cpp utilizzandolo su una decina di progetti contemporaneamente e lo maledico tutte le volte che ci penso... ).

Concetti astratti

Utilizzare termini il più generici possibile, tipo appunto: generico, esso, data, routine, o ad esempio anche dare il nome variabile è bellissimo

Acronimi

Definire il numero più ampio di acronimi possibile, ricordandosi ovviamente di evitare spiegazioni.

es:
ABPP SCRTD

Lingue straniere

Utilizzare definizioni di variabili in lingua straniera. Nel dettaglio quelle tipo Klingon, Hobbitese e Esperanto vanno benissimo.

Maisucolo randomico

In questo caso le variabili sono molto lunghe e approfittando che i parser dei compilatori sono sempre case sensitive si inserisce una maiuscola a caso, tipo VariaBileseCondaria()

Accenti

Mettiamo subito l’esempio così si capisce...

typedef struct { char ch; } ínt;

ìnt iPippo;

Lunghezza

I parser dei compilatori hanno un numero massimo di lettere verificate tipicamente 8. Francamente nei compilatori Microsoft non lo so se sono davvero 8. Comunque sia una volta dichiarate le costanti posso ottenere una cosa di questo tipo:
Numero_Massimo_diCaratteri
Numero_Massimo_diLettere

In entrambi i casi il compilatore le tratterrà allo stesso modo “Numero_M”

ASCII esteso

Utilizzare nella dichiarazione delle variabili i caratteri estesi ASCII ( ma questa è proprio un suicidio istantaneo dopo una decina di variabili scritte così, come farete davvero non lo so... auguri!)

Riferimenti matematici

Utilizzare nomi di variabili tipo barra/asterisco=addendo... .

Nomi accecanti

Utilizzare nomenclature non convenzionali

esempio:
Freud *superman=robocop+startrek

Lower Case l Looks a Lot Like the Digit 1

se non sei un anglofono copia il sottotitolo e traducilo con google capirai... subito ;)

Riusa le variabili globali come private o locali

Questa è una tipica possibilità del C che viene spacciata per un pregio, invece è un difetto. Dichiarare ad esempio una variabile globale Spare e una uguale all’interno delle funzioni rende difficoltoso seguire il flusso della variabile. Diciamo che, tendenzialmente, più variabili globali usi e più il codice diventa illeggibile e meno sicuro.

Cd wrttn wtht vwls s mch trsr

Anche le abbreviazioni abbassano notevolmente il grado di capibilità del codice, per ovvi motivi.

HOT Tips di offuscamento del codice

The longer it takes for a bug to surface, the harder it is to find.- Roedy Green

Una delle indicazioni più gettonate da parte dei team manager è quella di richiedere lo sviluppo di codice autoesplicante. Questo perchè si sono accorti che le modifiche a posteriori hanno l’effetto nefasto di disallineare i commenti dal codice. Come abbiamo già chiarito il codice autoesplicante è esattamente l’opposto di quello che stiamo facendo noi quindi....

Aggiungi tonnellate commenti

Il vero motivo per richiedere codice autoesplicante è che i commenti oltre a disallinearsi con il codice se scritti in in quantità diventano comunque difficili da leggere, spesso si ottiene l’effetto contrario a quello sperato. I commenti sono per definizione ambigui e fuorvianti. Così, scrivi commenti come se fossi il Proust degli schiacciatasti ( suffisso dato agli ingegneri informatici di origine ignota, l'equivalente per gli ingegneri elettronici è di spellafili per i maccanici girabulloni... e così via ognuno ha il suo). Scrivi, su tutte le linee di codice Includendo tutorial ed esempi di utilizzo del sorgente proprio come se fosse un manuale di istruzioni. Descrivi tutte le funzioni anche le più stupide.
es:
i++; // incrementa di uno
L’effetto minimo è quello di far perdere tempo a chi deve modificare il tuo codice, se poi hai la premura di inserire anche qualche minchiata quà e là si possono ottenere spiacevoli effetti collaterali.

Togli gli spazi

Elimina tutti gli spazi del codice in modo che le linee diventino cluster di caratteri. Queste possono diventare espressioni artistiche di pregievole livello.es:
l’effetto del codice senza spazi... è questo...

@ifdef PUMP_TIMEOUT // Verifico che il tempo trascorso dall'ultima attivazione // sia superiore a Tmin_Partenze ( campo K_Tmin_Partenze )timeCurrTimeDate = COleDateTime::GetCurrentTime();timeCurrTimeDate = timeCurrTimeDate - timeTime_Active_Pump; timeDelta.SetTime(0,lKTimer,0);if ( timeCurrTimeDate > timeDelta ){if (lTimerHold <= lTimerEs ){#endif lTimerHold = lTimerEs;iCurrIdx =(int) lId;#ifdef PUMP_TIMEOUT}}#endif

Inverti il senso

Se dalla descrizione della funzione deve tornare il valore vero, invertite il senso.

Es:
If ( isNegative( iValue ) )

isNegative è una funzione che ritorna uno stato booleano In questo caso il softwareista si aspetta che il parametro iValue sia vero quando è un valore negativo...

Mischia i linguaggi

Questo nella stesura del codice è davvero spiazzante. Se riuscite a mischiare le applicazioni fra Delphi, C++, Pyton, Php renderete il programma mooooolto illeggibili.

La notazione Ungara

In questo caso la notazione ungara è una vera arma nucleare nella guerra di offuscamento del codice. Basta mischiare i tipi di dato con la notazione in modo casuale.

es:
unsigned char pPointer;

Mascherare il codice=

Any fool can tell the truth, but it requires a man of some sense to know how to lie well.- Samuel Butler (1835 - 1902)
Incorrect documentation is often worse than no documentation.- Bertrand Meyer

Includere in una sezione di codice una parte di commento nel seguente modo
for(j=0; j<array_len; j+ =8)
{

total += array[j+0 ];
total += array[j+1 ];
total += array[j+2 ];/* Main body of
total += array[j+3]; * loop is unrolled
total += array[j+4]; * for greater speed.
total += array[j+5]; */
total += array[j+6 ]; total += array[j+7 ];

}

Definizione di macro

Dichiarare una macro in modo che l’operazione vista non sia quella effettuata così come descritto nell'esempio seguente.
@define a=b a=0-b

Prolungamento di linea

la seguente dichiarazione di macro
@define local_var xy_z
potrebbe diventare
@define local_var xy\
_z // local_var OK
l’effetto è quello che il back slash estende la linea senza aggiungere line feed o carriage return. Il pregio di questa dichiarazione é che la variabile non può più essere trovata con uno strumento di ricerca come il find o il grep.

Overloading

Anche qui possiamo mischiare le operazioni approfittando di questa potente feature del C++. Ovviamente l’overloading migliore è quello che ti permette di differenziare le operazioni in modo tale che l’operazione ad esempio i = i + 5; dia un diverso risultato rispetto all'istruzione i += 5. L’overloading è lo stato dell’arte fra le operazioni di offuscamento del codice perchè agisce nel tempo, proprio mentre il nostro ignaro succedaneo continuerà a lavorare con il nostro software. Overloadare funzioni come il not “!” o la “new” è una vera libidine....

Doppio puntatore

Dal punto di vista algoritmico il doppio puntatore è una struttura dati che non serve a nulla. Ma fa tanto Nerd...
es:
int **ipp;
int i = 5;
int *ip1 = &i,ipp = &ip1;

CallBack

Questo è un tipo di riferimento a funzione, che proviene da una notoria sega mentale algoritimica informatica. Sostanzialmente le callback sono riferimenti a funzioni. Una tipica implementazione è quella di permettere ad una stessa funzione di gestire diverse strutture dati .
es: ( ripreso da Wikip)
@include <stdlib.h>
@include <string.h>
@define QUANTI_SOLDATI 100
struct TSoldato {
int grado;
char nome[80];
}
Esercito[QUANTI_SOLDATI];
int ConfrontaSoldati(const void *ptr1, const void *ptr2)
{

const TSoldato *s1=(const TSoldato*)ptr1; // GNU GCC Compiler necessita della parola chiave "struct" prima di TSoldato // sia a sinistra che a destra dell'operatore di assegnazione
const TSoldato *s2=(const TSoldato*)ptr2; // come sopra
if(s1->grado!=s2->grado)
return s1->grado - s2->grado;
return strcmp(s1->nome, s2->nome);

}
void OrdinaEsercito()
{

qsort(Esercito, QUANTI_SOLDATI, sizeof(TSoldato), ConfrontaSoldati); // come sopra, all'interno della 'sizeof' prima di TSoldato

// scrivere la parola chiave "struct"
}
La funzione Ordina esercito richiama una funzione standard del C la qsort che ha bisogno di una funzione di callaback in grado di eseguire il confronto di un tipo di dato creato dall’utente. Come potete notare non ci sono riferimenti alla procedura e la ConfrontaSoldati ha un uso ambiguo come parametro.

Codice ricorsivo

Più codice ricorsivo implementi e meno leggibile diventa il codice. Ad esempio prendete un cronometro e misurate il tempo che impiegate a comprendere che tipo di operazione matematica esegue questa funzione ricorsiva.

int x (int n)
{

if (n <= 1) return 1;

else

return n * x (n-1);

}

Certo è, che se al nome della funzione avessi utilizzato un altro nome tipo, anzichè x che ne so “fatt”, allora il tempo di comprensione sarebbe stato nullo.

Funzioni monotone

Le funzioni dovrebbero essere tendenzialmente endofunzioni idempotenti. Prendo a piene mani dalla terminologia matematica forzando un po' il significato. Più parametri complessi vengono utilizzati e più l’indice di mantenibilità diminuisce.

Namespace

Le strutture/unioni e la definizione di tipo struttura/unione hanno diverse dichiarazioni. Puoi usarle insieme dichiarandole con lo stesso nome. Se poi sono anche simili ( ma non uguali ) è meglio.
typedef struct { char* pTr; size_t lEn; } snafu;
struct snafu { unsigned cNt char* pTr; size_t lEn; } A;

Macro inutili

Definisci funzioni che sembrano macro ma che non fanno niente. Magari commentando gli argomenti

  1. define fastcopy(x,y,z) /*xyz*/

fastcopy(array1, array2, size); /* does nothing */

Nascondi le istanze

Sempre con la define dichiara variabili del preprocessore cambiandole fra un header e l’altro
@define xxx global_var // in file std.h
@define xy_z xxx // in file ..\other\substd.h
@define local_var xy_z // in file ..\codestd\inst.h Define
@ifndef DONE
@ifdef TWICE // put stuff here to declare 3rd time around

void g(char* str);

@define DONE
@else // TWICE
@ifdef ONCE // put stuff here to declare 2nd time around

void g(void* str);

@define TWICE
@else // ONCE // put stuff here to declare 1st time around

void g(std::string str);

@define ONCE
@endif // ONCE
@endif // TWICE
@endif // DONE

Ritorni di funzione

Nella stesura di una funzione canonica ci si aspetta che ci sia un unico punto di ritorno dalla funzione. Aumentare il numero di ritorni diminusice il livello di mantenibilità

Unità di misura

Mai spiegare l’unità di misura delle variabili numeriche

Gotchas

Il termine gotchas è una forma pecoreccia ritengo di origine americana della affermazione “I’ve got you”. Si traduce in un “Ho te” o anche “Ti ho preso” o “Ti tengo d’occhio” che nell’ambito della programmazione è evidentemente riferito al grande demone del programmatore che è il baco. Mai commentare una sezione di codice che potrebbe presentare problemi o potenziali bachi, perchè favorire così tanto il nostro successore ?

Documentare variabili

Mai documentare la dichiarazione delle variabili. Queste infatti sono il cuore della logica e gestiscono il flusso dei dati. Le variabili numeriche hanno range di valori legittimi, decimali , base tempi di aggiornamento, formattazione di visualizzazione, valore di inizializzazione. Spiegare una variabile significa spiegare l’algoritmo e il programma, state alla larga!

GOTO riesumato

La programmazione a GOTO contrastata dai puristi informatici è il pane quotidiano di chi programma in assembler. Si può scrivere codice strutturato anche utilizzando i goto ma se si abusa allora è tutta un'altra storia.

x ( char * pointer )
{

loop:
if( x++ < 10)
goto loop;

}


Offusca un sistema embedded

Nello sviluppo di sistemi embedded in real-time il controllo delle temporizzazioni è fondamentale. Più il sistema diventa complesso e più le sezioni di codice aumenteranno. Il tempo di esecuzione diventerà sempre meno marginale.

Loop bloccanti

Inserisci nel codice cicli operativi bloccanti. Ad esempio evita di utilizzare gli interrput per definire i timer che utilizzerai nel ciclo e usa sistematicamente delle funzioni di delay. Spostando o copiando sezioni di codice il software/firmware avrà risposte diverse.
es:delay(int sec){for ( i=0;i<sec;i++);}

Watchdog fantasma

Inserisci il watchdog in un interrupt timer. Se il loop principale del firmware per qualche motivo entra in dead lock il programma non se ne accorgerà.

Watchdog a schiovere

Anzichè avere un unico reset watchdog come sarebbe auspicabile, sparpagliali su tutto il codice a casaccio. Chi viene dopo di te, difficilmente prenderà la sonda dell’oscilloscopio per verificare i tempi. Se si convince che sei molto stretto con i tempi le modifiche diventano più complicate.

Awards dell’offuscamento del Codice

Se siete dei programmatori Java indubbiamente conoscerete questo link. Ci sono molti programmi commerciali che adottano tecniche varie di offuscamento del codice. Nel mondo è in atto una vera e propria guerra del software. Il fenomeno della pirateria informatica è poca cosa in confronto allo scontro fra le case madri come Google, Apple e Microsoft.Tutte indistintamente si copiano programmi compreso i bachi...Per limitare questi furti esistono una serie di tecniche e di programmi che hanno lo scopo di modificare il codice sorgente generante rendendolo difficile da gestire. ( link )
Ma c’è anche chi ne ha fatto diventare una vera è propria arte. In questo sito ( link ) c’è la possibilità di partecipare ad un concorso annuale dove i programmatori si sfidano nella gara di offuscamento del codice. Alcuni sono veramente incredibili...

16

Commenti e note

Inserisci un commento

di ,

Una piacevole lettura, articolo molto istruttivo ;) Divertente anche l'aneddoto di GuidoB

Rispondi

di ,

Noooo! se mi capita in mano un codice del genere, ci metto meno tempo a rifare tutto da capo! Cavolo, non pensavo che ci fosse così tanta competizione!

Rispondi

di ,

GuidoB, grazie per il passaggio e per i complimenti. A riguardo del tuo amico, posso aggiungere poco... è un vero professionista ;)

Rispondi

di ,

Complimenti Kirkegaard per l'articolo, avevo dimenticato di farli nel commento precedente.
Tanto per chiarire il commento precedente, mi permetto di fare un esempio. Il codice è inventato, ma ricalca gli stessi errori concettuali di un caso realmente accaduto, con la relativa surrealista conversazione. Oggi ricordare queste cose mi mette di buon umore. Volevo condividerle con voi e magari strappare un sorriso.

ESEMPIO DI CODICE:

/* File: constants.h */

#define TEMP 60

...

/* File: main.c */
...
#include "constants.h"
...

if (fornoInTemperatura(TEMP)) {
startTimer(&timerId, TEMP)
}
...

LA MIA RICHIESTA DI CHIARIMENTO:

- G: Scusa, che cos'è TEMP?
- X: La TEMPeratura, no?
- G: Ah. Mi sembrava qualcosa di TEMPoraneo. Il nome confonde un po', sai. Ma non potevi leggere la temperatura dal file di configurazione?
- X: E che differenza fa?
- G: Se il cliente vuole cambiarla basta modificare un file di testo. Così com'è adesso bisogna fare una versione nuova del sorgente e ricompilare per ogni cambio.
- X: TANTO NON LO CAMBIANO. Se proprio vogliono vengono da me, glielo cambio e glielo ricompilo.
- G: Sì, e poi bisogna ritestare, consegnare il nuovo sw, far ripartire la macchina... Poi non ho capito perché passi la temperatura a startTimer.
- X: Quella non è la TEMPeratura, è il TEMPo che il forno deve mantenere la temperatura.
- G: Ma hai usato la STESSA costante!
- X: Certo! Se guardi, TEMP vale 60. Il forno va portato a 60 gradi e deve starci per un'ora, cioè 60 minuti, come ben sai.
- G: E non potevi definire una costante per il tempo e una per la temperatura e dargli dei nomi che non confondano?
- X: Di costanti ce ne sono già anche troppe! Ce n'è già una col valore giusto, perché ne dovrei definire un'altra???
- G: Ma così se il cliente vuole aumentare il tempo a 90 minuti, anche la temperatura gli va a 90 gradi!!!
- X: Ti ho già detto che TANTO NON LO CAMBIANO! Per quello che mi pagano, ho già fatto anche troppo!

Mi fermo qui per decenza. Saluti a tutti!

Rispondi

di ,

Un programmatore "malvagiamente empatico" ah ah, oserei dire diabolico. Io invece ho lavorato con uno che inseriva mine vaganti, ma in BUONA FEDE. Refrattario alle coding rules, era capace di sostenere la bontà del suo orrendo codice, rifiutando i miei consigli dal momento che non era compito mio impicciarmi del suo lavoro. Del resto le peer review non si facevano, non c'era mai tempo. Era un demolitore della qualità, un inconsapevole sabotatore di prodotti, un ritardatore di dimostrazioni e consegne, in sintesi un allontanatore di clienti. A scanso di equivoci con altri ex-colleghi, dirò solo che accadde molti anni fa, e il tipo non era italiano. Alla fine non servì per mantenere il lavoro. Per motivi più grandi ci cacciarono via tutti e chiusero la baracca.

Rispondi

di ,

Dimaios, a me è venuto in mente il termine antitutorial per programmatori non professionisti, comunque grazie a tutti... sentitamente.

Rispondi

di ,

Kirkegaard, ho letto con piacere! Devo anche ringraziare dimaios per il suo link. Ho imparato il C++ a metà anni 90 ed il libro di Carlo Pescio era uno che mi aveva attirato all'epoca, anche se non ho avuto mai occasione per acquistarlo. Poterlo sfogliare nella versione gratuita messa a disposizione dall'autore è impagabile.

Rispondi

di ,

Kirkegaard, aspettavo con ansia un tuo ritorno, ben sapendo che lo avresti fatto alla grande!

Rispondi

di ,

Standing ovation!

Rispondi

di ,

Favoloso articolo, Kirkegaard non delude mai ;)

Rispondi

di ,

Ecco perchè io non capisco il software...!!

Rispondi

di ,

Grazie Kirkegaard che meraviglia! Volevo tenermelo da parte per gustarmelo con calma, ma poi non ho resistito e l'ho ingoiato in un sol boccone. Articolo da leggere e rileggere e rileggere ...

Rispondi

di ,

Kirkegaard, è veramente uno spasso ..... mi hai fatto ricordare alcuni corsi di ingegneria del software. Fondamentalmente hai realizzato la negazione del "Manuale di Stile" pubblicato da chi non ha certo bisogno di presentazioni nella community dei programmatori professionisti. http://www.eptacom.net/pubblicazioni/cpp_stile/all.html

Rispondi

Inserisci un commento

Per inserire commenti è necessario iscriversi ad ElectroYou. Se sei già iscritto, effettua il login.