Come gestisci un salto di complessità?


13

Sembra un'esperienza rara ma comune che a volte stai lavorando a un progetto e all'improvviso qualcosa si presenta inaspettatamente, getta una chiave enorme nelle opere e accelera molto la complessità.

Ad esempio, stavo lavorando su un'applicazione che parlava con i servizi SOAP su varie altre macchine. Ho montato un prototipo che ha funzionato bene, poi ho continuato a sviluppare un front-end regolare e in genere ho fatto funzionare tutto in modo carino, abbastanza semplice e facile da seguire. Ha funzionato alla grande fino a quando non abbiamo iniziato i test su una rete più ampia e improvvisamente le pagine hanno iniziato a scadere poiché la latenza delle connessioni e il tempo necessario per eseguire i calcoli su macchine remote hanno portato a richieste scadute ai servizi soap. Si è scoperto che dovevamo modificare l'architettura per estendere le richieste ai propri thread e memorizzare nella cache i dati restituiti in modo da poterli aggiornare progressivamente in background anziché eseguire calcoli su richiesta per richiesta.

I dettagli di quello scenario non sono troppo importanti - anzi non è un grande esempio in quanto era abbastanza prevedibile e le persone che hanno scritto molte app di questo tipo per questo tipo di ambiente potrebbero averlo anticipato - tranne che illustra un modo in cui si può iniziare con una premessa e un modello semplici e improvvisamente avere una escalation di complessità ben nello sviluppo del progetto.

Quali strategie avete per affrontare questi tipi di cambiamenti funzionali di cui si presenta la necessità - spesso a causa di fattori ambientali anziché di modifica delle specifiche - più avanti nel processo di sviluppo o come risultato di test? Come si fa a bilanciare l'evitamento dell'ottimizzazione precoce / YAGNI / i rischi di ingegnerizzazione eccessiva della progettazione di una soluzione che mitiga i possibili problemi ma non necessariamente probabili rispetto allo sviluppo di una soluzione più semplice e facile che sia probabilmente efficace ma che non incorpora la preparazione per ogni possibile eventualità?

Modifica: la risposta di Crazy Eddie include "lo fai schifo e trovi il modo meno costoso per implementare la nuova complessità". Ciò mi ha fatto pensare a qualcosa di implicito nella domanda ma non ho sollevato in modo specifico.

Una volta che hai colpito quel dosso, e hai incorporato le modifiche necessarie. Fai ciò che manterrà il progetto il più vicino possibile al programma ma che può influire sulla manutenibilità o torni alla tua architettura e lo ripeti su un livello più dettagliato che può essere più mantenibile ma respingerà tutto durante lo sviluppo?

Risposte:


8

Quello che mi viene in mente leggendo questo è il motto agile: affrontare i compiti più rischiosi e / o meno ben compresi prima nel ciclo di vita del progetto . Provo a mettere insieme uno scheletro funzionante del progetto il prima possibile, per dimostrare che il concetto funziona. Questo a sua volta consente anche di eseguire qualsiasi tipo di test crudeli per rilevare se l'architettura offre davvero la sua promessa in circostanze di vita reale. Inoltre, se nella soluzione è inclusa una tecnologia / piattaforma / strumento nuovi e sconosciuti, prenderlo presto anche sulla piastra.

Se l'architettura di base è OK, le singole funzionalità possono essere aggiunte e testate in modo incrementale e refactored quando necessario, con costi relativamente inferiori. La necessità di cambiare l'architettura è il grande rischio, che si dovrebbe affrontare in anticipo. Ciò fornisce un rapido feedback: nel peggiore dei casi, se l'intero concetto cade, lo sappiamo presto e possiamo interrompere il progetto con una perdita minima.


6

Il tuo esempio ha toccato alcuni degli aspetti più difficili della programmazione, vale a dire il calcolo distribuito e la programmazione concorrente , che stanno diventando sempre più ampiamente utilizzati e rendono il lavoro dei programmatori sempre più difficile.

