Cosa fai se raggiungi un punto morto progettuale con metodi evolutivi come Agile o XP?


11

Mentre leggevo il famoso post sul blog di Martin Fowler Is Design Dead? , una delle impressionanti impressioni che ho avuto è che, dato che in Metodologia Agile e Programmazione estrema, il design e la programmazione sono evolutivi, ci sono sempre punti in cui le cose devono essere riformulate.

Potrebbe essere possibile che quando il livello di un programmatore è buono e capiscono le implicazioni del design e non commettono errori critici, il codice continua a evolversi. Tuttavia, in un contesto normale, qual è la realtà di base in questo contesto?

In un giorno normale dato uno sviluppo significativo entra nel prodotto, e quando si verifica un cambiamento critico nei requisiti non è un vincolo che quanto mai desideriamo, gli aspetti fondamentali del design non possono essere modificati? (senza buttare via gran parte del codice). Non è del tutto probabile che si raggiunga un vicolo cieco su ogni ulteriore possibile miglioramento di design e requisiti?

Non sto sostenendo alcuna pratica non agile qui, ma voglio sapere da persone che praticano metodi di sviluppo agili o iterativi o evolutivi, come per le loro esperienze reali.

Hai mai raggiunto tali vicoli ciechi ? Come sei riuscito a evitarlo o a sfuggirlo? Oppure ci sono misure per garantire che il design rimanga pulito e flessibile mentre si evolve?

Risposte:


17

Ho appena letto il link all'articolo che hai postato, devo dire che Fowler ha fatto ottimi punti e molte cose che ha detto, sostengo il nostro team da anni.

IMO, se fai un progetto decente, non dovresti entrare in quella che sarebbe considerata una situazione senza uscita. Ho sempre visto il software come costituito da blocchi . Credo ancora in un design iniziale, ma l'obiettivo principale non è quello di progettare l'intero prodotto, ma di fornire un'architettura / direzione generale in modo che il tuo team possa visualizzare un'immagine comune a cui tutti stiamo lavorando. Se hai un mucchio di pezzi di cubo e triangolo, è utile delineare come un castello sarebbe messo insieme prima di iniziare semplicemente a schiaffeggiare i pezzi.

Da quando vengo dalla terra di OO, per me ogni blocco è una classe e la superficie di quel blocco è l'interfaccia pubblica (ciò che è visibile da classi esterne o derivate). Se segui buoni principi SOLIDI, ti assicurerai che ogni blocco sia estremamente semplice e abbia un'interfaccia pubblica intuitiva. Tornando alla mia analogia, vuoi assicurarti che il codice crei solo forme semplici. Ogni volta che crei classi troppo complesse (molte funzioni, molte variabili), crei forme che sono difficili da riutilizzare quando cambiano i requisiti.

Concordo con Fowler sul fatto che il rischio / sfida più grande per il design evolutivo è che lasci le decisioni di progettazione al tempo di programmazione e ti aspetti che ogni singolo sviluppatore prenda tali decisioni. È qui che il sistema può guastarsi se non si dispone di meccanismi di feedback adeguati. Ogni volta che viene richiesta una nuova funzionalità, è estremamente allettante trovare semplicemente la funzione che deve essere estesa, mettere un qualche tipo di condizionale al suo interno e semplicemente aggiungere un sacco di codice proprio all'interno di quella funzione. E a volte, questo potrebbe essere tutto ciò che è necessario, ma questa è anche (IMO) la singola pratica più comune che porta a componenti senza uscita. Questo non ha nulla a che fare con il design evolutivo. Questo è ciò che si chiama "no design".

Fintanto che ti prendi il tempo di fare un passo indietro e dire, aspetta un minuto, questa classe ha già 15 variabili membro, lasciami estrarre 6 di queste e metterle nella loro classe autonoma, il tuo software sarà composto da una luce di peso leggero, flessibile e riutilizzabile. Sicuramente se i PM arrivano e cambiano la metà dei requisiti dei prodotti, potresti dover rimuovere alcuni dei tuoi blocchi, rimetterli sullo scaffale e disegnarne di nuovi (proprio come quando si costruisce un castello, non è possibile utilizzare tutti i tuoi cilindri). Ma a quel punto, questa è solo una parte del fare affari. Requisiti modificati e mantenendo il codice flessibile e modulare, dovresti essere in grado di cambiare il tuo prodotto per allinearlo con la tua nuova direzione aziendale.

