1  Il linguaggio di programmazione e il software

Partiamo da alcuni concetti basilari che ci permettono di contestualizzare più facilmente quelli che introdurremo via via nel corso.

1.1 Definizioni

La programmazione è il processo di ideazione e scrittura di istruzioni, nella forma statica, ad esempio un file di testo, identificate come codice sorgente, che un computer può ricevere per eseguire compiti predefiniti. Queste istruzioni sono codificate in un linguaggio di programmazione, che traduce gli algoritmi e le finalità del programmatore, in un formato comprensibile ed eseguibile dal computer.

Un programma informatico è una sequenza di istruzioni scritte per eseguire una specifica operazione o un insieme di operazioni su un computer. Queste istruzioni sono codificate in uno dei linguaggi che il computer specifico può comprendere e utilizzare per eseguire attività come calcoli, manipolazione di dati, controllo di dispositivi e interazione con l’utente. Pensate a un programma come a una ricetta di cucina. La ricetta elenca gli ingredienti necessari (dati) e fornisce istruzioni passo-passo (algoritmo) per preparare un piatto. Allo stesso modo, un programma informatico specifica i dati da usare e le istruzioni da seguire per ottenere un risultato desiderato.

Un linguaggio di programmazione è un linguaggio formale che fornisce un insieme di regole e sintassi per scrivere programmi informatici. Questi linguaggi permettono ai programmatori di comunicare con i computer e di creare software da distribuire al difuori di quello usato per la sua creazione.

Alcuni esempi di linguaggi di programmazione includono Python, Java, C++, SQL, Rust, Haskell, Prolog, C, Assembly, Fortran, JavaScript e altre centinaia (o forse migliaia). Ad esempio Wikipedia ne ha una lista estesa (Wikipedia contrib. 2024a).

WIKIPEDIA CONTRIB., 2024a. List of programming languages [online]. 2024. Disponibile all'indirizzo : https://en.wikipedia.org/wiki/List_of_programming_languages

1.2 Il linguaggio naturale e di programmazione

I linguaggi di programmazione hanno dei punti in comune e delle differenze coi linguaggi naturali (come l’italiano o l’inglese). Quest’ultime sono principalmente:

  1. Precisione e rigidità: I linguaggi di programmazione sono estremamente precisi e rigidi. Ogni istruzione deve essere scritta in un modo specifico affinché il computer possa comprenderla ed eseguirla correttamente. Anche un piccolo errore di sintassi può impedire il funzionamento di un programma.

  2. Ambiguità: I linguaggi naturali sono spesso ambigui e aperti a interpretazioni. Le stesse parole possono avere significati diversi a seconda del contesto. I linguaggi di programmazione, invece, sono progettati per essere privi di ambiguità; ogni istruzione ha un significato preciso e univoco.

  3. Vocabolario limitato: I linguaggi naturali hanno un vocabolario vastissimo e in continua espansione. I linguaggi di programmazione, al contrario, hanno un vocabolario limitato costituito da parole chiave e comandi definiti dal linguaggio stesso.

  4. Forma di mediazione: I linguaggi naturali sono direttamente utilizzati per comunicare, quelli di programmazione non sono comprensibili immediatamente dai computer, ma devono essere tradotti in una forma opportuna per mezzo di programmi ad hoc.

1.3 L’algoritmo

Un algoritmo è

Un insieme di regole che definiscono con precisione una sequenza di operazioni” (Stone 1971).

STONE, Harold S., 1971. Introduction to Computer Organization and Data Structures. USA : https://dl.acm.org/doi/10.5555/578826; McGraw-Hill, Inc. ISBN 0070617260.

Tale definizione, per quanto generica, coglie i due aspetti fondanti, cioè regole, intese come prescrizioni sintattiche o semantiche, che si traducono in operazioni richieste ad un agente, umano o informatico.

