Rimozione di valori codificati e design difensivo rispetto a YAGNI


10

Prima un po 'di background. Sto codificando una ricerca da Età -> Valuta. Esistono 7 parentesi per età, quindi la tabella di ricerca è di 3 colonne (da | a | tasso) con 7 righe. I valori cambiano raramente: sono tassi legiferati (prima e terza colonna) che sono rimasti gli stessi per 3 anni. Ho pensato che il modo più semplice per archiviare questa tabella senza codificarla sia nel database in una tabella di configurazione globale, come un singolo valore di testo contenente un CSV (quindi "65,69,0,05,70,74,0,06" è come i livelli 65-69 e 70-74 verrebbero memorizzati). Relativamente facile da analizzare quindi utilizzare.

Quindi mi sono reso conto che per implementare questo avrei dovuto creare una nuova tabella, un repository per avvolgerlo, test a livello di dati per il repository, test unitari attorno al codice che non appiattisce il CSV nella tabella e test attorno alla ricerca stessa. L'unico vantaggio di tutto questo lavoro è evitare la codifica forzata della tabella di ricerca.

Quando si parla con gli utenti (che attualmente utilizzano direttamente la tabella di ricerca - guardando una copia cartacea) l'opinione è praticamente che "le tariffe non cambiano mai". Ovviamente ciò non è corretto - le tariffe sono state create solo tre anni fa e in passato le cose che "non cambiano mai" hanno avuto l'abitudine di cambiare - quindi per me per programmare in modo difensivo questo non dovrei assolutamente archiviare la tabella di ricerca in l'applicazione.

Tranne quando penso a YAGNI . La funzione che sto implementando non specifica che le tariffe cambieranno. Se le tariffe cambiano, cambieranno ancora così raramente che la manutenzione non è nemmeno una considerazione e la funzionalità non è in realtà abbastanza critica da compromettere qualsiasi cosa se ci fosse un ritardo tra la variazione della tariffa e l'applicazione aggiornata.

Ho praticamente deciso che nulla di valore andrà perso se codifico la ricerca e non sono troppo preoccupato per il mio approccio a questa particolare funzionalità. La mia domanda è: come professionista ho debitamente giustificato questa decisione? I valori di codifica rigida sono una cattiva progettazione, ma andare alla difficoltà di rimuovere i valori dall'applicazione sembra violare il principio YAGNI.

MODIFICA Per chiarire la domanda, non sono preoccupato per l'implementazione effettiva. Sono preoccupato di poter fare una cosa brutta e rapida e giustificarla dicendo YAGNI, oppure posso adottare un approccio più difensivo e ad alto sforzo, che anche nel migliore dei casi alla fine ha bassi benefici. Come programmatore professionista, la mia decisione di implementare un progetto che so essere difettoso dipende semplicemente da un'analisi costi / benefici?

EDIT Mentre tutte le risposte sono state molto interessanti poiché penso che ciò dipenda dalle scelte progettuali di un individuo, penso che le risposte migliori siano state quelle di @ Corbin e di @EZ Hart in quanto hanno sollevato cose che non avevo considerato nella domanda:

  • la falsa dicotomia di "rimuovere correttamente i valori codificati" spostandola nel database e "applicare YAGNI in modo efficiente" utilizzando la codifica hardware. C'era una terza opzione di mettere la tabella di ricerca nella configurazione dell'app, che non comporta il sovraccarico del modo corretto e senza l'efficienza di YAGNI. Generalmente non siamo limitati a nessuna / o decisione, e quindi si riduce a una decisione costi / benefici.
  • la generazione di codice può ridurre il sovraccarico di spostare i valori codificati nel database e in un modo che rimuove anche la mia decisione troppo ingegnerizzata di elaborare un CSV nella tabella. Potenzialmente questo aggiunge anche un problema di manutenzione a lungo termine con il codice generato se i requisiti di base cambiano per il metodo di ricerca. Tutto ciò influisce solo sull'analisi costi / benefici, ed è probabile che se avessi avuto quell'automazione disponibile non avrei nemmeno considerato l'hard-coding di qualcosa del genere.

Sto contrassegnando la risposta di @ Corbin come corretta perché cambia le mie ipotesi sui costi di sviluppo e probabilmente aggiungerò alcuni strumenti di generazione del codice al mio arsenale nel prossimo futuro.


Non dimenticare, se le tariffe cambiano e tutto ciò che hai sono valori codificati, potresti sbagliare i calcoli dei record storici quando le tariffe cambiano (e lo faranno, indipendentemente da ciò che il tuo cliente ti sta dicendo).
Andy,

Risposte:


6

Hai trovato un difetto nel tuo processo di sviluppo. Quando è difficile fare la cosa giusta (creare la tabella, i repo, i test repo, i test appiattiti ...), gli sviluppatori troveranno un modo per aggirarlo. Questo di solito comporta fare la cosa sbagliata. In questo caso, ti sta tentando di trattare i dati dell'applicazione come logica dell'applicazione. Non farlo Aggiungi invece utili automazioni al tuo processo di sviluppo. Usiamo CodeSmith per generare il codice noioso, boilerplate che nessuno vuole scrivere. Dopo aver creato una tabella, eseguiamo CodeSmith e genera DAO, DTO e stub test unitari per ciascuno.

A seconda delle tecnologie che stai utilizzando, dovresti avere opzioni simili. Molti strumenti ORM genereranno modelli da uno schema esistente. Le migrazioni delle rotaie funzionano nella direzione opposta: tabelle dai modelli. La meta-programmazione in linguaggi dinamici è particolarmente efficace nell'eliminazione del codice del boilerplate. Dovrai lavorare un po 'di più per generare tutto ciò di cui hai bisogno se hai un'applicazione multi-livello complessa, ma ne vale la pena. Non lasciare che la sensazione "wow, questo è un dolore al collo" ti impedisca di fare la cosa giusta.

Oh, e non archiviare i tuoi dati in un formato che richiede un'ulteriore elaborazione (CSV). Ciò aggiunge solo ulteriori passaggi che richiedono attenzione e test.


Non direi che una migrazione di Rails crea tabelle dai modelli ... Le migrazioni di Rails descrivono le modifiche alle tabelle. L'esecuzione di una migrazione modifica il database. Le proprietà del modello corrispondenti alla struttura della tabella vengono create in fase di esecuzione.
Kevin Cline,

Questo mi interessa, non avevo considerato di automatizzare parti dello sforzo iniziale a quel livello. E avere quell'automazione significherebbe che potrei creare un tavolo completo piuttosto che usare il CSV per risparmiare tempo. Ciò di cui mi preoccupo è la manutenzione a lungo termine del codice generato. Generandolo in anticipo ho ipotizzato presto che il metodo di ricerca non cambierà mai. Immagino che se è una possibilità dovrei semplicemente prenderlo in considerazione come parte del costo / beneficio, dando il costo ridotto della piastra della caldaia. +1
Rebecca Scott

La domanda era: "dovrei codificare i valori quando il mio client dice che non cambieranno?" e la tua risposta è "no, e in effetti dovresti lanciare un ORM al problema". Non sono d'accordo. Esistono opzioni più semplici che consentirebbero all'OP di evitare l'hard-coding. Fare grandi aggiunte all'architettura per supportare cose esplicitamente designate non necessarie non è etico. È un peccato che molti sviluppatori siano predisposti a fare queste cose. E devo dire che non sono d'accordo al 100% con il tuo suggerimento di non "lasciare che la sensazione" wow, questo è un dolore al collo "ti impedisca di fare la cosa giusta". Quei sentimenti contano!
user1172763,

10

Digitare ed estendere la risposta di @ Thorbjørn Ravn Andersen: Mantenere il calcolo / la ricerca in un posto è un buon inizio.

