ADR260416
2026/04/16 · Marco Orlandin · 6 minGitFlow-lite con release-please per deploy automatici su progetto cliente
Autore: Marco Orlandin, Architect
Data: 16 Aprile 2026
Status: Implementato
Settore: Enterprise / CI-CD pipeline
Constraint principali: Team singolo (consulente esterno), progetto Django su AWS ECS Fargate, cliente non tecnico, deploy staging e produzione separati, nessun GitHub Team plan (no approval gate nativo)
Contesto e problema
Applicazione web Django per un cliente, ad uso operativo quotidiano. Stack Django + Celery + PostgreSQL, deploy su AWS ECS Fargate via GitHub Actions. Due ambienti target: staging (deploy da branch develop) e produzione (deploy da tag).
Il problema: come strutturare il flusso git → CI → deploy in modo che:
- Staging si aggiorni automaticamente ad ogni push su
develop - Produzione si aggiorni solo su release esplicita
- La release sia tracciabile (cosa è cambiato, quando, perché)
- Il processo non dipenda dalla memoria di una persona
Il contesto rende la scelta meno ovvia di quanto sembri. È un progetto cliente, non un prodotto proprio. Il cliente non leggerà mai un changelog. Il team è una persona. L'overhead di qualsiasi processo ricade interamente su chi scrive codice.
Requisiti non funzionali
- Deploy staging automatico su push a
develop - Deploy produzione automatico su tag semantico
- Tracciabilità: ogni release deve avere un changelog leggibile
- Zero intervento manuale per il deploy (nessun "ok adesso mando in prod")
- Basso overhead quotidiano per uno sviluppatore singolo
- Compatibile con GitHub Free plan (no branch protection rules avanzate)
Opzioni valutate
Opzione 1: Push manuale + tag manuale
- Pro: Zero setup. Nessun tool aggiuntivo. Massima flessibilità.
- Contro: Il deploy dipende da una decisione umana ("ora taggo"). Il changelog non esiste o è scritto a mano (quindi non esiste). La history diventa un muro di "fix", "update", "wip" dopo due settimane. Già sperimentato su altri progetti: è il motivo per cui sto valutando alternative.
Opzione 2: GitFlow completo (Gitflow-avh)
- Pro: Branching model maturo e documentato. Supporto per hotfix, release branch, feature branch. Ampiamente conosciuto.
- Contro: Troppi branch per un team di una persona. Release branch che vivono giorni senza senso quando non c'è un QA separato. Merge da
release/*versomainEdevelopè cerimonia che non aggiunge valore. Overhead sproporzionato per il contesto.
Opzione 3: Trunk-based development + tag manuale
- Pro: Semplicità massima. Un branch, feature flag dove serve. Adottato da Google, raccomandato da DORA.
- Contro: Richiede feature flag infrastruttura che non esiste ancora. Ogni push va in staging E potenzialmente in produzione, e serve disciplina o gating che GitHub Free non offre. Per un consulente esterno su codice cliente, il rischio di pushare qualcosa di rotto in un branch che triggera il deploy è troppo alto.
Opzione 4: GitFlow-lite + commitizen + release-please
- Pro: Due branch (
develop,main).developè il branch di default, ogni push deploya staging.release-pleaseapre una PR automatica con version bump + changelog quando ci sono commit convenzionali accumulati. Merge della PR tagga e deploya produzione. Il changelog si scrive da solo. Il tag è il trigger, non una decisione. - Contro: Richiede conventional commits (disciplina). Commitizen è un tool in più nella toolchain.
release-pleaseè una GitHub Action da configurare e mantenere. Il developer deve capire il formato dei commit message.
Decisione
Scelta l'Opzione 4: GitFlow-lite con commitizen per conventional commits e release-please per release automatiche.
Motivazioni principali:
- Il costo dell'Opzione 1 (push manuale) non si paga al setup, si paga ogni volta che serve capire cosa è cambiato. Dopo un mese di "fix stuff" nella history, il debug di un deploy rotto diventa archeologia.
- GitFlow completo è progettato per team con ruoli separati (dev, QA, release manager). Un team di una persona non ha bisogno di release branch che vivono tre giorni.
- Trunk-based richiede infrastruttura (feature flag, gating) che non esiste e non vale la pena costruire per questo progetto.
- L'Opzione 4 ha il rapporto costo/beneficio migliore: il costo è un hook pre-commit e un workflow GitHub Actions. Il beneficio è deploy tracciabili, changelog automatici, e nessuna decisione umana nel path verso produzione.
Implementazione passo-passo
1. Branch develop come default
GitHub default branch impostato su develop, non main. Tutti i push quotidiani vanno su develop. main riceve merge solo da release-please. Questo protegge la produzione senza branch protection rules (che richiedono GitHub Team plan).
2. Conventional commits via commitizen
.commitlintrc.yaml nel repo. Hook pre-commit via commitizen che rifiuta commit message non conformi. Formato: type(scope): description dove type è feat, fix, chore, docs, refactor, test, ci. Lo scope è opzionale ma incoraggiato.
3. release-please per version bump e changelog
GitHub Action release-please configurata su push a main. Accumula commit convenzionali, apre una PR con:
- Version bump in
VERSION(semantic versioning) CHANGELOG.mdgenerato automaticamente- Commit di release
Al merge della PR, release-please crea il tag git. Il tag triggera il workflow di deploy produzione.
4. CI/CD pipeline a due stage
feature/* → develop → [CI: test + build + deploy staging]
↓
release-please PR (auto)
↓
develop → main (merge PR) → tag → [CI: build + deploy production]
Il deploy staging include: Docker build, ECR push, DB migration (ECS task one-off), ECS service update, ALB smoke test. Il deploy produzione è identico, con variabili d'ambiente diverse.
5. Nessun approval gate automatico
GitHub Free plan non supporta required reviewers su branch protection. L'approval gate è umano: la PR di release-please resta aperta finché non viene mergiata manualmente. È l'unico punto di decisione umana nel flusso, e serve come "sei sicuro?" prima di andare in produzione.
Conseguenze osservate
Positive
- Deploy tracciabili: ogni release ha un tag semantico e un changelog. "Cosa c'è in produzione?" ha sempre una risposta.
- Zero decisioni nel deploy path: staging è automatico. Produzione è un merge di PR. Nessun "adesso taggo manualmente".
- History leggibile: conventional commits forzano una struttura.
git log --onelineracconta una storia, non una lista della spesa. - Onboarding semplificato: se il progetto passa a un altro sviluppatore, il flusso è documentato nel codice (workflow, hook, config).
Negative
- Frizione quotidiana: ogni commit deve rispettare il formato. Per fix veloci, la tentazione di fare
git commit --no-verifyè reale. Resistere è disciplina. - Dipendenza da GitHub Actions: release-please è una Action. Se GitHub Actions ha downtime, il flusso si blocca. Mitigazione: il deploy manuale resta possibile come fallback.
- Overhead percepito: per un singolo sviluppatore, conventional commits possono sembrare cerimonia. Il valore emerge nel tempo, non al primo commit.
Quando non applicare questo approccio
- Prototipi e spike: se il codice potrebbe essere buttato domani, conventional commits sono zavorra.
- Team con release manager dedicato: GitFlow completo o trunk-based con feature flag sono probabilmente più adatti.
- Progetti senza CI/CD: se il deploy è manuale, release-please non ha un trigger su cui agire. Va prima messo in piedi un deploy automatizzato, poi ha senso pensare alla release automation.
- Monorepo con servizi multipli: release-please gestisce un progetto per repo. Per monorepo servono configurazioni aggiuntive (manifest mode) o tool diversi.
Stack utilizzato
- VCS: Git, GitHub (Free plan)
- Branching: GitFlow-lite (
develop+main) - Commit convention: Conventional Commits via commitizen
- Release automation: release-please (GitHub Action)
- CI/CD: GitHub Actions (test, build, deploy)
- Deploy target: AWS ECS Fargate (staging + produzione)
- Container registry: AWS ECR