Credo che questo approccio evolutivo alla progettazione funzioni con ogni livello di abilità dell'ingegnere. Personalmente, ho realizzato software per molto tempo e prima che il nostro team passasse alla metodologia agile, ero responsabile della spedizione di diversi componenti principali dal mio PC di sviluppo quasi direttamente al cliente con quasi nessun controllo di qualità. Allo stesso tempo, quei componenti sono sempre rimasti flessibili e mantenibili.

Sto solo cercando di dire che mi considero relativamente decente nella progettazione del software. Allo stesso tempo, se mi chiedessi di scrivere un documento di progettazione di 100 pagine, consegnarlo a un programmatore e aspettarti che funzioni, probabilmente non avrei potuto progettarmi da un sacchetto di carta. Quando si avvia il lavoro, a volte vorrei delineare alcuni diagrammi simili a UML (molto semplificati, non in un linguaggio completo), ma quando comincio a programmare, refactoring secondo le necessità e il mio codice finale non assomiglierebbe mai a quello che ho disegnato in origine. Anche se passo un mese o due a pensare a ogni piccolo dettaglio, non riesco a immaginare qualcun altro che sia in grado di prendere i miei diagrammi e inventare un solido software senza modificare il design mentre codifica.

Dall'altra parte dello spettro, attualmente nella mia squadra (ora agile e lo sostengo pienamente) abbiamo un paio di ragazzi che si sono uniti a noi da un territorio incastonato dove hanno fatto C solo negli ultimi 15 anni. Ovviamente ho aiutato con alcune lezioni iniziali di pianificazione e preparazione, ma mi sono anche assicurato di dare seguito a revisioni periodiche del codice e sessioni di brainstorming in cui discutiamo delle applicazioni di SOLID e dei principi di progettazione. Hanno prodotto un po 'di codice spaghetti che mi ha fatto rabbrividire un po', ma con una leggera spinta da parte mia, hanno iniziato a refactoring ciò che era già stato prodotto e la cosa divertente è che uno di loro è tornato da me qualche giorno dopo e dice: Odio per dirlo ma dopo aver spostato quel codice, questo sembra molto più leggibile e comprensibile. Vicolo cieco evitato. Punto I ' Sto cercando di fare è che anche qualcuno che è completamente nuovo a OO può produrre un codice abbastanza decente, purché abbia un mentore con più esperienza, per ricordargli che il "design evolutivo" non è la stessa cosa di "nessun design". E anche alcune delle sue classi "più complesse" non sono così spaventose perché ogni classe non ha molta responsabilità (cioè non tanto codice), quindi il peggio peggiora, se quella classe "vicoli ciechi", noi buttalo e scrivi una classe sostitutiva che abbia la stessa interfaccia pubblica (finora non ho mai visto la necessità di questa contingenza in tutto ciò che abbiamo scritto e ho fatto revisioni del codice due volte a settimana).

Come nota finale, sono anche fermamente convinto dei documenti di progettazione (almeno per le condizioni aziendali del mio attuale team) ma l'obiettivo principale per i nostri documenti di progettazione è la Memoria organizzativa , quindi i documenti effettivi vengono scritti dopo che il codice è stato prodotto e refactoring. Prima della codifica, generalmente abbiamo una fase di progettazione rapida (a volte non così rapida) in cui delineamo le classi su tovaglioli / mspaint / visio e ricordo sempre alle persone che questa fase produce un percorso da seguire, non un progetto e mentre iniziano a programmare, tutto ciò che non ha senso dovrebbe essere cambiato. Anche con questi promemoria, i ragazzi più recenti tendono a provare a inserire il codice nel design originale, non importa quanto sia innaturale anche per loro. Questo di solito emerge nelle revisioni del codice.

Dang, ho scritto molto. Mi dispiace per quello.


1
+1 Ne è valsa la pena per ogni parola. Mi imbatto in queste situazioni mentre mi hai descritto e dopo che la scadenza è stata rispettata, chiedo loro di ripulire (leggi il refactor) di più da quello che penso sia il buonsenso del design. Ma spesso le persone trovano ripetuta la stessa domanda: perché sto facendo di nuovo la stessa cosa? Immagino ora di avere la risposta: se hai bisogno di un time to market più veloce e permetti all'evoluzione del design, il refactoring non è una compensazione per i tuoi vecchi peccati ma in realtà è una cosa legittima da fare.
Dipan Mehta,

Sì, hai scritto molto, ma è stata una buona cosa. Mi è piaciuto molto leggerlo :).
Radu Murzea,

11

Direi che i fenomeni di "progettazione senza uscita" sono ortogonali ai metodi agili. Quello che voglio dire è che è possibile fare a cascata, passare molto tempo in anticipo su un (cattivo) design. Quindi dedica molto tempo all'implementazione solo per ritrovarti in un vicolo cieco.

Semmai, i metodi agili dovrebbero aiutarti a scoprire prima che hai fatto cattive scelte di progettazione. La ragione di ciò è che il tuo backlog dovrebbe avere prima gli articoli con il più alto valore per il cliente e dovresti concentrarti sulla fornitura di utili incrementi del software. Se il tuo design ti consente di offrire un alto valore e utilità è già positivo per qualcosa :-) Al contrario, potresti avere un cattivo design in una situazione a cascata in cui potresti non scoprire per molti anni che questo design non può offrire qualsiasi valore e utilità - tutto ciò che hai è l'illusione che sia un buon design. Come si suol dire, la prova è nel budino.

Il rovescio della medaglia è che anche in metodi agili è importante avere una visione praticabile per la progettazione del sistema che guida le decisioni dall'iterazione all'iterazione. Penso che Ken Schwabber abbia detto qualcosa come se avessi un team di terribili sviluppatori che produrranno software scadente ripetutamente mediante iterazione. Agile significa semplicemente non passare molto tempo in anticipo perché sei limitato a ciò che puoi imparare o immaginare prima di iniziare l'implementazione ( e anche i requisiti cambiano). Tuttavia, ci sono alcune situazioni in cui devi fare un lavoro iniziale (es. Ricerca) e poi devi farlo.

Come evitare i vicoli ciechi?

Direi principalmente anticipando i requisiti futuri. Questo è qualcosa che ottieni con esperienza e familiarità con progetti / prodotti simili. Questa anticipazione è in parte ciò che ti aiuta a mettere in atto un buon design perché ti poni molte domande "what if" sul tuo sistema attuale. Per me questo è il componente critico. Tecniche come OO ti stanno semplicemente aiutando quando sai già cosa stai facendo.

Cosa fai se hai un vicolo cieco?

A "vicolo cieco" non è diverso da qualsiasi altro blocco tecnico si sarà colpito durante lo sviluppo di tutto ciò che è nuovo. La prima cosa da capire è che non ci sono veri e propri "vicoli ciechi" che ti costringono a tornare indietro completamente. Almeno il tuo apprendimento fino a questo punto è ciò che ti consente di andare avanti, quindi lo sforzo non è stato sprecato. Quando colpisci un vicolo cieco, hai un problema . Il problema è ciò che deve cambiare per soddisfare alcuni requisiti nuovi (o vecchi) e come ottimizzare apportare questa modifica. Tutto quello che devi fare ora è risolvere questo problema. Sii grato che questo sia un software e non, ad esempio un progetto di aeroplano, perché il cambiamento è molto più semplice. Identifica i problemi, risolvili == refactor == ingegneria del software. A volte è richiesto molto lavoro ...

