Come mantenere un prodotto software grande e complesso mantenibile negli anni?


156

Lavoro come sviluppatore di software da molti anni. È stata la mia esperienza che i progetti diventano più complessi e non realizzabili man mano che un numero maggiore di sviluppatori viene coinvolto nello sviluppo del prodotto.

Sembra che il software in una certa fase dello sviluppo abbia la tendenza a diventare "hacker" e "hacker" soprattutto quando nessuno dei membri del team che ha definito l'architettura lavora più all'interno dell'azienda.

Trovo frustrante che uno sviluppatore che deve cambiare qualcosa abbia difficoltà a ottenere il quadro generale dell'architettura. Pertanto, vi è la tendenza a risolvere i problemi o apportare modifiche in un modo che funziona contro l'architettura originale. Il risultato è un codice che diventa sempre più complesso e ancora più difficile da capire.

C'è qualche consiglio utile su come mantenere il codice sorgente davvero mantenibile nel corso degli anni?


9
consiglio vivamente i libri: "Software Project Survival Guide" di Steve McConnell, "Rapid Development" di Steve McConnell, "Refactoring" di Martin Fowler
Imran Omar Bukhsh

15
... e 'Clean Code' di Uncle Bob;) (Robert C. Martin)
Gandalf,

34
Non è proprio questa domanda qualcosa che ha generato diversi decenni di lettura pesante e interi corsi nelle università?
detenere il

17
@Eric Yin - Non sono d'accordo con i commenti. Per me sono un odore di codice e nei progetti a più lungo termine tendono a fare più male che bene perché inevitabilmente diventano obsoleti e diventano fuorvianti.
JohnFx,

8
@Eric Yin: cercare il codice di auto-documentazione. Usa i commenti di intenti solo dove migliorano la comprensione.
Mitch Wheat,

Risposte:


138

L'unica vera soluzione per evitare il marciume del codice è codificare bene!

Come codificare bene è un'altra domanda. È abbastanza difficile anche se sei un programmatore eccellente che lavora da solo. In una squadra eterogenea, diventa ancora molto più difficile. In (sub) progetti in outsourcing ... basta pregare.

Le solite buone pratiche possono aiutare:

  1. Mantienilo semplice.
  2. Mantienilo semplice. Questo vale soprattutto per l'architettura, il "quadro generale". Se gli sviluppatori hanno difficoltà a ottenere il quadro generale, stanno per codificarlo. Quindi semplifica l'architettura in modo che tutti gli sviluppatori la ottengano. Se l'architettura deve essere meno che semplice, gli sviluppatori devono essere addestrati a comprendere quell'architettura. Se non lo interiorizzano, non dovrebbero codificarlo.
  3. Obiettivo per basso accoppiamento e alta coesione . Assicurati che tutti nel team capiscano questa idea. In un progetto che consiste in parti vagamente accoppiate e coesive, se alcune parti diventano disordine non mantenibili, puoi semplicemente scollegare e riscrivere quella parte. È più difficile o quasi impossibile se l'accoppiamento è stretto.
  4. Sii coerente. Quali standard seguire sono poco importanti, ma per favore segui alcuni standard. In una squadra, tutti dovrebbero seguire gli stessi standard ovviamente. D'altra parte, è facile diventare troppo attaccati agli standard e dimenticare il resto: ti preghiamo di capire che mentre gli standard sono utili, sono solo una piccola parte della creazione di un buon codice. Non farne un gran numero.
  5. Le revisioni del codice possono essere utili per far lavorare una squadra in modo coerente.
  6. Assicurati che tutti gli strumenti - IDE, compilatori, controllo di versione, sistemi di compilazione, generatori di documentazione, librerie, computer , sedie , ambiente generale ecc. Ecc. - siano ben mantenuti in modo che gli sviluppatori non debbano perdere tempo con problemi secondari come come combattere conflitti di versione del file di progetto, aggiornamenti di Windows, rumore e qualsiasi cosa banale ma irritante. Dover perdere ripetutamente tempo considerevole con cose così poco interessanti abbassa il morale, che almeno non migliorerà la qualità del codice. In una grande squadra, potrebbero esserci uno o più ragazzi il cui compito principale è quello di mantenere gli strumenti per sviluppatori.
  7. Quando prendi decisioni tecnologiche, pensa a cosa sarebbe necessario per cambiare tecnologia; quali decisioni sono irreversibili e quali no. Valuta le decisioni irreversibili con molta attenzione. Ad esempio, se decidi di scrivere il progetto in Java , questa è una decisione praticamente irreversibile. Se decidi di utilizzare un formato binario auto-bollito per i file di dati, questa è anche una decisione abbastanza irreversibile (una volta che il codice è in circolazione e devi continuare a supportare quel formato). Ma i colori della GUI possono essere facilmente regolati, le funzionalità inizialmente escluse possono essere aggiunte in un secondo momento, quindi sottolinea meno su tali problemi.

