Ogni software house ha una storia sul progetto che sembrava semplice nel brief e si è trasformato in una lotta di sei mesi. Scope sottostimato, architettura scelta per le ragioni sbagliate, deployment lasciato all'ultima settimana, performance testate solo in locale. Questo articolo è uno sguardo trasparente su come strutturiamo il percorso dall'idea alla produzione — non la versione idealizzata, ma il processo reale che usiamo dopo aver imparato da quegli errori iniziali.
Fase 1: Discovery — il lavoro prima del lavoro
La discovery è la fase più sottovalutata di un progetto software. La maggior parte dei clienti vuole saltarla e la maggior parte dei team di sviluppo junior è felice di assecondarlo — la discovery non produce codice e tutti sono ansiosi di iniziare a costruire. Ma il costo di una decisione presa alla settimana otto che avrebbe potuto essere presa alla settimana uno si misura sempre in mesi di rework.
Cosa produce la discovery
- Problem statement: quale specifico problema operativo o di business stiamo risolvendo? Come viene misurato oggi? Come appare il successo in termini numerici?
- Flussi utente: l'insieme completo delle azioni che un utente deve compiere per raggiungere il proprio obiettivo — non wireframe, ma percorsi narrativi.
- Bozza del modello dati: quali sono le entità core e le loro relazioni? Da dove provengono i dati e dove devono arrivare?
- Inventario delle integrazioni: ogni sistema di terze parti a cui la piattaforma deve connettersi, con documentazione API esaminata e flussi di autenticazione mappati.
- Registro dei rischi: i 5 principali rischi tecnici, di prodotto e organizzativi e una strategia di mitigazione per ciascuno.
- Definition of done: criteri espliciti e misurabili per ogni fase di delivery.
Una discovery approfondita richiede tipicamente due settimane. Previene due mesi di rework. Il ROI è chiaro, eppure rimane la fase più frequentemente saltata nello sviluppo software delle agenzie.
Fase 2: Design dell'architettura
L'architettura non riguarda la scelta dello stack più sofisticato — riguarda la scelta dello stack più semplice che soddisfa i requisiti noti con spazio per evolvere. Ogni componente non necessario nell'architettura iniziale è un onere di manutenzione futuro e un ostacolo di onboarding per ogni sviluppatore che si unirà al progetto.
L'Architecture Decision Record
Ogni decisione architetturale significativa — il motore database, il pattern di messaggistica, il meccanismo di autenticazione, il modello di deployment — dovrebbe essere documentata in un Architecture Decision Record (ADR). Un ADR cattura: il contesto e i vincoli che hanno portato alla decisione, le opzioni considerate, l'opzione scelta e la motivazione, e le conseguenze della scelta.
# ADR-003: Selezione Message Broker
## Stato: Accettato
## Contesto
La piattaforma richiede comunicazione asincrona tra il servizio ordini,
il servizio inventario e il servizio notifiche. Il volume atteso è di 50.000
eventi/giorno al lancio, scalando a ~500.000 eventi/giorno entro 18 mesi.
## Opzioni considerate
1. **RabbitMQ**: maturo, basato su AMQP, ottima semantica di routing, overhead ops ridotto
2. **Apache Kafka**: alto throughput, event log, replay, alto overhead operativo
3. **AWS SQS**: managed, basso overhead, routing limitato, nessun replay
## Decisione
RabbitMQ deployato su CloudAMQP (servizio managed).
## Motivazione
- Il volume non giustifica la complessità operativa di Kafka in questa fase
- Il routing con topic exchange copre i requisiti di fan-out
- Il servizio managed CloudAMQP elimina l'overhead operativo
- La migrazione a Kafka è possibile a volumi più alti senza cambiare le interfacce dei servizi
## Conseguenze
- Nessun replay degli eventi da offset arbitrario (mitigato memorizzando gli eventi su PostgreSQL)
- Il team operativo deve imparare RabbitMQ (curva di apprendimento bassa)Fase 3: Sviluppo — struttura degli sprint e quality gate
Lavoriamo con sprint di due settimane con una struttura costante. I primi tre giorni sono dedicati allo sviluppo del deliverable principale dello sprint. I successivi tre giorni completano i deliverable secondari e affrontano il debito tecnico. Gli ultimi quattro giorni sono dedicati a test, code review, documentazione e preparazione della demo dello sprint. Questo ritmo previene il pattern comune in cui i test vengono compressi all'ultimo giorno.
Quality Gate
Ogni pull request deve superare quality gate automatici prima che inizi la revisione umana. Eseguire manualmente una suite di test completa è troppo lento e applicato in modo non uniforme — l'automazione è l'unico meccanismo di enforcement affidabile.
name: CI Quality Gate
on:
pull_request:
branches: [main, develop]
jobs:
quality-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- name: Installa dipendenze
run: pnpm install --frozen-lockfile
- name: Verifica tipi TypeScript
run: pnpm tsc --noEmit
- name: Lint
run: pnpm lint
- name: Test unitari con copertura
run: pnpm test:cov
env:
CI: true
- name: Verifica soglia copertura
run: |
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Copertura $COVERAGE% sotto la soglia dell'80%"
exit 1
fi
- name: Test di integrazione
run: pnpm test:integration
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testFase 4: Infrastruttura e deployment
L'infrastruttura deve essere trattata come codice fin dal primo giorno. Creare infrastruttura manualmente tramite la console cloud è veloce inizialmente e catastrofico quando devi riprodurre un ambiente, fare debug di un incidente o fare onboarding di un nuovo membro del team. Usiamo Terraform per l'infrastruttura, Docker per la containerizzazione e GitHub Actions per il CI/CD.
Strategia degli ambienti
Strategia degli ambienti
Deployment senza downtime
{
"deploymentConfiguration": {
"deploymentCircuitBreaker": {
"enable": true,
"rollback": true
},
"minimumHealthyPercent": 100,
"maximumPercent": 200
},
"deploymentController": {
"type": "ECS"
},
"healthCheckGracePeriodSeconds": 30
}
// Rolling deployment: ECS avvia i nuovi task prima di fermare quelli vecchi.
// Circuit breaker: se il nuovo deployment fallisce gli health check,
// ECS fa automaticamente rollback alla task definition precedente.Fase 5: Observability — non puoi risolvere ciò che non puoi vedere
L'observability è la capacità di comprendere lo stato interno di un sistema dalle sue uscite esterne. I tre pilastri sono le metriche (come si comporta il sistema?), i log (cosa è successo e quando?) e le trace (come è fluita questa richiesta attraverso il sistema?). In produzione, avrai bisogno di tutti e tre.
- Metriche: strumenta ogni servizio con metriche Prometheus — tasso di richieste HTTP, tasso di errori, percentili di latenza (p50, p95, p99), profondità della coda, utilizzo del connection pool del DB.
- Logging strutturato: emetti log JSON con campi coerenti (traceId, tenantId, userId, operazione, durata). Non loggare mai stringhe in chiaro in produzione.
- Distributed tracing: propaghi il contesto trace OpenTelemetry attraverso tutti i confini di servizio. Una singola trace deve mostrare il percorso completo di una richiesta.
- Alert: PagerDuty o Opsgenie per picchi di latenza p95, anomalie nel tasso di errori, soglie di profondità delle code e lag della replica del database.
- Dashboard: dashboard Grafana per ogni servizio con metriche RED (Rate, Error, Duration) visibili a colpo d'occhio.
Fase 6: Scalabilità — proattiva, non reattiva
La maggior parte del lavoro di scalabilità avviene prima che il traffico arrivi, non durante l'incidente. Il load testing su infrastruttura simile alla produzione identifica i colli di bottiglia prima dei clienti. Usiamo k6 per il load testing, partendo da un profilo di traffico realistico basato sul pattern giornaliero previsto e scalando fino a 3× il picco atteso.
import http from "k6/http";
import { check, sleep } from "k6";
export const options = {
stages: [
{ duration: "5m", target: 100 }, // Ramp up
{ duration: "10m", target: 500 }, // Picco atteso
{ duration: "5m", target: 1500 }, // Stress test 3×
{ duration: "5m", target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ["p(95)<500"], // 95% delle richieste sotto 500ms
http_req_failed: ["rate<0.01"], // Meno dell'1% di tasso di errore
},
};
export default function () {
const payload = JSON.stringify({
idOrdine: `ORD-${Math.random().toString(36).substring(7)}`,
articoli: [{ idProdotto: "PROD-001", quantita: 2 }],
});
const risposta = http.post("https://api.staging.example.com/ordini", payload, {
headers: { "Content-Type": "application/json", Authorization: `Bearer ${__ENV.API_TOKEN}` },
});
check(risposta, {
"status è 201": (r) => r.status === 201,
"tempo di risposta OK": (r) => r.timings.duration < 500,
});
sleep(1);
}Gli errori più comuni e come evitarli
- 1Saltare la discovery e iniziare a scrivere codice da un file Figma. Il file Figma non risponde alle domande sulle integrazioni, i requisiti di performance o la proprietà dei dati.
- 2Scegliere l'architettura prima di comprendere il profilo di carico. Un sistema che prevede 100 utenti/giorno e uno che ne prevede 100.000 hanno architetture ottimali differenti.
- 3Lasciare la sicurezza alla fine. Le vulnerabilità OWASP Top 10 costano molto meno da prevenire che da rimediare in produzione.
- 4Infrastruttura manuale. Se non riesci a ricreare l'ambiente di produzione da zero in due ore, perderai giorni durante il prossimo incidente.
- 5Nessuna baseline di performance prima del lancio. Non sai se le tue ottimizzazioni funzionano davvero se non hai una baseline con cui confrontarle.
Conclusioni
Costruire piattaforme software scalabili è una disciplina, non un talento. I team che consegnano costantemente software funzionante nei tempi previsti non sono necessariamente i più brillanti — sono quelli con un processo ripetibile: discovery rigorosa, architettura deliberata, quality gate automatizzati, infrastructure as code e observability dal primo giorno. Le scorciatoie che sembrano velocità alla settimana uno si accumulano come debito al mese sei. Il processo è la difesa contro l'entropia.
Nexora gestisce lo sviluppo di prodotto end-to-end: dalla discovery e dal design dell'architettura fino al deployment in produzione e al passaggio di consegne operativo. Se stai costruendo una nuova piattaforma o stai cercando di risollevare un progetto in difficoltà, parliamone.
Tag
Prossimo passo
Hai bisogno di implementare questa architettura?
Il nostro team di ingegneria costruisce e scala i sistemi descritti in questo articolo. Dalla discovery alla produzione — con risultati misurabili.
Articoli correlati