Come può il mio team evitare errori frequenti dopo il refactoring?

20

Per darti un po 'di background: lavoro per un'azienda con circa dodici sviluppatori di Ruby on Rails (+/- stagisti). Il lavoro a distanza è comune. Il nostro prodotto è composto da due parti: un nucleo piuttosto grasso, e ridotto a grandi progetti dei clienti costruiti su di esso. I progetti dei clienti in genere espandono il core. La sovrascrittura delle funzionalità chiave non si verifica. Potrei aggiungere che il nucleo ha alcune parti piuttosto brutte che hanno urgente bisogno di refactoring. Ci sono specifiche, ma soprattutto per i progetti dei clienti. La parte peggiore del core non è testata (non come dovrebbe essere ...).

Gli sviluppatori sono divisi in due team, lavorando con uno o due ordini di acquisto per ogni sprint. Di solito, un progetto cliente è strettamente associato a uno dei team e degli ordini di acquisto.

Ora il nostro problema: piuttosto spesso ci rompiamo a vicenda. Qualcuno del Team A espande o ridefinisce la caratteristica principale Y, causando errori imprevisti per uno dei progetti dei clienti del Team B. Per lo più, le modifiche non vengono annunciate sui team, quindi i bug arrivano quasi sempre inaspettati. La squadra B, inclusa la PO, ha pensato che la caratteristica Y fosse stabile e non l'avesse testata prima di essere rilasciata, ignara delle modifiche.

Come sbarazzarsi di questi problemi? Che tipo di 'tecnica di annuncio' mi puoi consigliare?

    
posta SDD64 12.06.2014 - 08:41
fonte

10 risposte

24

Raccomando di leggere Lavorare efficacemente con il codice legacy di Michael C. Feathers . Spiega che hai davvero bisogno di test automatici, di come puoi aggiungerli facilmente, se non li hai già, e quali "odori di codice" per refactoring in che modo.

Oltre a ciò, un altro problema fondamentale nella tua situazione sembra una mancanza di comunicazione tra le due squadre. Quanto sono grandi queste squadre? Stanno lavorando a diversi backlog?

È quasi sempre una cattiva pratica dividere i team in base alla tua architettura. Per esempio. un team principale e un team non-core. Invece, vorrei creare squadre sul dominio funzionale, ma cross-component.

    
risposta data 12.06.2014 - 10:06
fonte
41

We worst part of the core are untested (as it should be...).

Questo è il problema. Il refactoring efficiente dipende molto dalla suite di test automatizzati. Se non ne hai, iniziano a comparire i problemi che stai descrivendo. Questo è particolarmente importante se usi un linguaggio dinamico come Ruby, dove non esiste un compilatore per catturare gli errori di base relativi al passaggio dei parametri ai metodi.

    
risposta data 12.06.2014 - 09:00
fonte
5

Le risposte precedenti che indicano che i test unitari sono migliori, ma ritengo che potrebbero esserci altri problemi fondamentali da affrontare. Sono necessarie interfacce chiare per l'accesso al codice principale dal codice per i progetti del cliente. In questo modo, se si rifatta il codice core senza alterare il comportamento osservato attraverso le interfacce , il codice dell'altro team non si interromperà. Questo renderà molto più facile sapere cosa può essere "sicuro" rifattorizzato, e ciò che ha bisogno di una, possibilmente un'interfaccia di rottura, riprogettazione.

    
risposta data 12.06.2014 - 12:48
fonte
5

Altre risposte hanno evidenziato punti importanti (più test unitari, team di funzioni, interfacce pulite ai componenti principali), ma c'è un punto che trovo mancante, che è il controllo delle versioni.

Se blocchi il comportamento del core eseguendo un rilascio 1 e lo metti in un sistema di gestione degli artefatti privato 2 , qualsiasi progetto del cliente può dichiararlo dipendenza dalla versione core X e non verrà interrotta dalla prossima versione X + 1 .

La "politica di annuncio" si riduce quindi ad avere un file CHANGES insieme a ogni versione, oppure a una riunione di gruppo per annunciare tutte le funzionalità di ogni nuova versione principale.

Inoltre, penso che sia necessario definire meglio ciò che è "core" e quale sottoinsieme di essa è "chiave". Sembra che tu (correttamente) eviti di apportare molte modifiche ai "componenti chiave", ma consenti modifiche frequenti a "core". Per poter contare su qualcosa, devi mantenerlo stabile; se qualcosa non è stabile, non chiamarlo core. Forse potrei suggerire di chiamarlo componenti "helper"?