In altre parole, un algoritmo è una sequenza ben definita di passi o operazioni ben codificata, che, a partire da un input, produce un output in un tempo finito e, quindi, presenta la seguente serie di caratteristiche:

  • Finitudine: L’algoritmo deve terminare dopo un numero finito di passi.

  • Determinismo: Ogni passo dell’algoritmo deve essere definito in modo preciso e non ambiguo.

  • Input: L’algoritmo riceve zero o più dati in ingresso.

  • Output: L’algoritmo produce uno o più risultati.

  • Effettività: Ogni operazione dell’algoritmo deve essere fattibile ed eseguibile in un tempo finito.

Possiamo quindi comporre una definzione di linguaggio di programmazione legata a quella di algoritmo:

Un linguaggio di programmazione è uno strumento che consente all’essere umano di esprimere razionalmente i propri obiettivi e di realizzarli come algoritmi per l’esecuzione da parte di un computer. In sostanza, un linguaggio di programmazione è composto da due elementi fondamentali: un insieme di istruzioni e le regole per organizzarle in modo coerente e funzionale.

Per un testo, introduttivo e liberamente disponibile, sugli algoritmi, si può far riferimento a (Erickson 2019).

ERICKSON, Jeff, 2019. Algorithms [online]. Independent. Disponibile all'indirizzo : https://jeffe.cs.illinois.edu/teaching/algorithms/

1.3.1 La rappresentazione

Un algoritmo è un concetto astratto, esistente come idea nella mente di chi lo crea. Quando si vuole condividere un algoritmo con qualcun altro, è cruciale scegliere una rappresentazione che lo comunichi in modo completo e accurato. Alcune rappresentazioni di algoritmi sono comprensibili solo dagli esseri umani, come quelle che utilizzano il linguaggio naturale o i diagrammi. Al contrario, un programma è una rappresentazione di un algoritmo destinata ad essere leggibile da una macchina, generalmente un computer. L’esecuzione di un programma, cioè l’applicazione di un algoritmo in un contesto particolare da parte di un computer, è detta processo.

Rappresentare un algoritmo richiede, pertanto, l’uso di un insieme di simboli e di regole di combinazione dei medesimi. I simboli fondamentali utilizzati per rappresentare gli algoritmi sono chiamati primitivi. I simboli primitivi definiscono le operazioni base, le unità minime di lavoro che un algoritmo può eseguire. Devono essere combinati seguendo una specifica sintassi, che è l’insieme delle regole che definiscono come questi simboli possono essere correttamente organizzati per formare istruzioni, cioè comandi elementari.

Quando i simboli primitivi sono assemblati secondo la sintassi corretta, il risultato è una rappresentazione significativa dell’algoritmo, ovvero la realizzazione del suo scopo astratto, pronto per essere applicato concretamente. Le istruzioni create combinando questi primitivi costituiscono il codice sorgente di un programma, che può essere eseguito da un computer per realizzare l’algoritmo desiderato.

Alcuni esempi di rappresentazione degli algoritmi includono:

  • Linguaggio naturale: Rappresentazioni verbali utilizzabili dagli esseri umani.

  • Diagrammi: Rappresentazioni visive come i diagrami di flusso.

  • Pseudocodice: Notazione semi-formale usata principalmente per la comprensione umana.

  • Codice sorgente: Rappresentazioni formali in un linguaggio di programmazione specifico.

Un esempio di algoritmo è dato dall’ordinamento di una lista di numeri interi. Uno dei più semplici algoritmi di ordinamento è il Bubble Sort (Wikipedia contrib. 2024b), che può avere le seguenti rappresentazioni:

WIKIPEDIA CONTRIB., 2024b. Bubble sort [online]. 2024. Disponibile all'indirizzo : https://en.wikipedia.org/wiki/Bubble_sort
Accessed: 2024-07-09
  • Linguaggio naturale: Gli elementi adiacenti vengono scambiati se sono nell’ordine sbagliato e ciò prosegue fino a quando tutto l’elenco risulti ordinato.

  • Diagramma di flusso1, come da figura seguente:

1 Un diagramma di flusso è una rappresentazione grafica che mostra la sequenza di passaggi o decisioni necessarie per completare un processo o risolvere un problema, utilizzando simboli standardizzati come rettangoli, ovali e frecce per illustrare l’ordine e le relazioni tra le diverse fasi.

flowchart TD
  Start --> Controlla(Sono presenti elementi da confrontare?)
  Controlla -- No --> End(Fine)
  Controlla -- Sì --> Confronta(Confronta elementi adiacenti)
  Confronta --> Scambia(Gli elementi sono nell'ordine sbagliato?)
  Scambia -- Sì --> ScambiaElementi(Scambia gli elementi)
  Scambia -- No --> ProssimoElemento(Passa al prossimo elemento)
  ScambiaElementi --> ProssimoElemento
  ProssimoElemento --> Controlla

Diagramma di flusso dell’algoritmo di Bubble Sort

  • Pseudocodice2 nella forma di una sequenza ordinata di definizioni e comandi:

    procedura BubbleSort(A: lista di elementi ordinabili)
      n := lunghezza(A)
    
      ripeti
        scambiato := falso
    
        per i := 1 a n-1 incluso esegui
          se A[i-1] > A[i] allora
            temp := A[i-1]
            A[i-1] := A[i]
            A[i] := temp
    
            scambiato := vero
    
          fine se
    
        fine per
    
        n := n - 1
    
      fino a scambiato == falso
    
    fine procedura

2 Lo pseudocodice è una descrizione informale di un algoritmo, scritta in un linguaggio simile a quello naturale, che combina elementi di linguaggi di programmazione per rendere comprensibili i passaggi logici, senza preoccuparsi della sintassi specifica di un linguaggio formalizzato.

Nel seguito sarà data anche un’ulteriore rappresentazione in codice sorgente Python. In questo esempio, abbiamo dato più rappresentazioni dell’algoritmo Bubble Sort per illustrare le diverse finalità di comunicazione tese a scopi diversi. Infatti, generalmente, si creano prima delle rappresentazioni informali allo scopo di ideazione e progettazione, per poi procedere con il programma vero e proprio, di cui le altre rappresentazioni potrebbero essere viste a mo’ di documentazione.

1.3.2 Le strategie di generazione

La generazione degli algoritmi è un’arte in sé, non esistendo una ricetta garantita per trovarne almeno uno corretto. Tuttavia, alcune strategie generali possono aumentare le probabilità di successo. La capacità di ideare algoritmi efficaci richiede creatività, pensiero analitico e una profonda comprensione del problema da risolvere.

Alcune strategie:

  • Approccio top-down: Un approccio comune alla scoperta degli algoritmi è scomporre un problema complesso in sottoproblemi più piccoli e gestibili. Questo metodo, noto come approccio top-down, prevede di iniziare con una visione globale del problema e poi dividere il problema in parti sempre più piccole fino a raggiungere sottoproblemi che possono essere risolti direttamente. Ogni sottoproblema è affrontato individualmente e le soluzioni sono poi combinate per risolvere il problema originale. Questo approccio è particolarmente utile quando si affrontano problemi complessi che possono essere suddivisi in componenti indipendenti.

  • Approccio bottom-up: Un altro metodo è l’approccio bottom-up, che prevede di partire da casi specifici o esempi concreti e di cercare di generalizzare la soluzione. Questo approccio consiste nell’identificare soluzioni per piccoli problemi specifici e poi combinare queste soluzioni per affrontare problemi più grandi. È un metodo efficace quando le soluzioni a problemi semplici possono essere adattate e combinate per risolvere problemi più complessi. L’approccio bottom-up è spesso utilizzato in combinazione con l’approccio top-down per fornire una comprensione completa del problema e della sua soluzione.

  • Induzione: L’induzione è una tecnica matematica che può essere molto utile nella determinazione di algoritmi. Consiste nel risolvere il problema per un caso base semplice e poi dimostrare che se il problema può essere risolto per un caso generico, allora può essere risolto anche per il caso successivo. Questa tecnica è particolarmente utile per i problemi ricorsivi, dove la soluzione a un problema dipende dalla soluzione di istanze più semplici dello stesso problema.

  • Contraddizione: La contraddizione è un’altra tecnica matematica che può essere utilizzata nella definizone di algoritmi. Consiste nel dimostrare che l’assunzione opposta porta a una contraddizione, e quindi l’assunzione iniziale deve essere vera. Questa tecnica è utile per provare che certi problemi non possono essere risolti o che certe proprietà devono essere vere per un algoritmo efficace.

1.3.3 La metodologia di Polya

George Polya, nel suo libro How to Solve It (Polya 1945), propone una metodologia strutturata per la risoluzione dei problemi che può essere applicata alla scoperta degli algoritmi.

POLYA, George, 1945. How to Solve It. Princeton University Press. ISBN 9780691119663.

1.3.3.1 Le fasi

Polya suggerisce quattro fasi principali:

  1. Comprendere il problema: Prima di tutto, è fondamentale avere una chiara comprensione del problema. Ciò include identificare esattamente cosa è richiesto, quali sono i dati di input e quale deve essere l’output. Una comprensione chiara aiuta a focalizzare l’attenzione sui punti cruciali del problema.

  2. Formulare piani: Una volta compreso il problema, il passo successivo è ideare uno o più piani per risolverlo. Questo può includere la suddivisione del problema in sottoproblemi, l’uso di analogie con problemi già risolti, o l’applicazione di tecniche matematiche come l’induzione. È utile pensare a diverse strategie e considerare quale possa essere la più efficace.

  3. Eseguire i piani: Dopo aver formulato un piano, bisogna metterlo in pratica. Questo implica scrivere il codice, testare le soluzioni e verificare che l’algoritmo funzioni correttamente per i casi di test previsti. Durante questa fase, è possibile che si scoprano nuovi sottoproblemi o che si debba modificare il piano originale.

  4. Valutare l’efficacia: Infine, è importante valutare l’efficacia della soluzione. Questo include verificare che l’algoritmo funzioni correttamente per tutti i casi di input, che sia efficiente in termini di tempo e risorse, e che sia possibile migliorarlo ulteriormente. La valutazione permette di rifinire l’algoritmo e di identificare eventuali punti deboli o aree di miglioramento.

Queste fasi aiutano a strutturare il processo di definizione di algoritmi in modo sistematico e metodico, aumentando le probabilità di trovare soluzioni efficaci ai problemi. La scrittura degli algoritmi, quindi, non è solo una questione di creatività, ma anche di applicazione rigorosa di tecniche e metodologie strutturate.

1.3.3.2 Esempio di applicazione

Applichiamo la metodologia di Polya per definire un algoritmo, rappresentato con codice sorgente, che ordini una lista di numeri interi in ordine crescente.

  1. Comprendere il problema: Ordinare una lista di numeri interi in ordine crescente; ad esempio [64, 34, 25, 12, 22, 11, 90] deve diventare [11, 12, 22, 25, 34, 64, 90].

  2. Formulare piani:

    • Piano 1: Confrontare ogni elemento con ogni altro elemento e scambiarli se sono nell’ordine sbagliato. Tuttavia, questo potrebbe non essere efficiente.

    • Piano 2: Confrontare elementi adiacenti e scambiarli se sono nell’ordine sbagliato, ripetendo questo processo fino a quando la lista è ordinata. Questo ci porta all’idea dell’algoritmo Bubble Sort.

  3. Eseguire i piani: Immplementiamo l’algoritmo in codice sorgente Python, eventualmente preceduto da uno pseudocodice come quello presentato sopra:

1def bubble_sort(lista):
2  n = len(lista)

3  for i in range(n):
4    scambiato = False
    
5    for j in range(0, n-i-1):
6      if lista[j] > lista[j + 1]:
7        lista[j], lista[j + 1] = lista[j + 1], lista[j]
        
8        scambiato = True
          
9    if not scambiato:
10      break

# Esempio di utilizzo
11lista = [64, 34, 25, 12, 22, 11, 90]

12print("La lista da ordinare è: ", lista)

13bubble_sort(lista)

14print("La lista ordinata è: ", lista)
1
Definizione della funzione bubble_sort.
2
Calcolo della lunghezza della lista.
3
Ciclo esterno che ripete il processo di ordinamento.
4
Inizializzazione della variabile scambiato a False.
5
Ciclo interno che percorre la lista fino alla fine non ordinata.
6
Confronto tra elementi adiacenti.
7
Scambio degli elementi se sono nell’ordine sbagliato.
8
Impostazione della variabile scambiato a True se avviene uno scambio.
9
Verifica se sono stati effettuati scambi.
10
Interruzione del ciclo se non ci sono stati scambi, la lista è ordinata.
11
Esempio di lista da ordinare.
12
Stampa della lista da ordinare.
13
Chiamata della funzione bubble_sort sulla lista.
14
Stampa della lista ordinata.

Da notare che abbiamo applicato l’algoritmo all’input così come dato nella definizione del problema, ma, in generale, più coppie input/output dovrebbero essere fornite per coprire anche casi particolari.

  1. Valutare l’efficacia:

    • L’algoritmo Bubble Sort funziona correttamente per l’elenco di esempio fornito.

    • L’algoritmo ha una complessità temporale di O(n^2), che può essere migliorata utilizzando algoritmi di ordinamento più efficienti come il Quick Sort o il Merge Sort.

    • L’algoritmo è semplice da implementare e comprendere, ma non è adatto per grandi dataset a causa della sua inefficienza.

In questo esempio, abbiamo applicato le fasi della metodologia di Polya per determinare l’algoritmo Bubble Sort per l’ordinamento di una lista di numeri interi. Questo dimostra come una struttura metodica possa aiutare a definire e implementare algoritmi efficaci in un programma in uno specifico linguaggio di programmazione.

1.4 Dal codice sorgente all’esecuzione

Per comprendere come un programma scritto in un linguaggio di programmazione passi dal file di testo contenente il codice sorgente, all’esecuzione delle istruzioni da parte del computer, è fondamentale capire che questo processo richiede l’attivazione di un altro programma, sul medesimo computer o altro, che traduca quel codice sorgente in azioni operabili dal primo computer. Quest’ultimo programma può essere un compilatore o un interprete, cioè appartenere ad una delle due macrocategorie che definiscono come il codice sorgente viene tradotto ed eseguito:

  • Un compilatore è un programma che traduce l’intero codice sorgente di un programma scritto in un linguaggio di alto livello (come C o C++) in codice macchina, che è il linguaggio comprensibile direttamente dalla CPU. Questa traduzione avviene una sola volta, generando un file eseguibile che può essere eseguito direttamente dalla CPU.

  • Un interprete, invece, è un programma che esegue il codice sorgente direttamente, istruzione per istruzione, senza produrre un file eseguibile separato. L’interprete legge una riga di codice, la traduce in codice macchina e la esegue immediatamente. Questo processo viene ripetuto per ogni riga del codice sorgente.

È importante notare che alcuni linguaggi di programmazione possono essere sia compilati che interpretati, a seconda dell’implementazione disponibile. Ad esempio, Java utilizza sia la compilazione (per generare bytecode) che l’interpretazione, e la Java Virtual Machine (JVM) spesso utilizza anche la compilazione just-in-time (JIT) per tradurre il bytecode in codice macchina nativo durante l’esecuzione3.

3 Java utilizza un approccio ibrido che coinvolge sia la compilazione che l’interpretazione. Inizialmente, il codice sorgente Java viene compilato in bytecode da un compilatore Java. Questo bytecode è un codice intermedio indipendente dalla piattaforma che può essere eseguito su qualsiasi sistema dotato di una Java Virtual Machine (JVM). La JVM è un ambiente di esecuzione virtuale che consente l’esecuzione del bytecode Java su qualsiasi dispositivo o sistema operativo, garantendo così la portabilità del codice. Durante l’esecuzione, la JVM interpreta il bytecode e, per ottimizzare le prestazioni, spesso utilizza la compilazione just-in-time (JIT). La compilazione JIT traduce dinamicamente il bytecode in codice macchina nativo specifico per la piattaforma in uso, migliorando così la velocità di esecuzione delle applicazioni Java. Anche altri linguaggi di programmazione adottano un approccio simile. Ad esempio, C# utilizza il Common Intermediate Language (CIL), che viene eseguito sulla Common Language Runtime (CLR), un componente del .NET Framework. Python, invece, utilizza un bytecode che viene interpretato dalla Python Virtual Machine (PVM), con alcune implementazioni che supportano la compilazione JIT, come PyPy. Questi approcci condividono l’obiettivo di rendere il codice portabile e di ottimizzare le prestazioni attraverso tecniche di compilazione e interpretazione.

Detto ciò, i passaggi macro perché un programma sia eseguito e possa produrre gli effetti pronosticati, sono:

  1. Il programmatore scrive il codice sorgente utilizzando un editor di testo o un ambiente di sviluppo integrato (integrated development environment, IDE). Questo codice contiene le istruzioni del programma, scritte secondo la sintassi del linguaggio di programmazione scelto.

  2. L’interprete o il compilatore vengono eseguiti con input il programma e un componente, l’analizzatore lessicale, legge il codice sorgente e lo divide in lessemi, che sono sequenze di caratteri che corrispondono agli elementi atomici del linguaggio. Ogni lessema viene identificato come un token specifico, come una parola chiave, un operatore o un identificatore.

  3. A seguire un secondo compoenente, il parser, riceve la sequenza di token dall’analizzatore lessicale e costruisce un albero di sintassi, che rappresenta la struttura grammaticale del programma. Il parser verifica che il codice rispetti le regole sintattiche del linguaggio.

  4. Un altro componente effettua la verifica che il programma abbia un senso logico. Ad esempio, controlla che le variabili siano dichiarate prima di essere utilizzate e che i tipi di dati siano compatibili con le operazioni eseguite su di essi.

  5. Il compilatore, a questo punto, genera una rappresentazione intermedia del programma, che è più vicina al linguaggio macchina ma ancora indipendente dall’architettura specifica del computer. Ciò è tipico dei linguaggi compilati, anche se alcuni interpreti possono generare un bytecode intermedio.

  6. Il compilatore ottimizza codice intermedio al fine di migliorare le prestazioni del programma, riducendo il numero di istruzioni o migliorando l’efficienza delle operazioni.

  7. Il codice intermedio ottimizzato viene tradotto in codice macchina, che è specifico per l’architettura del computer su cui il programma verrà eseguito.

  8. Linking: Il codice macchina viene combinato con altre librerie e moduli necessari per formare un eseguibile completo.

  9. Esecuzione: L’eseguibile viene caricato nella memoria del computer e il processore esegue le istruzioni, portando a termine le operazioni definite nel programma.

Nel caso di un interprete, i passaggi di generazione del codice intermedio e macchina possono essere sostituiti da una valutazione diretta delle istruzioni del programma, eseguendole una per una. In pratica, l’interprete traduce ogni singola istruzione del codice sorgente in un formato conprensibile dalla CPU e passa questa istruzione alla CPU stessa per l’esecuzione. Questo processo continua fino a quando tutte le istruzioni del programma non sono state eseguite.

1.5 Il ciclo di vita del software

Un software è composto da uno o più programmi e, quando eseguito, realizza un compito con un grado di utilità specifico. La gerarchia concettuale, dal più generale all’elemento più granulare, è: software, programmi, moduli, istruzioni.

Un modulo è una componente autonoma di un programma che contiene una parte specifica della logica o delle funzionalità del sistema. I moduli facilitano la gestione della complessità, permettono il riutilizzo del codice e migliorano la manutenibilità del software.

Così come il disegno dei programmi è quello computazionale degli algoritmi, il disegno del software è funzionale per determinare i suoi obiettivi e architetturale per la decomposizione nei programmi e nei relativi moduli.

Per creare il software, quindi, è necessario percorrere una sequenza di fasi ben definita che, concisamente, è data da:

  • La progettazione di un’applicazione inizia con la fase di analisi dei requisiti, in cui si identificano cosa deve fare il software, chi sono gli utenti e quali sono i requisiti funzionali e non funzionali che deve soddisfare.

  • Segue il disegno funzionale che dettaglia come ogni componente del sistema possa rispondere alle funzionalità richieste. In questa fase si descrivono le operazioni specifiche che ogni componente deve eseguire, utilizzando diagrammi di processo per rappresentare il flusso di attività al fine di rispondere ai requisiti.

  • Il disegno architetturale riguarda l’organizzazione ad alto livello del sistema software. In questa fase si definiscono i componenti principali del sistema e come essi interagiscono tra di loro per supportare le attività di processo. Questo include la suddivisione del sistema in moduli o componenti, la definizione delle interfacce tra di essi e l’uso di tecniche di modellazione per rappresentare l’architettura del sistema.

  • Una volta che l’architettura è stata progettata, si passa alla fase di implementazione, in cui i programmatori scrivono il codice sorgente nei linguaggi di programmazione scelti. I programmatori lavorano sui vari moduli, integrandoli secondo l’architettura definita per costruire i programmi completi.

  • Dopo l’implementazione, è essenziale verificare che il software funzioni correttamente:

    • Testing: Scrivere ed eseguire test per verificare che il software soddisfi i requisiti specificati. I test sono di diversi generi in funzione dell’oggetto di verifica, come test unitari per segmenti di codice (spesso a livello di moduli), test di integrazione per componenti, e test di sistema nella sua interezza.

    • Debugging: Identificare e correggere gli errori (bug) nel codice. Questo può includere l’uso di strumenti di debugging per tracciare l’esecuzione del programma e trovare i punti in cui si verificano gli errori.

  • Una volta che il software è stato testato e ritenuto pronto, si passa alla fase di messa a disposizione delle funzionalità agli utenti (in inglese, deployment):

    • Distribuzione: Rilasciare il software agli utenti finali, che può includere l’installazione su server, la distribuzione di applicazioni desktop o il rilascio di app mobile.

    • Manutenzione: Continuare a supportare il software dopo il rilascio. Questo include la correzione di bug scoperti dopo il rilascio, l’aggiornamento del software per miglioramenti e nuove funzionalità, e l’adattamento a nuovi requisiti o ambienti.

La complessità del processo, riportato in modo molto sintetico, induce la necessità di avere dei team con qualità individuali diverse e il programmatore, oltre alle competenze specifiche, deve saper innestare la propria attività specifica all’interno di un quadro la cui articolazione è non triviale anche per obiettivi di progetto molto limitati. Tra le qualità principali troviamo il saper creare o comprendere i vari artifatti di disegno di algoritmi e riuscire a tramutarli in codice sorgente e programmi.

La modularità del software facilita questa trasformazione, permettendo a diversi programmatori, o allo stesso ma in tempi successivi, di focalizzarsi su singole parti del programma, garantendo al contempo che le varie componenti si integrino correttamente in un sistema funzionante e coerente.