Essere stupidi per ottenere una migliore produttività?

110

Ho passato molto tempo a leggere libri diversi su "buon design", "modelli di design", ecc. Sono un grande fan di SOLID approccio e ogni volta che ho bisogno di scrivere un semplice pezzo di codice, penso al futuro. Quindi, se l'implementazione di una nuova funzione o di una correzione di un bug richiede solo l'aggiunta di tre righe di codice come questa:

if(xxx) {
    doSomething();
}

Non significa che lo farò in questo modo. Se penso che questo pezzo di codice probabilmente diventerà più grande nel prossimo futuro, penserò ad aggiungere astrazioni, a spostare questa funzionalità da qualche altra parte e così via. L'obiettivo che sto perseguendo è mantenere la complessità media come prima delle mie modifiche.

Credo che dal punto di vista del codice, sia piuttosto una buona idea - il mio codice non è mai abbastanza lungo, ed è abbastanza facile capire i significati per entità diverse, come classi, metodi e relazioni tra classi e oggetti.

Il problema è che ci vuole troppo tempo e spesso sento che sarebbe meglio se implementassi quella funzionalità "così com'è". Si tratta di "tre righe di codice" rispetto a "nuova interfaccia + due classi per implementare quell'interfaccia".

Dal punto di vista del prodotto (quando parliamo del risultato ), le cose che faccio sono abbastanza insensate. So che se lavoreremo alla prossima versione, avere un buon codice è davvero grandioso. Dall'altro lato, il tempo che hai speso per rendere "buono" il tuo codice potrebbe essere stato speso per implementare un paio di utili funzionalità.

Spesso mi sento molto insoddisfatto dei miei risultati - un buon codice che può solo fare A è peggio di un codice cattivo che può fare A, B, C e D.

È probabile che questo approccio porti a un guadagno netto positivo per un progetto software o è una perdita di tempo?

    
posta loki2302 28.07.2017 - 15:36
fonte

17 risposte

152

good code that only can do A is worse than bad code that can do A, B, C, D.

Questo mi odora di generalità speculativa . Senza sapere (o almeno essere ragionevolmente sicuri) che i tuoi clienti avranno bisogno di caratteristiche B, C e D, stai solo superfluomente complicando il tuo progetto. Il codice più complesso è più difficile da capire e mantenere a lungo termine. La complessità aggiuntiva è giustificata solo dalle utili funzionalità extra. Ma in genere siamo pessimi nel predire il futuro. La maggior parte delle funzionalità che riteniamo potrebbero essere necessarie in futuro non saranno mai richieste nella vita reale.

Un buon codice che può solo fare A (ma sta facendo una cosa semplice e pulita) è MEGLIO di un codice cattivo che può fare A, B, C, D (alcuni dei quali potrebbero em> essere necessario qualche volta in futuro).

    
risposta data 06.09.2011 - 13:22
fonte
87

Anecdote time:

Ho avuto due sviluppatori per me che si sono orientati verso l'over-engineering in questo modo.

Per uno di questi, questo fondamentalmente ha fermato la sua produttività, specialmente quando ha avviato un nuovo progetto. Soprattutto se il progetto fosse, per sua natura, abbastanza semplice. In definitiva un software che funziona ora è ciò di cui abbiamo bisogno. Questo è diventato così grave che ho dovuto lasciarlo andare.

L'altro sviluppatore che era incline all'ingegneria lo compensava essendo estremamente produttivo. Come tale, nonostante tutto il codice estraneo, ha comunque consegnato più velocemente della maggior parte. Tuttavia, ora che si è trasferito, mi trovo spesso irritato dalla quantità di lavoro extra necessario per aggiungere funzionalità in quanto devi modificare (completamente inutile) i livelli di astrazione ecc.

Quindi la morale è che l'over-engineering consumerà tempo extra che potrebbe essere meglio speso per fare cose utili. E non solo il tuo tempo, ma anche quelli che devono lavorare con il tuo codice.

Quindi non farlo.

Vuoi che il tuo codice sia il più semplice possibile (e non più semplice). Gestire "maybes" non semplifica le cose, se si indovina in modo sbagliato, il codice sarà reso più complesso senza alcun guadagno reale da mostrare.

    
risposta data 06.09.2011 - 12:55
fonte
26

I principi SOLID e KISS / YAGNI sono opposti quasi polari. Uno dirà che se doSomething () non può essere giustificato come parte integrante del lavoro che sta facendo la classe che lo chiama, dovrebbe essere in una classe diversa che è liberamente accoppiata e iniettata. L'altro ti dirà che se questo è l'unico posto in cui viene usato DoSomething, anche l'estrazione in un metodo potrebbe essere stato eccessivo.