8
Questi sono ottimi punti. Devo ammettere che faccio fatica a "mantenerlo semplice". Sembra significare cose diverse per persone diverse in contesti diversi, il che rende "semplice" piuttosto complesso (ma poi ho una naturale tendenza a complicare le cose).
Kramii,

3
Concordo perfettamente con i tuoi punti, in particolare "KIS". Ma vedo la tendenza che sempre più sviluppatori (più giovani?) Usano strutture piuttosto complesse per descrivere anche i contesti più semplici.
Chrmue,


8
Umm e "8. Keep it simple".
Dawood ibn Kareem, l'

7
# 6 è degno di un +10.
Jim G.

55

I test unitari sono tuoi amici . La loro attuazione impone un basso accoppiamento. Significa anche che le parti "hacky" del programma possono essere facilmente identificate e refactored. Significa anche che qualsiasi modifica può essere testata rapidamente per garantire che non rompano la funzionalità esistente. Questo dovrebbe incoraggiare i tuoi sviluppatori a modificare i metodi esistenti anziché duplicare il codice per paura di rompere le cose.

I test unitari funzionano anche come ulteriore bit di documentazione per il tuo codice, delineando cosa dovrebbe fare ogni parte. Con test unitari approfonditi, i programmatori non dovrebbero avere bisogno di conoscere l'intera architettura del programma per apportare modifiche e utilizzare classi / metodi esistenti.

Come bell'effetto collaterale, i test unitari potrebbero anche ridurre il conteggio dei bug.


3
Punto molto importante Ho assunto un sistema legacy, molte classi, molte righe di codice, nessuna documentazione, nessun test unitario. Dopo aver diligentemente creato unit test per tutte le correzioni e miglioramenti del codice, la progettazione del sistema si è evoluta in uno stato più pulito e più sostenibile. E abbiamo il "coraggio" di riscrivere parti fondamentali significative (coperte da test unitari).
Sam Goldberg,

40

Tutti qui sono pronti a menzionare il marciume del codice , e capisco perfettamente e sono d'accordo con questo, ma manca ancora il quadro più grande e il problema più grande qui a portata di mano. Il marciume del codice non succede e basta. Inoltre, vengono citati test unitari che sono buoni, ma non risolvono realmente il problema. Si può avere una buona copertura del test unitario e un codice relativamente privo di bug, tuttavia avere ancora codice e design marciti.

Hai detto che lo sviluppatore che lavora a un progetto ha difficoltà a implementare una funzione e manca il quadro più ampio dell'architettura generale, e quindi implementa un hack nel sistema. Dov'è la leadership tecnica per imporre e influenzare il design? Dove sono le revisioni del codice in questo processo?

In realtà non stai soffrendo di marciume del codice, ma stai soffrendo di marciume della squadra . Il fatto è che non dovrebbe importare se i creatori originali del software non fanno più parte del team. Se il responsabile tecnico del team esistente comprende appieno e in modo completo il design di base ed è bravo nel ruolo di responsabile tecnico, questo non sarebbe un problema.


Ottimo punto, hai colpito l'occhio di tori! Triste a dirsi, ma è esattamente quello che sta succedendo qui. E sembra impossibile cambiare le cose senza il comando tecnico ...
chrmue,

4
@chrmue Immagino solo il modo in cui vanno le cose, ma mi sto stancando. In molti modi vorrei essere di nuovo uno sviluppatore junior quando non ero così consapevole di quanto tutto ciò che mi circonda sembra sbagliato. Sembra che stia colpendo presto la mia crisi di metà carriera. Ma sono sconclusionato ... felice di aiutare.
maple_shaft

1
@Murph Perché non dovresti avere un leader di squadra onnisciente durante la fase di manutenzione? Ogni capo che non mi sarei mai aspettato niente di meno da un capo squadra a prescindere, e quando ero un capo squadra non mi aspettavo niente di meno da me stesso.
maple_shaft

1
@maple_shaft perché a) Non presumo che ci sia un team leader dedicato a quell'unico progetto e questo è più o meno il primo requisito eb) Penso che tu debba capire il design e l'implementazione (tutto) e questo è difficile. Da un lato, tutti sosteniamo che non dovremmo avere un solo carattere di tutta la conoscenza dei nostri progetti e tuttavia qui stiamo dicendo che dobbiamo averne uno? Questo non si somma?
Murph,

2
@maple_shaft probabilmente sono un vecchio programmatore scontroso (-: Ma c'è un problema in quanto spesso è lo "stile" di implementazione che deve essere seguito in profondità in una base di codice esistente - e che può essere estraneo sia al programmatore che al lead (per molti delle ragioni del mondo reale)
Murph,

18

Ci sono diverse cose che possiamo fare:

Assegnare a una persona la responsabilità generale dell'architettura. Quando si sceglie quella persona, assicurarsi che abbiano la visione e l'abilità per sviluppare e mantenere un'architettura e che abbiano l'influenza e l'autorità per aiutare gli altri sviluppatori a seguire l'architettura. Quella persona dovrebbe essere uno sviluppatore esperto di fiducia della direzione e rispettato dai colleghi.

Crea una cultura in cui tutti gli sviluppatori si appropriano dell'architettura. Tutti gli sviluppatori devono essere coinvolti nel processo di sviluppo e mantenimento dell'integrità architettonica.

Sviluppa un ambiente in cui le decisioni sull'architettura possano essere facilmente comunicate. Incoraggia le persone a parlare di design e architettura, non solo nel contesto del progetto attuale, ma anche in generale.

Le migliori pratiche di codifica semplificano la visualizzazione dell'architettura dal codice: dedicare del tempo al refactoring, commentare il codice, sviluppare unit test, ecc. Cose come le convenzioni di denominazione e le pratiche di codifica pulite possono aiutare molto nella comunicazione dell'architettura, quindi come team è necessario prendersi del tempo per sviluppare e seguire i propri standard.

Garantire che tutta la documentazione necessaria sia chiara, concisa, aggiornata e accessibile. Rendi pubblici sia i diagrammi di architettura di alto che di basso livello (bloccarli al muro) e mantenibili pubblicamente.

Infine (come perfezionista naturale) devo riconoscere che l'integrità architettonica è una degna aspirazione, ma che ci possono essere cose più importanti, come la creazione di un team che può lavorare bene insieme e spedire effettivamente un prodotto funzionante.


1
+1, in particolare per "creare un team che possa lavorare bene insieme e spedire effettivamente un prodotto funzionante".
Deworde

1
È una buona idea avere una persona come radice responsabile dell'architettura. Tuttavia hai un "odore di squadra" se questa responsabilità viene usata spesso: la squadra dovrebbe naturalmente giungere a congiunzioni comuni, invece di fare affidamento su una persona per fornire le risposte. Perché? La conoscenza totale del progetto è sempre condivisa, fissarla su una persona porterà a problemi più grandi alla fine: solo la sua visione è soddisfatta, tagliando efficacemente le ali del resto della squadra. Assumi invece le persone migliori e lascia che lavorino insieme.
casper

1
@casper: esattamente. Esprimi ciò che avevo in mente piuttosto meglio di me.
Kramii,

18

Il modo in cui mi occupo di questo problema è quello di tagliarlo alla radice:

La mia spiegazione utilizzerà i termini di Microsoft / .NET , ma sarà applicabile a qualsiasi piattaforma / toolbox:

  1. Utilizza gli standard per denominazione, codifica, check-in, flusso di bug, flusso di processo - praticamente qualsiasi cosa.
  2. Non abbiate paura di dire addio ai membri del team che non aderiscono agli standard. Alcuni sviluppatori semplicemente non possono lavorare all'interno di un set definito di standard e diventeranno nemici di quinta colonna sul campo di battaglia per mantenere pulita la base di codice
  3. Non abbiate paura di assegnare i membri del team con competenze inferiori ai test per lunghi periodi di tempo.
  4. Usa tutti gli strumenti del tuo arsenale per evitare di controllare il codice in decomposizione: ciò comporta strumenti dedicati, nonché test unitari pre-scritti che testano i file di build, i file di progetto, la struttura delle directory, ecc.
  5. In una squadra di circa 5-8 membri, fai in modo che il tuo migliore ragazzo faccia il refactoring quasi costantemente - ripulendo il casino che gli altri si lasciano alle spalle. Anche se trovi i migliori specialisti sul campo, avrai comunque un pasticcio: è inevitabile, ma può essere limitato dal costante refactoring.
  6. Scrivi test unitari e li mantieni - NON dipendere dai test unitari per mantenere pulito il progetto, non lo fanno.
  7. Discutere tutto. Non abbiate paura di passare ore a discutere di cose in squadra. Questo diffonderà le informazioni e rimuoverà una delle cause profonde del codice errato: confusione su tecnologie, obiettivi, standard, ecc.
  8. Fai molta attenzione quando i consulenti scrivono il codice: il loro codice sarà, quasi per definizione, la vera roba da merda.
  9. Effettuare recensioni preferibilmente come fase del processo prima del check-in. Non abbiate paura di eseguire il rollback degli commit.
  10. Non usare mai il principio di apertura / chiusura se non nell'ultima fase prima del rilascio: porta semplicemente a lasciare che l'odore del codice in decomposizione.
  11. Ogni volta che si riscontra un problema, prenditi il ​​tempo necessario per comprenderlo al massimo prima di implementare una soluzione: la maggior parte del marciume del codice deriva dall'implementazione della soluzione a problemi non completamente compresi.
  12. Usa le giuste tecnologie. Questi spesso arriveranno in set e saranno freschi: è meglio dipendere da una versione beta di un framework da cui ti verrà garantito il supporto in futuro, piuttosto che dipendere da framework estremamente stabili, ma obsoleti, che non sono supportati.
  13. Assumi le persone migliori.
  14. Dimentica il resto: non stai gestendo un bar.
  15. Se la gestione non è il miglior architetto e interferisce nel processo decisionale, trova un altro lavoro.

1
Ora, cosa faresti se dovessi mantenere e migliorare un'app VB6 di 12 anni da incubo con centinaia di forme e classi mentre lavoravi su una riscrittura in C # nel "tempo libero"?
jfrankcarr,

7
Non sono d'accordo con # 3. Il test non dovrebbe essere visto come una punizione per i non addestrati. In realtà dovrebbe essere fatto da tutti gli sviluppatori e considerato altrettanto importante quanto la programmazione e la progettazione! Se hai il signor Untrained nella squadra, allora tiralo fuori dalla squadra per un po 'di allenamento !.
NWS,

1
"Non sono d'accordo con il n. 3. I test non devono essere visti come una punizione per i non allenati." - Non dovrebbe e non è quello che ho scritto, lasciatemi spiegare: i test sono un buon modo per permettere alle persone che non si fidano ancora di impegnarsi a modificare il proprio codice. I migliori tester che ho trovato sono quelli che aspirano a diventare collaboratori del codice e stanno dimostrando la loro competenza osservando il codice, eseguendo il programma e mostrando la capacità di correlare i loro risultati in fase di esecuzione con il codice sorgente. Non è una punizione - la sua formazione.
casper

1
"Non sono d'accordo con il n. 10 - che aiuta con la qualità del codice se fatto correttamente" Questo è assolutamente falso nella mia esperienza lavorativa: il codice bloccato non può essere refactored, nel senso che rimarrà nel suo stato attuale fino allo sblocco. Questo stato può essere verificato per funzionare in una fase, ma in una fase successiva questa verifica è un falso positivo: tutto il codice deve essere lasciato aperto per il refactoring fino allo stadio prima del test finale del sistema.
casper

3
@casper: IMHO, Il principio di apertura / chiusura non dovrebbe essere inteso come "non è possibile cambiare l'origine", ma piuttosto "progettare il codice come se fosse congelato". Assicurarsi che sia possibile estendere il codice come essenziale senza richiedere modifiche ad esso. Il risultato è intrinsecamente più vagamente accoppiato e altamente coeso del codice medio. Questo principio è cruciale anche quando si sviluppa una libreria per l'utilizzo da parte di terzi, poiché non possono semplicemente entrare e modificare il codice, quindi è necessario che sia correttamente estensibile.
Kevin Cathcart,

12

Pulisci il codice marcio mediante refactoring, mentre scrivi i test unitari. Paga (questo) debito di progettazione su tutto il codice che tocchi, ogni volta che:

  • Sviluppa una nuova funzionalità
  • Risolvi un problema

Velocizza notevolmente il tuo ciclo di sviluppo test-first:

  • Refactoring per convertire i moduli di codice in un linguaggio di scripting
  • Utilizza macchine di prova veloci basate su cloud

Codice del refattore per utilizzare un accoppiamento basso (di unità altamente interne coesive) da:

  • Più semplici, (più) funzioni (routine)
  • moduli
  • Oggetti (e classi o prototipi)
  • Funzioni pure (senza effetti collaterali)
  • Preferire la delega, piuttosto che l'eredità
  • Livelli (con API)
  • Collezioni di piccoli programmi monouso che possono operare insieme

La crescita organica è buona; il grande design frontale è male.

Avere un leader che sia informato sul design attuale. In caso contrario, leggi il codice del progetto fino a quando non sarai informato.

Leggi libri di refactoring.


1
+1 Buona prima risposta, modo perfetto per presentarti a noi!
yannis,

11

Risposta semplice: non puoi .

Ecco perché dovresti mirare a scrivere software piccolo e semplice . Non è facile.

Questo è possibile solo se pensi abbastanza a lungo al tuo problema apparentemente complesso da definirlo nel modo più semplice e conciso possibile.

La soluzione a problemi veramente grandi e complessi può spesso essere ancora risolta basandosi su moduli piccoli e semplici.

In altre parole, come altri hanno sottolineato, la semplicità e l'accoppiamento sciolto sono gli ingredienti chiave.

Se ciò non è possibile o fattibile, probabilmente stai facendo ricerche (problemi complessi senza soluzioni semplici conosciute o nessuna soluzione nota). Non aspettatevi che la ricerca produca direttamente prodotti sostenibili, non è questo lo scopo della ricerca.


9

Lavoro su una base di codice per un prodotto che è stato in continuo sviluppo dal 1999, quindi come puoi immaginare è ormai abbastanza complesso. La più grande fonte di pirateria informatica nella nostra base di codice è dalle numerose volte che abbiamo dovuto portarlo da ASP Classic ad ASP.NET , da ADO ad ADO.NET, dai postback ad Ajax , passando da librerie UI a standard di codifica, ecc.

Tutto sommato abbiamo fatto un lavoro ragionevole per mantenere la base di codice mantenibile. Le principali cose che abbiamo fatto che hanno contribuito a questo sono:

1) Refactoring costante - Se devi toccare un pezzo di codice che è confuso o difficile da capire, ci si aspetta che tu prenda il tempo extra per ripulirlo e ti venga dato il margine di manovra nel programma per farlo. I test unitari lo rendono molto meno spaventoso, perché puoi testare più facilmente contro le regressioni.

2) Mantenere un ambiente di sviluppo pulito - Prestare attenzione all'eliminazione del codice non più utilizzato e non lasciare copie di backup / copie funzionanti / codice sperimentale nella directory del progetto.

3) Standard di codifica coerenti per la vita del Progetto - Ammettiamolo, le nostre opinioni sugli standard di codifica si evolvono nel tempo. Suggerisco di attenermi allo standard di codifica con cui hai iniziato per la vita di un progetto, a meno che tu non abbia il tempo di tornare indietro e adattare tutto il codice per conformarsi al nuovo standard. È fantastico che tu abbia superato la notazione ungherese ora, ma applica quella lezione a nuovi progetti e non passa solo a metà flusso su quel nuovo progetto.


8

Dato che hai taggato la domanda con la gestione del progetto, ho provato ad aggiungere alcuni punti non di codice :)

  • Pianifica il turnover - supponi che l'intero team di sviluppo sarà scomparso quando raggiungerà la sua fase di manutenzione - nessuno sviluppatore degno del suo sale vuole essere bloccato mantenendo il suo sistema per sempre. Inizia a preparare i materiali per la consegna non appena hai tempo.

  • Coerenza / uniformità non possono essere sottolineate abbastanza. Ciò scoraggerà una cultura di "andare da soli" e incoraggerà i nuovi sviluppatori a chiedere, se sono in dubbio.

  • Keep it mainstream - tecnologie utilizzate, modelli di progettazione e standard - perché un nuovo sviluppatore del team (a qualsiasi livello) avrà maggiori possibilità di mettersi rapidamente in funzione.

  • Documentazione - in particolare architettura - perché sono state prese le decisioni e standard di codifica. Conserva anche riferimenti / note / tabelle di marcia nella documentazione del dominio aziendale: rimarrai sorpreso dalla difficoltà per le aziende di spiegare cosa fanno a uno sviluppatore senza esperienza nel dominio.

  • Stabilisci le regole in modo chiaro, non solo per il tuo attuale team di sviluppo, ma pensa ai futuri sviluppatori della manutenzione. Se ciò significa inserire un collegamento ipertestuale alla documentazione standard relativa alla progettazione e alla codifica in ogni pagina, così sia.

  • Assicurati che l'architettura e in particolare i livelli di codice siano chiaramente delimitati e separati: ciò consentirà potenzialmente la sostituzione dei livelli di codice man mano che arrivano nuove tecnologie, ad esempio sostituendo un'interfaccia utente di moduli Web con un'interfaccia utente jQuery HTML5 , ecc., Che può acquistare circa un anno di maggiore longevità.


