Il codice commentato può essere una preziosa documentazione?


83

Ho scritto il seguente codice:

if (boutique == null) {
    boutique = new Boutique();

    boutique.setSite(site);
    boutique.setUrlLogo(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getLogo());
    boutique.setUrlBoutique(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getUrl());
    boutique.setNom(fluxBoutique.getNom());
    boutique.setSelected(false);
    boutique.setIdWebSC(fluxBoutique.getId());
    boutique.setDateModification(new Date());

    boutiqueDao.persist(boutique);
} else {
    boutique.setSite(site);
    boutique.setUrlLogo(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getLogo());
    boutique.setUrlBoutique(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getUrl());
    boutique.setNom(fluxBoutique.getNom());
    //boutique.setSelected(false);
    boutique.setIdWebSC(fluxBoutique.getId());
    boutique.setDateModification(new Date());

    boutiqueDao.merge(boutique);
}

C'è una linea commentata qui. Ma penso che renda il codice più chiaro, rendendo evidente quale sia la differenza tra ife else. La differenza è ancora più evidente con l'evidenziazione del colore.

Commentare un codice come questo può mai essere una buona idea?

Risposte:


109

La maggior parte delle risposte si concentra su come riformattare questo caso specifico, ma lasciatemi offrire una risposta generale al motivo per cui il codice commentato è solitamente negativo:

Innanzitutto, il codice commentato non viene compilato. Questo è ovvio, ma significa che:

  1. Il codice potrebbe non funzionare nemmeno.

  2. Quando le dipendenze del commento cambiano, non si romperà ovviamente.

Il codice commentato è molto "codice morto". Più a lungo rimane lì, più marcisce e fornisce sempre meno valore allo sviluppatore successivo.

In secondo luogo, lo scopo non è chiaro. Hai davvero bisogno di un commento più lungo che fornisca il contesto del perché ci sono righe commentate casualmente. Quando vedo solo una riga di codice commentata, devo cercare come è arrivato lì solo per capire perché è arrivato lì. Chi lo ha scritto? Che impegno? Qual era il messaggio / contesto di commit? Eccetera.

Considera le alternative:

  • Se l'obiettivo è fornire esempi di utilizzo di una funzione / api, fornire un test unitario. I test unitari sono codice reale e si interromperanno quando non saranno più corretti.
  • Se lo scopo è preservare una versione precedente del codice, utilizzare il controllo del codice sorgente. Preferirei piuttosto fare il checkout di una versione precedente, quindi attivare / disattivare i commenti in tutta la base di codice per "ripristinare" una modifica.
  • Se lo scopo è mantenere una versione alternativa dello stesso codice, utilizzare il controllo del codice sorgente (di nuovo). Ecco a cosa servono i rami, dopo tutto.
  • Se lo scopo è di chiarire la struttura, considerare come è possibile ristrutturare il codice per renderlo più ovvio. La maggior parte delle altre risposte sono buoni esempi di come potresti farlo.

5
Penso che manchi un motivo importante: Documentazione: se lo scopo è quello di documentare opzioni di progettazione alternative, dovrebbe essere fornita una spiegazione dell'alternativa e soprattutto il motivo per cui è stato scartato invece del codice originale.
Sarien,

14
Le opzioni di progettazione sono meglio spiegate in un linguaggio umano che in un linguaggio di programmazione.
Mark E. Haase,

3
Come sarebbe possibile per gli sviluppatori successivi che subentrano nel mio progetto sapere che esiste un'implementazione alternativa / precedente / non riuscita nel controllo del codice sorgente? I nuovi sviluppatori dovrebbero esaminare tutte le cronologie delle versioni e i registri delle modifiche? Oppure è una pratica comune utilizzare i commenti per collegarsi all'hash di un commit precedente per ogni utile implementazione alternativa? Se è così, non l'ho mai notato.
Moobie,