Questo è ciò che rende i buoni programmatori degno del loro peso in oro. La struttura "corretta" è un caso per caso, che richiede la conoscenza dell'attuale base di codice, il percorso futuro del programma e le esigenze dell'azienda dietro il programma.

Mi piace seguire questa semplice metodologia in tre fasi.

  • Al primo passaggio, fallo funzionare.
  • Al secondo passaggio, rendilo pulito.
  • Al terzo passaggio, rendilo SOLIDO.

Fondamentalmente, ecco come si fondono i KISS con SOLID. Quando scrivi per la prima volta una riga di codice, per quello che sai sarà una tantum; deve semplicemente funzionare e non importa a nessuno, quindi non essere elegante. La seconda volta che posizioni il cursore su quella riga di codice, hai smentito la tua ipotesi originale; nel rivisitare questo codice, è probabile che lo si estenda o ci si agganci da qualche altra parte. Ora, dovresti pulirlo un po '; estrai i metodi per le informazioni più comuni, riduci o elimina la codifica di copia / incolla, aggiungi alcuni commenti, ecc. ecc. La terza volta che torni a questo codice, ora è un incrocio importante per i percorsi di esecuzione del tuo programma. Ora dovresti trattarlo come una parte fondamentale del tuo programma e applicare le regole SOLID laddove possibile.

Esempio: è necessario scrivere una semplice riga di testo sulla console. La prima volta che succede, Console.WriteLine () va bene. Quindi si ritorna a questo codice dopo che i nuovi requisiti dettano anche la scrittura della stessa riga in un log di output. In questo semplice esempio, potrebbe non esserci un sacco di codice "copia / incolla" ripetitivo (o forse c'è), ma è comunque possibile eseguire alcune operazioni di pulizia di base, magari estrarre un metodo o due per evitare di integrare le operazioni di I / O in una logica aziendale più profonda . Quindi, si torna quando il client richiede lo stesso output di testo a una named pipe per un server di monitoraggio. Ora, questa routine di emissione è una specie di grosso problema; stai trasmettendo lo stesso testo in tre modi. Questo è l'esempio da manuale di un algoritmo che trarrebbe beneficio da un pattern Composite; sviluppare una semplice interfaccia IWriter con un metodo Write (), implementare quell'interfaccia per creare le classi ConsoleWriter, FileWriter e NamedPipeWriter e un'altra volta per creare una classe composita "MultiWriter", quindi esporre una dipendenza IWriter sulla classe, configurare il MultiWriter composito con i tre scrittori reali e collegarlo. Ora, è piuttosto SOLIDO; da questo punto in poi, ogni volta che i requisiti impongono che l'output debba andare ovunque, basta creare un nuovo IWriter e collegarlo al MultiWriter, senza bisogno di toccare alcun codice di lavoro esistente.

    
risposta data 06.09.2011 - 17:46
fonte
17

good code that only can do A is worse than bad code that can do A, B, C, D.

1) Avere un codice che fa solo ciò che dovrebbe fare.

If I feel like this piece of code is likely to become larger in the nearest future, I'll think of adding abstractions, moving this functionality somewhere else and so on.

2) Se pianifichi il tuo codice per fare A, B, C e D, il cliente ti chiederà in definitiva E.

Il tuo codice dovrebbe fare ciò che dovrebbe fare, non dovresti pensare ora alle implementazioni future, perché non finirai mai di cambiare continuamente il tuo codice, e più importante sarà sovrascrivere il tuo codice. Dovresti rifattorizzare il tuo codice non appena ne hai bisogno, a causa delle funzionalità presenti, non faticando a prepararlo per qualcosa che non è ancora in grado di fare, a meno che tu non lo pianifichi come parte dell'architettura del progetto.

Ti suggerisco di leggere un buon libro: The Pragmatic Programmer . Aprirà la tua mente e ti insegnerà cosa dovresti fare e cosa non dovresti fare.

Anche il codice completo è una grande risorsa piena di informazioni utili che dovrebbero essere letti da ogni sviluppatore (non solo programmatore).

    
risposta data 06.09.2011 - 20:21
fonte
12

very time I need to write a simple piece of code, I think about future

Forse ecco il problema.

Nelle fasi iniziali, non hai idea di quale sarebbe il prodotto finale. O se ce l'hai, è sbagliato. Di sicuro. È come un ragazzo di 14 anni che ha chiesto qualche giorno fa su Programmers.SE se doveva, per la sua futura carriera, scegliere tra web app e non ricordo cos'altro: è abbastanza ovvio che tra qualche anno, le cose gli piacerà cambiare, sarà interessato da altri domini, ecc.

