Cosa c'è di sbagliato nei commenti che spiegano il codice complesso?


236

Molte persone affermano che "i commenti dovrebbero spiegare" perché ", ma non" come "". Altri affermano che "il codice dovrebbe essere auto-documentante" e che i commenti dovrebbero essere scarsi. Robert C. Martin afferma che (riformulato alle mie stesse parole) spesso "i commenti sono scuse per il codice scritto male".

La mia domanda è la seguente:

Cosa c'è di sbagliato nello spiegare un algoritmo complesso o un pezzo di codice lungo e contorto con un commento descrittivo?

In questo modo, invece di altri sviluppatori (incluso te stesso) che devono leggere l'intero algoritmo riga per riga per capire cosa fa, possono semplicemente leggere il commento descrittivo amichevole che hai scritto in un inglese semplice.

L'inglese è "progettato" per essere facilmente compreso dagli umani. Java, Ruby o Perl, tuttavia, sono stati progettati per bilanciare la leggibilità umana e la leggibilità del computer, compromettendo così la leggibilità umana del testo. Un essere umano può capire un pezzo di inglese molto più velocemente di poter comprendere un pezzo di codice con lo stesso significato (purché l'operazione non sia banale).

Quindi, dopo aver scritto un codice complesso scritto in un linguaggio di programmazione parzialmente leggibile dall'uomo, perché non aggiungere un commento descrittivo e conciso che spieghi il funzionamento del codice in un inglese amichevole e comprensibile?

Alcuni diranno "il codice non dovrebbe essere difficile da capire", "riduci le funzioni", "usa nomi descrittivi", "non scrivere il codice spaghetti".

Ma sappiamo tutti che non è abbastanza. Queste sono semplici linee guida - importanti e utili - ma non cambiano il fatto che alcuni algoritmi sono complessi. E quindi sono difficili da capire leggendoli riga per riga.

È davvero così male spiegare un algoritmo complesso con alcune righe di commenti sulla sua operazione generale? Cosa c'è di sbagliato nello spiegare un codice complicato con un commento?


14
Se è così contorto, prova a rifattorizzarlo in pezzi più piccoli.
Vaughan Hilts,

151
In teoria, non c'è differenza tra teoria e pratica. In pratica, c'è.
Scott Leadley,

5
@mattnz: più direttamente, nel momento in cui scrivi il commento sei immerso nel problema che questo codice risolve. La prossima volta che visiterai, avrai meno capacità con questo problema .
Steve Jessop,

26
"Cosa" fa la funzione o il metodo dovrebbe essere ovvio dal suo nome. Come lo fa è ovvio dal suo codice. Perché è fatto in questo modo, quali ipotesi implicite sono state usate, quali documenti bisogna leggere per capire l'algoritmo, ecc. - Dovrebbero essere nei commenti.
SK-logic

11
Ritengo che molte delle risposte sottostanti stiano intenzionalmente fraintendendo la tua domanda. Non c'è niente di sbagliato nel commentare il tuo codice. Se ritieni di dover scrivere un commento esplicativo, devi farlo.
Tony Ennis,

Risposte:


408

In parole povere:

  • Non c'è niente di sbagliato nei commenti di per sé. Cosa c'è di sbagliato è scrivere codice che necessita di quel tipo di commenti o supporre che sia OK scrivere codice contorto purché lo si spieghi in un inglese semplice.
  • I commenti non si aggiornano automaticamente quando si modifica il codice. Ecco perché spesso i commenti non sono sincronizzati con il codice.
  • I commenti non facilitano il test del codice.
  • Chiedere scusa non è male. Quello che hai fatto che richiede scusa per (scrivere codice che non è facilmente comprensibile) è male.
  • Un programmatore che è in grado di scrivere un semplice codice per risolvere un problema complesso è meglio di uno che scrive un codice complesso e quindi scrive un lungo commento che spiega cosa fa il suo codice.

Linea di fondo:

Spiegare te stesso è buono, non è necessario farlo è meglio.


91
Spesso è impossibile giustificare che il codice di riscrittura del denaro del datore di lavoro sia più autoesplicativo, quando un buon commento può fare il lavoro in molto meno tempo. Un programmatore doveroso deve usare ogni volta il proprio giudizio.
carrello

34
@aecolley Iniziare a scrivere codice autoesplicativo è ancora meglio.
Tulains Córdova,

127
A volte il codice autoesplicativo non è abbastanza efficiente per risolvere un problema con HW&SW di oggi. E la logica aziendale è notoriamente ... tortuosa. Il sottoinsieme di problemi con soluzioni software eleganti è significativamente più piccolo dell'insieme di problemi che sono economicamente utili da risolvere.
Scott Leadley,

62
@rwong: al contrario, mi ritrovo spesso a scrivere più commenti nella logica aziendale, perché è importante mostrare esattamente come il codice si allinea ai requisiti dichiarati: "questa è la linea che impedisce a tutti noi di andare in galera per frode via cavo nella sezione Indipendentemente da il codice penale ". Se è solo un algoritmo, beh, un programmatore può capire lo scopo da zero, se assolutamente necessario. Per la logica aziendale è necessario un avvocato e il cliente nella stessa stanza allo stesso tempo. Forse il mio "buon senso" è in un dominio diverso da quello del programmatore medio di app ;-)
Steve Jessop,

29
@ user61852 Tranne il fatto che ciò che è autoesplicativo per te che hai appena scritto quel codice e trascorso l'ultimo periodo $ immerso in esso potrebbe non essere autoesplicativo per te che devi mantenerlo o modificarlo tra cinque anni, figuriamoci tutto possibili persone che non sei tu che potrebbero doverlo guardare. "Autoesplicativo" è un nebuloso santo graal di definizioni.
Shadur,

110

Esistono diverse ragioni per complicare o confondere il codice. I motivi più comuni vengono risolti meglio rifattorizzando il codice per renderlo meno confuso, non aggiungendo commenti di alcun tipo.

