Lavorare su Legacy Codebases (o vita di un contraente) (Italiano (Italian))

Lavorare su Legacy Codebases (o vita di un contraente)

Comments

NOTE: Apart from English (and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.

Wednesday, 06 November 2024

//

Less than a minute

Introduzione

Come sviluppatore freelance uno dei set di abilità è necessario imparare rapidamente è come lavorare su basi di codice esistenti in modo efficace. Sono stato fortunato ad aver costruito un mucchio di sistemi --graffi; questo è un JOY come sviluppatore esperto tuttavia non è sempre il caso.

Le sfide

I sistemi di legacy hanno sfide significative; specialmente quando gli sviluppatori/architetti chiave (se siete abbastanza fortunati da averli) sono andati avanti.

Documentazione

Ciò è spesso trascurato soprattutto nelle piccole imprese. In generale, sono necessari 4 tipi chiave di documentazione:

  1. Nuove guide per sviluppatori così come si esegue il progetto localmente (più su questo in un minuto), quali considerazioni ci sono (se per esempio avete bisogno di un quadro diverso dalla corrente, soprattutto vero con Node tristemente).
  2. Documenti di spiegamento in questi giorni in cui CI/CD è visto come l'oro-standard di distribuzione è spesso trascurato che è necessario sapere come distribuire il sistema manualmente. Questo è particolarmente vero se si sta lavorando su un sistema che è stato in giro per un po 'e ha un sacco di passaggi manuali.
  3. Procedure di risoluzione dei problemi; cosa si fa quando il sistema si spegne? Chi chiami? Quali sono le cose chiave da controllare? Questo è spesso trascurato, ma è CRECIAL in un sistema di produzione.
  4. Documenti di progettazione architettonica / di sistema; come funziona il sistema? Quali sono i componenti chiave? Quali sono le principali dipendenze? Pensate a questo come alla vostra roadmap quando imparate un nuovo sistema. Fornisce una panoramica ad alto livello su come funziona il sistema che può essere critico (soprattutto nei sistemi più grandi e distribuiti). Questo dovrebbe includere quali repository di codice sorgente sono utilizzati, quali sistemi CI/CD sono utilizzati, quali database sono utilizzati per ogni elemento dell'applicazione. Questo può comprendere qualsiasi cosa, da un semplice diagramma a un diagramma UML completo di ogni componente del sistema. Per esempio in un'applicazione React questo includerebbe tipicamente un diagramma dei componenti e come essi interagiscono tra loro e qualsiasi servizio di backend. In un'applicazione.NET Core questo includerebbe un diagramma dei servizi e come essi interagiscono tra loro (e il front end & altri sistemi di backend) ed eventualmente ASP.NET Views e quali servizi possono utilizzare.

La documentazione è una delle cose che noi sviluppatori spesso odiamo fare (non è codice) ma è cruciale. Come puoi vedere I LOVE Markdown, con quelli come Mermaid e PlantUML puoi creare alcuni diagrammi e diagrammi di flusso davvero belli che possono essere inclusi nella tua documentazione.

Deve essere ATTUALE; gli sviluppatori spesso parlano di bit-rot; dove le dipendenze di un progetto diventano obsoleti / rischi di sicurezza downright. Questo vale anche per la documentazione; se non è corrente non è utile.

Ci sono diversi livelli di documentazione ma in generale ogni volta che si cambia il codice a cui si fa riferimento in un documento si dovrebbe aggiornare tale documento.

Eseguire il sistema localmente

Questo è spesso una sfida, soprattutto se il sistema è stato intorno per un po '. È necessario sapere quale versione di Node /.NET ecc, quale versione del database, quale versione del framework ecc. Questo è spesso trascurato, ma è CRECIAL per alzarsi e funzionare rapidamente.

Ho spesso visto gli sviluppatori dire che questo non è rilevante in questi giorni di sistemi cloud e grandi applicazioni distribuite, ma non sono d'accordo; è necessario essere in grado di eseguire il sistema localmente per debug problemi in modo rapido ed efficace.

  1. Profilazione - questo sembra essere diventato un 'avanzato sviluppatore solo' abilità, ma è fondamentale essere in grado di profilare il vostro codice per vedere dove sono le strozzature. Ciò è particolarmente vero in un sistema legacy in cui non si può avere il lusso di essere in grado di riscrivere il sistema da zero. Strumenti come dotTrace e dotMemorysono inestimabili qui. Soprattutto nelle applicazioni ASP.NET; i problemi più grandi sono generalmente 'leaks' nel sistema; perdite di memoria, perdite di connessione ecc. Mentre l'applicazione sembrerà eseguire FINE per un po'si trova improvvisamente si sta schiantando / utilizzando tutta la memoria sul server.
  2. Trovare i problemi nel debug - Che ti piaccia o no i giorni di Response.Write in Classic ASP sono finiti. Per un'applicazione anche modestamente complessa (soprattutto quella che non hai scritto) è fondamentale passare attraverso il codice. Per seguire una richiesta dal suo inizio attraverso il servizio di identificare ciò che potrebbe accadere, quali eccezioni non possono essere catturati ecc.
  3. Logging - Ancora una volta spesso trascurato (scusa ma nessun filtro di eccezione ASP.NET Core nella parte superiore dello stack non è sufficiente). Riferendosi alla mia Post precedente, la registrazione è una parte fondamentale per lavorare in codice LOCALLY. Ricorda che è possibile utilizzare i tipi di registrazione dei clienti (per esempio utilizzando il Generatori sorgente di registrazione) in ASP.NET Core. Puoi quindi specificare quali tipi di eventi dovrebbero essere registrati e quali no. Questo è fondamentale in un sistema legacy in cui non si può avere il lusso di essere in grado di riscrivere il sistema da zero. Per Approfondimenti dell'applicazione si può desiderare Viaggi utente e altre funzionalità; tuttavia essere consapevoli questo diventa costoso veloce e sta usando LogInformation per ogni richiesta. Potresti voler usare un Processore di Telemetria personalizzato per filtrare le richieste che non vuoi registrare.
  4. Testing - Troppo spesso vedo sviluppatori 'testing in preprod' o pensando che Unit Test poi check-in è sufficiente in termini di test. Il suo RARELY IS, c'è un sacco di valore in entrambi utilizzando uno strumento come Resharper / NCrunch per eseguire automaticamente i test di unità; tuttavia ricordate Test di unità sono proprio questo; testano un'unità di codice. È necessario mantenere la visibilità su come il sistema funziona effettivamente in concerto con altri componenti. È qui che entrano in gioco i test di integrazione; testano come funziona il sistema nel suo complesso. Puoi usare uno strumento come SpecFlowCity name (optional, probably does not need a translation) scrivere questi test in un formato leggibile dall'uomo. Ancora una volta; cruciale in un sistema legacy in cui non si può avere il lusso di essere in grado di riscrivere il sistema da zero.

In OGNI progetto lavoro sul primo passo è ottenere il sistema (o gran parte di esso) in esecuzione localmente. Vedendo il codice, eseguendo il codice, debug il codice si può ottenere una sensazione per come funziona il sistema.

La base di codici

In ogni sistema questo è il tuo fonte della verità, non importa quello che dicono i documenti, ciò che gli altri vi dicono su come dovrebbe funzionare è come funziona.

Questo spesso impegnativo, è come trovare la strada in una nuova città senza roadmap. Fortunatamente nelle applicazioni si dispone di un punto di ingresso (un caricamento pagina / una chiamata front end API ecc.) Scegli un punto e inizia da lì.

Utilizzare qualsiasi cosa è necessario per interagire con esso, che si tratti di PostMan, Rider HttpClient o anche una pagina web. Aggancia il tuo debugger, fai la chiamata e seguila. Sciacquare e ripetere per ogni parte del sistema.

Rifattorizzazione

Generalmente lascia questo finche' non capisci il sistema. E' SEMPRE tentare di 'gettarlo via e ricominciare' tuttavia resistere a questa tentazione. Specialmente per un sistema funzionante FUNZIONA ricostruire / anche refactoring un sistema è un rischio enorme. Sì, è divertente ma per ogni riga di codice si cambia si rischia di introdurre nuovi bug snd emozionanti.

Come tutto il resto (soprattutto quando si contrae) è necessario giustificare il lavoro che si esegue su un sistema. Questa giustificazione deve essere focalizzata su uno dei seguenti elementi:

  1. Prestazioni - il sistema è lento e deve essere accelerato (di nuovo attenzione, quello che si trova lento può essere il modo in cui il sistema è progettato per funzionare). Rendere una parte fumare veloce può introdurre problemi ulteriormente verso il basso il sistema. Vuoi "uccidere" il server del database, se fai troppe richieste hai un meccanismo che non causerà eccezioni a cascata?
  2. Sicurezza - il sistema è insicuro e deve essere reso sicuro. Questo è difficile, soprattutto in un sistema legacy. Con bit-rot si può scoprire che il sistema utilizza vecchie versioni di librerie che hanno conosciuto problemi di sicurezza. Questa è una buona giustificazione per il lavoro; tuttavia essere consapevoli che potrebbe essere necessario refactory un LOT del sistema per aggiornarlo; di nuovo portando al problema 'nuovi bug'.
  3. Mantenibilità - il sistema è difficile da mantenere e deve essere reso più facile da mantenere. In generale questo diventa un sacco di lavoro; la vita attuale della base di codice lo giustifica? Se si sta spendendo più tempo a fare queste modifiche per migliorare la manutenbilità di quanto si sarebbe mai salvato per il cliente, allora non vale la pena (e ancora, cambiato codice == nuovi bug).
  4. Esperienza utente - Io di solito do la priorità a queste questioni. Per utente Non importa se il vostro codice è 1% migliore; sono quelli che PAGAno per il lavoro che fate alla fine. Se si può rendere la loro esperienza migliore di quello che è generalmente ne vale la pena. Tuttavia, essere consapevoli che questo può essere un 'pendio scivoloso' di cambiamenti; si può scoprire che si sta cambiando un sacco del sistema per fare un piccolo cambiamento per l'utente.

L'attività di lavorare sui sistemi legacy

Questo è spesso trascurato, soprattutto dagli sviluppatori. Devi essere in grado di giustificare il lavoro che stai facendo su un sistema. Ciò è particolarmente vero in un ambiente contrattuale in cui si è pagati per l'ora. Alla fine, non e' il tuo codice e non i tuoi soldi. Il motivo per cui stai facendo un cambiamento è spesso più importante del cambiamento stesso.

Ho lavorato come appaltatore per oltre un decennio, non è facile; come appaltatore ogni ora del vostro tempo è un 'costo' per il cliente. È necessario aggiungere valore di spostamento al sistema rispetto al costo. Se non lo siete allora sarete rapidamente alla ricerca di un nuovo contratto.

Come sviluppatori, tendiamo ad essere uomini d'affari schifosi siamo concentrati sulla 'perfezione' ad ogni turno. In realtà, non c'è bisogno di fare un sistema "perfetto" (direi che non c'è una cosa del genere); basta fornire valore al cliente.

Su un impegno a più lungo termine questo include garantire nuovo codice è manutenibile ed efficiente dal punto di vista dei costi da eseguire. Nei sistemi legacy questo è MOLTO più duro. Spesso si deve guadare attraverso una palude, essendo ansioso che come si impara il sistema non si può offrire molto valore. I'm not making any changes Pensi che... I'm just learning the system. Questo è un errore; si sta imparando il sistema per renderlo migliore. Stai imparando il sistema per renderlo più efficiente. Stai imparando il sistema per renderlo piu' maneggevole. Se un cliente non può accettare questo passaggio, allora è necessario essere molto attenti a come si comunica questo (o cercare un nuovo contratto).

Il popolo

Ancora una volta spesso trascurato, un sacco di tempo sei portato in come un appaltatore perché qualche persona chiave ha lasciato (non essere coinvolto nella politica di questo; non è la vostra preoccupazione). È necessario essere in grado di lavorare con le persone che sono lì per raggiungere gli obiettivi del progetto. In un contratto avrai generalmente i termini del tuo fidanzamento specificato (nel mio contratto attuale si tratta di'migliorare l'affidabilità e ridurre i costi di gestione'); concentrati su questo. Se non sei sicuro di cosa significhi allora chiedi.

Tenete a mente chi è il vostro contatto diretto; soprattutto nei primi due mesi. Tienili informati (probabilmente non avrai nuove funzionalità per vantarti di come ottieni filato su come funziona il sistema). In genere invio una e-mail di sintesi ogni settimana / due settimane al mio contatto diretto; questo è un buon modo per tenerli informati di ciò che stai facendo e quello che stai trovando.

Ricorda, questa è la persona che approverà la tua fattura; non ci dovrebbero essere sorprese al momento della fattura. Una volta che si inizia a controllare il codice regolarmente questo è meno di un problema; si ha un record di esattamente quello che hai fatto e l'impatto che ha avuto. Fino ad allora, dovete tenerli informati. Devono sapere cosa hai fatto e perche' dovrebbero pagarti.

Affidabilità

Ancora una volta, torna al codice legacy; se si fa una modifica in generale è necessario implementarlo.Anche il meglio di noi farà di tanto in tanto questo casino, soprattutto nei sistemi legacy codice ci sarà qualcosa Non potevi saperlo. quando vi schierate. Questo torna alla registrazione - se si dispone di un server di staging avere la registrazione un LOT (ma mantenuto per un breve periodo) poi quando si implementa in QUESTO è possibile raccogliere ulteriori informazioni su ciò che non è riuscito.

Non importa quanto tu sia bravo, quanti test hai fatto sul posto siamo tutti umani. Questa è una parte fondamentale della regola "non dispiegare in un venerdì"; aspettatevi che un dispiegamento causi un nuovo problema su un sistema. Preparati a lavorare finche' non sara' risolto. Se non sai perché ha fallito, aggiungi altri test per riprodurre il problema e più logging per assicurarti di catturare problemi simili in futuro.

Specialmente per i sistemi di produzione il vostro sistema di staging potrebbe non essere 1:1 (soprattutto per quanto riguarda il carico), utensile come k6 può aiutare a simulare il carico (anche meglio localmente dove si può fare la corretta profilazione come accennato in precedenza).

Distribuzione

Ancora una volta spesso trascurato nel fervore per CI / CD è il motivo di questi. Semplice, farai un casino. Avere un modo molto veloce ed efficiente per distribuire un sistema significa che quando lo si rompe si può anche risolvere più rapidamente. Se il vostro sistema di revisione del codice CI significa che ci vogliono 2 giorni per ottenere un PR fuso allora che è il più veloce si può ragionevolmente fi un sistema. Se il vostro sistema CD significa che si prende giù il sistema in esecuzione; abituarsi a LONG notti.

Un meccanismo efficiente per fissare e distribuire il codice è essenziale per un efficiente gasdotto di sviluppo. Se ci vuole più tempo per distribuire una correzione di quanto ci sia voluto per trovare e implementare quella correzione in codice, allora sei meno propenso a risolvere le cose.

Sistemare le app Legacy

Ho messo questo in virgolette in quanto questo è un problema; per le applicazioni legacy (soprattutto quando la rielaborazione su larga scala è fuori dai limiti) ci sono due approcci principali.

  • Patch in esecuzione

Questo è il processo di semplice correzione del codice esistente; ancora una volta assicurarsi di testare accuratamente e avere i processi in atto per ripristinare / rapidamente reimpiegare eventuali correzioni. Non mentirò che questo tipo di sviluppo è raramente divertente in quanto si sono ancora probabilmente guadare attraverso la palude della base di codice esistente. Tuttavia, è un male necessario in molti casi.

Come al solito dovresti assicurarti di avere UNA FORMA DI PROVA per eseguire il codice corrente; idealmente dovrebbe anche testare per il fallimento per il problema che stai cercando di risolvere PRIMA di fare la correzione . Essi IDYLL per questo è quello di avere un test di unità che si rivolge da vicino l'area di codice che è necessario risolvere, ma questo è spesso al di fuori della portata del lavoro per grandi sistemi distribuiti.

In genere uso un sistema di test in quest'ordine di preferenza:

  1. Test di unità - ancora una volta questi sono preferiti in quanto è possibile indirizzare questi per esercitare solo il codice su cui si sta lavorando. Tuttavia, in un sistema legacy questi spesso non sono presenti & estremamente difficili da aggiornare (per esempio in un sistema con molte chiamate esterne / che è disordinato nel modo in cui costruisce; non usando DI per esempio).
  2. Test di integrazione - Si tratta di un termine sovraccaricato in quanto può coprire qualsiasi cosa da un test di unità che passa attraverso più strati di codice (questi sono meglio evitati) per utilizzare i tipi di eccellente Verifica fino ai test del Selenio. In generale si vuole testare il sistema nel suo complesso; tuttavia essere consapevoli che questi test possono essere lenti e fragili. Utilizzando Verify puoi spesso essere un ottimo approccio per i sistemi legacy in quanto puoi almeno'verificare' non stai rompendo la superficie API del sistema.
  3. Test manuali - OGNI sviluppatore dovrebbe provare ad eseguire test manuali per qualsiasi checkin di codice. Questo è solo garantire che ciò che ci si aspetta che l'utente (questo può essere un utente effettivo o un altro componente del sistema che interagisce con il vostro codice) per vedere è quello che effettivamente vedono. Questo può essere semplice come eseguire il sistema localmente e controllare l'output di una pagina o complesso come eseguire una suite completa di test su un server di staging.
  • Skinning di cipolla Per lavorare sui sistemi legacy generalmente non avrete il lusso di una rielaborazione completa. Qui è dove uso l'approccio'skinning cipolla'.

Per consentire di aggiornare PARTS di un sistema è possibile identificare componenti che possono essere separati da un monolito esistente in un microservizio (per un certo valore di'micro'). Questo può essere un ottimo modo per iniziare a modernizzare un sistema senza il rischio di una rielaborazione completa. Esempi comuni potrebbero essere la suddivisione degli endpoint delle API in nuovi progetti che utilizzano elementi più aggiornati. Il terrore di DRY può tuttavia entrare in gioco qui. Una soluzione mal strutturata ha spesso un sacco di componenti 'helper' o'service' che dovrebbero essere davvero in progetti diversi (o anche in pacchetti Nuget per un riutilizzo più globale).

Coprirò ulteriormente questo approccio in un futuro articolo in quanto è un elemento chiave di come lavoro sui sistemi legacy e non ovvio per molti sviluppatori.

Essere pagati

Ora che abbiamo tutto questo fuori dalla strada arriva il spinoso problema dei pagamenti. Ho una regola generale:

Qualsiasi problema di pagamento sto cercando di andare avanti

Se sono in ritardo di pagamento; dipende dal vostro contratto, ma a SENZA entro 30 giorni, ma generalmente più vicino a 7; questo è un cattivo segno. Se ci sono cinguettii sopra la fattura (nickle-and-dimeing) pensare se la società è in grado di pagare per i vostri servizi su base continuativa.

Non scherzare con questo; se hai fatto il tuo lavoro è necessario essere pagati in modo tempestivo. Non importa quanto ti piace; se un cattivo cliente PUO' sfruttarti lo faranno. Non ne vale mai la pena.

Siate onesti; caricate solo per il tempo effettivamente lavorato; assicuratevi che sia chiaro quello che avete fatto e perché l'avete fatto.

Ho guidato team di sviluppatori e sono stato uno sviluppatore; sono stato un appaltatore e sono stato un cliente. Ho visto tutti i lati di tutto questo e posso dirtelo; se non vieni pagato in tempo allora devi andare avanti. Sul flip-side se si dispone di un appaltatore (o un ETP) che non sta consegnando, allora è necessario affrontare questo rapidamente. Ognuno ha lotte personali, ma soprattutto in un ambiente contrattuale è necessario fornire valore o non caricare per il tempo quando non lo siete.

Per quanto riguarda il tasso; troverete il vostro livello; personalmente faccio pagare di più per i progetti in cui ho più responsabilità (o che non sembrano divertenti). Faccio pagare meno per i progetti dove sto imparando una nuova tecnologia o per le startup. Ho anche lavorato meno giorni, ma ho mantenuto il mio tasso costante. Ma non accettare un basso tasso di palla; sei un professionista e si dovrebbe essere pagati come tale.

In conclusione

Beh, e' cosi'. Oggi sono fuori dal lavoro e domani per il funerale di mia nonna e francamente ho un po' di panico che ho un enorme sistema di legacy da imparare, quindi ho pensato di vomitare i miei pensieri su come lavorare su questi sistemi è come & le sfide.

logo

©2024 Scott Galloway