10 cattive abitudini di programmazione che amiamo segretamente

L'abbiamo fatto tutti: abbiamo preso un biscotto quando la mamma non stava guardando, bevuto un po 'troppo vino per cena, lasciato l'auto in un parcheggio dopo che il contatore è scaduto. Abbiamo persino aggirato Deadman's Curve un po 'troppo velocemente. E sì, abbiamo tutti violato un numero qualsiasi delle regole cardinali della programmazione, quelle che tutti concordano siano cattive. E segretamente ci è piaciuto.

Abbiamo puntato il naso alle regole di una buona programmazione, digitato codice che è totalmente cattivo e abbiamo vissuto. Non c'erano fulmini dagli dei della programmazione. I nostri desktop non sono esplosi. In effetti, il nostro codice è stato compilato e spedito ei clienti sembravano abbastanza soddisfatti.

Questo perché una cattiva programmazione non è allo stesso livello, per esempio, di leccare un recinto elettrico o tirare la coda di una tigre. Il più delle volte, funziona. Le regole sono più spesso linee guida o suggerimenti stilistici, non direttive rigide che devono essere obbedite o seguirà la morte in codice. Certo, il tuo codice potrebbe essere ridicolizzato, forse anche pubblicamente. Ma il fatto che tu stia andando contro le convenzioni aggiunge un po 'di emozione a sovvertire, anche inavvertitamente, ciò che equivale (il più delle volte) ai costumi sociali del codice piacevole.

Per rendere le cose più complesse, a volte è meglio infrangere le regole. (Shhhh!) Il codice esce più pulito. Potrebbe anche essere più veloce e più semplice. Le regole sono generalmente un po 'troppo ampie e un programmatore abile può migliorare il codice infrangendole. Non dirlo al tuo capo, ma a volte ha senso programmare a modo tuo.

Quello che segue è un elenco di nove regole che alcuni potrebbero considerare inattaccabili, ma molti di noi infrangono spesso, con successo e piacere.

Cattiva abitudine di programmazione n. 1: copiare 

È sbagliato farlo a scuola. Sul lavoro, le regole non sono così chiare. Ci sono certamente alcuni blocchi di codice che non dovrebbero essere rubati. Se proviene da codice proprietario, non piegarlo nel tuo stack, soprattutto se è contrassegnato da un messaggio di copyright. Scrivi la tua versione. È quello per cui ti pagano.

La domanda più complicata arriva quando il creatore originale vuole condividere. Forse è su uno di quei forum di programmazione online. Forse è codice open source con una licenza (BSD, MIT) che consente di agganciare una o tre funzioni. Non c'è motivo legale che ti fermi. E sei pagato per risolvere i problemi, non per reinventare la ruota.

Il più delle volte, i vantaggi della copia sono convincenti e gli svantaggi possono essere limitati con un po 'di attenzione. Il codice che ottieni da una fonte attendibile ha già avuto almeno un giro di riflessione applicato ad esso. L'autore originale ha cercato una soluzione e ha trovato qualcosa. Le invarianti del ciclo e il flusso di dati sono stati elaborati.

Le domande difficili sono se ci sono alcuni bug infondati o alcune ipotesi diverse sul ruolo o sui dati sottostanti. Forse il tuo codice mescola puntatori nulli mentre il codice originale non li ha mai controllati. Se riesci a risolvere i problemi, è come se il tuo capo ricevesse l'input da due programmatori. È programmazione in coppia senza le fantasiose scrivanie.

Cattiva abitudine di programmazione n. 2: codice non funzionante

Nell'ultimo decennio circa, il paradigma funzionale è in ascesa. Gli accoliti per costruire il tuo programma a partire da chiamate di funzioni annidate amano citare studi che mostrano come il codice sia più sicuro e più privo di bug rispetto al vecchio stile di variabili e cicli, tutti messi insieme in qualsiasi modo renda felice il programmatore. I devoti parlano con lo zelo dei veri credenti, rimproverando gli approcci non funzionali nelle revisioni del codice e nelle richieste di richiamo. Potrebbero anche avere ragione sui vantaggi.

