mani che digitano sulla tastiera di un computer portatile

Facciamo pulizia: come organizzare e mantenere repository Git

Curare il proprio ambiente di sviluppo affinché sia il più pulito e chiaro possibile è fondamentale per chi lavora con il codice per rendere fluide e veloci tante delle operazioni che ogni giorno è chiamato a svolgere. Questo è ancora più vero, per chi come noi, ogni giorno deve lavorare su progetti diversi, con i rispettivi repository di codice, e deve gestire importanti flussi di produzione e rilascio codice. Partire da un ambiente pulito fa la differenza.

Per questo, investiamo molto tempo ed energie per capire dove possiamo migliorare, facciamo esplorazioni e sperimentazioni che, se funzionano, potrebbero anche diventare nostre best practice. Damiano - sviluppatore backend e devops del team Nephila - ha recentemente approfondito e sperimentato modalità e strumenti alternativi utili ad una corretta manutenzione del proprio ambiente di sviluppo, e ce ne parla in questo blog post.

Anche se al momento non sono state introdotte come best practice aziendali, le esplorazioni e sperimentazioni di Damiano sono parte del processo iterativo con cui miglioriamo i nostri flussi, pratiche, e processi, e sono molto interessanti perché aprono un dibattito sul tema della cura e della manutenzione in particolare dei repository Git. Prima di addentrarci nei dettagli però, una piccola premessa sugli strumenti che abbiamo scelto di utilizzare in azienda: Git e Gitlab.

Attrezziamoci con Git e Gitlab

Per fortuna, è la stessa tecnologia che ci aiuta nella gestione del codice che scriviamo tutti i giorni, mettendo a disposizione strumenti come Git: un software open source che consente di tenere traccia del proprio lavoro, di versionare il codice (scrivere, modificare, annullare modifiche, ecc), di avere uno storico del progetto e collaborare facilmente con le persone che lavorano allo stesso progetto. Uno strumento indispensabile, per chi si occupa di sviluppo software come noi!

Per altri strumenti di versioning, leggi anche: Le migliori estensioni di Visual Studio Code

Nello specifico, in azienda utilizziamo Git insieme a GitLab: una piattaforma web di gestione di repository Git. Con Gitlab è possibile organizzare il proprio codice in repository e sfruttare Git in tutto il suo potenziale. L’utilizzo di questi strumenti è quello che consente di svolgere le più comuni operazioni di aggiornamento del codice, come ad esempio:

  • pull: quando scarichi in locale il codice
  • push: quando carichi sul repository pubblico le tue modifiche
  • merge: quando unisci tutte le modifiche proposte al codice originale.

Ti starai chiedendo se ci sono motivi particolari che ci hanno portato a scegliere di lavorare con questi strumenti. Git è lo standard del settore e permette una grande flessibilità nel lavoro, sia individuale che di gruppo ma, oltre a questo, lo abbiamo scelto perché rispetto agli strumenti analoghi è stato pensato sin dall’inizio per permettere di lavorare in gruppo in parallelo su feature diverse. Gitlab, poi, è uno degli strumenti più diffusi per la gestione di repository git on-premise (ovvero installati sui server aziendali anziché in cloud); oltre alla gestione dei repository git, include anche la gestione di workflow di sviluppo (gestione issue e progetti), continuous integration e continuous delivery, registry docker, registry di pacchetti e via dicendo. Inoltre, permette di installare in modo integrato strumenti esterni di collaborazione (ad esempio, Mattermost che noi utilizziamo) ed è una vera e propria soluzione quasi chiavi in mano per la gestione di progetti di sviluppo. Gitlab facilita anche la collaborazione con i tanti soggetti esterni (team di clienti, partner, ecc) con cui collaboriamo e possiamo utilizzarlo come vera e propria piattaforma di aggregazione verso tutte queste realtà. Per noi, che spesso ci troviamo a lavorare in team integrati, questa funzione è essenziale.

Pulizie in salsa dev

Quante volte ti è capitato di accedere al tuo Gitlab e non sapere da che parte iniziare nello svolgimento delle tue operazioni giornaliere da sviluppatore e sviluppatrice, perché i tuoi repository non erano ben organizzati e non avevi una situazione visivamente chiara dello stato dell’arte?

È per questo che tenere ben pulito l’ambiente dove viene caricato il codice sorgente è fondamentale: innanzitutto, permette di ridurre al minimo la confusione per chi sta iniziando a lavorare ad un nuovo progetto per la prima volta. Immaginiamo, ad esempio, di avere nell’ambiente di lavoro una decina di progetti, di cui quattro di questi sono attivi ma non vengono aggiornati costantemente e finiscono verso il fondo. In questo caso, nonostante la funzione “filtro” venga in nostro soccorso, non è sempre immediato riuscire a individuare il nome del progetto giusto, soprattutto se ha un nome simile ad un altro già esistente. Ad esempio, se abbiamo “awesome-tracker” e poi “awesome-tracker-docker”, ma non abbiamo aggiornato il progetto principale (il primo) e nel frattempo altri progetti, inclusa la versione docker, sono stati aggiornati, ci ritroveremo la versione docker più in alto rispetto alla principale.