Il tuo processo di pensiero difensivo contro YAGNI è un problema comune. In questo caso, suggerirei di essere informato da altre due cose.

Innanzi tutto - come sono state presentate le esigenze dell'utente? Hanno specificato la modificabilità delle tariffe? In caso contrario, la complessità aggiunta fa parte di qualcosa che si può fatturare o no? (o se fai parte del personale, puoi invece dedicare il tuo tempo a svolgere altri lavori?) In tal caso, procedi sicuramente e fornisci ciò che è stato ragionevolmente richiesto.

In secondo luogo, e forse ancora più importante, è improbabile che la semplice modificabilità soddisfi un requisito effettivo di fronte a un cambiamento legislativo. Se e quando le tariffe cambiano, è probabile che si verifichino una data di interruzione e alcune elaborazioni prima e dopo. Inoltre, se la stessa interfaccia deve quindi eseguire qualsiasi tipo di elaborazione dei reclami datata, potrebbe essere necessario cercare la tariffa corretta in base alla data di entrata effettiva, anziché alla data effettiva.

In breve, sto notando che un vero requisito modificabile potrebbe essere piuttosto complesso, quindi a meno che o fino a quando non sarà realizzato, il semplice è probabilmente migliore.

In bocca al lupo


1
+1 per il tuo secondo punto. Non cerco di nascondere ciò che gli organi legislativi potrebbero fare in futuro.
David Thornley,

Fallo in una funzione di libreria. È OK avere un solo utente. Quando e se i requisiti cambiano, hai un posto dove cambiare. (Potrebbe essere necessario aggiungere una funzione per effettuare ricerche per il valore a partire da una data specifica.)
BillThor

Risposta migliore e più completa, +1
Andy,

7

Trasforma la ricerca effettiva in una funzione di libreria. Puoi quindi vedere tutto il codice usando quella ricerca nel tuo repository di origine, quindi sai quali programmi devono essere aggiornati quando le tariffe cambiano.


La ricerca verrà implementata solo in un unico posto (qualcosa come una PensionRateLookupclasse forse) che verrà quindi utilizzato a livello globale. Lo farei indipendentemente dal fatto che sia archiviato all'esterno dell'app o codificato, in questo modo solo l'implementazione della PensionRateLookupclasse deve essere mantenuta. Il mio problema è come ho usato YAGNI per giungere alla conclusione che è accettabile codificare a fondo la tabella di ricerca.
Rebecca Scott,

Grazie per la tua risposta però. Mantenere l'implementazione della ricerca in un unico posto è sicuramente un buon design.
Rebecca Scott,

YAGNI è valido solo se è possibile spedire nuovi file binari quando le tariffe cambiano. Se non è possibile, ma è possibile modificare la configurazione, renderlo un valore di configurazione letto all'avvio con la rappresentazione testuale corrente come predefinita.

Spedisco una nuova versione di solito settimanalmente, quindi l'aggiornamento della ricerca non è un problema. Inserirlo nella configurazione del client è in realtà peggiore in quanto non posso cambiare la configurazione del client con un nuovo binario. L'alternativa come la vedo io è metterlo nel database, il che significa un grande sforzo.
Rebecca Scott,

Allora qual è il problema? Quando le tariffe cambiano, aggiorni il codice e spedisci a tutti i clienti?

2

Fammi vedere se ho fatto bene la tua domanda. Hai 2 opzioni per implementare una funzione: o decodifichi un valore e la tua funzione è facile da implementare (ma non ti piace la parte hardcode) o fai uno sforzo enorme per "ripetere" un sacco di cose che vengono fatte così sei in grado di sviluppare la tua funzionalità in modo pulito. È corretto?

La prima cosa che mi viene in mente è "La cosa più importante nel conoscere le buone pratiche è sapere quando stai meglio senza di esse".

In questo caso, lo sforzo è molto elevato, quindi puoi farlo in modo pulito, le possibilità di effetti collaterali di questo cambiamento sono grandi e il rendimento che otterrai sarebbe piccolo (come hai spiegato, sembra probabile che non lo farà modificare).