Ma a volte devi solo tirare fuori un rotolo di nastro adesivo. Il codice meravigliosamente ingegnerizzato e ben pianificato richiede tempo, non solo per immaginare ma anche per costruire e successivamente per navigare. Tutti questi livelli aggiungono complessità e la complessità è costosa. Gli sviluppatori di un bellissimo codice funzionale devono pianificare in anticipo e assicurarsi che tutti i dati vengano trasmessi lungo percorsi corretti. A volte è solo più facile raggiungere e modificare una variabile. Magari metti un commento per spiegarlo. Anche l'aggiunta di scuse lunghe e umilianti alle generazioni future nel commento è più veloce che riprogettare l'intero sistema per farlo nel modo giusto.

Cattiva abitudine di programmazione n. 3: spaziatura non standard

La maggior parte degli spazi nel software non ha effetto sulle prestazioni del programma. Ad eccezione di alcuni linguaggi come Python che utilizzano la spaziatura per indicare blocchi di codice, la maggior parte degli spazi ha effetto zero sul comportamento del programma. Tuttavia, ci sono programmatori ossessivi che li contano e insistono sul fatto che contano. Uno di loro una volta disse al mio capo con tono molto serio che stavo scrivendo "Codice non standard" e lo vide immediatamente. Il mio peccato? Violare la regola ESLint space-infix-ops non riuscendo a mettere uno spazio su entrambi i lati di un segno di uguale.

A volte devi solo pensare a qualcosa di più profondo del posizionamento degli spazi. Forse ti stai preoccupando che il database si sovraccarichi. Forse ti stai preoccupando in qualche modo che un puntatore nullo possa bloccare il tuo codice. Praticamente qualsiasi parte del codice è più importante degli spazi, anche se i comitati preposti agli standard preposti agli standard hanno riempito pagine di regole sul posizionamento di questi spazi o schede.

La cosa sorprendente è che ci sono molti buoni strumenti che riformatteranno automaticamente il tuo codice per aderire a regole di linting ben definite. Gli esseri umani non hanno bisogno di passare il tempo a pensare a questo. Se è così importante, possono eseguirlo attraverso lo strumento per risolvere il problema.

Cattiva abitudine di programmazione n. 4: usare goto

Il divieto di utilizzare gotorisale a un'era prima che molti degli strumenti di programmazione strutturata esistessero. Se i programmatori volessero creare un ciclo o passare a un'altra routine, avrebbero bisogno di digitare GOTOseguito da un numero di riga. Dopo alcuni anni, i team di compilatori consentono ai programmatori di utilizzare un'etichetta di stringa anziché un numero di riga. All'epoca questa era considerata una nuova funzionalità.

Alcuni hanno chiamato il risultato "codice spaghetti". Era impossibile per chiunque leggere il codice in un secondo momento e seguire il percorso di esecuzione. Era un miscuglio di fili, per sempre aggrovigliati. Edsger Dijkstra ha vietato il comando con un manoscritto dal titolo ironico "Goto Statement Considered Harmful".

Ma la ramificazione assoluta non è il problema. È il groviglio che ne risulta. Spesso un abile breako returnoffrirà una dichiarazione molto chiara su ciò che il codice sta facendo in quella posizione. A volte l'aggiunta gotoa un'istruzione case produrrà qualcosa che è più semplice da capire di un elenco strutturato in modo più appropriato di blocchi if-then-else a cascata.

Ci sono controesempi. Il buco di sicurezza "goto fail" nello stack SSL di Apple è uno dei casi migliori. Ma se stiamo attenti a evitare alcuni dei problemi nodosi delle dichiarazioni e dei loop case, possiamo inserire salti buoni e assoluti che rendono più facile per il lettore capire cosa sta succedendo. Possiamo inserire un breako un returnche sia più pulito e più gradito a tutti, tranne forse agli gotoodiatori.

Cattiva abitudine di programmazione n. 5: non dichiarare i tipi