C'è un avvertimento a questo però. A volte, due approcci di codice equivalenti possono differire in prestazioni e raggiungibilità in un modo in cui uno è performante e l'altro è leggibile. In tal caso, è accettabile utilizzare la variante performante, ma inserire la variante leggibile nei commenti in modo da comprendere meglio lo scopo del codice. A volte, una riga di codice (commentata) può essere più chiara di una spiegazione dettagliata.
Flater

263

Il problema più grande con questo codice è che hai duplicato quelle 6 righe. Una volta eliminata quella duplicazione, quel commento è inutile.

Se si crea un boutiqueDao.mergeOrPersistmetodo, è possibile riscriverlo come:

if (boutique == null) {
    boutique = new Boutique();
    boutique.setSelected(false);
}

boutique.setSite(site);
boutique.setUrlLogo(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getLogo());
boutique.setUrlBoutique(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getUrl());
boutique.setNom(fluxBoutique.getNom());
boutique.setIdWebSC(fluxBoutique.getId());
boutique.setDateModification(new Date());

boutiqueDao.mergeOrPersist(boutique);

Il codice che crea o aggiorna un determinato oggetto è comune, quindi è necessario risolverlo una volta, ad esempio creando un mergeOrPersistmetodo. Certamente non dovresti duplicare tutto il codice di assegnazione per quei due casi.

Molti ORM hanno integrato il supporto per questo in qualche modo. Ad esempio, potrebbero creare una nuova riga se idè zero e aggiornare una riga esistente se idnon è zero. La forma esatta dipende dall'ORM in questione e poiché non ho familiarità con la tecnologia che stai utilizzando, non posso aiutarti.


Se non si desidera creare un mergeOrPersistmetodo, è necessario eliminare la duplicazione in qualche altro modo, ad esempio introducendo un isNewBoutiqueflag. Potrebbe non essere carino, ma è comunque molto meglio che duplicare l'intera logica di assegnazione.

bool isNewBoutique = boutique == null;
if (isNewBoutique) {
    boutique = new Boutique();
    boutique.setSelected(false);
}

boutique.setSite(site);
boutique.setUrlLogo(CmsProperties.URL_FLUX_BOUTIQUE + fluxBoutique.getLogo());
boutique.setUrlBoutique(CmsProperties.URL_FLUX_BOUTIQUE + fluxBoutique.getUrl());
boutique.setNom(fluxBoutique.getNom());
boutique.setIdWebSC(fluxBoutique.getId());
boutique.setDateModification(new Date());

if (isNewBoutique)
    boutiqueDao.persist(boutique);
else
    boutiqueDao.merge(boutique);

166

Questa è un'idea assolutamente orribile . Non chiarisce quale sia l'intento. Lo sviluppatore ha commentato la riga per errore? Per testare qualcosa? Cosa sta succedendo?!

A parte il fatto che vedo 6 righe assolutamente uguali in entrambi i casi. Piuttosto, dovresti impedire questa duplicazione del codice. Quindi sarà più chiaro che in un caso si chiama inoltre setSelected.


9
Concordato. Suppongo che vedendo che la riga commentata è un vecchio comportamento che è stato rimosso. Se è necessario un commento, dovrebbe essere in linguaggio naturale, non in codice.
Jules

4
Sono completamente d'accordo! Di recente ho trascorso ore intere a cercare di capire e ripulire alcune applicazioni che ho ereditato che sono quasi del tutto illeggibili a causa di questa pratica. Include anche il codice che è stato disconnesso da tutti gli altri codici ma non rimosso! Credo che questo sia uno degli scopi principali dietro i sistemi di controllo della versione. Ha commenti e le modifiche che ne derivano. Alla fine, ho avuto almeno 2 settimane di lavoro aggiunte al mio piatto in gran parte grazie a questa pratica.
bsara,

punto di vista simile in questo post: non inquinare base di codice con codice commentato
Nick Alexeev

120

No, è un'idea terribile. Sulla base di quel pezzo di codice mi vengono in mente i seguenti pensieri:

  • Questa riga viene commentata perché lo sviluppatore ha eseguito il debug e ha dimenticato di ripristinare la riga al suo stato precedente
  • Questa riga è commentata perché una volta faceva parte della logica aziendale, ma non è più il caso
  • Questa riga è commentata perché ha causato problemi di prestazioni sulla produzione e lo sviluppatore ha voluto vedere quale impatto ha avuto su un sistema di produzione