EDIT : se segui le convenzioni del sistema di versioning semantico , allora qualsiasi modifica incompatibile nell'API del core deve essere contrassegnato da una modifica della versione principale . Cioè, quando cambi il comportamento del nucleo precedente o rimuovi qualcosa, non aggiungi solo qualcosa di nuovo. Con questa convenzione, gli sviluppatori sanno che l'aggiornamento dalla versione "1.1" a "1.2" è sicuro, ma passare da "1.X" a "2.0" è rischioso e deve essere attentamente esaminato.

1: Penso che questo sia chiamato un gioiello, nel mondo di Ruby
2: L'equivalente a Nexus in Java o PyPI in Python

    
risposta data 12.06.2014 - 15:23
fonte
3

Come altre persone hanno detto, una buona serie di test unitari non risolverà il tuo problema: avrai problemi durante l'unione delle modifiche, anche se passano tutte le suite di test del team.

Lo stesso per TDD. Non vedo come possa risolvere questo.

La tua soluzione non è tecnica. È necessario definire chiaramente i confini "core" e assegnare un ruolo di "cane da guardia" a qualcuno, sia esso lo sviluppatore principale o l'architetto. Qualsiasi modifica al core deve passare attraverso questo watchdog. È responsabile di assicurarsi che tutti i risultati di tutte le squadre si uniscano senza troppi danni collaterali.

    
risposta data 12.06.2014 - 15:45
fonte
2

Come soluzione a lungo termine, è necessaria anche una comunicazione migliore e più tempestiva tra i team. Ognuno dei team che utilizzeranno mai, ad esempio, la caratteristica principale Y, dovrà essere coinvolto nella creazione dei test point pianificati per la funzione. Questa pianificazione, di per sé, metterà in evidenza i diversi casi d'uso inerenti alla caratteristica Y tra le due squadre. Una volta che la funzione dovrebbe funzionare è inchiodata, e i test sono implementati e concordati, c'è un ulteriore cambiamento nello schema di implementazione che è richiesto. Il team che rilascia la funzionalità è necessario per eseguire il testcase, non il team che sta per usarlo. L'attività, se presente, che dovrebbe causare collisioni, è l'aggiunta di una nuova testcase da uno dei due team. Quando un membro del team pensa a un nuovo aspetto della funzionalità che non è stato testato, dovrebbe essere libero di aggiungere una testcase che ha verificato passando nella propria sandbox. In questo modo, le uniche collisioni che si verificheranno saranno a livello di intento e dovrebbero essere bloccate prima che la funzionalità refactored venga rilasciata in the wild.

    
risposta data 12.06.2014 - 23:40
fonte
2

Mentre ogni sistema ha bisogno di efficaci suite di test (il che significa, tra le altre cose, automazione) e anche se questi test, se usati in modo efficace, cattureranno questi conflitti prima di adesso, questo non risolve i problemi sottostanti.

La domanda rivela almeno due problemi sottostanti: la pratica di modificare il "core" per soddisfare i requisiti per i singoli clienti e il fallimento dei team a comunicare e coordinare il loro intento di apportare modifiche. Nessuna di queste è la causa principale e dovrete capire perché questo viene fatto prima di poterlo risolvere.

Una delle prime cose da determinare è se sia gli sviluppatori sia i manager si rendano conto che c'è un problema qui. Se almeno alcuni lo fanno, allora devi scoprire perché o pensano di non poter fare nulla al riguardo, o scegliere di non farlo. Per coloro che non lo fanno, potresti provare ad aumentare la loro capacità di anticipare in che modo le loro azioni attuali possono creare problemi futuri o sostituirli con persone che possono farlo. Fino a quando non avrai una forza lavoro consapevole di come stanno andando le cose, difficilmente riuscirai a risolvere il problema (e forse neanche allora, almeno a breve termine).

Potrebbe essere difficile analizzare il problema in termini astratti, almeno inizialmente, quindi concentrarsi su un incidente specifico che ha provocato un problema e cercare di determinare come è successo. Poiché le persone coinvolte sono probabilmente difensive, sarà necessario essere attenti alle motivazioni personali e post hoc per scoprire cosa sta realmente accadendo.

Esiste una possibilità che esita a menzionare perché è così improbabile: le esigenze dei clienti sono talmente disparate che non c'è comunanza insufficiente per giustificare il codice di base condiviso. Se è così, allora hai effettivamente più prodotti separati, e dovresti gestirli come tali e non creare un accoppiamento artificiale tra loro.

    
risposta data 13.06.2014 - 19:04
fonte
1

Sappiamo tutti che i test unitari sono la strada da percorrere. Ma sappiamo anche che è davvero difficile adattarli realisticamente a un nucleo.