Le persone che amano le lingue dattiloscritte hanno ragione. Scriviamo codice migliore e più privo di bug quando aggiungiamo dichiarazioni chiare del tipo di dati di ciascuna variabile. Fermarsi un momento per precisare il tipo aiuta il compilatore a segnalare errori stupidi prima che il codice inizi a essere eseguito. Può essere un dolore, ma aiuta. È un approccio alla programmazione con cinture e bretelle che blocca i bug.

I tempi sono cambiati. Molti dei compilatori più recenti sono abbastanza intelligenti da dedurre il tipo guardando il codice. Possono lavorare avanti e indietro nel codice finché non possono essere sicuri che la variabile deve essere a stringo an into qualcos'altro. E se questi tipi dedotti non si allineano, i compilatori possono sollevare un flag di errore. Non hanno più bisogno di noi per digitare le variabili.

Ciò significa che ora è più facile salvare alcuni bit tralasciando alcune delle dichiarazioni più semplici. Il codice diventa un po 'più pulito e il lettore di solito è abbastanza in grado di indovinare che la variabile denominata iin un ciclo for è un numero intero.

Cattiva abitudine di programmazione n. 6: codice Yo-yo

Ai programmatori piace chiamarlo "codice yo-yo". Per prima cosa i valori vengono memorizzati come stringhe. Quindi vengono analizzati in numeri interi. Quindi vengono riconvertiti in stringhe. È terribilmente inefficiente. Puoi quasi sentire la fatica della CPU sotto tutto il carico aggiuntivo. I programmatori intelligenti che scrivono codice veloce progettano le loro architetture per ridurre al minimo le conversioni. Il loro codice viene eseguito più velocemente grazie alla loro pianificazione.

Ma che ci crediate o no, a volte ha senso. A volte hai una libreria strabiliante che fa un miliardo di cose intelligenti all'interno della sua scatola nera proprietaria. A volte il capo scriveva un assegno a sette cifre per autorizzare tutto il genio dentro quella scatola nera. Se la libreria vuole i dati in stringhe, li dai alla libreria in stringhe anche se di recente li hai convertiti in interi.

Certo, potresti riscrivere tutto il codice per ridurre al minimo la conversione, ma ciò richiederebbe tempo. A volte va bene che il codice esegua un minuto, un'ora, un giorno o persino una settimana in più perché la riscrittura del codice richiederebbe ancora più tempo. A volte aumentare il debito tecnico è più economico che costruirlo proprio in primo luogo.

A volte la libreria non è codice proprietario, ma codice che hai scritto tu stesso molto tempo fa. A volte è più veloce convertire i dati una volta in più che riscrivere tutto in quella libreria. Quindi vai avanti e scrivi il codice yo-yo. Va tutto bene, ci siamo passati tutti.

Cattiva abitudine di programmazione n. 7: scrivere le proprie strutture dati

Una delle regole standard è che un programmatore non dovrebbe mai scrivere codice per la memorizzazione dei dati dopo aver completato il corso sulle strutture dati nel secondo anno. Qualcun altro ha già scritto tutte le strutture dati di cui avremo bisogno e il loro codice è stato testato e ritestato nel corso degli anni. È fornito in bundle con la lingua ed è probabilmente gratuito. Il tuo codice potrebbe contenere solo bug.

Ma a volte le librerie della struttura dei dati sono un po 'lente. A volte ci costringono a una struttura che può essere standard ma sbagliata per il nostro codice. A volte le librerie ci spingono a riconfigurare i nostri dati prima di utilizzare la struttura. A volte le librerie includono protezioni per cinture e bretelle con funzionalità come il blocco del filo e il nostro codice non ne ha bisogno.

Quando ciò accade, è il momento di scrivere le nostre strutture dati. A volte è molto, molto più veloce. E a volte rende il nostro codice molto più pulito perché non includiamo tutto il codice extra per riformattare i dati esattamente così.

Cattiva abitudine di programmazione n. 8: loop antiquati

