Come attirare le attenzioni del programmatore in determinate condizioni?

13

Iniziamo con un esempio.

Diciamo che ho un metodo chiamato export che dipende pesantemente dallo schema del DB. E per "dipende pesantemente" intendo che so che aggiungere una nuova colonna a una certa tabella spesso (molto spesso) porta al corrispondente cambio di metodo export (di solito dovresti aggiungere anche il nuovo campo ai dati di esportazione).

I programmatori dimenticano spesso di cambiare il metodo export , dal momento che non è molto chiaro che dovresti anche guardarlo. Il mio obiettivo è forzare il programmatore esplicitamente a prendere una decisione per determinare se ha dimenticato di guardare il metodo export o semplicemente non vuole aggiungere un campo ai dati di esportazione. E sto cercando la soluzione di design per questo problema.

Ho due idee, ma entrambe hanno difetti.

Smart "Leggi tutto" wrapper

Posso creare lo smart wrapper che assicura che tutti i dati siano letti esplicitamente.

Qualcosa del genere:

def export():
    checker = AllReadChecker.new(table_row)

    name    = checker.get('name')
    surname = checker.get('surname')
              checker.ignore('age') # explicitly ignore the "age" field

    result = [name, surname] # or whatever

    checker.check_now() # check all is read

    return result

Quindi, checker asserisce se table_row contiene altri campi che non sono stati letti. Ma tutta questa cosa sembra piuttosto pesante e (forse) influisce sulla perfomance.

"Controlla che metodo" unittest

Posso semplicemente creare l'unittest che ricorda l'ultimo schema della tabella e fallisce ogni volta che la tabella viene cambiata. In quel caso il programmatore vedrebbe qualcosa come "non dimenticare di controllare il metodo export ". Per nascondere il programmatore di avviso sarebbe (o non sarebbe - questo è un problema) check out export e manualmente (che è un altro problema) risolvere il test aggiungendo nuovi campi in esso.

Ho qualche altra idea, ma sono troppo fastidiose da implementare o troppo difficili da comprendere (e non voglio che il progetto diventi un puzzle).

Il problema precedente è solo un esempio della più ampia classe di problemi che incontro di volta in volta. Voglio legare alcune parti di codice e / o infrastruttura in modo che cambiando uno di loro immediatamente avvisi programmatore per controllare un altro. Di solito hai alcuni strumenti semplici come estrapolare la logica comune o scrivere un unittest affidabile, ma sto cercando lo strumento per casi più complessi: forse alcuni schemi di progettazione di cui sono ora a conoscenza.

    
posta Vadim Pushtaev 29.09.2015 - 17:49
fonte

3 risposte

11

Sei sulla buona strada con la tua idea di test unitario, ma la tua implementazione è sbagliata.

Se export è correlato allo schema e lo schema è cambiato, ci sono due possibili casi:

  • O il export funziona ancora perfettamente, perché non è stato modificato da una leggera modifica dello schema,

  • Oppure si rompe.

In entrambi i casi, l'obiettivo della build è di rintracciare questa possibile regressione. Una serie di test, che si tratti di test di integrazione, test di sistema, test funzionali o altro, assicurano che la procedura export funzioni con lo schema corrente , indipendentemente dal fatto che sia cambiato o non dal precedente commit. Se quei test passano, bene. Se falliscono, questo è un segnale per lo sviluppatore che potrebbe aver perso qualcosa e una chiara indicazione su dove cercare.

Perché la tua implementazione è sbagliata? Bene, per diversi motivi.

  1. Non ha nulla a che fare con i test unitari ...

  2. ... e, in realtà, non è nemmeno un test.

  3. La parte peggiore è che la correzione del "test" richiede, beh, la modifica del "test", ovvero un'operazione completamente non correlata a export .

Invece, eseguendo test effettivi per la procedura export , ci si assicura che lo sviluppatore corregga il export .

Più in generale, quando incontri una situazione in cui un cambiamento in una classe richiede sempre o di solito una modifica in una classe completamente diversa, questo è un buon segno che hai sbagliato la progettazione e stai violando il Principio di Responsabilità Unica.

Mentre parlo in modo specifico delle classi, si applica più o meno anche ad altre entità. Ad esempio, una modifica nello schema del database dovrebbe essere riflessa automaticamente nel codice, ad esempio tramite generatori di codice utilizzati da molti ORM, o almeno dovrebbe essere facilmente localizzata: se aggiungo la Description alla tabella Product e io uso nessun ORM o generatore di codice, mi aspetto almeno di fare una modifica singola all'interno della classe Data.Product del DAL, senza la necessità di cercare in tutto il codice base e trovare alcune occorrenze di Product class nel, per esempio, livello di presentazione.

