Come attirare le attenzioni del programmatore in determinate condizioni?


13

Cominciamo con un esempio.

Diciamo che ho un metodo chiamato exportche dipende fortemente dallo schema del DB. E per "dipende fortemente" intendo che so che l'aggiunta di una nuova colonna a una determinata tabella spesso (molto spesso) porta alla exportmodifica del metodo corrispondente (di solito è necessario aggiungere anche il nuovo campo ai dati di esportazione).

I programmatori spesso dimenticano di cambiare exportmetodo poiché non è davvero chiaro che dovresti anche guardare questo. Il mio obiettivo è costringere il programmatore a prendere esplicitamente una decisione per determinare se si è dimenticato di esaminare il exportmetodo o semplicemente non desidera aggiungere un campo ai dati di esportazione. E sto cercando la soluzione di progettazione per questo problema.

Ho due idee, ma entrambe hanno dei difetti.

Wrapper "Leggi tutto" intelligente

Posso creare il wrapper intelligente che garantisce che tutti i dati vengano letti in modo esplicito.

Qualcosa come questo:

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, checkerafferma se table_rowcontiene altri campi che non sono stati letti. Ma tutto questo sembra un po 'pesante e (forse) influenza la perfomance.

"Controlla quel metodo" unittest

Posso solo creare il unittest che ricorda l'ultimo schema della tabella e fallisce ogni volta che la tabella viene cambiata. In tal caso il programmatore vedrebbe qualcosa come "non dimenticare di controllare il exportmetodo". Per nascondere il programmatore di avvertenze, sarebbe (o non sarebbe - questo è un problema) il check-out exporte manualmente (è un altro problema) risolvere il test aggiungendo nuovi campi al suo interno.

Ho alcune altre idee ma sono troppo problematiche da implementare o troppo difficili da capire (e non voglio che il progetto diventi un enigma).


Il problema sopra è solo un esempio della più ampia classe di problemi che incontro di volta in volta. Voglio associare alcuni pezzi di codice e / o infrastruttura, quindi cambiando uno di essi si avverte immediatamente il programmatore per controllarne un altro. Di solito hai alcuni strumenti semplici come estrarre la logica comune o scrivere unittest affidabile, ma sto cercando lo strumento per casi più complessi: forse alcuni modelli di design di cui sono ora a conoscenza.


Puoi forse generare exportsulla base dello schema?
coredump,

Non può essere generato estaticamente, ecco perché dovrei chiedere al programmatore di guardare il codice e prendere una decisione.
Vadim Pushtaev,

Sarebbe una soluzione aggiungere due elenchi di nomi di campo (esportazione e non esportazione) alla classe di esportazione e fare un test unitario per verificare che quei due elenchi comprendano insieme l'intero set di campi dal database?
Sjoerd Job Postmus,

Puoi generare automaticamente un test che controlla se exportha tutto ciò di cui hai realisticamente bisogno?
biziclop,

1
un commento nel codice sorgente una soluzione troppo semplicistica? Di solito le cose si perdono perché non c'è nessun promemoria, un commento lo aggiusterà.
gbjbaanb,

Risposte:


11

Sei sulla buona strada con l'idea del test unitario, ma l'implementazione è sbagliata.

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

  • O exportfunziona ancora perfettamente bene, perché non è stato influenzato da una leggera modifica nello schema,

  • O 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 qualcos'altro, assicurano che la exportprocedura funzioni con lo schema corrente , indipendentemente dal fatto che sia cambiata o meno dal precedente commit. Se questi test superano, ottimo. Se falliscono, questo è un segno per lo sviluppatore che potrebbe essersi perso qualcosa e un'indicazione chiara 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, effettivamente la modifica del "test", ovvero un'operazione completamente estranea al export.

Invece, eseguendo test effettivi per la exportprocedura, ti assicuri che lo sviluppatore risolverà il export.


Più in generale, quando si verifica una situazione in cui un cambiamento in una classe richiede sempre o di solito un cambiamento in una classe completamente diversa, questo è un buon segno che hai sbagliato il tuo progetto e stai violando il principio di responsabilità singola.

Mentre parlo in modo specifico delle lezioni, si applica più o meno anche ad altre entità. Ad esempio, una modifica dello schema del database dovrebbe riflettersi automaticamente nel tuo codice, ad esempio tramite generatori di codice utilizzati da molti ORM, o almeno dovrebbe essere facilmente localizzato: se aggiungo una Descriptioncolonna alla Producttabella e non utilizzo ORM o generatori di codice, Almeno mi aspetto di apportare una singola modifica all'interno della Data.Productclasse del DAL, senza la necessità di cercare in tutta la base di codice e trovare alcune occorrenze diProduct classe, ad esempio nel livello di presentazione.