Anche la programmazione "normale" (thread singolo su una macchina) è così enormemente complessa per qualsiasi programma non banale, che ci vuole una grande abilità e anni di esperienza per ottenerne qualcosa di buono - ma ancora lontana dal "risolto". Anche a questo livello le complessità, principalmente dovute all'esplosione combinatoria , superano di gran lunga la capacità del cervello umano di comprendere e comprendere appieno. Pensare diversamente è sciocco.

Il calcolo distribuito e la programmazione concorrente aggiungono altre due dimensioni alla dimensione dello spazio "complessità", che cresce almeno in cubi (sp?) (N ^ 3) rispetto alla programmazione "normale". Solo per esempio, pensa ad alcune nuove serie di problemi e fallacie che dobbiamo affrontare. Anche giocare con un'idea, che potresti capire interconnessioni ed effetti collaterali su questa scala è ridicolo.

Chiaramente non ho proiettili d'argento, ma sono abbastanza sicuro che l' errore più grande che si possa fare è pensare di aver capito tutto e risolto.

Alcune idee su come affrontare tutto questo, oltre a ciò che altre risposte hanno già trattato:

  • Grande umiltà
  • Accetta che il tuo sistema / programma sia imperfetto, impermanente e incompleto .
  • Preparare errori
  • Abbraccia il cambiamento
  • Pianificare la ridondanza
  • Pensa alle prove future
  • Guarda (o studia) la biologia o la sociologia come si comportano i sistemi complessi
  • Fai del tuo meglio per evitare lo stato mutevole. Scegli protocolli stateless (come REST e HTTP).
  • La programmazione funzionale potrebbe alleviare parte del dolore

Immagino di poter continuare all'infinito. Soggetto molto interessante :)


Guarda (o studia) la biologia o la sociologia come si comportano i sistemi complessi - Dai. Il resto della tua risposta è stato solido, ma questo ha un'applicazione così periferica al problema descritto.
Jim G.

1
@Jim G. Forse. La biologia non aiuterà a ottimizzare i tuoi for-loop, ma se vuoi trovare nuove prospettive, approfondimenti o astrazioni efficaci (sullo sviluppo del software), ti aiuta a uscire dalla tua sandbox. Sostenendo che la biologia (o la sociologia) non ha nulla a che fare con la programmazione, è solo a pochi passi dal sostenere che OOP o i modelli di progettazione non hanno nulla a che fare con la programmazione. Ad esempio: OOP : biologia -> Alan Kay -> OOP / Smalltalk. Or Design Patterns : sociologia -> urban design -> Christopher Alexander -> A Pattern Language -> Design Patterns.
Maglob,

@Jim G. Cont. Alcune citazioni, Alan Kay: "Ho pensato che gli oggetti fossero come cellule biologiche e / o singoli computer in una rete, in grado di comunicare solo con i messaggi", e Wikipedia: "[Design Pattern] L'idea è stata introdotta dall'architetto Christopher Alexander in il campo dell'architettura [1] ed è stato adattato per varie altre discipline, compresa l'informatica "
Maglob,

Tutto a posto. Ti sto dando un +1 per provare al massimo per evitare lo stato mutevole e altre pepite. Il mio punto è che se il tuo manager ti affidasse una riduzione della complessità, sicuramente applicheresti il ​​rasoio di Occam al problema e ti metterai al lavoro. Non penso che tu o chiunque altro "guarderesti alla biologia" per un aiuto con il problema immediato.
Jim G.

2

Non sono d'accordo con lo spirito della risposta di @ Péter Török perché presume che una squadra (o un individuo) possa necessariamente prevedere gli elementi più rischiosi all'inizio del ciclo di vita del progetto. Ad esempio, nel caso del PO, il team non ha potuto prevedere la crescente complessità legata alla soluzione multi-threaded fino a quando le loro spalle non erano contro il muro.

La domanda del PO è buona e parla di un problema che molti negozi di sviluppo software hanno.