Dopo aver visto migliaia di righe di codice commentato, ora sto facendo l'unica cosa sensata quando lo vedo: lo rimuovo immediatamente.

Non esiste alcun motivo ragionevole per archiviare il codice commentato in un repository.

Inoltre, il tuo codice utilizza molte duplicazioni. Ti suggerisco di ottimizzarlo per la leggibilità umana il prima possibile.


1
Tuttavia, mi sbarazzo del codice duplicato, credo che difficilmente possa essere visto come un'ottimizzazione.
Alexis Dufrenoy,

23
è un'ottimizzazione per la leggibilità umana
jk.

11
@Traroth puoi ottimizzare per velocità, uso della memoria, consumo di energia o qualsiasi altra metrica, quindi non vedo che non puoi ottimizzare per la leggibilità (anche se come metrica è un po 'più instabile)
jk.

3
In effetti, intendevo la leggibilità umana. Piccolo suggerimento qui: la tua responsabilità più importante nella programmazione è il tuo codice. Quindi, meno è veramente di più qui.
Dibbeke,

4
Il software come una responsabilità è anche trattato su c2.com/cgi/wiki?SoftwareAsLiability Da lì: "Produrre più codice non è sempre un guadagno. Il codice è costoso da testare e mantenere, quindi se lo stesso lavoro può essere fatto con meno codice che è un vantaggio. Non commentare il codice morto, cancellalo. Il codice commentato diventa stantio e inutile molto velocemente, quindi puoi anche cancellarlo prima piuttosto che dopo, per perdere il disordine. Conserva buoni backup per renderlo più facile ".
ninjalj,

51

Vorrei solo aggiungere alla risposta di CodesInChaos, sottolineando che è possibile riformattare ulteriormente in piccoli metodi . La condivisione di funzionalità comuni per composizione evita i condizionali:

function fill(boutique) {    
  boutique.setSite(site);
  boutique.setUrlLogo(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getLogo());
  boutique.setUrlBoutique(CmsProperties.URL_FLUX_BOUTIQUE+fluxBoutique.getUrl());
  boutique.setNom(fluxBoutique.getNom());
  boutique.setIdWebSC(fluxBoutique.getId());
  boutique.setDateModification(new Date());
}    

function create() {
  boutique = new Boutique();      
  fill(boutique);
  boutique.setSelected(false);
  return boutiqueDao.persist(boutique);
}

function update(boutique) {
  fill(boutiquie);
  return boutiquieDao.merge(boutique); 
}

function createOrUpdate(boutique) {
  if (boutique == null) {
    return create();
  }
  return update(boutique);  
}

6
Penso che sia il suggerimento più pulito qui.
Alexis Dufrenoy,

+1, e aggiungerei anche che più eviti di passare intorno agli nulloggetti, meglio è (trovo che questa soluzione sia un buon esempio).
Nadir Sampaoli,

Vorrei passare boutiqueDaocome input per createe update.
Happy Green Kid Naps,

Come può funzionare? Come puoi sapere quando chiamare create e quando chiamare update? Il codice originale guarda alla boutique e sa se deve essere aggiornato o creato. Questo non fa nulla finché non chiami crea o aggiorna ...
Lyrion

Lyrion: Trivial, aggiungerò anche quel codice per chiarezza.
Alexander Torstling,

27

Mentre questo chiaramente non è un buon caso per il codice commentato, c'è una situazione che penso lo meriti:

// The following code is obvious but does not work because of <x>
// <offending code>
<uglier answer that actually does work>

È un avvertimento per chiunque vedrà in seguito che l'ovvio miglioramento non lo è.

Modifica: sto parlando di qualcosa di piccolo. Se è grande, spieghi invece.


5
Cosa c'è che non va // the following part done like it is because of X? Spiega il motivo per cui hai fatto qualcosa nel modo che hai fatto, non perché l'hai fatto , non ad esso in qualche particolare modo. Nel tuo esempio particolare, elimina completamente la necessità di un blocco potenzialmente grande di codice commentato. (Non ho votato per il downgrade, ma posso certamente vedere perché questo dovrebbe essere ridimensionato.)
un CVn del

13
Michael, perché rende chiaro per altri codificatori (e voi stessi giorni / settimane / mesi successivi) che sì, si ha provare che più pulito / approccio più intelligente, ma no, non ha funzionato a causa di X, in modo che non dovrebbe preoccuparsi di riprovare. Penso che questo sia un approccio completamente legittimo e ho votato a favore di questa risposta tristemente sepolta.
Garrett Albright,

1
@GarrettAlbright: Grazie, sono felice di vedere che qualcuno lo capisce.
Loren Pechtel,

3
@LorenPechtel: Non solo, stavo scrivendo più o meno esattamente la stessa cosa. Ci sono situazioni in cui è molto, molto utile sapere rapidamente quali soluzioni "ovvie" sono già state tentate senza successo e perché non funzionano.
JensG,

3
Oltre al codice fallito con spiegazione, vorrei anche commentare implementazioni alternative del codice che potrebbero essere più efficienti in un diverso ambiente di produzione. Ad esempio, ho codificato sia una versione temporale esponenziale semplice di un algoritmo, sia una versione temporale polinomiale complessa. Ma nella produzione attuale, nè piccolo e l'algo esponenziale è molto più veloce. Se in qualche modo dovesse ncambiare in seguito, come farebbe un futuro sviluppatore a seguire il mio progetto di una diversa implementazione del codice sepolto in profondità tra le centinaia di commit nel controllo del codice sorgente?
Moobie,

14

In questo esempio specifico, trovo il codice commentato molto ambiguo, in gran parte per i motivi indicati nella risposta di Dibkke . Altri hanno suggerito modi in cui potresti refactoring il codice per evitare anche la tentazione di farlo, tuttavia, se ciò non fosse possibile per qualche motivo (ad esempio, le linee sono simili, ma non abbastanza vicine), apprezzerei un commento come:

// Non è necessario deselezionare questa boutique, perché [WHATEVER]

Tuttavia, penso che ci siano alcune situazioni in cui lasciare (o addirittura aggiungere commenti commentati) non è riprovevole. Quando si utilizza qualcosa come MATLAB o NumPY, si può spesso scrivere codice equivalente che 1) scorre su un array, elaborando un elemento alla volta o 2) che gestisce l'intero array contemporaneamente. In alcuni casi, quest'ultimo è molto più veloce, ma anche molto più difficile da leggere. Se sostituisco del codice con il suo equivalente vettoriale, inserisco il codice originale in un commento vicino, in questo modo:

%% Il codice vettoriale qui sotto fa questo:

% for ii in 1:N
%    for jj in 1:N
%      etc.

% ma la versione a matrice funziona circa 15 volte più veloce sull'input tipico (MK, 03/10/2013)

Ovviamente, bisogna fare attenzione che le due versioni facciano effettivamente la stessa cosa e che il commento rimanga sincronizzato con il codice attuale o venga rimosso se il codice cambia. Ovviamente, si applicano anche le solite avvertenze sull'ottimizzazione prematura ...


"Ovviamente, bisogna fare attenzione che le due versioni facciano esattamente la stessa cosa e che il commento rimanga sincronizzato con ..." - proprio lì hai spiegato perché questa non è una buona idea.
sleske,

1
Bene, questo è un problema con tutti i commenti, giusto? Parte del codice vettoriale è sufficientemente opaco da rendere utili i commenti e avere una versione "srotolata" può essere utile per il debug.
Matt Krause,

Vero. Cercherei comunque di mantenere il commento il più breve possibile, non di utilizzare il codice sorgente completo. Ad ogni modo, se hai un esempio, chiedere come renderlo leggibile sarebbe una buona domanda (qui o su codereview.se).
sleske,

1
Nel tuo ultimo caso terrei entrambe le varianti di codice come codice compilabile.
CodesInChaos

12