Userei l'approccio hardcode (ma lo preparerei per essere flessibile in futuro) e, in caso di modifica di questo tasso in futuro, sfrutterei l'opportunità per refactificare tutta questa sezione del codice errata. Quindi il tempo e il costo potrebbero essere stimati correttamente e il costo per modificare il valore codificato sarebbe minimo.

Questo sarebbe il mio approccio :)


Grazie @Oscar. La parte tecnica di questo è def. corretta. E sono d'accordo su come affrontare il problema, eseguendo il refactoring solo quando è necessario. Quindi stai dicendo che come professionisti possiamo e dovremmo scegliere i nostri principi di progettazione, basati esclusivamente su costi / benefici? Ha senso.
Rebecca Scott,

@ Ben Scott: Prego :). Sì, dal mio punto di vista, i principi di progettazione sono stati creati / catalogati non perché hanno un bell'aspetto, ma perché apportano vantaggi come "pulizia" del codice, flessibilità, robustezza, ecc. Ma ci sono sempre 2 domande: 1- Ne ho bisogno ? 2- Le mie restrizioni (tempo, tecnico, ecc.) Mi consentono di implementarlo? Questo di solito è ciò che mi porta a scegliere il mio principio di design. Anche l'ingegnerizzazione eccessiva è negativa;) ps: se pensi che sia una risposta interessante, votala in modo che anche altre persone la leggano con una probabilità più alta. Grazie! :)
JSBach

votato ;-)
Rebecca Scott

2

Questo è un oggetto che "non cambierà" fino a quando non lo farà. È inevitabile che cambierà, ma quel tempo potrebbe essere un po 'lontano.

La tua giustificazione è corretta. Al momento, il cliente non ha chiesto la possibilità di modificare facilmente tali tariffe. Come tale, YAGNI.

Tuttavia, ciò che non vuoi è il codice che accede alle tue tariffe e interpreta i risultati sparsi in tutta la base di codice. Un buon design OO ti farebbe incapsulare la rappresentazione interna delle tue tariffe in una classe ed esporre solo uno o due metodi necessari per utilizzare i dati.

Avrai bisogno di quell'incapsulamento per evitare errori di copia e incolla, o dovrai eseguire un refactoring su tutto il codice che utilizza le tariffe quando è necessario apportare una modifica alla rappresentazione interna. Quando prendi questa precauzione iniziale, l'approccio più complicato può essere un semplice scambio e sostituire per la versione più descritta.

Inoltre, chiamare al client la limitazione del progetto corrente. Dì loro che, nell'interesse di mantenere il programma, hai codificato i valori, il che richiede una modifica della codifica per aggiornarli. In questo modo, quando vengono a conoscenza delle variazioni dei tassi in sospeso a causa della nuova legislazione, possono scegliere di aggiornare semplicemente la tabella di ricerca o eseguire le modifiche più complicate in quel momento. Ma metti quella decisione in grembo.


Grazie @berin, non ho menzionato SRP nella domanda ma era nel piano. È utile restituire la proprietà del problema al cliente.
Rebecca Scott,

Assegnare la colpa per quando le cose andranno male in futuro non mi sembra professionale.
Andy,

@Andy, quale parte di ciò sta assegnando la colpa? Presentare le limitazioni del progetto al cliente offre loro l'opportunità di dare la priorità al lavoro complicato ora e di togliere altre cose dal tavolo, di respingere la scadenza o di accettare il progetto limitato ora poiché altre cose sul tavolo sono più importanti per loro. Stai dando al tuo cliente la possibilità di scegliere il suo prodotto. Rendere il cliente consapevole del rischio / premio delle scelte che si desidera fare nel proprio interesse renderà il progetto più fluido.
Berin Loritsch,