Se non riesci a limitare ragionevolmente la modifica a una posizione (o perché sei nel caso in cui semplicemente non funzioni, o perché richiede un'enorme quantità di sviluppo), allora crei un rischio di regressione . Quando cambio classe Ae la classe Bda qualche parte nella base di codice 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 sai che i cambiamenti in una posizione causano problemi in una parte completamente diversa della base di codice, assicurati di avere abbastanza test che generano allarmi non appena appare una regressione a questo livello.

In tutti i casi, evitare di fare affidamento in tali casi solo sui commenti. Qualcosa di simile a:

// 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 lo leggeranno nella maggior parte dei casi, ma spesso finiranno per essere rimossi o spostati lontano dalla linea interessata e diventeranno impossibili da capire.


Sì, quel "test" non è davvero un test, è una sorta di trappola, una sorta di If you change the following line...che funziona automaticamente e non può essere semplicemente ignorata. Non ho mai visto qualcuno effettivamente usato simili trappole da cui il dubbio.
Vadim Pushtaev,

1
Il problema è che la classe Bnon smette di funzionare, forse inizia a funzionare in modo errato. Ma non posso prevedere il futuro e non posso scrivere test che prevedono il futuro, quindi provo a ricordare allo sviluppatore di scrivere quel nuovo test.
Vadim Pushtaev,

@VadimPushtaev: quando si tratta di test, "forse inizia a funzionare in modo errato" e "smette di funzionare" è esattamente la stessa cosa. Non puoi prevedere il futuro, ma dovresti essere in grado di conoscere esattamente i requisiti e verificare che tali requisiti siano soddisfatti dal tuo prodotto reale.
Arseni Mourzenko,

Sì, è una cosa. In realtà voglio ricordare allo sviluppatore di pensare a nuovi requisiti . Voglio solo salutare una mano: “Ciao, sei sicuro di non dimenticare l'esportazione? Chiedi al gestore o qualcosa del genere, è un problema comune ”.
Vadim Pushtaev,

Grazie comunque, la tua risposta mi ha aiutato a organizzare i miei pensieri e ora ho un certo piano.
Vadim Pushtaev,

3

Mi sembra che le tue modifiche non siano specificate. Supponi di vivere in un luogo che non ha codici postali, quindi non hai una colonna di codici postali nella tabella degli indirizzi. Quindi vengono introdotti i codici postali oppure inizi a trattare con i clienti che vivono dove ci sono codici postali e devi aggiungere questa colonna alla tabella.

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

Quando viene presa la decisione di aggiungere una colonna significativa (ovvero non solo una ricerca totale o denormalizzata memorizzata nella cache o un altro valore che non appartiene a un'esportazione o report o su una schermata di input), gli elementi di lavoro creati dovrebbero includere TUTTE le modifiche necessario: aggiunta della colonna, aggiornamento dello script popolato, aggiornamento dei test, aggiornamento dell'esportazione, dei report, delle schermate di input e così via. Questi potrebbero non essere tutti assegnati alla (o ritirati da) la stessa persona, ma devono essere fatti tutti.

A volte gli sviluppatori scelgono di aggiungere colonne stesse come parte dell'implementazione di alcune modifiche più grandi. Ad esempio, qualcuno potrebbe aver scritto un elemento di lavoro per aggiungere qualcosa a una schermata di input e a un report, senza pensare a come viene implementato. Se ciò accade molto, dovrai decidere se il tuo addetto agli elementi di lavoro 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'oggetto di lavoro- aderder a volte lascia fuori le cose. Se è quest'ultimo, allora hai bisogno di una cultura di "non solo cambiare lo schema; fermati e pensa a cos'altro influenza".

Se ci fossero molti sviluppatori e questo fosse successo più di una volta, avrei impostato un avviso di check-in per il responsabile del team o altra persona senior per essere avvisato delle modifiche allo schema. Quella persona potrebbe quindi cercare elementi di lavoro correlati per far fronte alle conseguenze della modifica dello schema e, se gli elementi di lavoro fossero mancanti, potrebbe non solo aggiungerli, ma anche educare chiunque li abbia esclusi dal piano.


2

Quasi sempre, quando creo un'esportazione creo anche un'importazione corrispondente. Dato che ho altri test che popolano completamente la struttura dei dati esportati, posso quindi costruire un test di unità di andata e ritorno che confronta un originale completamente popolato con una copia esportata quindi importata. Se sono uguali, l'esportazione / importazione è completa; se non sono gli stessi, il test unitario fallisce e so che il meccanismo di esportazione deve essere aggiornato.


questo può verificare che ogni oggetto sia salvato e ricaricato da db. Non copre il caso in cui un campo db esistente non abbia un campo oggetto corrispondente.
k3b,

@ k3b vero. Almeno nei miei sistemi, ciò si verifica generalmente se qualcosa non viene più utilizzato ma non è stato rimosso dallo schema, che è al di fuori dell'ambito del meccanismo di esportazione: i test unitari per la persistenza degli oggetti verificano se ogni campo di un oggetto è persisteva, ma non collaudo colonne inutilizzate in quanto ciò non avrebbe alcun effetto osservabile sulla funzione di sistema.
Pete Kirkham,
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.