Come gestire la complessità accidentale nei progetti software

73

Quando a Murray Gell-Mann è stato chiesto come Richard Feynman è riuscito a risolvere tanti problemi difficili, Gell-Mann ha risposto che Feynman aveva un algoritmo:

  1. annota il problema.
  2. Pensa davvero.
  3. Annota la soluzione.

Gell-Mann stava cercando di spiegare che Feynman era un tipo diverso di risolutore di problemi e non c'erano intuizioni da ricavare dallo studio dei suoi metodi. Mi sento allo stesso modo sulla gestione della complessità nei progetti software di media / grande dimensione. Le persone che sono brave sono solo intrinsecamente brave e in qualche modo riescono a stratificare e impilare varie astrazioni per rendere il tutto gestibile senza introdurre alcun cruft estraneo.

Quindi l'algoritmo di Feynman è l'unico modo per gestire la complessità accidentale o ci sono metodi reali che gli ingegneri del software possono applicare coerentemente per addomesticare la complessità accidentale?

    
posta davidk01 17.02.2014 - 12:33
fonte

6 risposte

100

When you see a good move, look for a better one.
—Emanuel Lasker, 27-year world chess champion

Nella mia esperienza, il più grande driver di complessità accidentale è che i programmatori si attengono alla prima bozza, solo perché funziona. Questo è qualcosa che possiamo imparare dalle nostre lezioni di composizione in inglese. Costruiscono in tempo per passare attraverso diverse bozze nei loro compiti, incorporando il feedback degli insegnanti. Le classi di programmazione, per qualche motivo, non lo fanno.

Ci sono libri pieni di modi concreti e oggettivi per riconoscere, articolare e correggere il codice subottimale: Pulisci codice , Lavorare efficacemente con il codice legacy e molti altri. Molti programmatori hanno familiarità con queste tecniche, ma non sempre hanno il tempo di applicarle. Sono perfettamente in grado di ridurre la complessità accidentale, semplicemente non hanno preso l'abitudine di provare .

Parte del problema è che spesso non vediamo la complessità intermedia del codice di altre persone, a meno che non sia stata sottoposta a una revisione tra pari in una fase iniziale. Il codice pulito sembra facile da scrivere, mentre in realtà di solito comporta diverse bozze. Scrivi il modo migliore che ti viene in mente all'inizio, noti le complessità inutili che introduce, quindi "cerca una mossa migliore" e un refactoring per rimuovere quelle complessità. Poi continui a "cercare una mossa migliore" finché non sei in grado di trovarne uno.

Tuttavia, non metti il codice per la revisione fino a dopo tutto quel churn, quindi esternamente sembra che potrebbe essere stato un processo simile a Feynman. Hai la tendenza a pensare che non puoi fare tutto in questo modo, quindi non ti preoccupare di provarci, ma la verità è l'autore di quel codice magnificamente semplice che hai appena letto di solito non puoi scrivere tutto in un pezzo così, o se possono, è solo perché hanno esperienza di scrivere codice simile molte volte prima, e ora possono vedere il modello senza le fasi intermedie. In ogni caso, non puoi evitare le bozze.

    
risposta data 17.02.2014 - 14:41
fonte
44

"L'abilità nell'architettura software non può essere insegnata" è un errore diffuso.

È facile capire perché molte persone lo credano (coloro che sono bravi a farlo vogliono credere di essere misticamente speciali, e quelli che non vogliono credere che non è colpa loro se non lo sono) .) È tuttavia sbagliato; l'abilità è solo un po 'più intensiva di pratica rispetto ad altre abilità software (ad esempio, la comprensione dei loop, gestione dei puntatori, ecc.)

Credo fermamente che la costruzione di sistemi di grandi dimensioni sia suscettibile di ripetute pratiche e di apprendere dall'esperienza nello stesso modo in cui diventare un grande musicista o oratore pubblico: una quantità minima di talento è una precondizione, ma non è un minimo enormemente deprimente che è fuori dalla portata della maggior parte dei professionisti.

Affrontare la complessità è un'abilità che acquisisci in gran parte provando e fallendo alcune volte. È solo che le molte linee guida generali che la comunità ha scoperto per la programmazione nel grande (usare i livelli, combattere la duplicazione ovunque alzi la testa, aderire religiosamente a 0/1 / infinito ...) non sono ovviamente corrette e necessarie per un principiante fino a quando non programmano qualcosa di grande. Finché non sei stato effettivamente morso da una duplicazione che ha causato problemi solo mesi dopo, non puoi semplicemente "prendere" l'importanza di tali principi.

    
risposta data 17.02.2014 - 13:07
fonte
22

Il pensiero pragmatico di Andy Hunt risolve questo problema. Si riferisce al modello Dreyfus, in base al quale ci sono 5 fasi di padronanza delle varie abilità. I novizi (fase 1) hanno bisogno di istruzioni precise per essere in grado di fare qualcosa correttamente. Gli esperti (fase 5), al contrario, possono applicare modelli generali a un determinato problema. Citando il libro,