Se, per scrivere tre righe di codice, crei una nuova interfaccia e due classi, sei troppo ingegnerizzato. Otterrai un codice che sarà difficile da mantenere e di difficile lettura , solo perché per ogni riga di codice utile, hai due righe di codice che non ti servono. Senza contare la documentazione XML, i test unitari, ecc.

Pensaci: se voglio sapere come viene implementata una funzionalità nel tuo codice, sarebbe più facile per me leggere venti righe di codice, o sarebbe più facile e veloce aprire dozzine di semi classi e interfacce vuote per capire quale usa quali, come sono collegati, ecc.?

Ricorda: codice più ampio significa più manutenzione. Non scrivere più codice quando puoi evitarlo.

Il tuo approccio è anche dannoso su altri lati:

  • Se hai bisogno di rimuovere una funzione, non è più facile capire dove viene utilizzato un metodo specifico di venti linee, piuttosto che perdere tempo a capire le dipendenze tra dozzine di classi?

  • Quando esegui il debug, non è più semplice avere una piccola traccia di stack, o preferisci leggere dozzine di righe per capire cosa viene chiamato da cosa?

Per concludere, sembra simile a ottimizzazione prematura . Stai cercando di risolvere il problema senza nemmeno essere sicuro se c'è un problema in un primo momento, e dov'è. Quando si lavora sulla versione 1 del prodotto, implementare le funzionalità che è necessario implementare in questo momento; non pensare a qualcosa che ti aspetti di implementare in due anni nella versione 14.

    
risposta data 06.09.2011 - 12:33
fonte
5

Scrivere un sacco di codice che (probabilmente) non sarà mai usato è un ottimo modo per ottenere un P45 . Non hai una sfera di cristallo e non hai idea della direzione finale che lo sviluppo impiegherà per trascorrere del tempo su questi funzioni costa solo soldi senza restituzione.

    
risposta data 06.09.2011 - 12:29
fonte
3

Il tentativo di prevedere ciò che potrebbe essere necessario dal codice in futuro spesso finisce con l'inutile sovraingegnerizzazione (un'abitudine che sto cercando attualmente di scuotere). Direi solo le tre linee. Quando sorge la necessità (e non prima), refactoring. In questo modo il tuo codice fa sempre ciò di cui ha bisogno senza essere troppo complicato e sviluppa naturalmente una buona struttura attraverso il refactoring.

    
risposta data 06.09.2011 - 12:25
fonte
3

Spesso dico che la codifica è come il lato chiaro / lato oscuro della Forza - il lato "leggero" richiede più sforzo, ma produce maggiori risultati. Il lato "oscuro" è veloce e facile e offre maggiori benefici immediatamente, ma ti corrompe lungo la strada. Una volta che inizi il sentiero oscuro, per sempre dominerà il tuo destino.

Mi imbatto sempre in questo, in ogni lavoro che abbia mai avuto, è come una maledizione che non posso sfuggire. La cultura aziendale è sempre il percorso del lato oscuro, e hack / correzioni rapide per spingere nuove funzionalità, e le mie suppliche e le grida del codice di refactoring e di scrittura cadono correttamente nel vuoto, se non lo fa Ho portato a termine il mio tentativo di "provare a cambiare le cose" (nessuna battuta che ho avuto accadendo una manciata di volte, perché volevo introdurre modelli di design e allontanarmi dagli hack veloci).

La triste verità è che spesso il lato stupido / oscuro è il modo in cui devi calpestare, devi solo assicurarti di calpestare leggermente. Ho lentamente e tristemente realizzato che i programmatori che capiscono il modo giusto di scrivere software, cioè seguire SOLID, usare schemi di progettazione, obbedire a SoC, ecc. Sono molto meno comuni degli hacker clueless che scriveranno un if statement per correggere un bug, e quando sorgono altri bug basta aggiungere questa affermazione invece di pensare "Forse c'è un modo migliore per farlo" e refactoring del codice per essere correttamente estensibile.

    
risposta data 06.09.2011 - 14:25
fonte
3

Essere consapevoli di ciò che potrebbe accadere (futuro) non è MAI male. Pensare a quale potrebbe essere un modo migliore di fare qualcosa è parte di ciò che ti rende bravo nel tuo lavoro. La parte più difficile è determinare se il tempo impiegato: il rapporto di profitto è giustificato. Tutti abbiamo visto situazioni in cui le persone fanno il "facile se" per fermare l'emorragia immediata (e / o urla), e che a mano a mano che si sommano, si ottiene un codice confuso. Molti di noi hanno anche sperimentato un'astrazione eccessiva che è un mistero una volta che il programmatore originale si sposta, il che produce anche codice confuso.