Ecco come vorrei affrontare il problema:

  1. Segui i consigli di Fred Brooks e organizza i tuoi sviluppatori come una squadra di chirurgia .
  2. Scegli un saggio e "benevolo" maestro chirurgo che possa entrambi: A) Garner ottenere la fiducia e il rispetto dei suoi pari; e B) Prendere decisioni difficili in modo tempestivo.
  3. Aspettatevi che il chirurgo specialista riduca la complessità nel front-end e nel back-end del processo di sviluppo.

Altre informazioni sul punto 3:

  1. Il maestro chirurgo dovrebbe fare uno sforzo consapevole per proporre la soluzione più semplice che funzionerà. Anni di esperienza significativa dovrebbero mettere il chirurgo in grado di farlo.
  2. L'organizzazione più ampia, ovvero i superiori del maestro chirurgo, dovrebbe offrire al team tempo e risorse sufficienti per ridurre la complessità dopo la data di spedizione. Ciò consentirà al team di sviluppo di spedire il codice in modo tempestivo ed eseguire i kaizen per ridurre la complessità su base continuativa.

Nel caso dell'OPHO avrebbero dovuto iniziare i test in precedenza per scoprire come (e se) la loro architettura funzionasse in circostanze di vita reale. A proposito suggerendo di avere un "chirurgo esperto", sembra che insinui fondamentalmente che ci sono persone che possono prevedere i rischi tecnici del progetto - il punto esatto con cui affermi di non essere d'accordo.
Péter Török,

@ Péter Török: ... Suggerendo di avere un "maestro chirurgo", sembra che insinui sostanzialmente che ci sono persone che possono prevedere i rischi tecnici del progetto : No, non lo sono. Sto dicendo che queste persone sono entrambe: A) Le più adatte a evitare completamente la complessità in primo luogo; e B) Più adatto a scavare una squadra dalla complessità dopo che il codice è stato spedito.
Jim G.

IMHO stiamo parlando della stessa cosa. L'esperienza che aiuta il tuo "chirurgo maestro" a scegliere la soluzione più semplice che può eventualmente funzionare è costruita dalle memorie di progetti e soluzioni passati e dalla conoscenza di quale soluzione ha funzionato (o meno) in quale caso specifico. In altre parole, esamina le soluzioni applicabili per problemi specifici e valuta i potenziali benefici e rischi di ciascuno. Questo è ciò che lo aiuta a scegliere quello giusto per la situazione attuale, evitando così i percorsi più rischiosi .
Péter Török,

1
Questo mi ricorda una citazione dell'ultimo, grande addestratore di cavalli Ray Hunt: "Come si ottiene un buon giudizio? Esperienza. Come si ottiene esperienza? Un cattivo giudizio".
glenatron,

1

Codice alle interfacce

Quando si scrivono nuove funzionalità che si interfacciano con altre funzionalità, creare un limite sotto forma di un'interfaccia (il tipo Java) attraverso la quale tutto passa. Questo sarà

  1. assicurarti di avere il pieno controllo sulle funzionalità utilizzate
  2. consentono di avere più implementazioni della stessa funzionalità.
  3. mantenere bassa la complessità generale perché i moduli sono collegati solo in modo sottile invece di essere completamente intrecciati.

0

si può iniziare con una premessa e un modello semplici e improvvisamente avere una escalation di complessità ben nello sviluppo del progetto

Non sorprendente.

Questo è lo sviluppo del software. Se non stai inventando qualcosa di nuovo, stai scaricando una soluzione esistente e comprovata.

C'è una piccola via di mezzo.

