Riesci a considerare un errore risolto quando la causa sottostante non è stata riparata?

3

Diciamo che esiste un sistema estensibile, che utilizza un algoritmo che richiede che gli elementi su cui opera siano unici.

La firma per chiamare questo algoritmo è la seguente: function algorithm([Collection of Elements])

Ora ci sono più moduli che usano questo algoritmo, che chiamano l'algoritmo in questo modo:

[Collection of Elements] elements = [...]
[make sure the Collection only has unique elements]
algorithm(elements)

Dì, c'è un nuovo modulo aggiunto che assomiglia a questo:

[Collection of Elements] elements = [...]
algorithm(elements)

(cioè non si assicura che gli elementi siano unici).

Ora, nella maggior parte dei casi tutti gli elementi sono unici. Ma una volta ogni tanto viene sollevata un'eccezione dall'algoritmo che si lamenta degli elementi duplicati.

Il modulo dovrebbe avere solo elementi unici assegnati all'algoritmo, e come tale l'eccezione a quel punto viene segnalata come un bug e dovrebbe essere risolta.

Ora, il bug sarebbe "corretto" semplicemente aggiungendo [make sure the Collection only has unique elements] al modulo incriminato [Fix # 1]? O il bug verrà risolto cambiando la firma dell'algoritmo in function algorithm([Set of Elements]) , in modo che il bug corretto sia impossibile ripresentarsi con eventuali moduli futuri [Fix # 2]?

Ti sto chiedendo specificatamente di questo aspetto in una recensione - se la correzione del bug dovesse essere "fatta" se esaminassi la Fix # 1? Le modifiche della correzione n. 2 dovrebbero essere aggiunte come una nuova attività di refactoring? La correzione n. 1 deve essere respinta finché non viene implementata come la correzione n. 2?

    
posta Timo Türschmann 22.01.2018 - 17:46
fonte

3 risposte

5

Le argomentazioni illegali derivano da una grande varietà: la gamma è più ampia di quella che i sistemi di tipo dei nostri linguaggi di programmazione possono generalmente esprimere. I nostri sistemi di tipi non sono linguaggi di restrizione generici e inoltre, molte condizioni che potremmo voler verificare sono dinamiche, quindi è necessario verificarle in fase di runtime.

Quindi, in generale, dobbiamo ancora controllare e gestire l'input errato, e, quando le funzioni sono adeguatamente documentate , passare un argomento illegale è colpa del chiamante piuttosto che del chiamato e del lancio un'eccezione è un comportamento ragionevole.

Tuttavia, detto questo, il guasto non determina necessariamente come viene affrontato tale problema, poiché è ragionevole dal punto di vista tecnico correggere entrambi:

  • il chiamante - per fare ciò che serve per generare l'argomento legale corretto o
  • il callee - per ampliare l'interfaccia, ad es. per consentire e gestire l'argomento (precedentemente illegale)

Il tuo pensiero di cambiare l'interfaccia per usare il sistema dei tipi per garantire che l'argomento appropriato sia super valido (specialmente in questo caso, ma nel caso generale di argomenti illegali, non sempre è possibile farlo). Tuttavia, in linea generale, questo cambiamento richiederà un compromesso in termini di manutenzione (e probabilmente di prestazioni) in merito alla modifica di tutti i chiamanti e di altri possibili approcci.

Se la maggior parte dei chiamanti sta facendo questo,

[Collection of Elements] elements = [...]
[make sure the Collection only has unique elements]
algorithm(elements)

Quindi forse dovresti fornire una versione dell'interfaccia all'algoritmo che fa il passo intermedio ( make sure... ) per loro. Questo non solo risolve il problema dei chiamanti (futuri) che non rispettano il contratto, ma rende la base di codice più ASCIUTTA.

Per aggiungere a ciò, l'algoritmo potrebbe offrire un'interfaccia con un numero di metodi, con prestazioni variabili scambiate contro la tolleranza dell'argomento. La versione precedente che consente i duplicati e la versione che non lo è (è ben documentata e genera violazioni) e consente ai chiamanti di fare ciò che è naturale per loro. Puoi anche includere la versione Set e vedere chi la usa. Non mi sembra un enorme onere per il confezionamento dell'algoritmo per fornire diversi metodi di questo tipo.

Can you consider a bug fixed when the underlying cause was not fixed?

Sì, se il bug immediato è stato risolto.

L'ingegneria comporta dei compromessi, alcuni dei quali favoriscono la situazione a breve termine e le realtà a rischio di debito tecnico che aumentano l'onere della manutenzione, il costo dei cambiamenti e la correttezza.

Tuttavia, in parte ciò va alla nozione di ciò che è effettivamente la causa sottostante, che può essere soggettiva. La causa sottostante è stata la mancata creazione di una documentazione adeguata? Mancata lettura / utilizzo della documentazione? Mancato mantenimento del codice ASCIUTTO? Mancata progettazione di un'API infallibile?

Hai ragione, naturalmente, che quando possibile dovremmo essere infallibili, così gli altri programmatori che consumano le nostre API (spesso sono noi) ricadono nel " pozzo di successo ".

The module should should have only given unique elements to the algorithm, and as such the exception at that point is reported as a bug and should be fixed.

Should Fix #1 be rejected until it is implemented like Fix #2?

No, non necessariamente. La sola correzione numero 1 è una soluzione rapida che accumula alcuni grani di debito tecnico, ma risolve il bug in modo valido .

(Per essere chiari penso che ci siano soluzioni migliori, che sono anche economiche da implementare.)

Questa decisione a breve termine (correzione n. 1) può essere in parte attenuata, ad esempio rafforzando la documentazione, poiché in genere ciò può essere fatto con poche spese. Dovresti anche riesaminare il codice (e / o testare) altri chiamanti. Inoltre, puoi aggiungere al tuo arretrato un elemento tecnico-contabile per determinare i costi (tempo e impatto delle prestazioni) per ripulire questo problema.

    
risposta data 22.01.2018 - 18:28
fonte
0

Entrambi sono correzioni al bug; entrambi rimuovono o impediscono che la situazione causi nuovamente il problema.

La correzione numero 1 è probabilmente un po 'miope, in quanto potrebbe esserci un'altra situazione in cui viene aggiunto un nuovo modulo e lo sviluppatore si dimentica / non conosce la verifica dell'unicità. Fix # 2 risolve questo potenziale problema futuro rendendo impossibile che gli elementi non siano unici, ma ha un costo di refactoring dei moduli esistenti per accettare la nuova firma. La quantità di refactoring potrebbe essere piccola o potrebbe essere grande, non lo sappiamo (ma si spera che almeno abbiate un'idea), e questo potrebbe dettare se si desidera implementare la correzione n. 1 o n. 2.

    
risposta data 22.01.2018 - 17:53
fonte
0

