Qual è il modo migliore per preparare il tuo design e codice per quei bug "sconosciuti sconosciuti" dal primo giorno?

8

Mi stavo solo chiedendo, c'è qualche metodo pratico o tecnica o addirittura dei trucchi per evitare quei bug "sconosciuti sconosciuti", specialmente quelli sporchi e casuali che spesso emergono negli ultimi minuti o almeno per mantenere queste cose in un livello minimo Lo chiedo perché quando lavori su una nuova piattaforma o usi una certa nuova tecnologia per la prima volta, come giustifichi che il tuo design e il codice siano abbastanza robusti? O queste cose possono essere apprese solo dal tempo e dagli errori?

(Uso C ++ per gran parte del mio tempo di lavoro)

Grazie!

    
posta CaptainJH 22.07.2011 - 06:07
fonte

7 risposte

5

Quasi venti anni fa, ho avuto un sacco di informazioni sull'eccellente libro di David Thielen "No Bugs: Delivering Error-Free Code in C e C ++", che ora è disponibile come PDF gratuito .

Mi ha insegnato due grandi idee ...

I bug non vengono dal nulla. Tutti noi programmatori ci sediamo e li scriviamo nel nostro codice con le nostre dita.

"Bug" connotes that some outside agency decided to infest your program with bugs and that if you live a clean life, and sacrifice small furry animals at the foot of your computer, they will go away... This concept is important because it colors your approach to debugging your code. If you view mistakes as "bugs," you hope none are found. (You hope the good fairy came by, sprinkled pixie dust, and the bugs left.)

Bugs should not be called bugs, they should be called Massive Fuck-Ups [MFUs]... MFUs exist because programs are written by people, and people make mistakes... You will write MFUs. You will sit down and with complete malice of forethought put MFUs in your code. Think about it - you know that you are the one putting the bugs in there. So if you sit down to code, you will be inserting some bugs.

Poiché è il destino inevitabile di tutti i programmatori di scrivere bug, ho bisogno di codificare in modo difensivo, comprese le cose che salteranno su, urlano e sventolano bandiere rosse quando rilevano un bug.

Essendo stato scritto nei primi anni '90, le specifiche su questo nel libro di Thielen sono piuttosto datate. Ad esempio, su Linux e Mac OS X, non è più necessario scrivere il proprio wrapper per il nuovo operatore C ++; puoi usare valgrind per questo.

Ma ci sono alcune cose che faccio abitualmente per C / C ++ / ObjC:

  1. Quando posso ragionevolmente, attivare l'opzione "Warnings are errors" del compilatore e correggerli tutti. (Conservo un progetto legacy in cui sistemare tutto in una volta richiederebbe settimane, quindi aggiusto un file ogni poche settimane e, in pochi anni, posso attivare questa opzione.)
  2. Utilizza uno strumento di analisi del codice statico, come PC-Lint di Gimpel o quello molto elegante ora integrato nell'Xcode di Apple. La copertura è ancora migliore, ma il costo è per le grandi società, non semplici mortali.
  3. Utilizza strumenti di analisi dinamica, come valgrind, per verificare problemi di memoria, perdite, ecc.
  4. Come dice Thielen (e vale ancora la pena leggere il capitolo): Assert The World . Naturalmente, nessuno tranne un idiota chiamerà la tua funzione con un puntatore nullo - e questo significa che qualcuno, da qualche parte, è un idiota che farà proprio questo. Potresti essere anche tu tra tre anni quando quello che stavi facendo oggi è diventato nebbioso. Quindi basta aggiungere un assert all'inizio della funzione per convalidare l'argomento del puntatore - ci vogliono tre secondi per scrivere e va via nell'eseguibile della versione.
  5. In C ++, RTTI è tuo amico. Di nuovo, nessuno tranne un idiota chiamerà la tua funzione con un puntatore al tipo di oggetto sbagliato - il che significa che, inevitabilmente, qualche idiota lo farà - e il costo per difendersi da ciò è trascurabile. Nel codice basato su C derivato da GObject, puoi fare la stessa cosa con le macro di cast dinamiche difensive.
  6. I test automatici di regressione e unità sono ora una parte fondamentale del mio repertorio. Su un progetto, sono parte integrante del sistema di generazione del rilascio e la compilazione non verrà completata a meno che non tutti passino.
  7. Un'altra parte fondamentale è la registrazione del codice in entrambi gli eseguibili di debug e release che possono essere abilitati in fase di esecuzione da qualcosa di simile a una variabile di ambiente.
  8. Scrivi test difensivi in modo che i programmatori che eseguono gli eseguibili di debug non possano ignorarli se falliscono. I messaggi di runtime sulla console possono essere ignorati. Il programma in crash con un assert non può essere ignorato.
  9. Durante la progettazione, fornire API pubbliche e implementazioni private a cui il codice esterno non può accedere. In questo modo, se hai un refactoring, nessuno si affida a qualche variabile di stato interiore magico o qualcosa del genere. Nelle classi C ++, sono un grande fan di protetto e privato per questo. Penso anche che le classi proxy siano fantastiche, ma in realtà non le uso personalmente.