It’s often difficult for experts to explain their actions to a fine level of detail; many of their responses are so well practiced that they become preconscious actions. Their vast experience is mined by nonverbal, preconscious areas of the brain, which makes it hard for us to observe and hard for them to articulate.

When experts do their thing, it appears almost magical to the rest of us—strange incantations, insight that seems to appear out of nowhere, and a seemingly uncanny ability to know the right answer when the rest of us aren’t even all that sure about the question. It’s not magic, of course, but the way that experts perceive the world, how they problem solve, the mental models they use, and so on, are all markedly different from nonexperts.

Questa regola generale di vedere (e di conseguenza evitare) diversi problemi può essere applicata specificamente al problema della complessità accidentale. Avere un determinato set di regole non è sufficiente per evitare questo problema. Ci sarà sempre una situazione che non è coperta da quelle regole. Abbiamo bisogno di acquisire esperienza per essere in grado di prevedere problemi o identificare soluzioni. L'esperienza è qualcosa che non può essere insegnata, può essere acquisita solo con costanti tentativi, fallimenti o successi e imparando dagli errori.

Questa domanda from Workplace è rilevante e IMHO sarebbe interessante da leggere in questo contesto.

    
risposta data 17.02.2014 - 13:10
fonte
4

Non lo si scrive, ma la "complessità accidentale" è definita come una complessità che non è inerente al problema, rispetto alla complessità "essenziale". Le tecniche richieste per "Addomesticare" dipenderanno da dove parti. Quanto segue si riferisce principalmente a sistemi che hanno già acquisito una complessità non necessaria.

Ho esperienza in una serie di grandi progetti pluriennali in cui la componente "accidentale" superava in modo significativo l'aspetto "essenziale" e anche quelli in cui non lo era.

In realtà, l'algoritmo di Feynman si applica in una certa misura, ma ciò non significa che "pensare in modo reale" significhi solo magia che non può essere codificata.

