Sai perché sviluppare codice è così stressante?
Il principale motivo è la quantità di aspetti da considerare e abilità che lo sviluppatore deve avere.
Mi spiego meglio.
Non ti basta scrivere ottimo codice. Non basta “indovinare" il design e l’architettura del tuo sistema, neanche scrivere test ed avere un sistema di Continuous Integration collaudato. Ti dico di più: non basta neanche saper scrivere applicazioni prive o quasi di bug.
La manutenzione e l’evoluzione di un sistema, se non trattati e considerati adeguatamente rischiano di rovinare tutti gli sforzi che il tuo team faranno per sviluppare la migliore applicazione possibile. Non c’è niente di più stressante che mettere tanto impegno e dedizione in qualcosa e non vedere ripagati i propri sforzi.
E tu vuoi evitare che accada, giusto?
Allora ascolta ciò che ho da dirti. Il modo in cui il tuo sistema dovrà evolvere è un requisito fondamentale che deve essere valutato sin dalla fase di progettazione. Se non ne tieni conto, corri il grosso rischio di rompere ad ogni rilascio la compatibilità tra i client delle tue applicazioni e le API del tuo backend che espongono le logiche di business del tuo sistema.
Ti faccio un esempio subdolo.
Immagina di avere un prodotto accessibile agli utenti tramite una applicazione WPF e una app IOS, magari sviluppata con Xamarin. Il tuo team è stato bravo: le funzionalità di business sono state scritte una sola volta per entrambi i tipi di client e sono esposte dal tuo backend tramite API REST.
Il tuo team è pronto a rilasciare una nuova - fantastica - funzionalità che però ha richiesto una modifica ad alcune API REST che renderanno incompatibili i client attualmente in produzione. Basterà rilasciare nello stesso momento il nuovo backend, il nuovo client WPF e l’app IOS, vero? Lo sarebbe se non fosse che l'Apple Store garantisce la presa in carico dell’app entro 5 giorni lavorativi dalla richiesta di pubblicazione della nuova versione.
Cosa significa?
Non puoi prevedere quando la nuova versione dell’app IOS sarà disponibile agli utenti.
E allora quando organizzo il rilascio del software? Hai due possibilità:
- Rilasciare il nuovo backend, il nuovo client WPF e nello stesso momento richiedere l’approvazione dell’app
- Richiedere l’approvazione dell’app e aspettare di rilasciare il backend e il client WPF ad approvazione avvenuta
Indipendentemente dalla scelta che farai ci sarà un periodo di tempo in cui l’app IOS non sarà compatibile con il backend. Infatti nel primo caso sarai nella situazione in cui avrai la vecchia app incompatibile con il nuovo backend, nel secondo la nuova app sarà incompatibile con il vecchio backend.
Devi scegliere quale inevitabile disagio subirà parte dei tuoi utenti. Per chi gioca a scacchi, è come quando subisci contemporaneamente scacco al re e alla regina.
Devi sapere che esistono 4 soluzioni che permettono di risolvere tutti gli scenari di manutenzione ed evoluzione che il tuo team può trovarsi di fronte.
Uno di queste risolve proprio lo scenario che ti ho proposto nell’esempio sopra, ma prima di svelarti quale analizziamo le soluzioni uno ad uno.
Sei pronto?
Partiamo!
Soluzione 2: Verifica la compatibilità tra client e server
Sai bene che la maggior parte delle applicazioni di una azienda si collegano a delle API server che - di norma - si occupano almeno di persistere lo stato dell’applicazione e dei suoi utenti.
Se il client dell’applicazione è uno solo e di tipo web sei nel caso fortunato. Analogamente allo scenario descritto nella soluzione precedente, non devi prestare nessuna accortezza particolare. Come sai bene, Il client web (pagine html, script javascript ed asset grafici) è distribuito dal server ad ogni richiesta http e di conseguenza, ogni rilascio del server basterà per mantenere allineato il client con il server.
Ancora oggi i client web offrono una esperienza utente limitata rispetto alle applicazioni desktop specialmente se paragonata a WPF. Per questo motivo spesso si propende per un client desktop rispetto a quello web: saggia scelta, ma dal punto di vista dell’evoluzione del software, richiede particolare attenzione.
Lascia che mi spieghi meglio.
Un client desktop, al contrario di un client web deve essere installato sul pc dell’utente prima di essere lanciato. Ci sono varie soluzioni per distribuire una applicazione, ma di questo argomento parleremo approfonditamente in un articolo dedicato.
Una volta che l’applicazione è installata sul pc potrà essere eseguita senza necessità di installarla nuovamente. Questo è il grande pregio ma allo stesso tempo il grande difetto di un client desktop. Senza nessun meccanismo di controllo di versione tra client e server, il client non può sapere quando il client stesso non è più compatibile a causa di un aggiornamento server.
Come ovviare a questo problema?
La soluzione più semplice è marcare ogni release del client con un numero di versione (pratica che dovresti già fare per gestire il ciclo di vita del tuo software). All’avvio della applicazione, il client deve chiamare un endpoint del server specificando la propria versione. L’endpoint del server risponde se il numero di versione specificato è compatibile con le proprie API.
Grazie a questo piccolo accorgimento, in caso di risposta positiva da parte del server il client può proseguire senza il rischio di anomalie, diversamente dovrà comunicare all’utente che è a disposizione una nuova versione del software.
Soluzione 3: Aggiorna automaticamente i client
Seppure molto pratica e semplice da implementare, la verifica della compatibilità tra client e server non è una soluzione ottimale dal punto di vista dell’esperienza d’uso poiché l’utente deve interrompere il proprio flusso di lavoro per scaricare la nuova versione dell’applicazione.
La soluzione migliore al problema della compatibilità tra client e il server è senza dubbio rendere l’applicazione in grado di auto aggiornarsi: all’avvio dell’applicazione il client verifica la propria versione e se questa non è l’ultima disponibile la scarica e la installa; tutto senza nessun intervento dell'utente.
A differenza di qualche anno fa dove introdurre un meccanismo di questo tipo era complesso e dispendioso, oggi esistono librerie e tool che facilitano e quasi azzerano il lavoro. Se lo scenario in cui ti trovi è quello di una applicazione desktop che dipende da servizi API, ti consiglio di prendere in considerazione questa soluzione.
Non sai quale tool di pacchettizzazione e aggiornamento delle applicazioni desktop scegliere? Noi sono anni che usiamo con successo e soddisfazione Squirrel. Squirrel è un tool open source, gratuito ed è basato su NuGet: con questa scelta sarai facilitato perché molte delle conoscenze che ti servono per creare i pacchetti sono le stesse che già possiedi.
Soluzione 4: server multi versione e rolling update
Le tre soluzioni che ti ho proposto fino ad ora bastano per gestire l’80% delle casistiche che tu ed il tuo team vi troverete ad affrontare.
ALT!
So bene che aspetti ancora una risposta. Riprendiamo l’esempio iniziale dell’applicazione IOS collegato a delle API di backend. È arrivato il momento di darti una soluzione per risolvere questo scenario.
Se non puoi controllare quando la tua applicazione sarà disponibile agli utenti, l’unica soluzione è sviluppare il server in modo tale da gestire più versioni allo stesso momento.
So cosa stai pensando.
Ma il server deve gestire tutte le possibili versioni del client?
Devo duplicare il codice del server per ogni versione?
Voglio rassicurarti: per entrambe le domande, la risposta è no. Andiamo con ordine.
Se hai necessità di un server multi versione, ti basta considerare solo le versioni che decidi di supportare: come minimo la versione corrente e quella nuova. In questo modo, quando rilascerai una nuova release del server e richiederai l’approvazione dell’applicazione IOS, gli utenti potranno utilizzare la vecchia versione dell’app fino alla pubblicazione della nuova.
Come implemento le API?
Quello che devi fare per supportare più versioni è implementare il codice come se supportassi solo l’ultima versione di ogni endpoint e poi implementare un trasformatore dei parametri dell’endpoint dalla versione X, all’ultima versione.
Ovviamente ci sono dei dettagli e delle sottigliezze da considerare, ma se segui alcune semplici regole per definire la struttura dei messaggi delle API, la gestione di più versioni risulta molto più semplice limitando il numero di i casi particolari da gestire.
Ti dico di più: esistono dei framework si serializzazione come protobuf di Google o Avro che effettuano questa trasformazione (up versioning o down versioning) per te.
Il mio consiglio è: se non sei in uno scenario in cui hai realmente bisogno di supportare più versioni, non farlo. Se non hai una reale esigenza, rischi di complicare il sistema; inoltre implementare questa tipologia di soluzione non è banale.
In definitiva: quali sono gli scenari in cui è utile implementare un server multi versione?
Abbiamo già parlato del caso di client mobile.
Un altro scenario in cui hai la necessità di gestire più versioni è rappresentato da una architettura a microservizi. Pensaci: i microservizi sono servizi mono funzionalità che evolvono in modo autonomo e indipendente dagli altri servizi del tuo sistema. Se evolvono in modo indipendente, è evidente che devono supportare più versioni contemporaneamente.
Ultimo aspetto: la gestione di più versioni contemporaneamente apre le porte ad interessanti scenari di test se affiancata ad un altra pratica chiamata “rolling update”.
Ti faccio un esempio per spiegarti di cosa si tratta.
Hai presente quando sulla app Instagram o Facebook del tuo telefono appare dal nulla una funzionalità che hai solo tu ed altri tuoi colleghi non hanno (ancora)? Ecco, questo è il risultato di un rolling update: un update parziale della applicazione destinato solo ad una parte dei tuoi utenti.
Se la release viene approvata, l’update viene esteso a tutta la popolazione degli utenti.
La tecnica dei rolling update hanno tre grossi vantaggi:
- Permette di rilasciare il software senza dare discontinuità del servizio agli utenti
- Permette di far valutare una funzionalità applicativa ad una piccola parte di utenti e nel caso piaccia di estenderla al resto degli utenti
- Permette di spostare parte dei test di qualità di una release su una parte degli utenti così da diminuire i costi di Quality Assurance
Per farti un esempio StackOverflow utilizza il rolling update proprio per far testare le nuove feature a parte degli utenti. Pensa che grazie a questa pratica riescono a rilasciare in produzione nuove versioni più volte al giorno!
Con tutti questi vantaggi devo supportare i rolling update anche io?
Devi sapere che questa pratica ha grossi vantaggi ma è molto onerosa da implementare ed ha grossi impatti sull’infrastruttura, sul modo di sviluppare software e sulla governance del tuo team.
È essenziale valutare bene i pro e i contro di questa scelta.
Per concludere
Come ti ho raccontato in questo articolo, saper scrivere del buon codice è solo una parte delle skill necessarie per poter sviluppare delle applicazioni funzionanti, mantenibili e che non metta di continuo sotto stress il tuo team.
Noi di Sviluppatore Migliore lo sappiamo bene, ed è per questo che abbiamo creato il primo corso di WPF in Italia che oltre ad insegnarti come sviluppare applicazioni mantenibili e senza bug, ti insegna a gestire tutti i problemi non strettamente relativi al codice ma che posso avere un grosso impatto sul risultato finale.
Affrettati perché blocchiamo solo pochi giorni ogni anno per aziende selezionate, perché prima di tutto siamo sviluppatori che lavorano su progetti reali.