Ovviamente, ciò che farai per una nuova lingua o tecnologia cambierà nei dettagli. Ma una volta che prendi nel tuo cuore le nozioni che i bug sono Massive Fuck-Ups che hai scritto con le tue dita, e il tuo codice è sotto costante assalto da un esercito di idioti, con te come capo generale, sono sicuro che tu scoprirò tecniche difensive idonee.

    
risposta data 22.07.2011 - 16:49
fonte
14

Bene, se lo sai, allora entrano nella categoria dei "bug sconosciuti conosciuti" (IE sai che qualcosa di questa "natura" si verificherà). Qualsiasi quantità di test unitari non li catturerà, sono davvero utili solo per casi noti.

Il modo in cui ci occupiamo di questo è mettere un servizio di registrazione degli errori sull'applicazione in esecuzione, riportare alla base quando si verifica e affrontarlo quando si presenta. Altrimenti puoi passare anni e ancora non coprire nulla ... ad un certo punto devi solo aspettare di vedere cosa succede nel mondo reale e di evolvere rapidamente.

Lato design, progettate per mantenere la sostenibilità come uno dei fattori chiave.

  • Modelli di apprendimento che funzionano e schemi da evitare. Più modelli coerenti e coerenti hai più comodo puoi essere che una specifica classe di problemi si verificherà o non si verificherà.
  • Rendi le cose ovvie. L'obsolescenza porta a confusione, porta a bug.
  • Convenzioni di denominazione forti fino in fondo. Se dai un nome alle cose e in modo consistente c'è un enorme vantaggio quando cerchi di cambiare le cose o di spiegarle a ... ogni cosa chiamata Factory farà X.
  • Scrivi le cose completamente. In questi giorni abbiamo il completamento automatico non utilizzare gli acronimi quando la parola completa rimuove la confusione.
  • Separa i livelli o le astrazioni ... in modo che gli specifici stili di problemi si verifichino in un livello specifico anziché "da qualche parte"
  • Isolare le classi di problemi in livelli e aspetti. Meno una parte ha a che fare con un'altra parte del codice, meglio è in generale. Anche se ci vuole un po 'di più a scrivere cose simili due volte.

E il tasto chiave ... Trova un mentore che ha già fatto tutti gli errori OPPURE ne crei un disastro finché non scopri cosa funziona e cosa non funziona.

    
risposta data 22.07.2011 - 07:26
fonte
4

Tutti i precedenti sono buoni punti. Ma c'è qualcosa non menzionato. Devi rendere i tuoi moduli e le tue funzioni paranoici. Range test tutti i parametri della funzione. Fai attenzione alle stringhe con inizi o terminazioni vuote o che sono troppo corte o troppo lunghe. Attenti ai booleani che non sono veri né falsi. In linguaggi non tipizzati come PHP, fai attenzione ai tipi di variabili inattese. Stai attento a NULL.

Questo codice paranoico è spesso codificato come affermazioni che possono essere disabilitate su una build di produzione per velocizzare le cose. Ma sicuramente preverrà i bug di panico dell'ultimo minuto.

    
risposta data 22.07.2011 - 09:24
fonte
3

Rob ha ragione nel dire che i test unitari non ti salveranno da bug sconosciuti MA i test delle unità aiuteranno a salvarti dall'introduzione di bug quando correggi i bug sconosciuti e ti salverà dalla re-introduzione accidentale vecchi bug TDD ti costringerà anche a progettare il tuo software dall'inizio per essere testabile e che ha un enorme valore positivo continuo.

    
risposta data 22.07.2011 - 08:20
fonte
2

Evita lo stato / "effetti collaterali" laddove possibile. Mentre i computer sono deterministici e forniscono lo stesso output per lo stesso input, la panoramica sull'input è sempre incompleta. Purtroppo, la maggior parte delle volte non ci rendiamo conto che quanto è incompleto.

Quando parli di applicazioni web, l'intero database, la richiesta corrente, la sessione dell'utente, le librerie di terze parti installate e molto altro sono parte dell'input. Quando si parla di thread, è ancora peggio: l'intero sistema operativo, con tutti gli altri processi gestiti dallo stesso schedulatore, è "parte dell'input".

I bug sono causati da un'errata valutazione del modo in cui viene gestito l'input o da un'errata valutazione dell'input. Questi ultimi sono, secondo la mia esperienza, quelli difficili: puoi osservarli solo "dal vivo", spesso, non hai più l'input.

Quando apprendi nuove tecnologie, infrastrutture, ecc., imho è la migliore procedura per avere una panoramica, quali componenti contribuiscono all'input e quindi provare per evitare quanti più di essi possibile .

    
risposta data 22.07.2011 - 09:43
fonte
0

Dato che il software diventa più complesso, è inevitabile che si verifichino alcuni bug. L'unico modo per evitarlo completamente è quello di sviluppare solo software banale - e anche in questo caso, sei tenuto a fare un errore di tanto in tanto.

L'unica cosa pratica che puoi fare è evitare la complessità non necessaria - per rendere il tuo software il più semplice possibile, ma non più semplice di così.

Questo è fondamentalmente ciò che sono tutti i più specifici principi e schemi di progettazione, rendendo le cose più semplici che possono essere. Il problema è che "semplice in che modo" può essere soggettivo. Intendi il design assolutamente semplice per i requisiti attuali o semplice da modificare per i requisiti futuri. E ci sono principi anche per questo.

I test unitari sono un esempio di questa incertezza. Da un lato, sono una complessità inutile - codice che deve essere sviluppato e mantenuto, ma che non svolge il lavoro. D'altra parte, sono un semplice modo per automatizzare i test, riducendo la quantità di test manuali molto più difficili da eseguire.

Indipendentemente dalla teoria del design che impari e da quanta esperienza acquisisci, il principio dominante (e talvolta l'unica guida che hai) è mirare alla semplicità.

    
risposta data 22.07.2011 - 08:24
fonte
0

Non c'è nulla di casuale sui bug del software, cause principali sono di natura perfettamente deterministica, istruzioni errate al computer.

I bug di threading possono essere non deterministici nel comportamento dell'esecuzione, ma non sono random nella causa principale.

Si verificano per esattamente la stessa ragione solo in momenti apparentemente non prevedibili nel tempo, ma ciò non li rende random solo apparentemente non prevedibile, una volta che conosci la causa principale puoi determinare in modo deterministico quando accadrà.

Ho detto apparentemente e ho fatto la distinzione per un motivo. Casuale significa una cosa, Fatto, fatto, accadendo o scelto senza metodo o decisione consapevole , che implica che ci sia un processo decisionale indipendente da parte del computer, non ci sono sta facendo esattamente quello che è stato detto di fare, semplicemente non gli hai detto di fare la cosa giusta in alcuni casi molto deterministici.

La semantica delle parole ci sono per un motivo, casuale non significa qualcosa di diverso solo perché qualcuno lo usa in-correttamente, significa sempre la stessa cosa. Un termine migliore sarebbe in non intenzionale o non ovvio errori logici.

Considerare i bug come random è quasi come accettare che ci sia qualche altra forza comprensibile al lavoro che non è completamente compresa agendo indipendentemente dal tuo input al computer, e che non è molto scientifico . Voglio dire, gli dei sono arrabbiati e colpiscono la tua domanda per un capriccio?

    
risposta data 22.07.2011 - 09:22
fonte

Leggi altre domande sui tag