L'unica volta che ho visto il codice commentato che era utile era nei file di configurazione, in cui viene fornito il codice per ogni opzione, ma commentato, rendendo facile abilitare le impostazioni semplicemente rimuovendo i marcatori di commento:

## Enable support for mouse input:
# enable_mouse = true

In questo caso, il codice commentato aiuta a documentare tutte le opzioni disponibili e come usarle. È anche convenzionale utilizzare i valori predefiniti in tutto, quindi il codice documenta anche le impostazioni predefinite.


7

In generale, il codice è auto-documentante solo per la persona che ha scritto il codice. Se è necessaria la documentazione, scrivere la documentazione. È inaccettabile aspettarsi che uno sviluppatore nuovo di una base di codice sorgente si sieda a leggere migliaia di righe di codice per tentare di capire da alto livello ciò che sta accadendo.

In questo caso, lo scopo della riga di codice commentata è mostrare la differenza tra due istanze di codice duplicato. Invece di provare a documentare sottilmente la differenza con un commento, riscrivi il codice in modo che abbia senso. Quindi, se ritieni ancora necessario commentare il codice, scrivi un commento appropriato.


2
Questo suona abbastanza vero. Molte persone (me compreso) pensano che il loro codice sia così fantastico che non ha bisogno di documentazione. Tuttavia, tutti gli altri nel mondo lo trovano inghiottito a meno che non sia accuratamente documentato e commentato.
Ryan Amos,

"Il codice è auto-documentante solo per la persona che ha scritto il codice " - Scegli un pezzo di codice complesso e non commentato che hai scritto un anno fa e prova a comprenderlo in un periodo di tempo limitato. Non puoi? Ooops.
JensG,

Penso che sia un po 'più sfumato. Un sacco di codice ben scritto è comprensibile e può essere compreso senza commenti. Il problema sta cercando di capire il quadro più ampio (anche a livello abbastanza locale) quando hai solo dettagli intricati da portare avanti. I commenti sono utili per spiegare parti di codice non ovvie, ma quando si hanno buone dotstring, spiegando a cosa servono effettivamente ciascuna funzione, classe e modulo, è necessario molto meno aiuto per dare un senso all'implementazione.
Carl Smith,

4

No, il codice commentato diventa stantio ed è presto peggio che inutile, spesso è dannoso, poiché cementa alcuni aspetti dell'implementazione, insieme a tutte le ipotesi attuali.

I commenti dovrebbero includere i dettagli dell'interfaccia e la funzione prevista; "funzione prevista": può includere, prima proviamo questo, poi proviamo quello, poi falliamo in questo modo.

I programmatori che ho visto tentano di lasciare delle cose nei commenti, sono semplicemente innamorati di ciò che hanno scritto, non vogliono perderlo, anche se non aggiunge nulla al prodotto finito.


2

Può essere in casi molto rari, ma non come l'hai fatto. Le altre risposte hanno abbastanza ben spiegato le ragioni di ciò.

Uno dei rari casi è una specifica RPM modello che utilizziamo nel mio negozio come punto di partenza per tutti i nuovi pacchetti, in gran parte per assicurarsi che non venga tralasciato nulla di importante. La maggior parte, ma non tutti i nostri pacchetti hanno un tarball contenente fonti che ha un nome standard ed è specificato con un tag:

Name:           foomatic
Version:        3.14
 ...
Source0:        %{name}-%{version}.tar.gz

Per i pacchetti senza fonti, commentiamo il tag e inseriamo un altro commento sopra di esso per mantenere il formato standard e indicare che qualcuno si è fermato e ha pensato al problema come parte del processo di sviluppo:

Name:           barmatic
Version:        2.71
 ...
# This package has no sources.
# Source0:        %{name}-%{version}.tar.gz

Non aggiungi il codice che sai che non verrà utilizzato perché, come altri hanno spiegato, potrebbe essere scambiato per qualcosa che appartiene lì. Può. tuttavia, sii utile per aggiungere un commento che spieghi perché manca il codice che ci si potrebbe aspettare:

if ( condition ) {
  foo();
  // Under most other circumstances, we would do a bar() here, but
  // we can't because the quux isn't activated yet.  We might call
  // bletch() later to rectify the situation.
  baz();
}

5
probabilmente quel commento non è commentato come codice però.
jk.

1
@jk: probabilmente hai ragione.
Blrfl,

1

Il codice commentato non viene utilizzato dall'applicazione, quindi deve essere accompagnato da ulteriori commenti che indicano perché non viene utilizzato. Ma in quel contesto, ci sono situazioni in cui il codice commentato può essere utile.

Quello che mi viene in mente è un caso in cui risolvi un problema usando un approccio comune e accattivante, ma poi si scopre che i requisiti del tuo problema reale sono leggermente diversi da quel problema. Soprattutto se i tuoi requisiti risultano richiedere molto più codice, la tentazione per i manutentori di "ottimizzare" il codice usando il vecchio approccio sarà probabilmente forte, ma farlo riporterà solo il bug. Mantenere l'implementazione "sbagliata" nei commenti aiuterà a dissipare ciò, perché puoi usarlo per illustrare esattamente perché questo approccio è sbagliato in questa situazione.

Questa non è una situazione che posso immaginare accadere molto spesso. Di solito, dovrebbe essere sufficiente spiegare le cose senza includere un esempio di implementazione "sbagliata". Ma posso immaginare un caso in cui ciò non è abbastanza, quindi poiché la domanda è se può essere utile, sì, può. Solo non il più delle volte.


1
Siamo spiacenti, ma non riesco a vedere alcun valore del codice di commento. Il codice che viene commentato non viene utilizzato, quindi non ha spazio nel codice di produzione.
Vladimir Kocjancic,

1
Si prega di definire "usato".
JensG,

Penso che intendesse "eseguito"
Alexis Dufrenoy il

-2

Questo non sembra buono amico.

Il codice commentato è ... non solo codice. Il codice è per l'implementazione della logica. Rendere un codice più leggibile in sé è un'arte. Come @CodesInChaos ha già suggerito che le linee di codice ripetitive non sono un'ottima implementazione della logica .

Pensi davvero che un vero programmatore preferirà la leggibilità rispetto all'implementazione logica. (a proposito abbiamo commenti e "complementi" da inserire nella nostra rappresentazione logica).

Per quanto mi riguarda, si dovrebbe scrivere un codice per il compilatore e va bene - se "capisce" quel codice. Per la leggibilità umana, i commenti sono positivi, per gli sviluppatori (a lungo termine), per le persone che riutilizzano quel codice (ad es. Tester).

Altrimenti puoi provare qualcosa di più flessibile qui, qualcosa di simile

boutique.setSite (sito) può essere sostituito con

setsiteof.boutique (sito). Esistono diversi aspetti e prospettive di OOP attraverso i quali è possibile aumentare la leggibilità.

Mentre questo codice sembra essere molto allettante all'inizio e si può pensare di aver trovato un modo per la leggibilità umana mentre anche il compilatore fa perfettamente il suo lavoro, e tutti iniziamo seguendo questa pratica porterà a un file fuzzy che diventerà meno leggibile nel tempo e più complesso in quanto si espanderà verso il basso.


15
"Per quanto mi riguarda, si dovrebbe scrivere un codice per il compilatore" Oh, per favore, non farlo. È così che finisci con le mostruosità che sembrano essere prese direttamente dall'Obfuscated C Contest e simili. I computer sono binari, mentre gli umani usano la logica fuzzy (questo vale il doppio per i proprietari di animali domestici). L'ora del computer è quasi gratis oggi (sostanzialmente solo l'utilizzo dell'elettricità) mentre il tempo del programmatore è relativamente molto costoso. Scrivi il codice per gli umani e il compilatore lo capirà. Scrivi il codice per il compilatore senza riguardo per gli umani e non farai molti amici nella squadra.
un CVn del

3
" scrivi codice per un compilatore " - In realtà no. La persona che dovresti avere in mente è quella a cui è stato affidato il compito di mantenere il tuo codice.
JensG,
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.