Tuttavia, ci sono casi in cui un commento ben scelto è la scelta migliore.

  • Se è l' algoritmo stesso che è complicato e confuso, non solo la sua implementazione - il tipo che viene scritto nelle riviste matematiche e che in seguito viene chiamato algoritmo di Mbogo - allora metti un commento all'inizio dell'implementazione, leggendo qualcosa del tipo "Questo è l'algoritmo di Mbogo per i riordini di widget, originariamente descritto qui: [URL del documento]. Questa implementazione contiene perfezionamenti di Alice e Carol [URL di un altro documento]." Non cercare di entrare in ulteriori dettagli di così; se qualcuno ha bisogno di maggiori dettagli, probabilmente dovrà leggere l'intero documento.

  • Se hai preso qualcosa che può essere scritto come una o due righe in una notazione specializzata e l'hai espanso in un grande globo di codice imperativo, mettere quelle una o due righe di notazione specializzata in un commento sopra la funzione è un buon modo per dì al lettore cosa dovrebbe fare. Questa è un'eccezione all'argomento "ma cosa succede se il commento non è sincronizzato con il codice", perché la notazione specializzata è probabilmente molto più facile da trovare bug rispetto al codice. (È il contrario se hai scritto una specifica in inglese.) Un buon esempio è qui: https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.cpp#1057 ...

    /**
     * Scan a unicode-range token.  These match the regular expression
     *
     *     u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
     *
     * However, some such tokens are "invalid".  There are three valid forms:
     *
     *     u+[0-9a-f]{x}              1 <= x <= 6
     *     u+[0-9a-f]{x}\?{y}         1 <= x+y <= 6
     *     u+[0-9a-f]{x}-[0-9a-f]{y}  1 <= x <= 6, 1 <= y <= 6
    
  • Se il codice è nel complesso semplice, ma contiene una o due cose che sembrano eccessivamente contorte, inutili o semplicemente sbagliate, ma devono essere così per motivi, allora metti un commento immediatamente sopra il bit sospetto, in cui tu dichiari le ragioni . Ecco un semplice esempio, in cui l'unica cosa che deve essere spiegata è perché una costante ha un certo valore.

    /* s1*s2 <= SIZE_MAX if s1 < K and s2 < K, where K = sqrt(SIZE_MAX+1) */
    const size_t MUL_NO_OVERFLOW = ((size_t)1) << (sizeof(size_t) * 4);
    if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
        nmemb > 0 && SIZE_MAX / nmemb < size)
      abort();
    

25
Questo è un oltraggio, 4dovrebbe essere CHAR_BIT / 2;-)
Steve Jessop,

@SteveJessop: qualcosa precluderebbe un'implementazione in cui CHAR_BITSera 16 e sizeof (size_t) era 2, ma il valore massimo di size_t era ad esempio 2 ^ 20 [size_t contenente 12 bit di riempimento]?
supercat

2
@supercat Non vedo nulla che ovviamente lo precluda in C99, il che significa che l'esempio è tecnicamente errato. Capita di essere tratto da (una versione leggermente modificata di) OpenBSD reallocarraye OpenBSD generalmente non crede nel soddisfare le possibilità che non si verificano nella loro ABI.
zwol,

3
@Zack: se il codice è progettato attorno a ipotesi POSIX, l'utilizzo di CHAR_BITS potrebbe dare l'impressione che il codice possa funzionare con valori diversi da 8.
supercat

2
@Zack: per rendere utili i tipi senza segno di larghezza esatta, la loro semantica dovrebbe essere definita indipendentemente dalla dimensione di int. Come è, dato uint32_t x,y,z;, il significato di (x-y) > zdipende dalla dimensione di int. Inoltre, un linguaggio progettato per scrivere codice robusto dovrebbe consentire ai programmatori di distinguere tra un tipo in cui si prevede che i calcoli superino l'intervallo del tipo e dovrebbe essere racchiuso silenziosamente, rispetto a uno in cui i calcoli che superano l'intervallo del tipo dovrebbero intrappolare, rispetto a uno in cui i calcoli non ci si aspetta che superi l'intervallo del tipo, ma ...
supercat

61

Quindi cosa c'è di sbagliato nello spiegare un codice complicato con un commento?

Non è una questione di giusto o sbagliato, ma della "migliore pratica", come definita nell'articolo di Wikipedia :

Una best practice è un metodo o una tecnica che ha costantemente mostrato risultati superiori a quelli ottenuti con altri mezzi e che viene utilizzato come riferimento.

Quindi la migliore pratica è cercare di migliorare prima il codice e usare l'inglese se ciò non è possibile.

Non è una legge, ma è molto più comune trovare codice commentato che richiede refactoring rispetto al codice refactored che richiede commenti, la migliore pratica riflette questo.


42
+1 per "è molto più comune trovare codice commentato che richiede refactoring rispetto al codice refactored che richiede commenti"
Brandon,

7
Ok, ma quanto spesso è quel commento: //This code seriously needs a refactor
Erik Reppen,

2
Naturalmente, qualsiasi cosiddetta migliore pratica non supportata da un rigoroso studio scientifico è semplicemente un'opinione.
Blrfl,

54

Verrà un giorno in cui il tuo codice bellissimo, perfettamente realizzato, ben strutturato e leggibile non funzionerà. O non funzionerà abbastanza bene. O sorgerà un caso speciale in cui non funziona e deve essere modificato.

A quel punto, dovrai fare qualcosa che cambia le cose in modo che funzioni correttamente. Soprattutto nel caso in cui ci siano problemi di prestazioni, ma anche spesso in scenari in cui una delle librerie, API, servizi web, gemme o sistemi operativi con cui stai lavorando non si comporta come previsto, puoi finire per dare suggerimenti che non lo sono necessariamente non elegante, ma sono controintuitivi o non ovvi.

Se non hai commenti per spiegare perché hai scelto questo approccio, ci sono ottime possibilità che qualcuno in futuro (e che qualcuno possa essere anche tu) guardi il codice, vedi come potrebbe essere "risolto" su qualcosa di più leggibile ed elegante e inavvertitamente annullare la correzione, perché non sembra una correzione.

Se tutti scrivessero sempre un codice perfetto, sarebbe ovvio che il codice che sembra imperfetto sta aggirando un intervento complicato dal mondo reale, ma non è così che funzionano le cose. La maggior parte dei programmatori spesso scrive codice confuso o un po 'intricato, quindi quando incontriamo questo è una naturale inclinazione a riordinarlo. Giuro che il mio io passato è un vero idiota ogni volta che leggo il vecchio codice che ho scritto.