dashboard di Github Projects che mostra come sono organizzati i repository dei progetti

Questo ci conduce a parlare di un altro problema: i repository che non vengono aggiornati spesso per scarso interesse o per mole di lavoro troppo alta, diventano obsoleti e “vanno a prendere polvere” nell’istanza git. Di conseguenza, avremo un’organizzazione visivamente poco chiara dei nostri repository, e probabilmente anche problemi di memoria e storage.

Per risolvere questo problema, quello che possiamo fare è impegnarci a mantenere, pulire e organizzare in modo efficace il luogo in cui archiviamo il codice pulito e attivo. Vediamo insieme qualche idea e spunti basati sulle esplorazioni di Damiano.

Ok, da dove comincio?

L’obiettivo è quello di pulire il luogo dove conserviamo il codice su cui lavoriamo ogni giorno. Per prima cosa, dovrai darti un metodo con il quale andare ad organizzare e classificare i tuoi repository.

Tra i vari metodi, Damiano ha sperimentato l’utilizzo di una strategia in stile kanban* con le seguenti colonne:

  • attivi: vengono aggiornati continuamente, quasi giornalmente. Fanno parte del tuo lavoro e ci trascorri la maggior parte del tempo.
  • stabili: non vengono aggiornati continuamente ma saltuariamente, ad esempio ‘una volta a settimana’. Fanno parte anche loro del tuo lavoro.
  • critici: progetti che devono essere aggiornati per motivi specifici come refactoring o aggiornamenti critici, e che non puoi aggiornare con leggerezza perché potrebbero "rompersi". Il loro destino è incerto, non sai ancora se dovrai aggiornarli o abbandonarli definitivamente.
  • abbandonati/definitivamente archiviati: non vengono aggiornati da anni e anni, sono sviluppati con tecnologie ormai legacy e non più supportate, alcuni di loro non sono neanche più in produzione.

Una volta fatto questo, una buona parte del lavoro è fatta: individuare e capire quali risorse hai a disposizione è già un primo passo per rendersi conto dello stato dell'arte del proprio lavoro, e soprattutto per capire cosa possiamo fare per migliorare il nostro codice.

esempio di una tipica organizzazione dei repository in modalità kanban

Adesso che è stato definito il (nostro) flusso kanban, il passaggio immediatamente successivo sarà quello di assegnare i nostri repository ad ogni colonna. Per farlo, possiamo creare una lista di priorità per riuscire ad individuare su quali repo/progetti dobbiamo intervenire prima.

Per individuare la "priorità" di ogni progetto pensa a quanto tempo devi impiegare per il suo aggiornamento e quali cambiamenti richiede. È chiaro che un aggiornamento da django 3 a django 4 richiederà molto più tempo di un semplice aggiornamento di qualche stringa per la localizzazione. Di conseguenza, il primo sarà disposto in alto, e il secondo in basso nella colonna "progetti attivi". I progetti andranno inseriti nell’ordine in cui vengono analizzate le colonne (dagli attivi ai meno attivi).

I progetti nell’ultima colonna - “definitivamente abbandonati” - devono essere archiviati, sfruttando la funzionalità “archivia” messa a disposizione da Gitlab o da altri strumenti di versionamento del codice.

Citando Github:
“When a repository is archived, its issues, pull requests, code, labels, milestones, projects, wiki, releases, commits, tags, branches, reactions, code scanning alerts, comments and permissions become read-only. To make changes in an archived repository, you must unarchive the repository first.”

L’ideale sarebbe in ogni caso toglierli dallo spazio di lavoro “comune” a tutti e mettere ogni repo in un gruppo apposito, mettendo finalmente un punto ai progetti oramai considerati “definitivamente abbandonati”.

I progetti attivi e stabili - nelle prime due colonne - vanno bene così, possiamo lasciarli posizionati qui senza compiere nessuna azione. Sfruttando le due colonne rimanenti (critici e abbandonati), andremo a mettere nella lista di priorità tutti i repository, disponendoli dal piú urgente al meno urgente.

Una volta terminata questa operazione dobbiamo chiederci: ha davvero senso lasciare gli ultimi progetti della lista attivi? Se la risposta è affermativa, è necessario verificare che effettivamente abbiano la giusta priorità, altrimenti possiamo archiviarli.

Ora sono rimasti solo progetti sui quali potremo andare a mettere le mani: abbiamo definito cosa c’è da fare e quanto effort metterci: è il momento di rimboccarci le maniche!