Se stai inventando qualcosa di nuovo, allora ci deve essere almeno una funzione che non capisci completamente. (Per capirlo appieno , dovresti avere un'implementazione funzionante, che useresti solo.)

Come gestirlo?

  1. Hanno aspettative realistiche. Stai inventando qualcosa di nuovo. Ci devono essere parti che non capisci.

  2. Hanno aspettative realistiche. Se sembra funzionare bene la prima volta, hai trascurato qualcosa.

  3. Hanno aspettative realistiche. Se fosse stato semplice, qualcun altro lo avrebbe fatto per primo, e tu potresti semplicemente scaricare quella soluzione.

  4. Hanno aspettative realistiche. Non puoi predire molto bene il futuro.


2
Aspetta, quindi quello che stai dicendo è: hai aspettative realistiche?
glenatron,

0

Progetta e codifica pensando all'obsolescenza. Supponiamo che ciò che codifichi oggi debba essere tagliato e sostituito domani.


0

L'ambiente dovrebbe far parte delle specifiche. Quindi una modifica all'ambiente È una modifica alle specifiche. Se, d'altra parte, hai basato il tuo prototipo e il design su un ambiente diverso da quello che era nelle specifiche, hai commesso un errore folle. Ad ogni modo, lo succhi e trovi il modo meno costoso per implementare la nuova complessità.


0

Come per la maggior parte dei problemi di programmazione, dipende , secondo me. Questo problema è così intrinseco al lavoro creativo, che non dovresti dimenticare che si verificheranno dei fallimenti, e va bene . La programmazione è un problema malvagio e di solito non conosci la soluzione giusta fino a quando non l'hai già risolto.

Tuttavia, ci sono una serie di fattori locali specifici che potrebbero entrare in gioco qui, come:

  • Gli obiettivi per questo sistema. È una cosa una tantum? Intendi far funzionare questo sistema a medio-lungo termine?

Per cose a breve termine, potrebbe non valerne la pena pensarci più che abbastanza per farlo funzionare. Il refactoring è costoso ed è qualcosa che non crea un valore finale immediato per il tuo utente. tuttavia, praticamente non riesco a pensare ad altro che al software assoluto usa e getta, dove è così a breve termine che non vale la pena migliorare il tuo design. È molto più importante essere in grado di capire ciò che hai fatto e risolverlo rapidamente, piuttosto che finire adesso. Se è a lungo termine, probabilmente alla fine ripagherà (e forse molto prima di quanto tutti pensino), o l'inverso (non farlo causerà dolore molto presto invece di "quando dovremo ripararlo"). Sono quasi tentato di dire "prenditi sempre il tempo per renderlo migliore", ma ci sono alcuni casi in cui ciò non è possibile.

  • Gli obiettivi della squadra. È più una cosa del tipo "fallo ora, a tutti i costi", o una cosa del tipo "facciamolo bene"?

Questo dovrebbe influenzare enormemente le tue decisioni. Il tuo team supporterà questa decisione dandoti risorse per la riprogettazione o richiederà la soluzione rapida da fare ora. Secondo me, se scopri che la squadra ti sta spingendo costantemente nella direzione sbagliata, è un'enorme bandiera rossa. Ho visto questo genere di cose finire in uno scenario in cui è in corso un estinguente costante, in cui non c'è mai tempo di riprogettare perché risolvi sempre i problemi che il tuo cattivo design crea. Ci può essere anche una via di mezzo, però: "nastro adesivo" ora, correggi al più presto (ma effettivamente fallo).

  • La tua comprensione del problema. Perché la soluzione precedente non ha funzionato?

Veramente importante. Pensa a qual è l'errore o il problema e perché sta accadendo. Questo tipo di situazione è una grande opportunità per trovare ipotesi, vincoli e interazioni imperfetti (o mancanti). In generale, preferisci sempre capire meglio il tuo problema invece di risolverlo. Questa è probabilmente la tua più grande difesa contro YAGNI / overengineering. Se si capisce il problema abbastanza bene, poi si risolverà esso e non altri problemi.

Infine, prova a costruire le cose nel modo giusto . Non sto parlando degli errori e dei problemi che affronti quando capisci di più sul problema o sul tuo errore umano intrinseco. Non intendo "non commettere errori e renderlo perfetto la prima volta" - è impossibile. Voglio dire, prova a gestire bene la complessità nel tuo lavoro quotidiano, aggiusta le finestre rotte, rendilo il più semplice possibile, migliora il tuo codice e il tuo pensiero in ogni momento. In questo modo quando (non se) il cambiamento bussa alla tua porta, puoi accoglierlo a braccia aperte invece che con un fucile.

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.