Continuous Integration (CI) è una pratica di sviluppo in cui ogni membro del team integra le proprie modifiche nel repository condiviso almeno una volta al giorno e ogni integrazione viene verificata da un build e test automatizzati. La CI rileva conflitti di codice ed errori di regressione nelle fasi iniziali, riducendo i costi di correzione. Secondo il Puppet State of DevOps Report, 2025, i team con CI correggono i bug 4 volte più velocemente dei team senza automazione.
Punti chiave
Continuous Integration (CI) è una metodologia di sviluppo che automatizza il processo di integrazione del codice di più contributori in un’unica codebase. Il termine è stato introdotto da Martin Fowler all’inizio degli anni 2000 come insieme di pratiche per prevenire l’“inferno dell’integrazione” — una situazione in cui gli sviluppatori lavorano isolati per settimane e, quando uniscono le modifiche, sorgono numerosi conflitti che richiedono giorni di risoluzione manuale.
Senza CI, uno sviluppatore completa una funzionalità, cerca di unire le proprie modifiche nel branch main e scopre che i colleghi hanno modificato gli stessi file. Risolvere i conflitti richiede ore e spesso rompe il codice funzionante. La CI risolve questo problema imponendo l’integrazione più volte al giorno: più frequente è l’integrazione, minori sono i conflitti e più facile è risolverli. La pratica dimostra che con l’integrazione giornaliera, la risoluzione dei conflitti richiede minuti, mentre con l’integrazione settimanale richiede ore.
Secondo l’IBM Systems Sciences Institute, il costo di correzione di un bug in fase di codifica è di $25, in fase di test di $100 e in fase di produzione di $2.500. La CI sposta il rilevamento dei difetti il più possibile a sinistra (shift left), trovando errori in fase di commit quando la correzione è praticamente gratuita. I team con CI dedicano in media il 15% del tempo al debugging, contro il 35% dei team senza CI.
Martin Fowler ha definito le pratiche chiave della CI che rimangono rilevanti indipendentemente dallo stack tecnologico. Seguire questi principi garantisce che la CI porti valore anziché diventare un peso burocratico. Lo sviluppo mobile impone requisiti aggiuntivi, ma il nucleo rimane invariato.
Tutto il codice del progetto è archiviato in un unico repository con un sistema di controllo versione unificato (Git). Un’unica fonte di verità elimina la situazione in cui una funzionalità viene sviluppata in un fork e non viene sincronizzata con la codebase principale per settimane. Nei progetti mobili, ciò significa che le parti Android, iOS e backend possono trovarsi in un unico repository (monorepo) o in repository separati con uno schema di versionamento condiviso.
Il build del progetto deve essere eseguibile con un unico comando. Per Android è ./gradlew assembleDebug, per iOS — xcodebuild o fastlane build. Lo script di build verifica la riproducibilità: il build sul server CI deve produrre lo stesso risultato di quello sulla macchina dello sviluppatore. Eventuali differenze ambientali vengono eliminate tramite containerizzazione o IaC (Infrastructure as Code).
Dopo il build, vengono eseguiti tutti i livelli di test: unitari, di integrazione e UI. Se i test falliscono, il commit è considerato non valido. Mantenere lo stato verde è una responsabilità condivisa del team. Nei progetti mobili, i test veloci (eseguiti entro 5 minuti per commit) sono spesso separati dai test lenti (test UI su dispositivi reali, eseguiti meno frequentemente).
// Esempio di test unitario con report compatibile con CI
class LoginViewModelTest {
private val repository = mock<AuthRepository>()
private val viewModel = LoginViewModel(repository)
@Test
fun loginWithValidCredentials_success() {
val email = "test@example.com"
val password = "ValidPass123"
whenever(repository.login(email, password))
.thenReturn(Result.success(User("token-xyz")))
val result = viewModel.login(email, password)
assertEquals(LoginState.Success, result)
verify(repository).login(email, password)
}
}
I risultati della CI sono pubblici per l’intero team: tutti possono vedere quale commit ha rotto il build. La trasparenza crea una cultura di responsabilità: gli sviluppatori verificano le loro modifiche prima del push e riparano il build rotto fuori turno. Il server CI invia notifiche a Slack o Telegram quando lo stato del build cambia.
Un sistema CI completo è composto da diversi componenti che interagiscono tra loro. Ogni componente è responsabile della propria parte del pipeline: dall’attivazione al report. Comprendere l’architettura della CI aiuta a diagnosticare problemi e ottimizzare le prestazioni.
Il componente centrale che gestisce la coda dei build, l’allocazione delle risorse e la pubblicazione dei risultati. Un server CI può essere basato su cloud (GitHub Actions, GitLab CI, CircleCI) o self-hosted (Jenkins, TeamCity). Il server monitora le modifiche nel repository tramite webhook o polling e attiva il pipeline a ogni push o pull request.
I runner sono macchine virtuali o fisiche che eseguono le attività di build. Nel CI cloud, i runner sono forniti dal provider e fatturati in base al tempo di utilizzo. I runner self-hosted sono installati sulla propria infrastruttura e richiedono manutenzione. I build iOS necessitano di runner macOS, i build Android necessitano di Linux o Windows.
Dopo il build, il sistema CI salva gli artefatti (APK, IPA, report di test) in un archivio — sono disponibili per il download e il deployment. La cache delle dipendenze (cache Gradle, cache CocoaPods) tra le esecuzioni accelera i build successivi da 3 a 5 volte.
| Componente | Scopo | Esempio |
|---|---|---|
| Server CI | Orchestrazione dei build | Jenkins, GitHub Actions |
| Runner | Esecuzione delle attività | Runner macOS per iOS |
| Repository | Archiviazione del codice | GitHub, GitLab |
| Archivio artefatti | Archiviazione artefatti | AWS S3, Artifactory |
| Notifica | Notifica al team | Slack, Telegram, email |
Lo sviluppo mobile ha requisiti speciali per la CI che differiscono dai progetti web o backend. Lunghi tempi di build (3–15 minuti per Android, 5–20 minuti per iOS), molteplici tipi di artefatti (APK, AAB, IPA), necessità di firma e offuscamento — tutto ciò richiede una configurazione personalizzata del pipeline CI.
Una CI tipica per Android include: linting (ktlint, detekt) e analisi statica, test unitari con JUnit e MockK, build di APK/AAB debug e release, test strumentali su emulatore all’interno della CI e pubblicazione degli artefatti. La cache Gradle accelera i build ripetuti — senza di essa, ogni build scarica le dipendenze da capo, perdendo 3–5 minuti.
La CI per iOS richiede un runner macOS per compilare codice Swift/Objective-C. Il pipeline include: installazione delle dipendenze CocoaPods o SPM, SwiftLint per il controllo dello stile, test unitari con XCTest, build IPA, firma del codice tramite Fastlane match e caricamento su TestFlight. Un runner self-hosted su Mac mini o Mac in un data center è un’alternativa ai runner macOS cloud.
Flutter e React Native si compilano in build nativi per entrambe le piattaforme. La CI deve supportare due runner: Linux per i build Android e macOS per i build iOS. La strategia ottimale è un pipeline suddiviso: build Android su un runner Linux, build iOS su un runner macOS, dopodiché entrambi gli artefatti vengono combinati in un’unica release.
La scelta di uno strumento CI dipende dalle dimensioni del team, dalle prestazioni richieste, dal budget e dallo stack tecnologico. Di seguito è riportato un confronto delle soluzioni più popolari con focus sullo sviluppo mobile. Le soluzioni self-hosted offrono controllo ma richiedono amministrazione; le soluzioni cloud offrono comodità ma limitano la configurazione.
Gratuito per repository pubblici (2000 minuti/mese). GitHub Actions offre un ecosistema di azioni pronte per Android (gradle/actions) e iOS (apple-actions). Lo svantaggio è che i runner macOS sono disponibili solo nei piani a pagamento. Ideale per progetti open source e piccoli team che già utilizzano GitHub.
Un server CI self-hosted open source. Jenkins si configura tramite Groovy Pipeline, supporta centinaia di plugin e funziona su qualsiasi hardware. Richiede un ingegnere DevOps per l’installazione e la manutenzione. Popolare nel segmento enterprise dove il controllo dell’infrastruttura è critico.
CI/CD integrato in GitLab con architettura runner aperta. GitLab CI consente di utilizzare i propri runner (incluso macOS) nel piano gratuito. La configurazione YAML è più potente di GitHub Actions ma più difficile da imparare. Adatto per team che utilizzano GitLab come piattaforma DevOps unificata.
Una CI cloud incentrata sulla velocità. CircleCI supporta immagini Docker, macOS e Android e memorizza automaticamente in cache le dipendenze. Il prezzo è basato su crediti — più costoso di GitHub Actions per i piccoli team, ma più veloce grazie a runner ottimizzati. Raccomandato per progetti di produzione con requisiti di velocità.
Consideriamo la configurazione della CI per un progetto Android utilizzando GitHub Actions. Il pipeline esegue analisi statica, build e test a ogni push e pull request nel branch main. La configurazione minima richiede 15 minuti e non richiede servizi esterni.
name: Android CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin
- run: ./gradlew ktlintCheck detekt
unit-tests:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew testDebugUnitTest
- uses: actions/upload-artifact@v4
with:
name: test-results
path: app/build/reports/tests/
Il pipeline è composto da due job paralleli: lint (esegue analisi statica) e unit-tests (dipende da lint — se il linting fallisce, i test non vengono eseguiti). Il job unit-tests carica il report di test come artefatto — il team può consultarlo nell’interfaccia di GitHub Actions senza scaricare file localmente.
Per evitare fallimenti della CI dovuti a errori banali, configura un hook pre-push in Git o un’attività Gradle che esegua gli stessi controlli localmente. Ad esempio: ./gradlew ktlintCheck detekt testDebugUnitTest. Se i controlli locali richiedono più di 3 minuti, suddividili in veloci (linter) e lenti (test), eseguendo i veloci prima di ogni commit e i lenti solo prima del push.
Domande frequenti
CI si concentra sull’integrazione e la verifica del codice (build + test), mentre CD aggiunge l’automazione del deployment. La CI garantisce che il codice sia corretto; la CD garantisce che questo codice corretto possa essere consegnato agli utenti. La CI è un prerequisito per la CD, ma la CD senza CI non funziona.
La frequenza minima è una volta al giorno per sviluppatore. La pratica ideale è fare push nel repository al completamento di ogni unità logica di lavoro (ogni 1–4 ore). Più frequente è l’integrazione, minori sono i conflitti e più facile è risolverli. Se passano più di 2 giorni tra le integrazioni, non stai usando la CI.
Per Android, GitHub Actions (gratuito, facile da configurare) o GitLab CI (runner propri) sono ottimali. Per iOS, CircleCI (miglior supporto macOS) o Bitrise (CI specializzata per progetti mobili). Per progetti multipiattaforma, GitLab CI con due runner (Linux + macOS).
Sì, ma con riserva. I test UI sono lenti (10–30 minuti) e instabili (flaky). La strategia ottimale: eseguire test veloci (unitari + integrazione) a ogni push, e i test UI su pull request, di notte o prima di un rilascio. Utilizza Device Farm o emulatori nella CI per i test UI.
Metriche di CI efficace: tempo di build inferiore a 15 minuti, percentuale di build verdi superiore all’85%, tempo medio di recupero dopo un fallimento inferiore a 30 minuti. Se il build fallisce frequentemente, la CI non aiuta ma ostacola. Rivedi i test: rimuovi i test flaky, ottimizza le dipendenze, riduci il tempo di build.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche