Criteri per una cattiva progettazione

La vita funziona molto semplicemente: spesso, per essere intelligenti, basta non fare cose stupide. Questo vale anche per lo sviluppo software: nella maggior parte dei casi, per fare bene una cosa, basta non farla male.

La maggior parte dei programmatori ha avuto esperienza con parti del sistema mal progettate. Ma ancora più tristemente, la maggior parte di voi avrà la triste esperienza di rendersi conto di essere stati gli autori di un tale sistema. Volevamo il meglio, ma è andata come sempre.

La maggior parte degli sviluppatori non aspira a una cattiva architettura e per molti sistemi arriva un punto in cui iniziano a dire che la sua architettura è terribile. Perché sta succedendo? Il design dell'architettura era cattivo fin dall'inizio o è diventato cattivo nel tempo?

La radice di questo problema è la mancanza di una definizione di "cattivo" design.

Mi sembra che sia la comprensione della qualità del design e le ragioni del suo "decadimento" le qualità più importanti per qualsiasi programmatore. Come nella maggior parte degli altri casi, l'importante è identificare il problema e sarà una questione di tecnologia risolverlo.

Definizione di "cattivo design"

Se decidi di vantarti del tuo codice di fronte a un collega programmatore, molto probabilmente verrai ridicolo in risposta: "Chi lo fa?", "Perché è così?" e "Farei le cose in modo diverso". Questo accade molto spesso.

Tutte le persone sono diverse, ma scrivi comunque il codice per i tuoi colleghi programmatori, quindi nel processo di sviluppo di ogni funzionalità, hai sempre bisogno di una fase di revisione quando altre persone guardano il tuo codice.

Ma anche se molte cose possono essere fatte in modi diversi, c'è una serie di criteri su cui tutti gli sviluppatori sarebbero d'accordo. Qualsiasi pezzo di codice che soddisfi i suoi requisiti ma mostri ancora una (o più) caratteristiche è un cattivo design.

Cattivo design:

  • Difficile da cambiare perché qualsiasi modifica influisce su troppe altre parti del sistema. ( Rigidità , Rigidità).
  • Quando vengono apportate modifiche, altre parti del sistema si interrompono inaspettatamente. ( Fragilità , Fragilità).
  • Il codice è difficile da riutilizzare in un'altra applicazione perché è troppo difficile estrarlo dall'applicazione corrente. ( Immobilità , Immobilità).

E la cosa divertente è che è quasi impossibile trovare un pezzo del sistema che non contenga nessuna di queste caratteristiche (ovvero flessibile, affidabile e riutilizzabile), soddisfi i requisiti e allo stesso tempo il suo design sia pessimo .

Pertanto, possiamo utilizzare queste tre caratteristiche per determinare in modo inequivocabile se un progetto è "cattivo" o "buono".

Cause di "cattiva progettazione"

Cosa rende un design rigido, fragile e inamovibile? Interdipendenza rigida dei moduli.

Un progetto è rigido se non può essere facilmente modificato. Questa rigidità è dovuta al fatto che una singola modifica a un pezzo di codice in un sistema intrecciato si traduce in modifiche a cascata nei moduli dipendenti. Questo accade sempre quando una persona sta lavorando al codice.

Ciò complica immediatamente l'intero processo di sviluppo commerciale: quando il numero di modifiche a cascata non può essere previsto dal progettista o dallo sviluppatore, è impossibile stimare l'impatto di tale modifica. Pertanto, cercano di posticipare tali cambiamenti a tempo indeterminato.

E questo a sua volta rende imprevedibile il costo del cambiamento. Di fronte a tale incertezza, i manager sono riluttanti ad apportare modifiche, quindi il design diventa ufficialmente rigido.

Ad un certo punto, il tuo progetto oltrepassa l'"orizzonte degli eventi" ed è destinato a cadere nel "buco nero" dell'architettura rigida.

La fragilità è la tendenza di un sistema a rompersi in più punti dopo un singolo cambiamento. Di solito si verificano nuovi problemi in luoghi che sono concettualmente estranei al luogo del cambiamento. Tale fragilità mina seriamente la fiducia nella progettazione e nella manutenzione del sistema.

Questo di solito accadeva quando non esistevano metodi privati. Basta rendere pubblici tutti i metodi e sarai condannato all'apparenza di un'architettura fragile. L'incapsulamento aiuta a far fronte a questo a livello micro. Ma a livello macro, hai bisogno di un'architettura modulare.

Quando un progetto ha un'architettura fragile, gli sviluppatori non possono garantire la qualità del prodotto.

Semplici modifiche in una parte dell'applicazione portano a bug in altre parti non correlate. Correggere questi errori porta a ulteriori problemi e il processo di scorta si trasforma in un famoso cane che si rincorre la coda.

Il progetto è immobile quando le parti necessarie del sistema sono fortemente legate ad altri dettagli indesiderati. Troppo del loro codice, dei loro approcci e soluzioni unici.

Ricordi il logger JUL, i cui sviluppatori hanno creato i propri livelli di registrazione senza una buona ragione? Questo è solo il caso.

Per dare a un designer un'idea di quanto sia facile riutilizzare un progetto esistente, basta pensare a quanto sarà facile utilizzarlo in una nuova applicazione.

Se il progetto è strettamente accoppiato, allora questo progettista sarà inorridito dalla quantità di lavoro necessaria per separare le parti richieste del sistema dai dettagli non necessari. Nella maggior parte dei casi, un tale progetto non è riutilizzabile, poiché il costo della sua separazione supera lo sviluppo da zero.

Rilevanza

Tutto cambia, ma tutto rimane uguale. (Proverbio cinese)

Molto buone domande sono state sollevate sopra. Quali sono i pericoli di sistemi fragili e rigidi? Sì, perché il processo di gestione di un progetto del genere diventa imprevedibile e incontrollabile. E il prezzo è esorbitante.

Come può un manager dare o non dare il via libera per aggiungere qualche funzionalità se non sa quanto tempo ci vorrà effettivamente? Come stabilire le priorità delle attività se non è possibile stimare adeguatamente il tempo e la complessità della loro implementazione?

E come possono gli sviluppatori ripagare lo stesso debito tecnico quando noi rastrelleremo pagandolo e non riusciamo a capire quanto rastrelleremo finché non rastrelleremo?

Anche i problemi con il riutilizzo o il test del codice sono molto rilevanti. I test unitari servono non solo a verificare alcune ipotesi sull'unità sottoposta a test, ma anche a determinare il grado della sua coesione e possono servire come indicatore di riutilizzo.

Ecco una citazione di Bob Martin per questo caso: "Per riutilizzare il tuo codice, devi fare lo sforzo di riutilizzarlo meno del costo di sviluppo da zero " . Altrimenti, nessuno si preoccuperà nemmeno di questa faccenda.

L'uso di principi e modelli di progettazione ha uno scopo: rendere il design buono. Se il loro utilizzo non ti dà alcun vantaggio (o viceversa viola i principi del “buon design”), allora qualcosa nel tuo conservatorio non va e, forse, lo strumento ha iniziato ad essere utilizzato per altri scopi.