Trovo che ci siano due approcci che devono essere presi. Prendili entrambi: non sono alternative. Uno è quello di affrontarlo frammentario e l'altro è quello di fare una rielaborazione importante. Quindi certamente, "annota il problema". Ciò potrebbe assumere la forma di un audit del sistema - i moduli del codice, il loro stato (odore, livello di test automatizzati, quanti membri dello staff pretendono di capirlo), l'architettura generale (ce n'è uno, anche se "ha problemi") ), stato dei requisiti, ecc. ecc.

È la natura della complessità "accidentale" che non esiste un problema che deve essere affrontato. Quindi è necessario triage. Dove fa male - in termini di capacità di mantenere il sistema e progredire nel suo sviluppo? Forse un po 'di codice è davvero maleodorante, ma non è una priorità assoluta e il fixing può essere fatto aspettare. D'altra parte, potrebbe esserci del codice che restituirà rapidamente il tempo trascorso per il refactoring.

Definisci un piano per ciò che sarà una migliore architettura e cerca di assicurarti che il nuovo lavoro sia conforme a tale piano - questo è l'approccio incrementale.

Inoltre, articola il costo dei problemi e usalo per costruire un business case per giustificare un refactoring. La cosa fondamentale qui è che un sistema ben progettato può essere molto più robusto e verificabile risultando in un tempo molto più breve (costo e programma) per implementare il cambiamento - questo ha un valore reale.

Una grossa rielaborazione arriva nella categoria "think real hard": è necessario farlo bene. È qui che avere un "Feynman" (beh, una piccola parte di uno andrebbe bene) ha un enorme successo. Un importante rilavorazione che non risulti in un'architettura migliore può essere un disastro. Le riscritture complete del sistema sono famose per questo.

Implicito in qualsiasi approccio è saper distinguere "accidentale" da "essenziale", vale a dire che è necessario avere un grande architetto (o un gruppo di architetti) che capisca veramente il sistema e il suo scopo.

Detto questo, la cosa fondamentale per me è test automatici . Se ne hai abbastanza, il tuo sistema è sotto controllo. Se non lo fai. . .

    
risposta data 18.02.2014 - 07:00
fonte
3

"Everything should be made as simple as possible, but no simpler."
— attributed to Albert Einstein

Consentitemi di abbozzare il mio algoritmo personale per gestire la complessità accidentale.

  1. Scrivi una user story o un caso d'uso. Rivedi con il proprietario del prodotto.
  2. Scrivi un test di integrazione che fallisce perché la funzione non è presente. Rivedi con il QA, o ingegnere capo, se c'è qualcosa del genere nella tua squadra.
  3. Scrivi test unitari per alcune classi che potrebbero superare il test di integrazione.
  4. Scrivi l'implementazione minima per quelle classi che superano i test unitari.
  5. Rivedi i test unitari e l'implementazione con un altro sviluppatore. Vai al passaggio 3.

L'intera magia del design sarebbe al passaggio 3: come si impostano queste classi? Questa diventa la stessa domanda di: come immagini di avere una soluzione per il tuo problema prima di avere una soluzione al tuo problema?

Sorprendentemente, solo immaginarti di avere la soluzione sembra essere uno dei principali consigli delle persone che scrivono sul problem-solving (chiamato "wishful thinking" di Abelson e Sussman in Struttura e interpretazione dei programmi per computer e "lavorazione all'indietro" in Come risolverlo )

D'altra parte, non tutti hanno lo stesso " gusto per le soluzioni immaginate ": ci sono soluzioni che solo tu trovi eleganti, e ce ne sono altre più comprensibili da un pubblico più ampio. Ecco perché è necessario rivedere il proprio codice con gli altri sviluppatori: non tanto per ottimizzare le prestazioni, ma per concordare soluzioni intese. Di solito questo porta a una riprogettazione e, dopo alcune iterazioni, a un codice molto migliore.

Se continui a scrivere implementazioni minimal per superare i tuoi test e scrivi test che sono comprensibili da molte persone, dovresti terminare con una base di codice in cui solo complessità irriducibile rimane.

    
risposta data 18.02.2014 - 17:06
fonte
2

Complessità accidentale

La domanda originale (parafrasata) era:

How do architects manage accidental complexity in software projects?

La complessità

accidentale si verifica quando chi ha la direzione di un progetto sceglie di aggiungere tecnologie esclusive e che la strategia generale degli architetti originali del progetto non ha inteso portare nel progetto. Per questo motivo è importante registrare il ragionamento alla base della scelta nella strategia.

La complessità accidentale può essere allontanata dalla leadership che si attiene alla strategia originale fino a quando apparirà necessario un distacco deliberato da tale strategia.

Evitare la complessità non necessaria

In base al corpo della domanda, lo riformulare in questo modo:

How do architects manage complexity in software projects?

Questa riformulazione è più appropriata al corpo della domanda, in cui l'algoritmo di Feynman è stato quindi introdotto, fornendo un contesto che propone che per i migliori architetti, di fronte a un problema, abbia una gestalt da cui poi costruiscono abilmente un soluzione, e il resto di noi non può sperare di imparare questo. Avere una gestalt di comprensione dipende dall'intelligenza del soggetto e dalla loro volontà di apprendere le caratteristiche delle opzioni architettoniche che potrebbero rientrare nel loro ambito.

Il processo di pianificazione del progetto utilizza l'apprendimento dell'organizzazione per creare un elenco dei requisiti del progetto, quindi tenta di costruire un elenco di tutte le opzioni possibili e quindi riconciliare le opzioni con i requisiti. La gestalt dell'esperto gli permette di farlo rapidamente, e forse con poco lavoro evidente, facendolo apparire facilmente a lui.

Ti sottometto che viene a lui a causa della sua preparazione. Affinché la gestione dell'esperto richieda familiarità con tutte le opzioni e la prospettiva di fornire una soluzione semplice che consenta le esigenze future previste che è determinata dal progetto, nonché la flessibilità di adattarsi alle mutevoli esigenze di il progetto. La preparazione di Feynman era che aveva una profonda comprensione dei vari approcci sia in matematica teorica che applicata e fisica. Era innatamente curioso e abbastanza intelligente da dare un senso alle cose che aveva scoperto sul mondo naturale che lo circondava.

L'esperto architetto della tecnologia avrà una curiosità simile, basandosi su una profonda comprensione dei fondamentali e un'ampia esposizione a una grande varietà di tecnologie. Lui (o lei) avrà la saggezza di attingere alle strategie che hanno avuto successo in tutti i domini (come Principi di programmazione Unix ) e quelli che si applicano a domini specifici (come schemi di progettazione e guide di stile ). Potrebbe non essere intimamente informato di ogni risorsa, ma saprà dove trovare la risorsa.

Creazione della soluzione

Questo livello di conoscenza, comprensione e saggezza può essere tratto dall'esperienza e dall'educazione, ma richiede intelligenza e attività mentale per mettere insieme una soluzione strategica gestalt che funzioni insieme in modo da evitare la complessità accidentale e non necessaria. Richiede all'esperto di mettere insieme questi elementi fondamentali; questi erano i knowledge worker che Drucker aveva previsto quando avevano coniato il termine.

Torna alle domande finali specifiche:

I metodi specifici per domare la complessità accidentale possono essere trovati nei seguenti tipi di fonti.

Seguendo i Principi di Unix Programming avrai la creazione di semplici programmi modulari che funzionano bene e sono robusti con interfacce comuni. Seguire i modelli di progettazione ti aiuterà a costruire algoritmi complessi che non sono più complessi del necessario. Le seguenti Guide di stile garantiscono che il tuo codice sia leggibile, manutenibile e ottimale per la lingua in cui è scritto il tuo codice. Gli esperti avranno interiorizzato molti dei principi contenuti in queste risorse e saranno in grado di riunirli in modo coerente e coerente.

    
risposta data 18.02.2014 - 02:54
fonte

Leggi altre domande sui tag