Una tecnica specifica che potrebbe essere utile per te quando estendi la funzionalità è provare a verificare temporaneamente e localmente che le funzionalità esistenti non siano state modificate. Questo può essere fatto in questo modo:

Pseudo codice originale:

def someFunction
   do original stuff
   return result
end

Codice test temporaneo sul posto:

def someFunctionNew
   new do stuff
   return result
end

def someFunctionOld
   do original stuff
   return result
end

def someFunction
   oldResult = someFunctionOld
   newResult = someFunctionNew
   check oldResult = newResult
   return newResult
end

Esegui questa versione con qualsiasi test di livello di sistema esistente. Se tutto va bene, sai che non hai rotto le cose e puoi quindi procedere alla rimozione del vecchio codice. Nota che quando controlli la corrispondenza dei vecchi e dei nuovi risultati, potresti anche aggiungere del codice per analizzare le differenze per catturare i casi che sai dovrebbe essere diversi a causa di un cambiamento previsto come una correzione di bug.

    
risposta data 13.06.2014 - 02:53
fonte
1

"Principalmente, le modifiche non vengono annunciate sui team, quindi i bug arrivano quasi sempre inaspettati"

Problema di comunicazione qualcuno? Che dire (oltre a quello che tutti gli altri hanno già sottolineato, che dovresti sottoporti a test rigorosi) assicurandoti che ci sia una comunicazione corretta? Che le persone siano consapevoli del fatto che l'interfaccia che stanno scrivendo cambierà nella prossima versione e quali saranno queste modifiche?
E dare loro l'accesso ad almeno un fittizio interace (con implementazione vuota) il prima possibile durante lo sviluppo in modo che possano iniziare a scrivere il proprio codice.

Senza tutto ciò, i test unitari non faranno molto se non sottolineare durante le fasi finali che c'è qualcosa di fuori combattimento tra le parti del sistema. Volete saperlo, ma volete conoscerlo presto, molto presto, e far sì che i team parlino tra loro, coordinino gli sforzi e abbiano effettivamente accesso frequente al lavoro che l'altra squadra sta facendo (quindi commit regolari, non un massiccio commettere dopo parecchie settimane o mesi, 1-2 giorni prima della consegna).
Il tuo bug NON è nel codice, certamente non nel codice dell'altra squadra che non sapeva che tu stavi scherzando con l'interfaccia contro cui stavano scrivendo. Il tuo bug è nel tuo processo di sviluppo, la mancanza di comunicazione e collaborazione tra le persone. Solo perché sei seduto in stanze diverse non significa che dovresti isolarti dagli altri ragazzi.

    
risposta data 13.06.2014 - 12:03
fonte
1

In primo luogo, hai un problema di comunicazione (probabilmente collegato anche a un problema di team building ), quindi penso che una soluzione al tuo caso dovrebbe essere incentrata su ... bene, comunicazione, invece di tecniche di sviluppo.

Dò per scontato che non sia possibile bloccare o forgiare il modulo principale quando si avvia un progetto cliente (altrimenti è semplicemente necessario integrare nella pianificazione aziendale alcuni progetti non correlati ai clienti che mirano all'aggiornamento del modulo principale ).

Quindi ci rimane il problema di cercare di migliorare la comunicazione tra i team. Questo può essere risolto in due modi:

  • con gli esseri umani. Ciò significa che la tua azienda designa qualcuno come architetto del modulo principale (o qualsiasi altro aspetto utile per il top management) che sarà responsabile della qualità e della disponibilità del codice. Questa persona incarnerà il nucleo. Pertanto, sarà condivisa da tutti i team e assicurerà una corretta sincronizzazione tra loro. Inoltre, dovrebbe anche agire come revisore del codice impegnato nel modulo principale per mantenere la sua coerenza;
  • con strumenti e flussi di lavoro. Imponendo Continuous Integration sul core, si renderà il codice core stesso il mezzo di comunicazione. Ciò richiederà prima un certo sforzo (con l'aggiunta di suite di test automatizzate su di esso), ma i rapporti notturni dell'IC saranno un aggiornamento di stato lordo del modulo principale.

Puoi trovare ulteriori informazioni su CI come processo di comunicazione qui .

Infine, hai problemi con la mancanza di lavoro di squadra a livello aziendale. Non sono un grande fan degli eventi di team building, ma questo sembra un caso in cui sarebbero utili. Avete riunioni a livello di sviluppatori su base regolare? Puoi invitare persone di altri gruppi alle retrospettive del tuo progetto? O forse qualche birra del venerdì sera?

    
risposta data 20.06.2014 - 15:33
fonte

Leggi altre domande sui tag