Quindi non considero i commenti come scuse per il cattivo codice, ma forse come una spiegazione del perché non hai fatto la cosa ovvia. Avere // The standard approach doesn't work against the 64 bit version of the Frobosticate Libraryconsentirà ai futuri sviluppatori, incluso il tuo sé futuro, di prestare attenzione a quella parte del codice e testare contro quella libreria. Certo, potresti inserire anche i commenti nei commit del controllo del codice sorgente, ma le persone li guarderanno solo dopo che qualcosa è andato storto. Leggeranno i commenti sul codice mentre cambiano il codice.

Le persone che ci dicono che dovremmo sempre scrivere codice teoricamente perfetto non sono sempre persone con molta esperienza di programmazione in ambienti del mondo reale. A volte è necessario scrivere codice che funzioni a un certo livello, a volte è necessario interagire con sistemi imperfetti. Ciò non significa che non puoi farlo in modi eleganti e ben scritti, ma le soluzioni non ovvie hanno bisogno di spiegazioni.

Quando scrivo codice per progetti di hobby che so che nessun altro leggerà mai, continuo a commentare parti che trovo confuse - ad esempio, qualsiasi geometria 3D implica matematica con cui non sono del tutto a casa - perché so quando torno tra sei mesi avrò completamente dimenticato come fare queste cose. Non è una scusa per il codice errato, è un riconoscimento di una limitazione personale. Tutto ciò che farei lasciandolo senza commenti è creare più lavoro per me stesso in futuro. Non voglio che il mio io futuro debba riapprendere inutilmente qualcosa se posso evitarlo adesso. Quale possibile valore avrebbe?


5
@Christian è? La prima riga fa riferimento a tale affermazione, certamente, ma oltre a ciò è un po 'più ampia per come la comprendo.
glenatron,

9
"Giuro che il mio io passato è un vero idiota ogni volta che leggo il vecchio codice che ho scritto." Quattro anni dopo la mia carriera di sviluppo e trovo che questo avvenga ogni volta che guardo qualcosa di più vecchio di 6 mesi circa.
Ken,

6
In molti casi, le informazioni storiche più istruttive e utili si riferiscono a cose che sono considerate ma decise. Ci sono molti casi in cui qualcuno sceglie l'approccio X per qualcosa e qualche altro approccio Y sembrerebbe migliore; in alcuni di questi casi, Y "quasi" funzionerà meglio di X, ma risulta avere alcuni problemi insormontabili. Se Y fosse evitato a causa di questi problemi, tale conoscenza può aiutare a impedire ad altri di perdere tempo in tentativi falliti di attuare l'approccio Y.
supercat

4
Di giorno in giorno uso molto anche i commenti sui lavori in corso: non sono lì a lungo termine, ma passare in una nota TODO o in una breve sezione per ricordarmi che cosa avrei fatto dopo può essere utile promemoria al mattino.
glenatron,

1
@Lilienthal, non credo che lo scorso para è limitato a progetti-ha detto personali "... ho ancora commentare le parti che trovo confuso."
Wildcard il

29

La necessità di commenti è inversamente proporzionale al livello di astrazione del codice.

Ad esempio, Assembly Language è, per la maggior parte dei casi pratici, incomprensibile senza commenti. Ecco un estratto da un piccolo programma che calcola e stampa i termini della serie Fibonacci :

main:   
; initializes the two numbers and the counter.  Note that this assumes
; that the counter and num1 and num2 areas are contiguous!
;
    mov ax,'00'                     ; initialize to all ASCII zeroes
    mov di,counter                  ; including the counter
    mov cx,digits+cntDigits/2       ; two bytes at a time
    cld                             ; initialize from low to high memory
    rep stosw                       ; write the data
    inc ax                          ; make sure ASCII zero is in al
    mov [num1 + digits - 1],al      ; last digit is one
    mov [num2 + digits - 1],al      ; 
    mov [counter + cntDigits - 1],al

    jmp .bottom         ; done with initialization, so begin

.top
    ; add num1 to num2
    mov di,num1+digits-1
    mov si,num2+digits-1
    mov cx,digits       ; 
    call    AddNumbers  ; num2 += num1
    mov bp,num2         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jz  .done           ;

    ; add num2 to num1
    mov di,num2+digits-1
    mov si,num1+digits-1
    mov cx,digits       ;
    call    AddNumbers  ; num1 += num2
.bottom
    mov bp,num1         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jnz .top            ;
.done
    call    CRLF        ; finish off with CRLF
    mov ax,4c00h        ; terminate
    int 21h             ;

Anche con i commenti, può essere piuttosto complicato grok.

Esempio moderno: i regex sono spesso costrutti di astrazione molto bassi (lettere minuscole, numero 0, 1, 2, nuove righe, ecc.). Probabilmente hanno bisogno di commenti sotto forma di campioni (Bob Martin, IIRC, lo riconosce). Ecco una regex che (penso) dovrebbe corrispondere agli URL HTTP (S) e FTP:

^(((ht|f)tp(s?))\://)?(www.|[a-zA-Z].)[a-zA-Z0-9\-\.]+\.(com|edu|gov|m
+il|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(/($|[a-zA-Z0-9\.
+\,\;\?\'\\\+&amp;%\$#\=~_\-]+))*$

Man mano che le lingue avanzano nella gerarchia dell'astrazione, il programmatore è in grado di utilizzare astrazioni evocative (nome della variabile, nomi delle funzioni, nomi delle classi, nomi dei moduli, interfacce, callback, ecc.) Per fornire la documentazione integrata. Trascurare di approfittare di questo e usare i commenti per scrivere su di esso è pigro, un disservizio e mancanza di rispetto nei confronti del manutentore.

Sto pensando di Numerical Recipes in C tradotto parola per parola a Numerical Recipes in C ++ , che deduco che era iniziato come Numerical Recipes (in Fortan), con tutte le variabili a, aa, b, c, cc, ecc mantenuta attraverso ogni versione. Gli algoritmi potrebbero essere stati corretti, ma non hanno sfruttato le astrazioni fornite dalle lingue. E mi fanno fregare. Esempio da un articolo del Dr. Dobbs - Trasformata di Fourier veloce :

void four1(double* data, unsigned long nn)
{
    unsigned long n, mmax, m, j, istep, i;
    double wtemp, wr, wpr, wpi, wi, theta;
    double tempr, tempi;

    // reverse-binary reindexing
    n = nn<<1;
    j=1;
    for (i=1; i<n; i+=2) {
        if (j>i) {
            swap(data[j-1], data[i-1]);
            swap(data[j], data[i]);
        }
        m = nn;
        while (m>=2 && j>m) {
            j -= m;
            m >>= 1;
        }
        j += m;
    };

    // here begins the Danielson-Lanczos section
    mmax=2;
    while (n>mmax) {
        istep = mmax<<1;
        theta = -(2*M_PI/mmax);
        wtemp = sin(0.5*theta);
        wpr = -2.0*wtemp*wtemp;
        wpi = sin(theta);
        wr = 1.0;
        wi = 0.0;
        for (m=1; m < mmax; m += 2) {
            for (i=m; i <= n; i += istep) {
                j=i+mmax;
                tempr = wr*data[j-1] - wi*data[j];
                tempi = wr * data[j] + wi*data[j-1];

                data[j-1] = data[i-1] - tempr;
                data[j] = data[i] - tempi;
                data[i-1] += tempr;
                data[i] += tempi;
            }
            wtemp=wr;
            wr += wr*wpr - wi*wpi;
            wi += wi*wpr + wtemp*wpi;
        }
        mmax=istep;
    }
}

Come caso speciale di astrazione, ogni lingua ha espressioni idiomatiche / snippet di codice canonico per determinate attività comuni (eliminazione di un elenco di collegamenti dinamici in C) e, indipendentemente da come appaiono, non dovrebbero essere documentate. I programmatori dovrebbero imparare questi modi di dire, poiché fanno parte ufficiosamente della lingua.

Quindi il take away: codice non idiomatico creato da blocchi di basso livello che non possono essere evitati richiede commenti. E questo è necessario WAAAAY meno di quanto accada.


1
Nessuno in realtà dovrebbe essere scrivendo una linea come questa in linguaggio assembly: dec dword [term] ; decrement loop counter. D'altra parte, ciò che manca nell'esempio di linguaggio dell'assieme è un commento prima di ogni "paragrafo di codice" che spiega cosa fa il prossimo blocco di codice. In tal caso, il commento sarebbe in genere equivalente a una singola riga in pseudocodice, ad esempio ;clear the screen, seguita dalle 7 righe effettivamente necessarie per cancellare lo schermo.
Scott Whitlock,

1
Sì, ci sono quelli che considererei alcuni commenti superflui nell'esempio dell'assembly, ma ad essere onesti, è piuttosto rappresentativo dello stile di assemblaggio "buono". Anche con un prologo di paragrafo di una o due righe, il codice sarebbe davvero difficile da seguire. Ho capito il campione ASM meglio dell'esempio FFT. Ho programmato una FFT in C ++ alla scuola elementare, e non assomigliava affatto a questo, ma poi stavamo usando STL, iteratori, funzioni e parecchie chiamate di metodo. Non veloce come la funzione monolitica, ma molto più facile da leggere. Proverò ad aggiungerlo per contrastare l'esempio NRinC ++.
Kristian H,

Volevi dire ^(((ht|f)tps?)\:\/\/)?(www\.)*[a-zA-Z0-9\-\.]+\.(com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\;\?\'\\\+&%\$#\=~_\-]+))*$? Fai attenzione agli indirizzi numerici.
izabera,

Più o meno il mio punto: alcune cose costruite da astrazioni di livello molto basso non sono facili da leggere o verificare. I commenti (e, per non allontanarsi troppo, TEST) possono essere utili e non un danno. Allo stesso tempo, non utilizzare le astrazioni di livello superiore disponibili (: alpha:: num: dove disponibile) rende più difficile la comprensione, anche con buoni commenti, rispetto all'utilizzo delle astrazioni di livello superiore.
Kristian H,

3
+1: "The need for comments is inversely proportional to the abstraction level of the code." praticamente riassume tutto proprio lì.
Gerrat,

21

Non credo che ci sia qualcosa di sbagliato nei commenti nel codice. L'idea che i commenti siano in qualche modo cattivi secondo me è dovuta ad alcuni programmatori che spingono le cose troppo lontano. C'è un sacco di carrozzerie in questo settore, in particolare verso visioni estreme. Da qualche parte lungo la strada il codice commentato è diventato equivalente a un codice errato e non sono sicuro del perché.

I commenti hanno problemi: è necessario tenerli aggiornati quando si aggiorna il codice a cui si riferiscono, cosa che accade troppo di rado. Una wiki o qualcosa del genere è una risorsa più appropriata per una documentazione approfondita sul tuo codice. Il tuo codice dovrebbe essere leggibile senza richiedere commenti. Il controllo della versione o le note di revisione dovrebbero essere dove descrivi le modifiche apportate al codice.

Tuttavia, nessuna delle opzioni precedenti invalida l'utilizzo dei commenti. Non viviamo in un mondo ideale, quindi quando uno qualsiasi dei precedenti fallisce per qualsiasi motivo, preferirei avere qualche commento da ripiegare.


18

Penso che stai leggendo un po 'troppo in quello che sta dicendo. Vi sono due parti distinte nel reclamo:

Cosa c'è di sbagliato nello spiegare (1) un algoritmo complesso o (2) un pezzo di codice lungo e contorto con un commento descrittivo?

(1) è inevitabile. Non credo che Martin non sarebbe d'accordo con te. Se stai scrivendo qualcosa come la radice quadrata inversa veloce , avrai bisogno di alcuni commenti, anche se è solo "un malevole hacking a livello di bit in virgola mobile". Tranne qualcosa di semplice come un DFS o una ricerca binaria, è improbabile che la persona che legge il tuo codice abbia esperienza con quell'algoritmo, e quindi penso che ci dovrebbe essere almeno una menzione nei commenti su ciò che è.

La maggior parte del codice non è (1), tuttavia. Raramente scriverai un software che non è altro che implementazioni mutex eseguite a mano, oscure operazioni di algebra lineare con scarso supporto di libreria e nuovi algoritmi noti solo al gruppo di ricerca della tua azienda. La maggior parte del codice è costituito da chiamate libreria / framework / API, IO, boilerplate e unit test.

Questo è il tipo di codice di cui parla Martin. E risponde alla tua domanda con la citazione di Kernighan e Plaugher nella parte superiore del capitolo:

Non commentare il codice errato: riscriverlo.

Se hai sezioni lunghe e contorte nel tuo codice, non sei riuscito a mantenere pulito il tuo codice . La migliore soluzione a questo problema non è quella di scrivere un commento lungo un paragrafo nella parte superiore del file per aiutare i futuri sviluppatori a confonderlo; la soluzione migliore è riscriverla.

E questo è esattamente ciò che dice Martin:

L'uso corretto dei commenti è per compensare la nostra incapacità di esprimerci nel codice ... I commenti sono sempre degli errori. Dobbiamo averli perché non possiamo sempre capire come esprimerci senza di loro, ma il loro uso non è motivo di celebrazione.

Questo è il tuo (2). Martin concorda sul fatto che il codice lungo e contorto abbia bisogno di commenti, ma attribuisce la colpa a quel codice sulle spalle del programmatore che lo ha scritto, non un'idea nebulosa che "sappiamo tutti che non è abbastanza". Sostiene che:

Il codice chiaro ed espressivo con pochi commenti è di gran lunga superiore al codice disordinato e complesso con molti commenti. Invece di passare il tempo a scrivere i commenti che spiegano il disordine che hai fatto, spendi pulendo quel disordine.


3
Se uno sviluppatore con cui stavo lavorando ha semplicemente scritto "hacking malvagio a livello di bit in virgola mobile" per spiegare l'algoritmo a radice quadrata veloce, mi farebbe parlare con me. Fintanto che includevano un riferimento a un posto più utile, sarei comunque felice.
Michael Anderson,

8
Non sono d'accordo in un modo - un commento che spiega come funziona qualcosa di brutto è molto più veloce. Dato un po 'di codice che probabilmente non verrà toccato di nuovo (la maggior parte del codice immagino), un commento è una soluzione aziendale migliore di un grande refactoring, che spesso introduce bug (poiché una correzione che uccide un bug affidato è ancora un bug). Un mondo perfetto di codice perfettamente comprensibile non è disponibile per noi.
gbjbaanb,

2
@trysis haha, sì, ma in un mondo in cui i programmatori sono responsabili e non gli uomini d'affari, non spediranno mai perché placcano per sempre un codice di base costantemente riformulato in una vana ricerca della perfezione.
gbjbaanb,

4
@PatrickCollins quasi tutto ciò che leggo sul web è farlo nel modo giusto la prima volta. Quasi nessuno vuole scrivere articoli su come sistemare i pasticci! I fisici dicono "data una sfera perfetta ..." Gli scienziati comp dicono "dato uno sviluppo greenfield ..."
gbjbaanb

2
La soluzione migliore è riscriverlo a tempo infinito; ma date la base di codice di qualcun altro, le scadenze aziendali tipiche e la realtà; a volte la cosa migliore da fare è commentare, aggiungere un TODO: Refactor e portare quel refactor nella prossima versione; e quella correzione che doveva essere fatta ieri fatta ora. La cosa su tutto questo discorso idealistico sul solo refactoring è che non spiega come funzionano veramente le cose sul posto di lavoro; a volte ci sono priorità più elevate e scadenze abbastanza presto che impediranno di fissare codici legacy di scarsa qualità. È così.
hsanders,

8

Cosa c'è di sbagliato nello spiegare un algoritmo complesso o un pezzo di codice lungo e contorto con un commento descrittivo?

Niente di simile. Documentare il tuo lavoro è una buona pratica.

Detto questo, qui hai una falsa dicotomia: scrivere codice pulito vs. scrivere codice documentato - i due non sono in opposizione.

Ciò su cui dovresti concentrarti è semplificare e astrarre il codice complesso in un codice più semplice, invece di pensare "il codice complesso va bene fintanto che è commentato".

Idealmente, il tuo codice dovrebbe essere semplice e documentato.

In questo modo, invece di altri sviluppatori (incluso te stesso) che devono leggere l'intero algoritmo riga per riga per capire cosa fa, possono semplicemente leggere il commento descrittivo amichevole che hai scritto in un inglese semplice.

Vero. Questo è il motivo per cui tutti gli algoritmi dell'API pubblica dovrebbero essere spiegati nella documentazione.

Quindi, dopo aver scritto un codice complesso scritto in un linguaggio di programmazione parzialmente leggibile dall'uomo, perché non aggiungere un commento descrittivo e conciso che spieghi il funzionamento del codice in un inglese amichevole e comprensibile?

Idealmente, dopo aver scritto un pezzo di codice complesso dovresti (non un elenco esaustivo):

  • consideralo una bozza (cioè piano di riscriverla)
  • formalizzare i punti di ingresso / interfacce / ruoli / etc dell'algoritmo (analizzare e ottimizzare l'interfaccia, formalizzare astrazioni, condizioni preliminari ai documenti, postcondizioni ed effetti collaterali e documentare casi di errore).
  • scrivere test
  • pulizia e refactor

Nessuno di questi passaggi è banale da fare (vale a dire che ciascuno può richiedere alcune ore) e i premi per farlo non sono immediati. In quanto tali, questi passaggi sono (quasi) sempre compromessi (dagli sviluppatori che tagliano gli angoli, i gestori che tagliano gli angoli, le scadenze, i vincoli di mercato / altre condizioni del mondo reale, la mancanza di esperienza, ecc.).

[...] alcuni algoritmi sono complessi. E quindi sono difficili da capire leggendoli riga per riga.

Non dovresti mai fare affidamento sulla lettura dell'implementazione per capire cosa fa un'API. Quando lo fai, stai implementando il codice client in base all'implementazione (anziché all'interfaccia) e ciò significa che il tuo accoppiamento del modulo è già sparato all'inferno, stai potenzialmente introducendo dipendenze non documentate con ogni nuova riga di codice che scrivi e già aggiungendo debito tecnico.

È davvero così male spiegare un algoritmo complesso con alcune righe di commenti sulla sua operazione generale?

No, va bene. L'aggiunta di alcune righe di commenti non è tuttavia sufficiente.

Cosa c'è di sbagliato nello spiegare un codice complicato con un commento?

Il fatto che non dovresti avere un codice complicato, se questo può essere evitato.

Per evitare il codice complicato, formalizza le tue interfacce, spendi ~ 8 volte di più nella progettazione dell'API di quanto spendi per l'implementazione (Stepanov ha suggerito di spendere almeno 10 volte l'interfaccia, rispetto all'implementazione), e vai allo sviluppo di un progetto con la consapevolezza che stai creando un progetto, non solo scrivendo un algoritmo.

Un progetto prevede documentazione API, documentazione funzionale, misure di codice / qualità, gestione del progetto e così via. Nessuno di questi processi è una procedura una tantum e rapida da eseguire (richiedono tutti del tempo, richiedono riflessione e pianificazione, e tutti richiedono di tornare periodicamente a loro e di rivederli / completarli con i dettagli).


3
"Non dovresti mai fare affidamento sulla lettura dell'implementazione per capire cosa fa un'API." A volte questo ti viene inflitto da un upstream che ti impegni a utilizzare. Ho avuto un progetto particolarmente insoddisfacente disseminato di commenti sul modulo "esiste il seguente brutto codice Heath Robinson perché simpleAPI () non funziona correttamente su questo hardware nonostante ciò che afferma il fornitore".
pjc50,

6

invece di altri sviluppatori (incluso te stesso) che devono leggere l'intero algoritmo riga per riga per capire cosa fa, possono semplicemente leggere il commento descrittivo amichevole che hai scritto in un inglese semplice.

Considero questo un leggero abuso di "commenti". Se il programmatore vuole leggere qualcosa invece dell'intero algoritmo, è a questo che serve la documentazione della funzione. OK, quindi la documentazione della funzione potrebbe effettivamente apparire nei commenti nella fonte (forse per l'estrazione da strumenti doc), ma sebbene sintatticamente sia un commento per quanto riguarda il tuo compilatore, dovresti considerarli cose separate con scopi separati. Non penso che "i commenti debbano essere scarsi" significhi necessariamente "la documentazione dovrebbe essere scarsa" o addirittura "le informazioni sul copyright dovrebbero essere scarse"!

I commenti nella funzione sono per qualcuno da leggere e il codice. Pertanto, se nel codice sono presenti alcune righe difficili da comprendere e non è possibile renderle facilmente comprensibili, un commento è utile per il lettore da utilizzare come segnaposto per tali righe. Questo potrebbe essere molto utile mentre il lettore sta solo cercando di ottenere un'idea generale, ma ci sono un paio di problemi:

  • I commenti non sono necessariamente veri, mentre il codice fa quello che fa. Quindi il lettore si prende la parola per questo, e questo non è l'ideale.
  • Il lettore non capisce ancora il codice stesso, quindi fino a quando non ci tornano più tardi non sono ancora qualificati per modificarlo o riutilizzarlo. In tal caso cosa stanno facendo leggendolo?

Ci sono eccezioni, ma la maggior parte dei lettori dovrà capire il codice stesso. I commenti dovrebbero essere scritti per aiutare questo, non per sostituirlo, motivo per cui in genere si consiglia che i commenti dovrebbero dire "perché lo stai facendo". Un lettore che conosce la motivazione per le prossime righe di codice ha maggiori possibilità di vedere cosa fanno e come.


5
Un posto utile per i commenti: nel codice scientifico, spesso puoi avere calcoli abbastanza complessi, che coinvolgono molte variabili. Per la sanità mentale del programmatore, ha senso mantenere i nomi delle variabili molto brevi, quindi puoi guardare la matematica, piuttosto che i nomi. Ma questo rende davvero difficile da capire per il lettore. Quindi una breve descrizione di ciò che sta succedendo (o meglio, un riferimento all'equazione in un articolo di rivista o simile), può essere davvero utile.
naught101

1
@ naught101: sì, soprattutto perché il documento a cui ti riferisci probabilmente utilizza anche nomi di variabili a lettera singola. Di solito è più facile vedere che il codice segue effettivamente il documento se si usano gli stessi nomi, ma ciò è in conflitto con l'obiettivo del codice di essere autoesplicativo (è invece spiegato dal documento ). In questo caso, un commento in cui ogni nome è definito, che dice cosa significa effettivamente, sostituisce i nomi significativi.
Steve Jessop,

1
Quando sto cercando qualcosa di specifico nel codice (dove viene gestito questo caso specifico?), Non voglio leggere e comprendere paragrafi di codice solo per scoprire che dopo tutto non è il posto giusto. Ho bisogno di commenti che riassumano in una sola riga cosa sta facendo il prossimo paragrafo. In questo modo, localizzerò rapidamente le parti del codice relative al mio problema e salterò su dettagli poco interessanti.
Florian F,

1
@FlorianF: la risposta tradizionale è che i nomi delle variabili e delle funzioni dovrebbero indicare approssimativamente di cosa tratta il codice, e quindi lasciarti sfogliare cose che sicuramente non riguardano ciò che stai cercando. Sono d'accordo con te sul fatto che ciò non sempre ha successo, ma non sono così fortemente d'accordo sul fatto che penso che tutto il codice debba essere commentato per aiutare la ricerca o la lettura. Ma hai ragione, è un caso in cui qualcuno sta leggendo il tuo codice (in un certo senso) e legittimamente non ha bisogno di capirlo.
Steve Jessop,

2
@Snowman Le persone potrebbero farlo con nomi di variabili. Ho visto il codice in cui l'elenco delle variabiliOfApples conteneva un elenco di banane. Qualcuno ha copiato il codice che elabora l'elenco di mele e lo ha adattato per Bananas senza preoccuparsi di cambiare i nomi delle variabili.
Florian F,

5

Spesso dobbiamo fare cose complicate. È certamente giusto documentarli per una comprensione futura. A volte il posto giusto per questa documentazione è nel codice, dove la documentazione può essere aggiornata con il codice. Ma vale sicuramente la pena considerare una documentazione separata. Questo può anche essere più facile da presentare ad altre persone, includere diagrammi, immagini a colori e così via. Quindi il commento è solo:

// This code implements the algorithm described in requirements document 239.

o anche solo

void doPRD239Algorithm() { ...

Certamente le persone sono contente delle funzioni nominate MatchStringKnuthMorrisPratto encryptAESo partitionBSP. Vale la pena spiegare nomi più oscuri in un commento. È inoltre possibile aggiungere dati bibliografici e un collegamento a un documento da cui è stato implementato un algoritmo.

Se un algoritmo è complesso e nuovo e non ovvio, vale sicuramente la pena un documento, anche se solo per la circolazione interna dell'azienda. Controlla il documento nel controllo del codice sorgente se sei preoccupato che si perda.

Esiste un'altra categoria di codice che non è tanto algoritmico quanto burocratico. Devi impostare i parametri per un altro sistema o interagire con i bug di qualcun altro:

/* Configure the beam controller and turn on the laser.
The sequence is timing-critical and this code must run with interrupts disabled.
Note that the constant 0xef45ab87 differs from the vendor documentation; the vendor
is wrong in this case.
Some of these operations write the same value multiple times. Do not attempt
to optimise this code by removing seemingly redundant operations.
*/

2
Discuterei contro la denominazione di funzioni / metodi dopo il loro algoritmo interno, la maggior parte delle volte il metodo utilizzato dovrebbe essere una preoccupazione interna, documentando in ogni caso la parte superiore della tua funzione con il metodo utilizzato, ma non chiamarlo doPRD239Algorithmche mi dice nulla della funzione senza dover cercare l'algoritmo, la ragione MatchStringKnuthMorrisPratte il encryptAESlavoro è che iniziano con una descrizione di ciò che fanno, quindi seguono una descrizione della metodologia.
scragar

5

Non ricordo dove l'ho letto, ma c'è una linea netta e chiara tra ciò che dovrebbe apparire nel codice e quello che dovrebbe apparire come un commento.

Credo che dovresti commentare il tuo intento, non il tuo algoritmo . Vale a dire ciò che intendevi fare, non quello che fai .

Per esempio:

// The getter.
public <V> V get(final K key, Class<V> type) {
  // Has it run yet?
  Future<Object> f = multitons.get(key);
  if (f == null) {
    // No! Make the task that runs it.
    FutureTask<Object> ft = new FutureTask<Object>(
            new Callable() {

              public Object call() throws Exception {
                // Only do the create when called to do so.
                return key.create();
              }

            });
    // Only put if not there.
    f = multitons.putIfAbsent(key, ft);
    if (f == null) {
      // We replaced null so we successfully put. We were first!
      f = ft;
      // Initiate the task.
      ft.run();
    }
  }
  try {
    /**
     * If code gets here and hangs due to f.status = 0 (FutureTask.NEW)
     * then you are trying to get from your Multiton in your creator.
     *
     * Cannot check for that without unnecessarily complex code.
     *
     * Perhaps could use get with timeout.
     */
    // Cast here to force the right type.
    return (V) f.get();
  } catch (Exception ex) {
    // Hide exceptions without discarding them.
    throw Throwables.asRuntimeException(ex);
  }
}

Qui non c'è alcun tentativo di affermare ciò che esegue ogni passaggio, tutto ciò che afferma è ciò che dovrebbe fare.

PS: Ho trovato la fonte a cui mi riferivo - Coding Horror: il codice ti dice come, i commenti ti dicono perché


8
Il primo commento: è già stato eseguito? Che cosa è ancora funzionato? Lo stesso per gli altri commenti. Per qualcuno che non sa cosa fa il codice, questo è inutile.
gnasher729,

1
@ gnasher729 - Prelevato dal contesto quasi ogni commento sarà inutile - questo codice è una dimostrazione di aggiunta di commenti che indicano l' intento piuttosto che tentare di descrivere . Mi dispiace che non faccia nulla per te.
OldCurmudgeon,

2
Un manutentore di quel codice non avrà un contesto. Non è particolarmente difficile capire cosa fa il codice, ma i commenti non aiutano. Se scrivi commenti, prenditi il ​​tuo tempo e concentrati quando li scrivi.
gnasher729,

A proposito: il commento di Ha ancora eseguito si riferisce a Futuree indica che un get()seguito da un segno di spunta nullrileva se Futureè già stato eseguito, documentando correttamente l' intento anziché il processo .
OldCurmudgeon,

1
@OldCurmudgeon: la tua risposta è abbastanza vicina a quello che stavo pensando, che aggiungerò questo commento come esempio del tuo punto. Mentre un commento non è necessario per spiegare il codice pulito, un commento È buono per spiegare perché la codifica è stata fatta UN MODO SU UN ALTRO. Nella mia esperienza limitata, i commenti sono spesso utili per spiegare le idiosincrasie del set di dati su cui il codice sta lavorando, o le regole aziendali che il codice dovrebbe applicare. Commentare il codice che viene aggiunto per correggere un bug è un buon esempio, se quel bug si è verificato perché un'ipotesi sui dati era errata.
Randall Stewart,

4

Ma sappiamo tutti che non è abbastanza.

Veramente? Da quando?

Un codice ben progettato con buoni nomi è più che sufficiente nella stragrande maggioranza dei casi. Gli argomenti contro l'utilizzo dei commenti sono ben noti e documentati (come si fa riferimento a).

Ma queste sono linee guida (come qualsiasi altra cosa). Nel raro caso (nella mia esperienza, circa una volta ogni 2 anni) in cui le cose andrebbero peggio se trasformate in funzioni leggibili più piccole (a causa di prestazioni o esigenze di coesione), allora vai avanti - inserisci un lungo commento che spieghi cosa è effettivamente la cosa facendo (e perché stai violando le migliori pratiche).


7
So che non è abbastanza.
Florian F,

2
Da quando? Apparentemente, conosci già la risposta a questo. "Un codice ben progettato con buoni nomi è più che sufficiente nella stragrande maggioranza dei casi." Quindi, probabilmente non è abbastanza in una minoranza di casi, il che è esattamente ciò che chiede il richiedente.
Ellesedil,

3
Cerco sempre di decifrare il codice di altre persone che vorrei aver aggiunto alcuni commenti più di una volta ogni due anni.
Ogre Salmo33

@ OgrePsalm33 - Hanno metodi piccoli e usano buoni nomi? Il codice errato è errato, indipendentemente dai commenti.
Telastyn,

2
@Telastyn Sfortunatamente, quando si lavora su una base di codice di grandi dimensioni, i metodi "piccoli" e i nomi "buoni" sono soggettivi per ogni sviluppatore (quindi è un buon commento, del resto). Uno sviluppatore che scrive il codice dell'algoritmo di elaborazione grafica Flarbigan per 7 anni, può scrivere qualcosa di perfettamente chiaro per lui e sviluppatori simili, ma sarebbe criptico per il nuovo ragazzo che ha trascorso gli ultimi 4 anni a sviluppare il codice dell'infrastruttura della griglia Perbian. Quindi, 2 settimane dopo, l'esperto di Flarbigan si dimette.
Ogre Salmo33,

2

Lo scopo principale del codice è comandare a un computer di fare qualcosa, quindi un buon commento non può mai sostituire un buon codice perché i commenti non possono essere eseguiti.

Detto questo, i commenti nella fonte sono una forma di documentazione per altri programmatori (incluso te stesso). Se i commenti riguardano problemi più astratti di quello che il codice sta facendo in ogni fase, stai andando meglio della media. Quel livello di astrazione varia con lo strumento che stai usando. I commenti che accompagnano le routine del linguaggio assembly generalmente hanno un livello di "astrazione" inferiore rispetto, ad esempio, a questo APL A←0⋄A⊣{2⊤⍵:1+3×⍵⋄⍵÷2}⍣{⍺=A+←1}⎕. Penso che probabilmente meriterebbe un commento sul problema che intende risolvere, hmmm?


2

Se il codice è banale, non è necessario un commento esplicativo. Se il codice non è banale, anche il commento esplicativo sarà probabilmente non banale.

Ora, il problema con un linguaggio naturale non banale è che molti di noi non sono molto bravi a leggerlo o scriverlo. Sono sicuro che le tue abilità comunicative scritte sono eccellenti, ma tuttavia qualcuno con una conoscenza minore della lingua scritta potrebbe fraintendere le tue parole.

Se fai del tuo meglio per scrivere un linguaggio naturale che non può essere frainteso, finisci con qualcosa come un documento legale (e come tutti sappiamo che sono più prolissi e difficili da comprendere del codice).

Il codice dovrebbe essere la descrizione più concisa della tua logica e non dovrebbe esserci molto dibattito sul significato del tuo codice perché il compilatore e la piattaforma hanno l'ultima parola.

Personalmente non direi che non dovresti mai scrivere un commento. Solo che dovresti considerare perché il tuo codice ha bisogno di un commento e come potresti risolverlo. Questo sembra essere un tema comune nelle risposte qui.


Esattamente quello che stavo pensando quando non ero d'accordo con l'affermazione "Un essere umano può capire un pezzo di inglese molto più velocemente che può capire un pezzo di codice con lo stesso significato (purché l'operazione non sia banale)" Il codice è sempre meno ambiguo e più conciso.
Stephenbayer,

0

Un punto non ancora menzionato è che a volte commentare esattamente ciò che fa un pezzo di codice può essere utile nei casi in cui una lingua utilizza una particolare sintassi per molteplici scopi. Ad esempio, supponendo che tutte le variabili siano di tipo float, considera:

f1 = (float)(f2+f3); // Force result to be rounded to single precision
f4 = f1-f2;

L'effetto del cast esplicito di un floata floatè di forzare il risultato ad essere arrotondato alla precisione singola; il commento potrebbe quindi essere visto semplicemente dicendo ciò che fa il codice. D'altra parte, confronta quel codice con:

thing.someFloatProperty = (float)(f2*0.1); // Divide by ten

Qui, lo scopo del cast è quello di impedire al compilatore di scricchiolare nel modo più efficiente di calcolo accurato (f2 / 10) [è più preciso che moltiplicare per 0,1f, e sulla maggior parte delle macchine è più veloce della divisione per 10.0f].

Senza il commento, qualcuno che stava rivedendo il precedente codice potrebbe pensare che il cast sia stato aggiunto con la convinzione errata che sarebbe stato necessario per impedire al compilatore di scricchiolare e che non era necessario. In effetti, il cast ha lo scopo di fare esattamente ciò che dice la specifica del linguaggio: forzare il risultato del calcolo ad arrotondare alla precisione singola anche su macchine in cui l'arrotondamento sarebbe più costoso che mantenere il risultato in maggiore precisione. Dato che un cast floatpuò avere un numero di significati e scopi diversi, avere un commento specifica quale significato è inteso in un particolare scenario può aiutare a chiarire che il significato reale si allinea con l'intento.


Non sono sicuro che J. Random Programmer, guardando il secondo esempio, realizzerà che la costante è scritta 0.1 per una buona ragione, piuttosto che perché il programmatore originale ha dimenticato di digitare una 'f'.
David K,

Soprattutto durante il debug, non si presume mai che qualcosa sia stato fatto per una buona ragione.
gnasher729,

@DavidK: lo scopo del mio secondo codice di esempio era di contrastarlo con il primo pezzo di codice. Nel secondo pezzo di codice, l'intenzione del programmatore è probabilmente quella di avere someFloatPropertyla rappresentazione più accurata f2/10possibile; lo scopo principale del secondo cast è quindi semplicemente quello di compilare il codice . Nel primo esempio, tuttavia, il cast chiaramente non è necessario per il suo scopo normale (cambiare un tipo di tempo di compilazione in un altro) poiché gli operandi lo sono già float. Il commento serve a chiarire che il cast è necessario per uno scopo secondario (arrotondamento).
supercat

Sono d'accordo con l'idea che non è necessario fare commenti sul (float)cast nel secondo esempio. La domanda riguarda la costante letterale 0.1. Hai spiegato (nel prossimo paragrafo del testo) perché dovremmo scrivere 0.1: "è più preciso che moltiplicare per 0,1f". Sto suggerendo che quelle sono le parole che dovrebbero essere nel commento.
David K,

@DavidK: Includerei sicuramente il commento se sapessi che 0.1f sarebbe inaccettabilmente impreciso, e userei 0.1f se sapessi che la perdita di precisione sarebbe accettabile e che 0.1f sarebbe effettivamente materialmente più veloce di 0.1 . Se non sapessi che nessuna di queste cose fosse vera, preferivo che la mia abitudine di codifica fosse quella di utilizzare doublecostanti o calcoli intermedi il cui valore potrebbe non essere rappresentabile come float[sebbene in lingue che richiedono fastidiosi cast espliciti da doppio a galleggiante, pigrizia può spingere ad usare l'uso di floatcostanti non per la velocità, ma per ridurre al minimo il fastidio].
supercat

-1

I commenti che spiegano cosa fa il codice sono una forma di duplicazione. Se si modifica il codice e si dimentica di aggiornare i commenti, ciò può causare confusione. Non sto dicendo di non usarli, basta usarli con giudizio. Mi iscrivo alla massima di zio Bob: "Commenta solo ciò che il codice non può dire".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.