Sono entrambe "correzioni" per l'errore, ma a seconda di come viene applicata l'unicità, potresti essere in difficoltà:

function algorithm(const UniqueList *list)

costringerà tutti i metodi ben educati a modificare la propria firma in modo che corrispondano. D'altra parte,

/* Hey, list must be unique! */
function algorithm(const List *list) {
    ...
}

può essere visto come un atto di giuramento. Anche un refactoring "utile" come

/* list is made unique if it isn't */
function algorithm(List *list) {
}

può essere mortale se qualcuno dei chiamanti si aspetta che la sua lista non cambi, o vuole essere responsabile della sua gestione della memoria mentre la funzione potrebbe liberare elementi non univoci e così via.

Il modo in cui proverei ad andare è:

  • deprecano la vecchia funzione, sostituendola con una che controlla e solleva l'inferno appropriato
  • fornisce due interfacce per entrambi i casi d'uso.

Quindi gli utenti decideranno se non fare nulla (possono farlo) o utilizzare la funzione che meglio si adatta alla loro situazione. Il codice che veramente ha bisogno di aggiungere è costituito da poche righe e alcuni cast (nella mia esperienza, molti utenti inizieranno a lasciar cadere con gratitudine il loro codice univoco per fare affidamento sui tuoi).

/**
 * Old function
 * WARNING: list must be unique
 * @deprecated code has been moved to algorithmU
 * @see algorithmU
 * @see algorithmN
 */
function algorithm(const List *listThatMustBeUnique) {
    assert(isUnique(listThatMustBeUnique);
    return algorithmU((const UniqueList *)listThatMustBeUnique);
}

/**
 * Applies algorithm to a unique list of elements.
 */
function algorithmU(const UniqueList *list)

/**
 * Generalized interface to algorithm.
 *
 * @see algorithmU
 */
function algorithmN(List *list) {
    // makeUnique comes from the best implementation of "ensure list is unique" seen around.
    UniqueList *list2 = makeUnique(list);
    return algorithmU(list2);
}
    
risposta data 22.01.2018 - 22:22
fonte

Leggi altre domande sui tag