Se usi Scrum questa modifica dovrebbe naturalmente essere guidata dalle storie degli utenti (che cosa ottiene l'utente da questa modifica?). Il processo sarebbe partito da una storia che non può essere facilmente adattata dal progetto attuale (oops) e si potrebbe discutere con il proprietario del prodotto su come scomporre questa storia. Continui ad applicare principi agili attraverso questo cambiamento.

Alcuni famosi grandi cambiamenti richiesti dal mondo del sistema operativo che mi vengono in mente:

Indipendentemente dal modo in cui li vedi, sono un sacco di lavoro. Il design originale quasi certamente non ha tenuto conto della possibilità che ciò accada (cioè la portabilità non era un grande requisito). Anche se il design era OO o meno non è probabilmente un fattore enorme. In un buon progetto le porzioni specifiche della piattaforma sarebbero in qualche modo isolate e il lavoro sarebbe più semplice.


In realtà le prime versioni di Windows NT implementavano un "Hardware Extract Layer" e supportavano DEC Apha e x86. Dato che nessuno ha mai acquistato macchine basate su DEC alfa, questo è stato tranquillamente abbandonato. Immagino che questa "indipendenza della macchina" esista ancora in un formato rudimentale nella versione attuale, quindi una porta ARM potrebbe non essere così difficile.
James Anderson,

3

Rifattorizzo permanentemente i miei progetti e utilizzo anche i diagrammi di classe UML. Voglio dire che creo uno o più diagrammi di classe per pacchetti. Ogni diagramma viene salvato nella radice del pacchetto. Ogni classificatore UML ha un proprio ID che è mappato al relativo ID Java. Ciò significa che quando apro il mio diagramma, questo viene automaticamente aggiornato con le ultime modifiche al refactoring del codice. Posso anche modificare direttamente i miei diagrammi di classe a livello grafico e tutto il mio progetto viene immediatamente riformulato. Funziona abbastanza bene ma non sostituirà mai l'uomo. Il mio diagramma di classe UML è anche solo una vista grafica del mio codice. È molto importante non mescolare il codice e il modello come sta facendo l'eclissi EMF perché non appena si esegue il refactoring si perdono anche le informazioni sul modello. Non uso mai il generatore di codice di sviluppo basato su modello perché questo è inutile. Io non

Detto questo, avere oltre 100 diagrammi di classe che rappresentano tutti i dettagli della struttura del mio progetto e pieno di note ovunque è davvero utile. Creo solo diagrammi di classe per progetti perché di solito gli sviluppatori non hanno il tempo di imparare o usare altri diagrammi. I diagrammi di classe sono anche così buoni perché aggiornati automaticamente. I diagrammi di classe possono essere creati dopo il codice semplicemente invertendo un pacchetto e aggiungendo note. È veloce e sempre accurato e iterativo al 100%.

Non creare confusione tra lo sviluppo guidato dal modello, che è un codice che genera un modello e di solito utilizza UML come presentazione grafica con diagrammi di classe UML aggiornati dal codice. Solo il codice sincronizzato UML ha un valore reale per me se più iterazioni.

Mi dispiace di essere così a lungo, ma penso che dovremmo dare una seconda possibilità ai diagrammi di classe UML se usati solo come vista grafica del nostro progetto. Significa che UML copre l'intero progetto e ha un singolo modello composto da grandi diagrammi di classe che rappresentano l'intero progetto. Sarebbe ridicolo avere centinaia di viste piccole e un modello per ogni vista all'interno di un progetto con centinaia di viste :-)


1
+1 per mostrare l'idea del codice postale UML! È interessante notare che non torniamo più ai documenti dei diagrammi dopo il codice!
Dipan Mehta,

Sì, questo è esattamente il problema con lo sviluppo guidato dal modello associato a UML. Generi la documentazione e non la usi mai in caso di modifiche al progetto. L'unione di modello e codice ci consente di utilizzare UML e di cambiarlo tutte le volte che ne abbiamo bisogno.
UML_GURU,

3

Ho raggiunto il vicolo cieco con il mio codice e altri codice a causa di cattiva progettazione, cambio di direzione, ecc. Ho anche visto molti altri riscontrare questo problema. Il grande errore (almeno mi sembra un errore) è il desiderio immediato di buttare via il codice di lavoro e reimplementare tutto da zero.

Ho affrontato ogni caso nello stesso modo che sembrava funzionare bene:

  • identificare perché il design attuale non funziona
  • elaborare un nuovo design e un piano di transizione
  • taggare il codice che non verrà utilizzato come obsoleto
  • implementare solo ciò di cui ho bisogno per il nuovo design al fine di soddisfare il bisogno immediato
  • rimuovere il codice che il nuovo codice rende obsoleto

Costi:

  • maggiore complessità grazie a 2 implementazioni nella base di codice contemporaneamente
  • più costo totale per funzionalità / correzione di errori rispetto a una riprogettazione / reimplementazione completa presupponendo casi e test di buon utilizzo

Benefici:

  • almeno un ordine di grandezza meno rischi perché hai ancora il vecchio design / codice funzionante
  • più veloce sul mercato per le funzionalità 1 a n-1 che non richiedono una riprogettazione completa
  • se il prodotto / codice finisce per morire prima di aver completato la riprogettazione completa, avrai risparmiato la differenza di tempo di sviluppo
  • approccio iterativo e tutti i vantaggi in esso contenuti

2

Circa un mese o due fa, il nostro progetto attuale è rimasto un po 'bloccato a causa di alcune decisioni di progettazione sbagliate (e della mancanza di molto design in un unico posto), con lo stile di sviluppo SCRUM.

La nostra soluzione (e quello che credo sia quella standard per SCRUM) è stata quella di dedicare un intero sprint (~ 2 settimane) a nient'altro che refactoring. Non sono state aggiunte nuove funzionalità durante questo periodo, ma siamo stati in grado di pensare alla base di codice corrente e progettare un sistema molto migliore per quello che stavamo facendo.

Ora abbiamo superato questo ostacolo e abbiamo aggiunto di nuovo nuove funzionalità.


Questo è un altro grande attrito da trovare. Dobbiamo dire al cliente - che ora stiamo costruendo la stessa cosa - di funzionalità che non sono una novità, dopo la consegna della prima versione! Quanto spesso e frequentemente (se mai) puoi permetterti di dirlo al cliente? o come si spiega?
Dipan Mehta,

Hai due opzioni di base, o prima dici semplicemente la semplice verità - che sebbene ciò che hai funzioni sia un disastro e deve essere riordinato per andare avanti o secondo che dici che stai costruendo infrastrutture per supportare lo sviluppo in corso ( che non è meno vero ma è filato per essere un po 'più positivo). Puoi concludere entrambi dicendo che per offrire la funzionalità abbiamo contratto un debito tecnico che ora deve essere rimborsato.
Murph,

@Dipan Mehta: supponiamo che un cliente desideri una casa a due piani. Lo progettate e lo consegnate. Quindi vogliono avere quattro piani aggiuntivi. Dici, bene, devo passare questo tempo solo per rendere l'edificio attuale più robusto in modo che possa contenere quattro piani aggiuntivi. Quindi non penso che questo dovrebbe essere un problema per il cliente se il piano originale includesse solo due piani. Se sono stati pianificati sei piani sin dall'inizio, sì, potrebbe essere un problema dirlo al cliente.
Giorgio,

@DipanMehta Siamo anche un po 'fortunati in quanto i clienti non conoscono necessariamente questo progetto. Si tratta di un aggiornamento a un prodotto che utilizzano attualmente, con una data di completamento semi-vaga intorno alla fine di quest'anno. Quindi non hanno nemmeno bisogno di sapere di un ritardo per il refactoring;) (Il manager che gestisce la maggior parte delle decisioni di progettazione è interno)
Izkata,

2

La chiave per limitare il costo delle modifiche di progettazione è mantenere il codice il più ASCIUTTO possibile. Ciò porterà la maggior parte del codice dell'applicazione a un livello molto elevato, in cui la maggior parte del codice esprime direttamente l'intenzione, e relativamente poco specifica il meccanismo. In questo caso, le decisioni di progettazione avranno l'espressione più piccola possibile nel codice e le modifiche di progettazione avranno il minor costo possibile.


2

La chiave per evitare vicoli ciechi di progettazione è riconoscere il più presto possibile quando il progetto deve cambiare e poi cambiarlo. I maggiori problemi non derivano dall'evoluzione continua del progetto, ma dal rifiuto di evolverlo fino a quando non diventa un problema enorme.

Ad esempio, Netflix ha una funzione di profilo, in cui diversi membri della famiglia possono fatturare allo stesso piano, ma hanno code separate. Alcuni anni fa, hanno annunciato che avrebbero dovuto annullare quella funzione, perché solo il 10% circa dei loro utenti lo utilizzava, ma a causa dell'attacco hackerato all'implementazione, stava consumando una quantità eccessiva di lavori di manutenzione. Dopo un tumulto, hanno morso il proiettile e hanno fatto una riprogettazione costosa per mantenere quei clienti.

Sono sicuro che c'erano alcuni ingegneri che hanno riconosciuto un design non ottimale quando hanno aggiunto per la prima volta quella funzione. Se lo avessero cambiato allora, non sarebbe stato un grosso problema.


1

Fred Brooks non ha detto qualcosa del tipo "Pensa di buttare via il primo"? Non sentirti troppo goffo a riguardo, i progetti di vicoli ciechi compaiono anche in progetti che provano a fare tutto il design anche in anticipo. Le riprogettazioni si verificano in tutti i tipi di sviluppo, sia perché era un progetto impraticabile sin dall'inizio (l'ultimo 20% che viene sorpreso - "il diavolo è nei dettagli") o perché un cliente cambia la sua attenzione. Non c'è davvero bisogno di campanelli d'allarme, non essere troppo preoccupato.

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.