Molto tempo fa, qualcuno che creava il linguaggio C voleva incapsulare tutte le possibilità astratte in un semplice costrutto. C'erano alcune cose da fare all'inizio, alcune cose da fare ogni volta attraverso il ciclo e un modo per dire quando tutto era finito. A quel tempo, sembrava una sintassi perfettamente pulita per catturare infinite possibilità.

Questo è stato allora. Ora alcuni rimproveri moderni vedono solo guai. Ci sono troppe cose in corso. Tutte queste possibilità di bontà sono ugualmente capaci di cattiveria. Rende la lettura e il grokking molto più difficili. Amano il paradigma più funzionale in cui non ci sono loop, solo funzioni applicate a elenchi, modelli computazionali mappati su alcuni dati.

Ci sono momenti in cui il modo loopless è più pulito, specialmente quando c'è solo una funzione ordinata e un array. Ma ci sono momenti in cui il ciclo vecchio stile è molto più semplice perché può fare molto di più. La ricerca della prima corrispondenza, ad esempio, è più semplice quando puoi interromperla non appena viene trovata.

Inoltre, le funzioni di mappatura incoraggiano una codifica più sciatta quando ci sono più cose da fare sui dati. Immagina di voler prendere il valore assoluto e quindi la radice quadrata di ogni numero. La soluzione più rapida è mappare la prima funzione e poi la seconda, ripetendo due volte i dati. 

Cattiva abitudine di programmazione n. 9: rompere i loop nel mezzo

Da qualche parte lungo la linea, un gruppo di creazione di regole ha dichiarato che ogni ciclo dovrebbe avere un "invariante", vale a dire un'affermazione logica che è vera per tutto il ciclo. Quando l'invariante non è più vero, il ciclo termina. È un buon modo per pensare a loop complessi, ma porta a divieti folli, come proibirci di usare a returno a breaknel mezzo del loop. Questo è un sottoinsieme della regola che proibisce le gotodichiarazioni.

Questa teoria va bene, ma di solito porta a un codice più complesso. Considera questo semplice caso che esegue la scansione di un array per una voce che supera un test:

mentre io
   
    

   ...

   if (test (a [i]) then return a [i];

   ...

}

Gli amanti delle invarianti di ciclo preferirebbero che aggiungessimo un'altra variabile booleana, la chiamassimo notFounde la usassimo in questo modo:

while ((notFound) && (i
   
    

...

if (test (a [i])) then notFound = false;

...

}

Se questo booleano ha un nome corretto, è un ottimo pezzo di codice auto-documentante. Potrebbe rendere più facile la comprensione per tutti. Ma aggiunge anche complessità. E significa allocare un'altra variabile locale e intasare un registro che il compilatore può o non può essere abbastanza intelligente da correggere.

A volte un gotoo un salto è più pulito.

Cattiva abitudine di programmazione n. 10: ridefinire operatori e funzioni

Alcuni dei linguaggi più divertenti ti permettono di fare cose veramente subdole come ridefinire il valore di elementi che sembrano dovrebbero essere costanti. Python, ad esempio, ti consente di digitare TRUE=FALSE, almeno nella versione 2.7 e precedenti. Questo non crea una sorta di collasso logico e la fine dell'universo; semplicemente scambia il significato di TRUEe FALSE. Puoi anche giocare a giochi pericolosi come questo con i preprocessori C e alcune altre lingue. Altre lingue ancora ti consentono di ridefinire operatori come il segno più.

Questo è un tratto, ma ci saranno punti all'interno di un grande blocco di codice quando sarà più veloce ridefinire una o più di queste cosiddette costanti. A volte il capo vuole che il codice faccia qualcosa di completamente diverso. Certo, potresti elaborare il codice e cambiare ogni occorrenza, oppure potresti ridefinire la realtà. Può farti sembrare un genio. Invece di riscrivere un'enorme libreria, capovolgi semplicemente un po 'e fa il contrario.

Forse è bene tracciare la linea qui. Non dovresti provarlo a casa, non importa quanto possa essere intelligente e divertente. Questo è troppo pericoloso, davvero ... onesto.