Guarderei la tua situazione e poni queste domande:

  1. Questo codice è fondamentale per la missione e lo sarà significativamente più stabile se re-code? In chirurgia parlare, è questo refactoring salvavita, o è solo elettiva e cosmetica?

  2. Sto prendendo in considerazione il codice di refactoring che stiamo per sostituire in 6 mesi?

  3. Sono disposto a dedicare tutto il tempo necessario per documentare il design e il mio ragionamento per i futuri sviluppatori come sono io a spendere per fare il refactoring?

  4. Per quanto riguarda il mio design elegante per l'aggiunta di funzionalità future, è questo in codice che gli utenti richiedono modifiche ogni settimana, o è questo il primo volta che l'ho toccato quest'anno?

Ci sono dei momenti in cui YAGNI e KISS vincono la giornata, ma ci sono giorni in cui un cambiamento fondamentale ti porterà dalla spirale al ribasso alla schifezza. Finché sei realistico nel valutare non solo ciò che vuoi, ma ciò che gli altri dovranno fare per mantenere il tuo lavoro, sarai più in grado di determinare qual è la situazione. Oh, e non dimenticare di scrivere ciò che hai fatto, e perché. Salverà chi ti segue, ma anche te stesso quando dovrai tornare più tardi.

    
risposta data 06.09.2011 - 15:29
fonte
3

Nella seconda edizione di Stroustrups "Il linguaggio di programmazione C ++", (non ho la pagina disponibile) ho letto

Don't add code spontaneously in place.

e sono andato bene seguendo il consiglio. Ovviamente ci sono dei compromessi, e devi trovare un equilibrio, ma i frammenti brevi sono meglio testabili di un grosso casino di spaghetti.

Ho spesso sperimentato, che mentre differenziando da un caso a due casi, se si pensa a 2 come n-casi, si apre una porta a molte nuove possibilità, alle quali non si sarebbe potuto pensare.

Ma allora devi chiedere la domanda YAGNI: ne vale la pena? Sarà davvero utile? Essere esperti significa che raramente si sbaglia, e come un principiante più spesso sbagliato.

Dovresti essere abbastanza critico da riconoscere un pattern e rilevare, se il tuo codice è difficile da mantenere, a causa di troppa astrazione, o difficile da mantenere, perché tutto è risolto sul posto.

La soluzione non è questo o quello, ma pensarci. :)

    
risposta data 06.09.2011 - 17:08
fonte
2

"Un buon codice che può fare solo A è peggio di un codice errato che può fare A, B, C e D".

Questo può avere un senso nello sviluppo del prodotto; Ma la maggior parte dei professionisti IT lavorano in "progetti" piuttosto che nello sviluppo del prodotto.

Nei "progetti IT", se si programma un buon componente, esso funzionerà senza intoppi per tutta la durata del progetto - il che potrebbe non durare più di 5 o 10 anni entro tale termine lo scenario di business potrebbe essere obsoleto e un nuovo progetto / prodotto ERP potrebbe averlo sostituito. Durante questo periodo di vita di 5/10 anni, a meno che non ci siano difetti nel tuo codice, nessuno noterà la sua esistenza ei meriti dei tuoi migliori pensieri sono passati inosservati! (a meno che tu non sia bravo a battere strong la tua tromba!)

Non molti hanno l'opportunità di programmare "Windows Ctl + Alt + Canc" e quelli che ottengono quella possibilità potrebbero non rendersi conto del potenziale futuro del loro codice!

    
risposta data 25.11.2011 - 10:09
fonte
1

Molti libri sullo sviluppo snello e / o agile contribuiranno a rafforzare questo approccio: fare ciò che è necessario in questo momento. Se sai che stai costruendo un framework, aggiungi le astrazioni. Altrimenti, non aggiungere complessità fino a ne hai bisogno. Raccomando Lean Software Development , che introdurrà molti altri concetti che possono fare una sostanziale differenza in termini di produttività.

    
risposta data 06.09.2011 - 15:45
fonte
1

È divertente come la gente parli di modo giusto / modo sbagliato di fare le cose. Allo stesso tempo, il compito della programmazione è ancora dolorosamente difficile e non offre buone soluzioni per scrivere sistemi complessi complessi.