*nello specifico per realizzare il flusso kanban, è stato utilizzato Github Projects, ma va benissimo qualsiasi altro strumento utile allo scopo (Trello, Notion, ecc ecc). Se vuoi saperne di più su Github Projects, ti rimandiamo alla documentazione ufficiale.

Modalità manutenzione: attivata!

Ora che abbiamo organizzato i nostri repository e dato una maggiore chiarezza al nostro ambiente di lavoro possiamo passare all’azione. Tenere un ambiente di lavoro pulito non significa solo organizzare in modo efficace e chiaro i propri repository, ma anche fare una costante manutenzione, archiviare ciò che non serve più e aggiornare il codice affinché non si creino conflitti di nessun tipo. Sui nostri repository ben organizzati ci troveremo quindi ad intervenire con operazioni di modularizzazione, refactoring e aggiornamenti di versione.

Modularizzare il codice e fare un po’ di refactoring, permette di lavorare in un repo pulito e non affaticare troppo la logica del codice per chi lo andrà a leggere. In generale, è utile tener presente che se un codice supera 100 righe, ha probabilmente bisogno di essere modularizzato: andranno così create delle funzioni intermedie che riassumono e rendono più chiara la logica alla base di quel metodo.

A tal proposito, gli IDE più comuni hanno già integrata la funzione refactor, che in questi casi può semplificarti la vita. Ti suggeriamo di approfondire nella documentazione del tuo IDE principale.

Leggi anche: Le migliori estensioni di Visual Studio Code

Semplificare il codice inoltre, oltre a rendere la lettura dello stesso più comoda e rapida, permette di ridurre la complessità generale in termini di tempo e risorse. Probabilmente quel triplo ciclo for che hai scritto potrebbe essere ridotto a un doppio ciclo for, consentendo di risparmiare tempo e memoria.

Dopo i refactoring, tra le operazioni più comuni che si compiono sul codice, ci sono gli aggiornamenti di versione. A noi capita spesso, lavorando con Django, di aggiornare i progetti all'ultima versione di Django. Per farlo, ti consigliamo l’utilizzo di Django Upgrade, seguendo questi passaggi:

  • installa pip django-upgrade nel progetto
  • lancia sul terminale `git ls-files -- '*.py' | xargs django-upgrade --target-version <nuovaversione>`

Purtroppo, non è finita qui: alcuni file dovranno essere modificati a mano. Dopo aver eseguito un aggiornamento del genere, è meglio ricontrollare tutti i file che sono stati modificati, in modo tale non solo di evidenziare i cambiamenti effettuati dallo strumento, ma anche controllare che non si sia rotto nulla.

È fondamentale avere una chiara comprensione di ciò che effettivamente modifica django-upgrade. Questo strumento può apportare cambiamenti significativi al codice, quindi capire esattamente cosa sta facendo è importantissimo. A titolo esemplificativo, ma non esaustivo, riportiamo:

  • aggiornamento della sintassi del codice per adattarla alle modifiche introdotte nelle nuove versioni di Django;
  • refactoring automatico: sostituzione di vecchi metodi o classi con quelli più recenti e consigliati;
  • funzionalità deprecate: identifica queste occorrenze nel codice e suggerisce alternative o modifiche necessarie per mantenere il progetto aggiornato.

Inoltre, è fortemente consigliato, prima di aggiornare, consultare le ottime release notes di Django: può essere estremamente utile per comprendere meglio le modifiche apportate dalle versioni più nuove.

Se il progetto è dotato di una suite di test, si può affrontare l'aggiornamento con maggiore sicurezza e serenità. I test forniscono un'ottima copertura per rilevare eventuali problemi che potrebbero emergere durante il processo di aggiornamento.

Insomma, prendersi il tempo per esaminare attentamente le modifiche, consultare le risorse disponibili e utilizzare i test come rete di sicurezza può fare la differenza tra un aggiornamento tranquillo e sereno ed uno pieno di imprevisti.

Ti segnaliamo inoltre, se per un motivo qualsiasi dovesse servirti django-upgrade prima di ogni commit, puoi specificarlo come hook nella configurazione di pre-commit.

Per saperne di più su pre-commit, leggi anche: I migliori strumenti per programmare con Python

***

Questo è tutto! Siamo arrivati alla fine di questo viaggio a tema sperimentazioni su come organizzare, manutenere e aggiornare i repository dei progetti su cui lavoriamo ogni giorno. Speriamo che questa lettura ti sia stata utile per darti l'ispirazione a migliorare l'organizzazione del tuo ambiente di sviluppo.

E tu, che ne pensi? Hai qualche suggerimento da darci o strumento da consigliare? Come sempre, ti aspettiamo nei commenti, qui sotto al blog post oppure sui nostri social.

Ci trovi su X, Facebook, Mastodon e Linkedin.

Alla prossima!