7

Una proprietà del codice altamente modificabile è la purezza delle funzioni .

Purezza significa che le funzioni dovrebbero restituire lo stesso risultato per gli stessi argomenti. Cioè, non dovrebbero dipendere dagli effetti collaterali di altre funzioni. Inoltre, è utile se non hanno effetti collaterali stessi.

Questa proprietà è più semplice da osservare rispetto alle proprietà di accoppiamento / coesione. Non devi fare di tutto per raggiungerlo, e personalmente lo considero più prezioso.

Quando la tua funzione è pura, il suo tipo è un'ottima documentazione da solo. Inoltre, scrivere e leggere la documentazione in termini di argomenti / valore di ritorno è molto più semplice di una menzione di uno stato globale (eventualmente accessibile da altri thread O_O).

Come esempio dell'utilizzo estensivo della purezza per aiutare la manutenibilità, puoi vedere GHC . Si tratta di un grande progetto di circa 20 anni in cui sono in corso grandi rifatturazioni e sono ancora state introdotte nuove funzionalità importanti.

Infine, non mi piace troppo il punto "Keep it simple". Non è possibile mantenere semplice il programma quando si modellano cose complesse. Prova a creare un semplice compilatore e il codice generato probabilmente finirà lentamente. Certo, puoi (e dovresti) rendere semplici le singole funzioni, ma di conseguenza l'intero programma non sarà semplice.


2
Un altro nome per quello che descrivi è la proprietà di essere deterministico
jinglesthula,

6

Oltre alle altre risposte, consiglierei i livelli. Non troppi ma sufficienti per separare diversi tipi di codice.

Utilizziamo un modello di API interno per la maggior parte delle applicazioni. Esiste un'API interna che si collega al database. Quindi un livello dell'interfaccia utente . Persone diverse possono lavorare su ogni livello senza interrompere o interrompere altre parti delle applicazioni.

Un altro approccio è quello di far leggere a tutti comp.risks e The Daily WTF in modo da apprendere le conseguenze di cattiva progettazione e cattiva programmazione e temeranno di vedere il proprio codice pubblicato su The Daily WTF .