Può darsi che un giorno noi, i programmatori, riusciremo finalmente a capire come scrivere software complessi. Fino ad allora ti suggerisco di iniziare sempre con l'implementazione di prototipi "stupidi" e poi dedicare il tempo necessario al refactoring in modo che i tuoi college possano seguire il tuo codice.

    
risposta data 06.09.2011 - 16:04
fonte
1

Avendo visto progetti prematuramente generalizzati che non si adattavano ai requisiti effettivi successivi, ho escogitato una regola per me:

Per requisiti ipotetici, scrivi solo codice ipotetico.

Cioè: sei ben consigliato di pensare ai cambiamenti che potrebbero verificarsi in seguito. Ma usa solo queste informazioni per scegliere un design per il codice che può essere facilmente modificato e refactored se tali requisiti vengono effettivamente visualizzati. Potresti anche scrivere del codice nella tua testa (codice ipotetico) che vorresti scrivere in quel caso, ma non scrivere alcun codice reale!

    
risposta data 12.09.2011 - 22:08
fonte
0

Penso che la mentalità che ti aiuterà sia cercare sempre soluzioni concrete per problemi di codifica invece di soluzioni astratte. Le astrazioni dovrebbero essere aggiunte solo quando effettivamente aiutano a semplificare la base di codice (quando per esempio ti permettono di rendere il codice base più piccolo).

Molti programmatori hanno scoperto che le astrazioni emergono quasi da sole quando ASCIUGANO il loro codice. I modelli di progettazione e le best practice ti aiutano a trovare opportunità per fare proprio questo, ma non sono di per sé obiettivi degni di essere perseguiti.

    
risposta data 06.09.2011 - 16:32
fonte
0

Penso che la sovraingegneria spesso appaia dall'insicurezza sulla scrittura del codice. Tutti i principi e i modelli astratti dovrebbero essere trattati come strumenti per aiutarti. Quello che spesso accade è che sono trattati come standard, bisogna conformarsi a.

Credo che un programmatore sia sempre in una posizione migliore per decidere come astrarre rispetto a un assioma.

Il resto è già stato detto da KeithS

    
risposta data 08.09.2011 - 00:14
fonte
0

Chiediti quali sono i vantaggi di un buon design:

  • Più facile da capire
  • Più semplice mantiene
  • Portable
  • Resta utile a lungo
  • Facile aggiunta di nuove funzionalità

Ora, chiediti se aggiungere tutti quegli strati di astrazione aggiunga davvero uno dei punti sopra menzionati. In caso contrario, lo stai facendo WRONG .

Se sei in grado di aggiungere nuove funzionalità aggiungendo 3 linee di codice come questa:

if (condition()) {
  doSomething()
}

Allora, per favore per favore fallo. Questo indica che il tuo progetto precedente era buono e facile da adattare. Solo una volta che le tue classi iniziano a crescere oltre una certa estensione, utilizza refactoring per dividere le funzioni ed eventualmente estrarre nuove classi.

La mia regola empirica è che le nuove funzionalità dovrebbero essere implementate nel modo più minimalista possibile, solo quando qualcosa è troppo grande da comprendere in anticipo (diciamo, richiede più di 1 giorno o mezza giornata per implementare) è possibile aggiungere un ruvido globale design. Da lì in poi, aggiungi solo i livelli di astrazione quando la dimensione del codice aumenta. Quindi dopo il refactoring! Dopo un po 'dovrebbe anche essere naturale per te quando devi progettare un pezzo un po' di più, o andare per la strada veloce. Un'altra indicazione che alcuni codici possono utilizzare per la pulizia è quando la riutilizzi. Ogni volta che aggiungi una nuova funzione o chiami il vecchio codice in una nuova posizione, è un buon momento per guardare il vecchio codice e vedere se puoi migliorarlo un po 'prima di aggiungerlo di nuovo. In questo modo, il codice a caldo diventerà lentamente più pulito, mentre le parti non interessanti si dissolveranno lentamente e non prenderanno il tuo tempo prezioso. (Se usi un codice vecchio e non riesci a capirlo rapidamente, potrebbe esserci una demotivazione per migliorarlo, ma ricorda che è anche un grande avvertimento che alcuni cleanup / riprogettazione sono appropriati).

Se lavori così, non sovrascriveresti mai nulla. Potrebbe volerci un po 'di disciplina per tornare al vecchio codice quando si desidera aggiungere qualcosa di nuovo, o per lasciare un nuovo codice leggermente più brutto di quanto si preferisce, ma si lavorerà verso qualcosa di produttivo anziché su un sistema troppo avanzato.

    
risposta data 28.07.2017 - 15:36
fonte

Leggi altre domande sui tag