Se non puoi limitare ragionevolmente la modifica a una posizione (o perché sei in un caso in cui semplicemente non funziona, o perché richiede un'enorme quantità di sviluppo), allora crei un rischio di < em> regressioni . Quando cambio la classe A e la classe B da qualche parte nel code base smette di funzionare, è una regressione.

I test riducono il rischio di regressioni e, cosa molto più importante, mostrano la posizione di una regressione. Questo è il motivo per cui quando conosci che cambia in una posizione causa problemi in una parte completamente diversa della base di codice, assicurati di avere abbastanza test che aumentano gli allarmi non appena appare una regressione a questo livello.

In tutti i casi, evitate di fare affidamento in questi casi solo sui commenti. Qualcosa come:

// If you change the following line, make sure you also change the corresponding
// 'measure' value in 'Scaffolding.Builder'.

non funziona mai. Non solo gli sviluppatori non leggeranno la maggior parte dei casi, ma finiranno spesso rimossi o spostati molto lontano dalla linea in questione e diventeranno impossibili da comprendere.

    
risposta data 29.09.2015 - 18:51
fonte
3

Mi sembra che le tue modifiche siano sottostimate. Dì che vivi da qualche parte che non ha i codici postali, quindi non hai una colonna del codice postale nella tabella degli indirizzi. Poi vengono introdotti i codici postali o inizi a trattare con i clienti che vivono dove ci sono i codici postali e devi aggiungere questa colonna al tavolo.

Se l'elemento di lavoro dice semplicemente "aggiungi la colonna del codice postale alla tabella degli indirizzi" allora sì, l'esportazione sarà interrotta, o almeno non esporterà i codici postali. Ma per quanto riguarda la schermata di input che viene utilizzata per inserire i codici postali? Il rapporto che elenca tutti i clienti e i loro indirizzi? Ci sono tonnellate di cose che devono essere cambiate quando aggiungi questa colonna. Il compito di ricordare queste cose è importante: non dovresti contare su artefatti di codice casuali per "intrappolare" gli sviluppatori nel ricordo.

Quando viene presa la decisione di aggiungere una colonna significativa (vale a dire, non solo un totale della cache o una ricerca denormalizzata o un altro valore che non appartiene a un'esportazione o un report o una schermata di input) gli elementi di lavoro creati devono includere TUTTE le modifiche necessarie: aggiunta della colonna, aggiornamento dello script popolato, aggiornamento dei test, aggiornamento dell'export, report, schermate di input e così via. Questi potrebbero non essere tutti assegnati (o prelevati) dalla stessa persona, ma devono essere tutti eseguiti.

A volte gli sviluppatori scelgono di aggiungere le colonne stesse come parte dell'implementazione di alcuni cambiamenti più grandi. Ad esempio, qualcuno potrebbe aver scritto un oggetto di lavoro per aggiungere qualcosa a uno schermo di input e un report, senza pensare a come è implementato. Se questo accade molto, dovrai decidere se il tuo addetto alla lavorazione deve conoscere i dettagli dell'implementazione (in modo da poter aggiungere tutti gli elementi di lavoro giusti) o se gli sviluppatori devono essere consapevoli che l'elemento di lavoro il sommatore a volte lascia le cose fuori. Se è il secondo, allora hai bisogno di una cultura di "non cambiare solo lo schema, fermati e pensa a cos'altro influisce".

Se c'erano molti sviluppatori e questo è accaduto più di una volta, avrei impostato un avviso di check-in per consentire al responsabile del team o ad altre persone senior di essere avvisati delle modifiche dello schema. Quella persona potrebbe quindi cercare gli elementi di lavoro correlati per gestire le conseguenze del loro cambiamento dello schema e, se mancassero gli elementi di lavoro, potrebbe non solo aggiungerli ma educare chiunque li abbia esclusi dal piano.

    
risposta data 30.09.2015 - 13:28
fonte
2

Quasi sempre, durante la creazione di un'esportazione creo anche un'importazione corrispondente. Dato che ho altri test che completano completamente la struttura dati da esportare, posso quindi creare un test unitario di andata e ritorno che confronta un originale completamente popolato con una copia esportata e importata. Se sono uguali, l'esportazione / importazione è completa; se non sono uguali, il test dell'unità fallisce e so che il meccanismo di esportazione deve essere aggiornato.

    
risposta data 30.09.2015 - 12:05
fonte

Leggi altre domande sui tag