6

Dal momento che molte di queste risposte sembrano concentrarsi su team di grandi dimensioni, anche fin dall'inizio, ho intenzione di mettere la mia opinione come parte di un team di sviluppo di due uomini (tre se includi il designer) per una startup.

Ovviamente, design e soluzioni semplici sono i migliori, ma quando hai il ragazzo che paga letteralmente il tuo stipendio respirando giù per il collo, non hai necessariamente il tempo di pensare alla soluzione più elegante, semplice e sostenibile. Con questo in mente, il mio primo grande punto è:

Documentazione Non commenti, il codice dovrebbe essere prevalentemente auto-documentante, ma cose come documenti di progettazione, gerarchie di classi e dipendenze, paradigmi architettonici, ecc. Tutto ciò che aiuta un programmatore nuovo, o addirittura esistente, a comprendere la base di codice. Inoltre, può essere utile documentare quelle pseudo-librerie strane che compaiono alla fine, come "aggiungere questa classe a un elemento per questa funzionalità", poiché impedisce anche alle persone di riscrivere la funzionalità.

Tuttavia, anche se hai un limite di tempo severo, trovo che un'altra cosa buona da tenere a mente è:

Evita gli hack e le soluzioni rapide. A meno che la correzione rapida non sia la correzione effettiva, è sempre meglio capire il problema sottostante a qualcosa, quindi risolverlo. A meno che tu non abbia letteralmente uno scenario "fallo funzionare nei prossimi 2 minuti, o sei licenziato", fare la correzione ora è un'idea migliore, perché non riparerai il codice in seguito, stai solo andando a passa al prossimo compito che hai.

E il mio consiglio personale preferito è più di una citazione, anche se non ricordo la fonte:

"Codice come se la persona che viene dopo di te è uno psicopatico omicida che sa dove vivi"


Ho sempre trovato utili i commenti di funzione e classe anche se solo per fornire la separazione dei segmenti di codice nelle posizioni di funzione e classe usando l'evidenziazione della sintassi. Raramente inserisco commenti nel codice funzione, ma scrivo una riga per ogni classe e funzione, come /** Gets the available times of a clinic practitioner on a specific date. **/o /** Represents a clinic practitioner. **/.
Nick Bedford,

5

Un principio che non è stato menzionato ma che trovo importante è il principio aperto / chiuso .

Non è necessario modificare il codice che è stato sviluppato e testato: tale parte di codice è sigillata. Invece, estendi le classi esistenti per mezzo di sottoclassi o usale scrivendo involucri, classi di decoratore o usando qualunque modello tu ritenga opportuno. Ma non modificare il codice di lavoro .

Solo i miei 2 centesimi.


Cosa succede se i requisiti aziendali espressi nel codice di lavoro cambiano? Un non-touch su codice scarsamente ponderato ma tecnicamente "funzionante" può farti del male a lungo termine, specialmente quando arriva il momento di apportare le modifiche necessarie.
jinglesthula,

@jinglesthula: Apri per estensione significa che puoi aggiungere la nuova funzionalità come nuova implementazione (ad es. classe) di un'interfaccia esistente. Naturalmente, il codice mal strutturato non lo consente: dovrebbe esserci un'astrazione come un'interfaccia che consente al codice di "cambiare" aggiungendo un nuovo codice anziché modificando il codice esistente.
Giorgio,