@BerinLoritsch Ma questa particolare decisione progettuale è simile a parti scadenti che dicono "Posso usare stucco per idraulici per collegare questo tubo che perde, questa è l'opzione più economica!" Le tariffe cambieranno. È un dato di fatto ed è irresponsabile per un professionista costruire un sistema che non lo permetta. E i risparmi sono probabilmente trascurabili nel grande schema del costo del progetto. Metti i dati in una tabella; il codice che ottiene i dati di ricerca è leggermente diverso, la logica che determina quale velocità utilizzare è la stessa in entrambi i modi. L'unica altra decisione è come gestire i dati storici dopo il ...
Andy,

le variazioni delle tariffe, che di solito vengono gestite semplicemente copiando la tariffa nel relativo record. A questo punto non è necessaria alcuna schermata di amministrazione, il sistema può quindi gestire quando le tariffe cambiano e sarà semplice script cambiarle. Niente di tutto questo dovrebbe durare più di qualche ora, ma impedirà al seminterrato di allagarsi l'anno prossimo quando lo stucco fallisce.
Andy,

2

Dividi la differenza e inserisci i dati della velocità in un'impostazione di configurazione. Puoi utilizzare il formato CSV che hai già, evitare il sovraccarico del database non necessario e, se mai la modifica è necessaria, sarà una modifica che il cliente dovrebbe essere in grado di fare senza dover ricompilare / reinstallare e senza fare messaggi nel database: possono semplicemente modificare il file di configurazione.

Di solito, quando si osserva una scelta tra due estremi (violazione di dati dinamici YAGNI e hard-coding), la risposta migliore è da qualche parte nel mezzo. Attenzione alle false dicotomie.

Ciò presuppone che tutta la configurazione sia in file; se è un posto difficile come il registro, probabilmente dovresti ignorare questo consiglio :)


