Astrazione
L'astrazione nasconde i dettagli implementativi dietro un'interfaccia stabile, così chi la usa dipende da cosa fa un componente, non da come lo fa. Nel codice a oggetti e funzionale si realizza tramite interfacce, tipi astratti e funzioni pure. Buone astrazioni riducono l'accoppiamento e permettono di cambiare l'implementazione senza rompere i client.
Test di Accettazione
Un test di accettazione verifica che una funzionalità soddisfi un requisito di business dal punto di vista dell'utente, espresso in termini di comportamento osservabile e non di stato interno. Di solito è scritto in forma Given/When/Then ed eseguito contro un sistema in esecuzione. Risponde alla domanda "abbiamo costruito la cosa giusta?" più che "abbiamo costruito la cosa correttamente?".
Aggregato
Nel Domain-Driven Design un aggregato è un insieme di oggetti di dominio trattato come un'unica frontiera di consistenza, accessibile solo tramite la sua radice (aggregate root). Tutti gli invarianti interni all'aggregato devono valere dopo ogni transazione. Gli aggregati vanno tenuti piccoli per ridurre la contesa e mapparsi in modo pulito su una singola transazione di database.
API (Application Programming Interface)
Un'API è il contratto tramite cui un software viene invocato da un altro, definendo operazioni, input, output e semantica degli errori. Le API ben progettate sono coerenti, evolvibili ed esplicite riguardo a versioning e modalità di fallimento. REST, gRPC e GraphQL sono stili comuni per le API di rete.
Compatibilità all'indietro
Una modifica è retrocompatibile se i client esistenti continuano a funzionare senza modifiche dopo il deploy. Aggiungere campi opzionali o nuovi endpoint è in genere retrocompatibile; rimuovere campi o irrigidire la validazione di solito non lo è. Il Semantic Versioning codifica questo contratto nel numero di versione MAJOR.
Bounded Context
Un bounded context è una frontiera esplicita all'interno della quale un determinato modello di dominio e il suo linguaggio ubiquo sono coerenti e non ambigui. Lo stesso termine (ad esempio "Account") può significare cose diverse in bounded context diversi, e l'integrazione fra essi avviene tramite contratti ben definiti. È il pattern strategico centrale del Domain-Driven Design.
Cache
Una cache memorizza il risultato di un calcolo o di un recupero costoso, così le richieste future per lo stesso dato sono servite più velocemente. Le cache barattano la freschezza con la latenza e richiedono una strategia di invalidazione (TTL, write-through, write-behind o eviction esplicita). I due problemi classicamente difficili sono l'invalidazione della cache e dare nomi alle cose.
Circuit Breaker
Il circuit breaker è un pattern di resilienza che interrompe le chiamate verso una dipendenza remota in errore una volta superata una soglia, fallendo rapidamente invece di accumulare thread bloccati. Dopo un periodo di raffreddamento consente alcune chiamate di prova (stato half-open) per testare il recupero prima di richiudersi. Librerie come Resilience4j lo implementano come macchina a stati con stati closed, open e half-open.
Clean Architecture
La Clean Architecture organizza il codice in livelli concentrici in cui le dipendenze puntano solo verso l'interno, verso le regole di business, mentre gli aspetti esterni come UI, database e framework restano ai bordi. Il livello di dominio non conosce i framework, quindi la logica di business è testabile in isolamento. È uno dei vari nomi della stessa idea della regola delle dipendenze, insieme a Hexagonal e Onion architecture.
Code Smell
Un code smell è un sintomo superficiale nel codice sorgente che spesso indica un problema di design più profondo, come metodi lunghi, classi enormi, logica duplicata o feature envy. Gli smell sono euristiche, non difetti: suggeriscono dove il refactoring può ripagare. Il termine è stato reso popolare da Kent Beck e Martin Fowler.
Coesione
La coesione misura quanto fortemente le responsabilità di un singolo modulo appartengono insieme. Alta coesione significa che un modulo svolge un compito ben definito; bassa coesione significa che mescola preoccupazioni non correlate. Alta coesione unita a basso accoppiamento è un obiettivo primario del buon design modulare.
Concorrenza
La concorrenza è la composizione di task che vengono eseguiti in modo indipendente e possono sovrapporsi nel tempo, a differenza del parallelismo, che è la loro esecuzione simultanea. La concorrenza riguarda la struttura (gestire molte cose insieme); il parallelismo riguarda l'esecuzione (fare molte cose nello stesso istante). Il codice concorrente corretto richiede una gestione attenta dello stato mutabile condiviso.
Continuous Integration (CI)
La Continuous Integration è la pratica di integrare frequentemente il lavoro degli sviluppatori in un ramo principale condiviso, con ogni integrazione che avvia una build e una suite di test automatizzate. Fa emergere presto i conflitti di integrazione e mantiene il ramo principale rilasciabile. La CI è un prerequisito per la Continuous Delivery e la Continuous Deployment.
Accoppiamento
L'accoppiamento misura il grado di interdipendenza tra i moduli; più un modulo deve conoscere gli interni di un altro, più stretto è l'accoppiamento. Un accoppiamento lasco permette ai moduli di cambiare in modo indipendente e di essere testati in isolamento. L'inversione delle dipendenze e le interfacce ben definite sono i principali strumenti per ridurlo.
Dependency Injection (DI)
La Dependency Injection fornisce i collaboratori di un componente dall'esterno, invece di farglieli costruire al suo interno. Questo inverte il controllo sulla creazione degli oggetti, migliorando testabilità e configurabilità. L'injection da costruttore è la forma preferita perché rende le dipendenze esplicite e supporta l'immutabilità.
Domain-Driven Design (DDD)
Il Domain-Driven Design è un approccio che pone il dominio di business e un linguaggio ubiquo condiviso al centro del design del software. I suoi pattern strategici (bounded context, context map) gestiscono la struttura su larga scala, mentre i pattern tattici (entità, value object, aggregati, repository) plasmano il modello. È stato introdotto da Eric Evans nel 2003.
Incapsulamento
L'incapsulamento raggruppa i dati con le operazioni che agiscono su di essi e limita l'accesso diretto allo stato interno. Permette a un oggetto di far valere i propri invarianti, così chi lo usa non può portarlo in una configurazione non valida. Le violazioni, come esporre internals mutabili, erodono la capacità dell'oggetto di garantire la correttezza.
Entità
Nel Domain-Driven Design un'entità è un oggetto definito da un'identità continua piuttosto che dai valori dei suoi attributi; due entità con campi identici restano distinte se le identità differiscono. Le entità hanno tipicamente un ciclo di vita e uno stato mutabile. Si contrappongono ai value object, definiti interamente dai loro attributi.
Consistenza Eventuale
La consistenza eventuale è un modello di consistenza in cui, in assenza di nuovi aggiornamenti, tutte le repliche di un dato convergeranno nel tempo allo stesso valore. È comune nei sistemi distribuiti che privilegiano disponibilità e tolleranza alle partizioni rispetto alla consistenza immediata. I client possono temporaneamente leggere dati non aggiornati.
Idempotenza
Un'operazione è idempotente se eseguirla più volte produce lo stesso effetto di eseguirla una sola volta. L'idempotenza è essenziale per i retry sicuri nei sistemi distribuiti, dove un client non può sapere se una richiesta andata in timeout sia stata davvero eseguita. In HTTP, GET, PUT e DELETE sono definiti idempotenti; POST in genere non lo è.
Immutabilità
Un oggetto immutabile non può essere modificato dopo la costruzione; ogni "modifica" produce una nuova istanza. L'immutabilità elimina una classe di bug causati dallo stato mutabile condiviso, rende gli oggetti intrinsecamente thread-safe e semplifica il ragionamento. I record Java, i campi final e i pattern copy-on-write sono modi comuni per ottenerla.
Inversion of Control (IoC)
L'Inversion of Control è un principio per cui è il framework o il container, e non il codice applicativo, a controllare il flusso di esecuzione e la creazione degli oggetti. La Dependency Injection è la forma concreta più comune di IoC. Abilita il basso accoppiamento rimuovendo la costruzione cablata dal codice di business.
Test di Integrazione
Un test di integrazione esercita più componenti insieme, tipicamente includendo infrastruttura reale come un database o un message broker, per verificare che collaborino correttamente. Cattura difetti che i test unitari con mock non possono vedere, come SQL errato o disallineamenti di serializzazione. Strumenti come Testcontainers eseguono dipendenze reali in container usa e getta.
Principio di Sostituzione di Liskov (LSP)
Il Principio di Sostituzione di Liskov afferma che gli oggetti di un sottotipo devono essere utilizzabili ovunque sia atteso il tipo base senza alterare la correttezza. I sottotipi non devono rafforzare le precondizioni né indebolire le postcondizioni del tipo base. È la "L" di SOLID e vincola l'uso dell'ereditarietà.
Mocking
Il mocking sostituisce un collaboratore reale con un test double controllabile che registra le interazioni e restituisce risposte programmate. Isola l'unità sotto test da dipendenze lente o non deterministiche. L'uso eccessivo di mock accoppia i test ai dettagli implementativi e può produrre test che passano mentre il sistema è rotto.
Monolite
Un monolite è un'applicazione distribuita come un'unica unità, con tutti i moduli che condividono un processo e di solito un database. I monoliti sono più semplici da costruire, testare e distribuire rispetto ai sistemi distribuiti e spesso sono il punto di partenza giusto. Un monolite modulare ben strutturato mantiene confini interni chiari che facilitano una futura suddivisione.
ORM (Object-Relational Mapping)
Un ORM mappa tra oggetti nel codice e righe in un database relazionale, generando SQL per conto dello sviluppatore. Riduce il codice ripetitivo ma può nascondere query costose e introdurre insidie come il problema N+1 e il caricamento lazy involontario. JPA/Hibernate è l'ORM dominante nell'ecosistema Java.
Funzione Pura
Una funzione pura restituisce lo stesso output per gli stessi input e non produce effetti collaterali osservabili. La purezza rende il codice banalmente testabile, memoizzabile e sicuro da parallelizzare. Isolare gli effetti collaterali ai bordi del sistema mantenendo puro il nucleo è una strategia di design ricorrente.
Race Condition
Una race condition si verifica quando la correttezza di un programma dipende dall'imprevedibile interleaving di operazioni concorrenti su stato condiviso. I sintomi sono fallimenti intermittenti e difficili da riprodurre. Si prevengono con la sincronizzazione, dati immutabili o confinando lo stato mutabile in un singolo thread o attore.
Refactoring
Il refactoring è la ristrutturazione disciplinata del codice esistente per migliorarne il design interno senza cambiarne il comportamento osservabile. Si basa su una suite di test veloci per dimostrare che il comportamento è preservato a ogni piccolo passo. L'obiettivo è mantenere il codice modificabile man mano che la comprensione evolve.
Repository Pattern
Il repository pattern fornisce un'astrazione simile a una collezione sopra lo storage dei dati, esponendo metodi come findById e save e nascondendo il meccanismo di persistenza. Il codice di dominio dipende dall'interfaccia del repository, non dal database, migliorando la testabilità e isolando le scelte di storage. È un pattern tattico del Domain-Driven Design.
REST (Representational State Transfer)
REST è uno stile architetturale per applicazioni di rete che modella il sistema come risorse identificate da URI e manipolate tramite un insieme uniforme di metodi HTTP. Vincoli come la statelessness e l'interfaccia uniforme abilitano scalabilità e caching. Le API REST mature usano l'ipermedia per guidare le transizioni di stato (HATEOAS).
SOLID
SOLID è un acronimo per cinque principi di design a oggetti: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation e Dependency Inversion. Insieme mirano a produrre codice più facile da capire, estendere e mantenere. Sono stati raccolti e resi popolari da Robert C. Martin.
Principio di Singola Responsabilità (SRP)
Il Principio di Singola Responsabilità afferma che un modulo dovrebbe avere una, e una sola, ragione per cambiare, ovvero essere responsabile verso un singolo attore o preoccupazione. Mescolare responsabilità accoppia cambiamenti non correlati e rende il modulo fragile. È la "S" di SOLID.
Semantic Versioning (SemVer)
Il Semantic Versioning è uno schema di versionamento MAJOR.MINOR.PATCH dove MAJOR segnala modifiche incompatibili dell'API, MINOR aggiunge funzionalità retrocompatibili e PATCH applica correzioni di bug retrocompatibili. Offre ai consumatori un contratto prevedibile sul rischio di aggiornamento. La specifica è mantenuta su semver.org.
Effetto Collaterale
Un effetto collaterale è qualunque interazione osservabile che una funzione ha con il mondo esterno oltre alla restituzione di un valore, come mutare stato, eseguire I/O o sollevare eccezioni. Gli effetti collaterali rendono il codice più difficile da testare e su cui ragionare. I design funzionali spingono gli effetti collaterali ai bordi e mantengono pura la logica centrale.
Debito Tecnico
Il debito tecnico è il costo futuro implicito derivante dallo scegliere ora una soluzione sbrigativa invece di una migliore ma più lenta. Come il debito finanziario, matura interessi man mano che il design subottimale rallenta il lavoro futuro. Un debito deliberato e tracciato può essere un compromesso ragionevole; il debito non gestito si accumula silenziosamente.
Test-Driven Development (TDD)
Il Test-Driven Development è un flusso di lavoro che scrive prima un test fallito (red), poi il codice minimo per farlo passare (green), e infine rifattorizza mantenendo i test verdi. Il ciclo guida il design dalla prospettiva del consumatore e produce una rete di sicurezza di test. È stato reso popolare da Kent Beck.
Thread Safety
Il codice è thread-safe se si comporta correttamente quando vi si accede da più thread contemporaneamente, senza data race né invarianti rotti. La thread safety si ottiene tramite immutabilità, sincronizzazione, operazioni atomiche o confinamento dei thread. Documentare il contratto di threading di una classe ne previene l'uso scorretto.
Test Unitario
Un test unitario verifica il comportamento di un pezzo di codice piccolo e isolato, tipicamente una singola classe o funzione, senza dipendenze esterne. I test unitari sono veloci e deterministici e formano l'ampia base della piramide dei test. Offrono feedback rapido durante lo sviluppo e il refactoring.
Value Object
Nel Domain-Driven Design un value object è definito interamente dai suoi attributi e non ha identità concettuale; due value object con attributi uguali sono intercambiabili. I value object dovrebbero essere immutabili e possono validarsi al momento della costruzione. Esempi sono Money, DateRange ed Email.
YAGNI (You Aren't Gonna Need It)
YAGNI è il principio secondo cui non si dovrebbe aggiungere funzionalità finché non è effettivamente richiesta, resistendo alla generalità speculativa. Le funzionalità premature aggiungono costo e rischio mentre servono bisogni che potrebbero non materializzarsi mai. Complementa principi orientati alla semplicità come KISS e DRY.
DRY (Don't Repeat Yourself)
DRY afferma che ogni pezzo di conoscenza dovrebbe avere un'unica rappresentazione autorevole in un sistema, così che una modifica avvenga in un solo posto. Riguarda la conoscenza duplicata, non semplicemente il testo duplicato; codice solo casualmente simile che cambia per ragioni diverse dovrebbe restare separato. È stato articolato in The Pragmatic Programmer.
Accoppiamento e Coesione
Un design sano massimizza la coesione all'interno dei moduli e minimizza l'accoppiamento tra essi. L'alta coesione tiene insieme la logica correlata così che un modulo sia comprensibile da solo; il basso accoppiamento permette ai moduli di evolvere in modo indipendente. La maggior parte delle decisioni strutturali di design può essere valutata dal suo effetto su questa coppia.
Twelve-Factor App
La Twelve-Factor App è una metodologia di dodici linee guida per costruire software-as-a-service portabile e scalabile, che copre configurazione, dipendenze, processi e altro. Fattori chiave includono conservare la configurazione nell'ambiente, trattare i servizi di backing come risorse collegate ed eseguire l'app come processi stateless. È alla base di gran parte della pratica cloud-native moderna.
Osservabilità
L'osservabilità è il grado in cui lo stato interno di un sistema può essere dedotto dai suoi output esterni, di solito tramite log, metriche e tracce (i tre pilastri). Va oltre il monitoraggio perché permette di rispondere a domande non previste al momento del design. Log strutturati e tracing distribuito ne sono abilitatori fondamentali.
Principio Aperto/Chiuso (OCP)
Il Principio Aperto/Chiuso afferma che le entità software dovrebbero essere aperte all'estensione ma chiuse alla modifica, ovvero il nuovo comportamento si aggiunge senza cambiare codice esistente e già testato. Si ottiene tipicamente tramite il polimorfismo e astrazioni ben posizionate. È la "O" di SOLID.
Principio di Segregazione delle Interfacce (ISP)
Il Principio di Segregazione delle Interfacce afferma che i client non dovrebbero essere costretti a dipendere da metodi che non usano, preferendo molte interfacce piccole e focalizzate a una sola generale. Riduce l'effetto a catena dei cambiamenti. È la "I" di SOLID.
Principio di Inversione delle Dipendenze (DIP)
Il Principio di Inversione delle Dipendenze afferma che i moduli di alto livello non dovrebbero dipendere da quelli di basso livello; entrambi dovrebbero dipendere da astrazioni, e le astrazioni non dovrebbero dipendere dai dettagli. Inverte la direzione convenzionale delle dipendenze nel codice sorgente. È la "D" di SOLID e la base concettuale della Dependency Injection.