5
  • Sii uno scout . Lascia sempre il codice più pulito di quello che hai trovato.

  • Correggi le finestre rotte . Tutti quei commenti "cambiano nella versione 2.0" quando sei nella versione 3.0.

  • Quando ci sono grandi hack, progetta una soluzione migliore come squadra e fallo. Se non riesci a risolvere l'hack come una squadra, non capisci abbastanza bene il sistema. "Chiedi aiuto a un adulto." Le persone più anziane potrebbero averlo visto prima. Prova a disegnare o estrarre un diagramma del sistema. Prova a disegnare o estrarre i casi d'uso che sono particolarmente confusi come diagrammi di interazione. Questo non lo risolve, ma almeno puoi vederlo.

  • Quali ipotesi non sono più vere che hanno spinto il design in una direzione particolare? Potrebbe esserci un piccolo refactoring nascosto dietro un po 'di quel casino.

  • Se spieghi come funziona il sistema (anche solo un caso d'uso) e ti ritrovi a doverti scusare più volte di un sottosistema, è il problema. Quale comportamento renderebbe più semplice il resto del sistema (non importa quanto sia difficile da implementare rispetto a quello che c'è). Il sottosistema classico da riscrivere è uno che inquina ogni sottosistema con la sua semantica operativa e implementazione. "Oh, devi eseguire il groz dei valori prima di inserirli nel sottosistema froo, quindi sbloccali di nuovo man mano che ottieni l'output dal froo. Forse tutti i valori dovrebbero essere analizzati quando letti dall'utente e dalla memoria, e il resto del sistema è sbagliato? Questo diventa più eccitante quando ci sono due o più grozificazioni diverse.

  • Trascorri una settimana in team rimuovendo gli avvisi in modo che siano visibili problemi reali.

  • Riformatta tutto il codice secondo lo standard di codifica.

  • Assicurati che il tuo sistema di controllo della versione sia collegato al tuo bug tracker. Ciò significa che i cambiamenti futuri sono piacevoli e responsabili e puoi capire PERCHÉ.

  • Fai un po 'di archeologia. Trova i documenti di progettazione originali e rivedi. Potrebbero essere su quel vecchio PC nell'angolo dell'ufficio, nello spazio dell'ufficio abbandonato o nel casellario che nessuno apre mai.

  • Ripubblicare i documenti di progettazione su un wiki. Questo aiuta a istituzionalizzare la conoscenza.

  • Scrivi procedure simili a liste di controllo per versioni e build. Questo impedisce alle persone di pensare, in modo che possano concentrarsi sulla risoluzione dei problemi. Automatizza le build laddove possibile.

  • Prova l' integrazione continua . Prima ricevi una build fallita, meno tempo il progetto può spendere dalle rotaie.

  • Se il capo della tua squadra non fa queste cose, è un male per l'azienda.

  • Cerca di assicurarti che tutti i nuovi codici ottengano test unitari adeguati con copertura misurata. Quindi il problema non può andare molto peggio.

  • Prova a testare l'unità alcuni dei vecchi bit che non sono testati. Questo aiuta a ridurre la paura del cambiamento.

  • Se possibile, automatizza il test di integrazione e regressione. Almeno avere una lista di controllo. I piloti sono intelligenti, ricevono un sacco di soldi e usano liste di controllo. Inoltre si rovinano abbastanza raramente.


4

Leggi e rileggi il codice completo di Steve McConnell. È come una bibbia di buona scrittura di software, dalla progettazione iniziale del progetto a una singola riga di codice e tutto il resto. Quello che mi piace di più è che è supportato da decenni di dati solidi; non è solo il prossimo miglior stile di codifica.


3

Sono venuto per chiamare questo "Winchester Mystery House Effect". Come la casa, è iniziato abbastanza semplice, ma nel corso degli anni molti diversi lavoratori hanno aggiunto così tante caratteristiche strane senza un piano generale che nessuno lo capisce più davvero. Perché questa scala non va da nessuna parte e perché quella porta si apre solo in un modo? Chissà?

Il modo per limitare questo effetto è iniziare con un buon design realizzato dove è abbastanza flessibile da gestire l'espansione. Diversi suggerimenti sono già stati offerti su questo.

Ma spesso ti occuperai di un lavoro in cui il danno è già stato fatto ed è troppo tardi per un buon design senza eseguire una riprogettazione e una riscrittura costose e potenzialmente rischiose. In quelle situazioni, è meglio provare a trovare modi per limitare il caos mentre lo abbraccia in una certa misura. Potrebbe infastidire la tua sensibilità progettuale che tutto debba passare attraverso un'enorme, brutta, singolare classe "manager" o che il livello di accesso ai dati sia strettamente associato all'interfaccia utente, ma impari a gestirlo. Codice difensivo all'interno di quel framework e cerca di aspettarti l'inaspettato quando compaiono i "fantasmi" del passato dei programmatori.


2

Il refactoring del codice e i test unitari vanno benissimo. Ma dal momento che questo progetto di lunga data è in corso per gli hack, questo significa che la direzione non sta mettendo piede per pulire il marcio. Il team deve introdurre degli hack, perché qualcuno non sta allocando risorse sufficienti per formare le persone e analizzare il problema / richiesta.

Mantenere un progetto di lunga durata è una responsabilità tanto del project manager quanto di un singolo sviluppatore.

Le persone non introducono hack perché a loro piace; sono costretti dalle circostanze.


Costretto dalle circostanze? Le persone introducono degli hack perché (a) non conoscono meglio => ha bisogno di coaching, (b) non vedono il quadro generale => comunicazione, documentazione e disciplina necessarie, (c) pensano di essere più intelligenti => è il più grande ostacolo da superare (d) forzato dalle circostanze => hotfix rapidi sono ok quando si è sotto pressione del tempo, ma qualcuno deve assumersi la responsabilità e ripulire il codice in seguito. Ogni altra "circostanza" è semplicemente BS . Ci sono eccezioni a quella regola empirica, ma le cosiddette eccezioni indicano "pigro".
JensG,

2

Voglio solo porre un problema non tecnico e un approccio (forse) pragmatico.

Se al tuo manager non interessa la qualità tecnica (codice gestibile, architettura semplice, infrastruttura affidabile e così via), diventa difficile migliorare il progetto. In questo caso è necessario educare detto manager e convincere a "investire" gli sforzi nella manutenibilità e nell'affrontare il debito tecnico .

Se sogni con la qualità del codice trovata in quei libri, hai anche bisogno di un capo che sia preoccupato per questo.