Ho preso in considerazione l'idea di averlo in un'impostazione di configurazione ma di metterlo da parte perché gli utenti non sono molto tecnici per quanto riguarda la modifica di una stringa CSV, quindi sarei quello che aggiorna la configurazione per N utenti (mentre faccio tutto il supporto IT in anche l'organizzazione), in termini di sforzo sarebbe più facile fare il lavoro iniziale per farlo entrare nel database che posso amministrare da un posto. Suppongo che se non fosse una considerazione (se gli utenti fossero in grado di gestire la propria configurazione individuale) non avrei questo problema. Quindi un ottimo punto, grazie.
Rebecca Scott,

Se hai combinato questo con gli altri suggerimenti della logica centralizzata, allora è un'ottima risposta. È puramente male cercare davvero la ricerca piuttosto che metterla in un modo che permetta di cambiarla tramite la configurazione. Ogni volta che qualsiasi altro sviluppatore lo incontra, ti odieranno per questo. Anche un negozio di sviluppo individuale dovrebbe considerare cosa succede se vengono colpiti da un autobus e dovrebbe rendere facile per il prossimo sviluppatore cambiare i valori senza una versione completa del software. Solo se odi il client e odi tutti gli altri sviluppatori, dovresti farlo hardcore. Quindi praticamente mai.
simbo1905,

2

Ho pensato che il modo più semplice per archiviare questa tabella senza codificarla sia nel database in una tabella di configurazione globale, come un singolo valore di testo contenente un CSV (quindi "65,69,0,05,70,74,0,06" è come i livelli 65-69 e 70-74 verrebbero memorizzati.

Ho appena creato una tabella DB. (Dove pensavi di conservare un intero tavolo in un unico file, sei impazzito?)

La tabella richiede 2 campi NON 3. Età e tasso. Questa riga successiva contiene il valore superiore! Stai de-normalizzando senza nemmeno saperlo!

Ecco la Sql per ottenere la tariffa per una persona di età che ha 67 anni.

Select * from RateTable where Age in (Select max(age) from RateTable where age <=67) 

Non preoccuparti di creare una schermata di manutenzione poiché è fuori portata. Se lo richiedono in seguito, invia una richiesta di modifica ed eseguilo.

MODIFICA: Come altri hanno detto, mantieni il codice per ottenere la tariffa centralizzata, nel caso in cui l'intera struttura della tariffa si ritorni.


Ciao @Morons, grazie per la tua risposta. La tabella avrebbe effettivamente bisogno di 3 colonne. È una tariffa unica per fascia d'età, da età minima a età massima (i 65-69 anni hanno il 5%). E sto conservando solo una piccola quantità di dati per uno scopo limitato, quindi cosa c'è di sbagliato nel fare una supposizione sulla struttura e andare per un CSV invece di un'intera tabella dedicata? Vorrei probabilmente aggiungere un delimitatore di riga, e dividere per riga quindi colonna e tirare le colonne nei campi richiesti. Questo sarà sempre letto molto più che scritto, quindi non sono troppo preoccupato per un tavolo completo.
Rebecca Scott,

La riga successiva contiene il valore superiore dell'intervallo ... Questa è la duplicazione dei dati. Dicendo <69 e> 7 sono la stessa cosa. L'unico motivo per avere entrambi i valori è se l'intervallo può avere fori. ... sto dicendo di usare un tavolo perché è più facile (e un design migliore). Non capisco perché pensi che archiviare un CSV in una tabella ti farà risparmiare tempo o fatica, hai ancora bisogno di una chiamata DB solo per ottenere quella stringa, quindi devi analizzarla. Come ti ho mostrato sopra, puoi ottenere la tariffa con una singola chiamata DB.
Morons,

Ah vero Stavo mantenendo il tavolo stesso simile al materiale sorgente ... e mi mancava che l'età fosse il limite superiore nella tua risposta perché avevo le tristezze che ti avevo dato.
Rebecca Scott,

1
"Stai demoralizzando senza nemmeno saperlo!" - all'inizio ho pensato che fosse un errore di battitura e che volevi dire "denormalizzante", ma più ci pensavo e più sembrava che avessi ragione in entrambi i casi :)
EZ Hart

@ez hart LOL vero.
Rebecca Scott,

1

Sono d'accordo con la maggior parte delle risposte fornite. Aggiungo anche che la coerenza con il resto dell'applicazione è importante. Se questo è l' unico posto nel codice che ha valori hardcoded probabilmente sorprenderà i manutentori. Ciò è particolarmente vero se si tratta di una base di codice ampia e stabile. Se si tratta di un piccolo programma gestito da te, la decisione è meno importante.

Ho un lontano ricordo della lettura di un noto ragazzo agile / OOP (come Dave Thomas o Kent Beck o qualcuno) che dice che la loro regola empirica per la duplicazione del codice era due e solo due: non rifattorizzare la prima volta che duplica qualcosa da potresti scriverlo solo due volte nella tua vita. Ma la terza volta ...

Questo non affronta esattamente la domanda, dal momento che stai interrogando YAGNI ma penso che parli della flessibilità generale delle regole agili. Il punto di agilità è adattarsi alla situazione e andare avanti.


Grazie @Dave, è utile. Sono l'unico manutentore, sin dall'inizio, ma è una base di codice ampia e relativamente stabile (migliaia di file, oltre 100 tabelle, ecc.) E sono ancora costantemente sorpreso (e sgomento). La coerenza è sicuramente uno dei miei obiettivi per il futuro.
Rebecca Scott

0

Codice fisso in una funzione.

  • Quando i valori cambiano, è possibile fatturare nuovamente il cliente
  • Quando i valori cambiano, è probabile che il formato della tabella debba cambiare, facendo CSV perderai tempo
  • Più facile da implementare, maggiori possibilità di essere in bilancio con il contratto attuale
  • Può trovare facilmente quando dovrà essere aggiornato

Permettetemi di installare questo valore scadente, quindi quando si rompe sicuramente tra un anno ottengo ripetute attività. Sembra losco, perché lo è.
Andy,
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.