[01] Introduzione
Informazioni logistiche
- Non ci sarà lo streaming però ci sono le videolezioni
- Teoria
- Lun 14:30-17:00 Aula 403
- Mer 14:30-17:00 Aula 403
- Laboratorio
- Gio 13:30-17:30 due turni equivalenti
- Turno A matricole pari
- Turno B matricole dispari
- Due persone per computer, a coppia
- Gio 13:30-17:30 due turni equivalenti
- Non c’è libro di testo, ma consigliati:
- Software Engineering (Carlo Ghezzi, Dino Mandridi)
- Design Patterns (Eric Freeman, Elisabeth Robison)
- Handbook of Software and Systems Engineering (Albert Endres, Dieter Rombath)
Esami
-
Laboratorio
- prova pratica di laboratorio di 4 ore
- OPPURE per chi segue tutti i laboratori ci saranno due laboratori valutati A COPPIE
-
Teoria
- prova orale
- Prima di fare l’orale bisogna fare il laboratorio
- La prova di laboratorio vale all’infinito
Introduzione
Storia
Con la diffusione dei primi computer in ambito accademico, negli anni ‘50 e ‘60 si è subito colta la necessità di superare metodi di produzione “artigianale” del software: sebbene il cliente e il programmatore coincidessero e i programmi fossero prettamente matematici, si iniziavano già a vedere i primi problemi. Negli anni ‘70, si inizia dunque a pensare a dei metodi, dei processi e a degli strumenti che potessero migliorare e “assicurare” la qualità del software, sviluppando un approccio di tipo ingegneristico costituito da una serie di fasi.
Approccio ingegneristico
- Target: ci si prefigge un obiettivo da raggiungere.
- Metric: si definisce una metrica per misurare la qualità del software, ovvero quanto esso si avvicina al target prefissato.
- Method, Process, Tool: si provano una serie di metodi, processi e strumenti per avvicinarsi all’obiettivo.
-
Measurements: si misura tramite la metrica stabilita se le strategie implementate sono state utili e quanto ci hanno avvicinato (o allontanato) all’obiettivo. A seconda dei risultati ottenuti vi sono due possibili strade:
- risultati soddisfacenti (aumento delle metrica) - accettiamo come buoni metodi e processi utilizzati.
- risultati insoddisfacenti (diminuzione della metrica)- ci sono dei peggioramenti o dei forti effetti collaterali, di conseguenza bisogna modificare il lavoro qualcosa: si possono cambiare target o metrica se ci si rende conto di non aver ben definito l’obiettivo, ma più comunemente bisogna rivedere i processi e metodi usati.
Ma che cosa si intede per target? Gli obiettivi da raggiungere possono essere di due tipi: la risoluzione dei problemi nella progettazione del software e l’assicurazione di una qualche qualità che il software dovrà avere. È dunque necessario chiedersi le seguenti domande:
- Quali problemi ci sono?
- Quali qualità deve avere il software?
Problemi principali
Vediamo allora a questo punto alcuni dei problemi che possono insorgere durante lo sviluppo di software, partendo dal presupposto che una delle più grandi fonti di problemi sono le persone. L’obiettivo della disciplina è infatti principalmente quello di risolvere i problemi di comunicazione, che possono essere:
- tra il programmatore e il cliente: sono esperti di domini diversi ed è difficile comprendersi;
- tra un programmatore e altri programmatori.
Un’altra fonte di problemi sono le dimensioni del software, che possono raggiungere valori molto elevati in termini di milioni di righe di codice e migliaia di “anni uomo” di lavoro. Lo sviluppo software non è più piccolo e domestico, e questo crea chiaramente problemi di manutenzione e gestione della codebase.
Il software è infine facilmente malleabile, ovvero modificabile nel tempo: il moltiplicarsi di versioni, evoluzioni e variazioni di target può creare non poche difficoltà.
Qualità
Per fare fronte ai problemi sopracitati si sviluppano allora una serie di processi per lo sviluppo software: essi non assicurano la bontà del programma finito, ma possono assicurare la presenza di proprietà desiderabili del prodotto, dette qualità. Le qualità del prodotto, che costituiscono a conti fatti un “valore per le persone”, si dividono innanzitutto in due tipi:
- qualità esterne: qualità che vengono colte dal cliente;
- qualità interne: qualità che vengono esclusivamente colte dallo sviluppatore.
Le qualità interne influenzano molto le qualità esterne (per esempio se ho un codice ottimizzato ed efficiente, il mio software produrrà i risultati più velocemente). Prima di vedere quali siano le proprietà auspicabili in un software, però, facciamo un’importante distinzione a livello terminologico tra requisiti e specifiche:
- I requisiti sono quello che il cliente vuole che il software faccia. Spesso sono cambiati in corso d’opera oppure sono espressi in modo sbagliato, per cui è necessaria un’interazione continua.
- Le specifiche sono ciò che è stato formalizzato dal programmatore a partire dai requisiti: si tratta di una definizione più rigorosa di che cosa dovrà fare il software. Si noti però che se i requisiti sono stati espressi in modo non corretto anche le specifiche risulteranno inesatte (vd. G1).
Fatta questa doverosa distinzione, vediamo quali sono le qualità che un software dovrebbe idealmente possedere.
Qualità del software
Un software di qualità deve funzionare, essere bello e “farmi diventare ricco”.
Un software deve... | Qualità | Descrizione |
---|---|---|
Funzionare | Correttezza | Un software è corretto se soddisfa la specifica dei suoi requisiti funzionali. Si tratta di una proprietà "matematica" e relativamente dimostrabile in modo formale. |
Affidabilità | Un software è affidabile quando ci si può fidare del suo funzionamento, ovvero ci si può aspettare che faccia ciò che gli è stato chiesto. Se è molto difficile perseverare la correttezza, in quanto si tratta una proprietà assoluta, l'affidabilità è invece relativa: un software può essere affidabile (o dependable) nonostante contenga qualche errore. | |
Robustezza | Un software è robusto se si comporta in modo accettabile anche in circostanze non previste nella specifica dei requisiti, senza generare effetti troppo negativi. | |
Essere bello | Usabilità | Un software è usabile (o user-friendly) se i suoi utenti lo ritengono facile da utilizzare. Si possono fare degli esperimenti (le grandi aziende lo fanno) per testare e quantificare l’usabilità del software ponendolo di fronte a dei soggetti umani (vd. NN23). |
Prestazioni | Ad ogni software è richiesto un certo livello di prestazioni. L'efficienza è una qualità interna e misura come il software utilizza le risorse del computer; la performance, d'altro canto, è invece una qualità esterna ed è basata sui requisiti dell'utente. Essa ha effetto sull'usabilità, e spesso viene considerata alla fine dello sviluppo software visto che vari avanzamenti tecnologici possono efficientare algoritmi e processi prima troppo costosi. | |
Verificabilità | Un software è verificabile se le sue proprietà sono verificabili facilmente: è importante essere in grado di poter dimostrare la correttezza e la performance di un programma, e in tal senso la leggibilità del codice è fondamentale. La verifica può essere fatta con metodi formali o informali, come il testing. È considerata una qualità interna, ma alcune volte può diventare una qualità esterna: per esempio, in ambiti in cui la sicurezza è critica il cliente può chiedere la verificabilità di certe proprietà. | |
Farmi diventare ricco | Riusabilità | Le componenti del software che costruiamo dovrebbero essere il più riutilizzabili possibile così da risparmiare tempo in futuro: ciò può essere fatto non legandole troppo allo specifico contesto applicativo del software che stiamo sviluppando. Con la proprietà di riusabilità, utilizziamo un prodotto per costruire - anche con modifiche minori - un altro prodotto (vd. MI15). |
Manutenibilità |
Per manutenzione software si intendono tutte le modifiche apportate al software dopo il rilascio iniziale.
Questa proprietà può essere vista come due proprietà separate:
|
Leggi rilevanti
La mancanza di requisiti è la prima causa del fallimento di un progetto.
Legge di Nielsen-Norman (NN23).
L’usabilità è misurabile.
Riutilizzare il software permette di incrementare la produttività e la qualità.
Un sistema che viene utilizzato cambierà.
Un sistema che evolve incrementa la sua complessita a meno che non si lavori appositamente per ridurla.
Qualità del processo
Un progetto è di qualità se segue un buon processo.
Sappiamo che il prodotto è influenzato dal processo che viene utilizzato per svilupparlo, di conseguenza possiamo parlare anche di qualità del processo.
Anche un processo deve funzionare, essere essere bello e farmi diventare ricco, ma dobbiamo interpretare queste parole in maniera differente.
Quali caretteristiche ha un processo di qualità?
Un processo deve... | Qualità | Descrizione |
---|---|---|
Funzionare | Robustezza | Un processo deve poter resistere agli imprevisti, come la mancanza improvvisa di personale o il cambiamento delle specifiche. Esistono certificazioni (CMM: Capability Maturity Model) che valutano la robustezza di alcuni processi aziendali e che vengono per esempio considerate nei bandi pubblici. |
Essere bello | Produttività | La produttività di un team è molto meno della somma della produttività individuale dei suoi componenti. È una metrica difficile da misurare: conteggi come il numero di linee codice scritte o la quantità di tempo-uomo richiesta per un lavoro si rivelano spesso un po’ fallaci (per esempio, la gravidanza umana non è un’attività parallelizzabile, ma si potrebbe dire che servono 9 mesi-donna per creare un bambino). |
Farmi diventare ricco | Tempismo | Un processo deve consegnare il prodotto nei tempi stabiliti, in modo da rispettare i tempi del mercato. È spesso conveniente la tecnica dello sviluppo incrementale, ovvero la consegna frequente di parti sempre più grandi del prodotto (es. compilatore ADA): essa permette infatti di conquistare il cliente ancor prima di avere il prodotto finito. |
Il processo di produzione del software
Il processo che seguiamo per costruire, consegnare, installare ed evolvere il prodotto software, dall’idea fino alla consegna e al ritiro finale del sistema, è chiamato processo di produzione software.
Innanzitutto occorre riconoscere diverse problematiche.
- I requisiti imposti dal cliente possono cambiare spesso.
- Produrre software non è solo scrivere codice (alla Programmazione I).
- Bisogna risolvere i problemi di comunicazione tra tutte le diverse figure in gioco (tra sviluppatori, tra progettista e sviluppatori, ecc).
- Bisogna essere rigorosi, anche se può essere difficile. Ci sono lati positivi e negativi: la rigorisità può facilitare la comprensione di ciò che bisogna fare ma implica al contempo molta fatica extra, e viceversa.
Ipotesi di Bauer-Zemanek (BZh3): Metodi formali riducono in maniera significativa gli errori di progettazione, oppure permettono di eliminarli e risolverli prima.
Trovare gli errori prima della fase di sviluppo permette di facilitarne la risoluzione e di risparmiare sia tempo che soldi: tanto prima si individua un errore, tanto più facile sarà risolverlo.
- Ci sono tanti aspetti da considerare, che andranno affrontati uno alla volta. Per parlare di aspetti diversi ho bisogno di metodi comunicazione diversi, che interessano ruoli diversi in tempi diversi (Aspect Oriented Programming).
Tenendo a mente tutto queste problematiche è necessario decidere come organizzare l’attività di sviluppo software in modo da mitgarle. Per modellare un ciclo di vita del software, occorre dunque in primo luogo identificare le varie attività necessarie e quindi:
- deciderne le precedenze temporali;
- decidere chi le debba fare.
In particolare, ci si pone due domande:
- cosa devo fare adesso?
- fino a quando e come?
L’ingegneria del software prova a rispondere a queste domande per individuare quali siano le fasi necessarie per sviluppare un software e quale sia la loro migliore disposizione temporale. È dunque bene specificare da subito che lo sviluppo di un programma non è solo coding: tale presupposto genera conseguenze disastrose.
Inizialmente, infatti, nell’ambito dello sviluppo software è stato adottato il modello code-and-fix, che consisteva nei seguenti passi:
- scrivi il codice;
- sistemalo per eliminare errori, migliorare funzionalità o aggiungere nuove funzionalità.
Ben presto però questo modello si è dimostrato pesantemente inefficace in gruppi di lavoro complessi, specialmente quando il cliente non era più lo sviluppatore stesso ma utenti con poca dimestichezza con i computer, generando codice estremamente poco leggibile e manutenibile.
Per organizzare meglio l’attività di sviluppo e non ricadere negli errori del passato gli ingegneri del software hanno dunque individuato diverse fasi del ciclo di vita di un software che, combinate, permettessero di produrre del software di qualità. Diamo dunque un’occhiata a quelle principali.
Le fasi del ciclo di vita del software
Studio di fattibilità
Lo studio di fattibilità è l’attività svolta prima che il processo di sviluppo inizi, per decidere se dovrebbe iniziare in toto. L’obiettivo è quello di produrre un documento in linguaggio naturale presentante diversi scenari di sviluppo con soluzioni alternative, con una discussione sui trade-off in termini di benefici e costi attesi.
Più specificatamente, il documento include:
- uno studio di diversi scenari di realizzazione, scegliendo:
- le architetture e l’hardware necessario;
- se sviluppare in proprio oppure subappaltare ad altri.
- stima dei costi, tempi di sviluppo, risorse necessarie e benfici delle varie soluzioni.
È spesso difficile fare un’analisi approfondita, a causa del poco tempo disponibile o di costi troppo elevati: spesso viene commissionata esternamente.
Analisi e specifica dei requisiti
L’analisi e specifica dei requisiti è l’attività più critica e fondamentale del processo di produzione del software. L’obiettivo è la stesura di un documento di specifica .
In questa fase i progettisti devono:
- comprendere il dominio applicativo del prodotto, dialogando con il cliente e la controparte tecnica;
- identificare gli stakeholders, ovvero tutte le figure interessate al progetto, e studiarne le richieste. Spesso non si tratta di figure omogenee (può essere il top manager fino al segretario) e le loro necessità sono molto diverse;
- capire quali sono le funzionalità richieste: la domanda più importante che deve porsi il programmatore è il cosa non il come; al cliente non devono infatti interessare gli aspetti tecnici e le scelte architetturali interne. Le specifiche vanno quindi viste dal punto di vista del cliente.
- stabilire un dizionario comune tra cliente e sviluppatore che può anche far parte della specifica per agevolare la comunicazione;
- definire altre qualità eventualmente richieste dal cliente: per esempio, “la centrale non deve esplodere” non è un dettaglio implementativo, ma un requisito. Queste ulteriori qualità, che non sempre sono solo esterne, si dicono requisiti non funzionali.
Lo scopo del documento di specifica è duplice: da una parte, deve essere analizzato e approvato da tutti gli stakeholders in modo da verificare il soddisfacimento delle aspettative del cliente, e dall’altra è usato dai programmatori per sviluppare una soluzione che le soddisfi, fungendo da punto di partenza per il design. È un documento contrattuale e deve essere scritto in modo formale per evitare contestazioni contrattuali e ambiguità.
Deve essere presente anche un piano di test, ovvero una collezione di collaudi che certificano la correttezza del lavoro: se questi test hanno esito positivo il lavoro viene pagato, altrimenti il progetto non viene accettato. A differenza dei lavori di altri tipi di ingegneria, per esempio l’ingegneria civile, dove il collaudo è diretto, nell’ingegneria del software è molto difficile collaudare tutti i casi e gli stati possibili.
Un altro output di questa fase può essere anche il manuale utente, ovvero la “vista esterna” (ciò che il cliente vuole vedere, evitando i dettagli implementativi) del sistema da progettare.
Legge di David: Il valore dei modelli che rappresentano il software da diversi punti di vista dipendono dal punto di vista preso (assunto), ma non c’è nessuna vista che è la migliore per ogni scopo.
Progettazione (design)
Il design è l’attività attraverso la quale gli sviluppatori software strutturano l’applicazione a diversi livelli di dettaglio. Lo scopo di questa fase è quello di scrivere un documento di specifica di progetto contenente la descrizione dell’architettura software (i diversi linguaggi e viste).
Durante questa fase occorre quindi:
- scegliere un’architettura software di riferimento;
- scomporre in moduli o oggetti gli incarichi e i ruoli: si tratta del cosiddetto object oriented design, non necessariamente accompagnato da object oriented programming;
- identificare i patterns, ovvero problemi comuni a cui è già stata trovata una soluzione generale giudicata come “bella” dalla comunità degli sviluppatori (ne vedremo un paio più avanti nel corso). I pattern favoriscono alcune qualità, come il design.
Programmazione e test di unità
In questa fase le “scatole nere” - i moduli o oggetti definiti al punto precedente - vengono realizzate e per ognuna di esse vengono definiti dei test unitari che ne mostrano la correttezza. Vi è infatti spesso la brutta abitudine di non fare il testing durante lo sviluppo di ciascun componente, ma solamente alla fine di tutto: questa usanza è molto pericolosa perché un problema scoperto solo alla fine è molto più oneroso da risolvere.
I singoli moduli vengono testati indipendentemente, anche se alcune funzioni da cui dipendono non sono ancora sono state implementate: per risolvere tale dipendenza si utilizzano allora moduli fittizzi (stub) che emulino le funzionalità di quelli mancanti. Altri moduli, detti driver, forniscono invece una situazione su cui applicare il modulo che sto testando. Nei linguaggi più utilizzati esistono framework che facilitano le suddette operazioni al programmatore.
L’obiettivo di questa fase è avere un insieme di moduli separati sviluppati indipendentemente con un’interfaccia concordata e singolarmente verificati.
Integrazione e test di sistema
In questa fase i moduli singolarmente implementati e testati vengono integrati insieme a formare il software finito. In alcuni modelli di sviluppo (come nello sviluppo incrementale) questa fase viene accorpata alla precedente.
Nei test, i moduli stub e driver vengono sostituiti con i componenti reali formando un sistema sempre più grande fino ad ottenere il risultato richiesto. È poi fondamentale testare che l’intero programma funzioni una volta assemblato (non basta che le singole parti funzionino!): test di questo tipo vengono detti test di integrazione.
L’integrazione può essere adottata seguendo un approccio top down o bottom up. La fase finale è l’alpha testing, ovvero il testing del sistema in condizioni realistiche.
Consegna, installazione e manutenzione
Dopo aver completato lo sviluppo, il software deve essere consegnato ai clienti. Prima di consegnarlo a tutti, si seleziona un gruppo di utenti per raccogliere ulteriore feedback; questa fase è chiamata beta testing.
L’installazione (deployment) definisce il run-time fisico dell’architettura del sistema. Per esempio, un servizio di rete potrebbe necessitare di apparecchiatura server da installare e particolari configurazioni.
Infine, la manutenzione può essere definita come l’insieme delle attività finalizzate a modificare il sistema dopo essere stato consegnato al cliente. La manutenzione può essere di tipo:
- correttivo: sistemare errori nel sistema;
- adattivo: adattare il software ai nuovi requisiti (vd. evolvibilità);
- perfettivo: migliorare certi aspetti interni al programma senza modificare gli aspetti esterni. Serve per migliorare la futura manutenzione riducendo il cosiddetto debito tecnico.
Come già detto, è necessario sviluppare avendo in mente la futura manutenzione di ciò che si sta scrivendo: infatti, il costo della manutenzione concorre al costo del software in una misura spesso superiore al 60%.
L’output di questa fase è un prodotto migliore.
Altre attività
Alle attività sopracitate se ne aggiungono altre:
- Documentazione: può essere vista come attività trasversale. Per esempio, un documento di specificazione contenente diagrammi UML e una descrizione narrativa che spiega le motivazione dietro certe decisioni può essere il risultato principale della fase di progettazione. È un’attività spesso da procastinare, perché le specifiche possono cambiare spesso. In alcuni gruppi esistono delle figure che si occupano di questa attività, anche se può essere pericoloso: non tutti possono capire ciò che un programmatore ha creato.
- Verifica e controllo qualità (Quality Assurance): nella maggior parte dei casi, la verifica è svolta attraverso review e ispezioni. L’obiettivo è anticipare il prima possibile la scoperta e la sistemazione degli errori in modo da evitare di consegnare sistemi difettosi. Andrebbe fatta costantemente e non tutta alla fine.
- Gestione del processo: gestione incentivi (premi di produzione), responsabilità, formazione del personale, perfezionamento del processo con l’esperienza guadagnata, eccetera.
- Gestione delle configurazioni: gestione delle relazioni inter-progettuali, ovvero delle risorse di sviluppo non appartenenti ad un singolo progetto. Un esempio potrebbe essere una libreria condivisa tra più progetti, i quali vorrebbero modificare la libreria stessa.
Tutte queste diverse attività saranno specificate successivamente entrando nel dettaglio.