O se vuoi solo domare un "progetto Frankenstein" questi sono i miei consigli:

  • Organizza e semplifica
  • Accorciare le funzioni
  • Dare priorità alla leggibilità rispetto all'efficienza (quando ovviamente accettabile e alcuni miglioramenti dell'efficienza sono troppo miseri per essere conservati)

Nella mia esperienza, la programmazione è entropica piuttosto che emergente (almeno nel popolare paradigma strutturato imperativo). Quando le persone scrivono il codice per "solo lavorare" la tendenza è quella di perdere la sua organizzazione. Ora organizzare il codice richiede tempo, a volte molto più che farlo funzionare.

Oltre all'implementazione delle funzionalità e alle correzioni di bug, prenditi il ​​tuo tempo per la pulizia del codice.


"... il progetto è condannato" - nella mia esperienza, non è necessariamente così. L'alternativa è educare detto manager e convincere a "investire" gli sforzi nella manutenibilità e nell'affrontare il debito tecnico
moscerino

Mi dispiace, non potevo trattenermi dal scriverlo, dato che avevo già un'esperienza quando il manager ha ignorato tutti i miei consigli sul debito tecnico. Ma penso che tu abbia ragione: circa un anno dopo aver rinunciato a quel progetto, il manager ha perso tutta la sua autorità sul team tecnico per la sua incapacità di gestirli.
Eric

1

Sono stato sorpreso di scoprire che nessuna delle numerose risposte ha evidenziato l'ovvio: rendere il software composto da numerose piccole librerie indipendenti. Con molte piccole librerie, puoi costruire un software grande e complesso. Se i requisiti cambiano, non devi buttare via l'intera base di codice o investigare su come modificare una grande base di clacson per fare qualcosa di diverso da quello che sta attualmente facendo. Decidi semplicemente quali di queste librerie sono ancora rilevanti dopo la modifica dei requisiti e come combinarle insieme per avere la nuova funzionalità.

Utilizzare qualsiasi tecnica di programmazione in quelle librerie che semplifichi l'utilizzo della libreria. Si noti che, ad esempio, qualsiasi linguaggio non orientato agli oggetti che supporta i puntatori a funzioni supporta effettivamente la programmazione orientata agli oggetti (OOP). Quindi, ad esempio in C, puoi fare OOP.

Puoi anche considerare di condividere quelle piccole librerie indipendenti tra molti progetti (i sottomoduli git sono i tuoi amici).

Inutile dire che ogni piccola libreria indipendente dovrebbe essere testata in unità. Se una particolare libreria non è testabile in unità, stai facendo qualcosa di sbagliato.

Se usi C o C ++ e non ti piace l'idea di avere molti piccoli file .so, puoi collegare tutte le librerie insieme in un file .so più grande, o in alternativa puoi fare collegamenti statici. Lo stesso vale per Java, basta cambiare .so in .jar.


Questo sembra essere un po 'troppo teorico dal mio punto di vista. Ovviamente i progetti che ho citato nella mia domanda erano costruiti con più librerie e moduli. La mia esperienza negli ultimi 26 anni di sviluppo del software è stata che più il progetto
inizia con

0

Semplice: riduci a zero i costi di manutenzione della maggior parte del codice fino a quando non hai un numero gestibile di parti mobili. Il codice che non deve mai essere modificato non comporta costi di manutenzione. Consiglio di puntare a far sì che il codice abbia davvero costi di manutenzione pari a zero , non tentando di ridurre il costo su molte iterazioni di refactoring di piccole dimensioni. Rendilo subito zero .

Va bene, è vero che è molto, molto più difficile di quanto sembri. Ma non è difficile iniziare. Puoi prendere un pezzo del codebase, testarlo, costruirci un'interfaccia piacevole se il design dell'interfaccia è un disastro e iniziare a far crescere le parti del codebase che sono affidabili, stabili (come in mancanza di motivi per cambiare), mentre contemporaneamente restringendo le parti che sono inaffidabili e instabili. Le basi di codice che sembrano un incubo da mantenere spesso non distinguono le parti mobili che devono essere sostituite da quelle che non lo sono, poiché tutto è considerato inaffidabile e incline a cambiare.

In realtà raccomando di andare fino in fondo per separare l'organizzazione della base di codice in parti "stabili" e "instabili", con le parti stabili che sono un enorme PITA da ricostruire e cambiare (il che è una buona cosa, dal momento che non dovrebbero aver bisogno di essere modificato e ricostruito se appartengono veramente alla sezione "stabile").

Non è la dimensione di una base di codice che rende difficile la manutenibilità. È la dimensione della base di codice che deve essere mantenuta. Dipendo da milioni di righe di codice ogni volta che, per esempio, utilizzo l'API del sistema operativo. Ma ciò non contribuisce ai costi di manutenzione del mio prodotto, poiché non devo mantenere il codice sorgente del sistema operativo. Uso solo il codice e funziona. Il codice che utilizzo e non devo mai mantenere non comporta costi di manutenzione da parte mia.

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.