Menu
Strumentazione o morte: come ho domato Gemini CLI nel mio progetto Android

Strumentazione o morte: come ho domato Gemini CLI nel mio progetto Android

di Nikolay Vlasov

Generare codice è diventato troppo facile.

Ho avuto un'esperienza significativa: in un progetto personale, ho provato il "vibe-coding" quasi senza revisione — seguendo il principio "ciò che l'agente scrive, accettiamo". All'inizio sembrava veloce e comodo. Ma in breve tempo il progetto ha iniziato a sgretolarsi: il codice è diventato ingombrante, difficile da manutenere, parte della funzionalità si rompeva, sono comparsi ritardi, perdite di memoria e, in generale, l'intero spettro di problemi che solitamente si accumulano in mesi — qui sono emersi quasi subito.

A un certo punto è diventato evidente: il problema non è che l'agente scrive codice "cattivo". Il problema è la velocità con cui questo codice si accumula senza controllo.


Cosa si rompe con questo approccio

Quando si rimuove il controllo manuale, iniziano a manifestarsi schemi tipici:

  • aggiramento dei livelli architettonici;
  • duplicazione della logica;
  • confini dei moduli sfumati;
  • dipendenze casuali;
  • complicazione del codice senza necessità;
  • graduale degrado delle prestazioni.

L'importante è che tutto questo non accada istantaneamente. Ogni singolo passo sembra "normale", ma complessivamente il sistema sfugge rapidamente al controllo.


Perché un solo prompt non è bastato

Il mio primo tentativo è stato scontato — ho raccolto un grande rules.md dove ho inserito:

  • restrizioni architettoniche;
  • naming conventions;
  • regole dei livelli;
  • accordi locali del progetto.

Questo ha aiutato in parte, ma non ha risolto il problema.

Nella pratica è emerso che:

  • un contesto lungo funziona in modo instabile;
  • parte delle regole viene ignorata col tempo;
  • il modello non applica sempre le restrizioni in modo coerente;
  • il costo e il tempo di risposta crescono con la dimensione del prompt.

Alla fine sono arrivato a una conclusione più pragmatica: Le regole importanti devono essere verificate, non solo descritte.


1. Konsist: l'architettura come contratto eseguibile

Restrizioni architettoniche e controllo dell
I guardrail architettonici creano un perimetro di sicurezza per il lavoro dell'agente IA

Per impostazione predefinita, l'agente sceglie la via di implementazione più semplice. Se può saltare un livello — lo farà.

Per limitare questo, ho iniziato a descrivere l'architettura tramite test con Konsist (uno strumento per verificare le regole architettoniche del codice Kotlin tramite test unitari).

Esempio:

@Test
fun `use cases should have UseCase suffix and reside in domain package`() {
    val classes = Konsist.scopeFromProject().classes()

    val violations = classes
        .filter { classDeclaration ->
            classDeclaration.name?.endsWith("UseCase") != true ||
            !classDeclaration.resideInPackage("com.core.domain")
        }

    if (violations.isNotEmpty()) {
        val message = buildString {
            appendLine("VIOLATION: UseCase naming conventions not followed")
            appendLine("FIX: Rename class to end with 'UseCase'")
            appendLine("FIX: Move class to com.core.domain package")
        }
        fail(message)
    }
}

Qui sono fondamentali per me due cose:

  1. La verifica rileva la violazione;
  2. Il messaggio fornisce una direzione chiara su come risolverla.

L'agente smette di lavorare "nel vuoto" e inizia a lavorare all'interno di un ciclo di feedback.


2. Compressione dei log: meno rumore — iterazioni più veloci

Uno dei problemi che ho riscontrato è il volume dei log.

Se dai all'agente l'intero output di Gradle o JUnit, si perde semplicemente in quel volume. Il contesto si riempie di rumore, non di segnale.

Per questo ho creato un semplice livello di compressione:

  • mantengo solo i test falliti;
  • prendo un breve messaggio d'errore;
  • limito lo stacktrace;
  • rimuovo tutto il superfluo.
Filtraggio e compressione dei log per un uso efficiente della finestra di contesto degli LLM
Compressione dei log: filtrare il „rumore“ permette al modello di concentrarsi sui bug reali

Esempio:

def parse_xml_reports(root_dir):
    summary = []

    for testcase in root.findall(".//testcase"):
        failure = testcase.find("failure")
        if failure is not None:
            message = failure.get("message", "No message")
            text = failure.text or ""

            stacktrace = "\n".join(text.strip().split("\n")[:15])

            summary.append(f"FAILED: {testcase.get('name')}")
            summary.append(f"Message: {message}")
            summary.append(f"Stacktrace:\n{stacktrace}\n")

    return summary

Dopo di ciò il ciclo „si rompe → ripariamo“ è diventato notevolmente più veloce e prevedibile.


3. Spotless e detekt: igiene di base senza intervento umano

Il livello successivo — verifica automatica della qualità:

  • Spotless (uno strumento universale per la formattazione automatica del codice);
  • detekt (analizzatore statico per Kotlin per cercare potenziali problemi e strutture complesse).

Ho smesso di percepirli come „strumenti aggiuntivi“. Sono semplicemente parte del processo.

Se il codice non supera queste verifiche — non è considerato pronto. L'agente torna indietro e corregge autonomamente.

Questo elimina:

  • micro-correzioni in fase di revisione;
  • dispute sullo stile;
  • graduale degrado della leggibilità.

4. Traduzioni: eliminazione di errori meccanici

strings.xml si è rivelato un punto di criticità inaspettato.

Gli LLM sbagliano regolarmente con:

  • apostrofi;
  • virgolette;
  • sequenze di escape.

Non ho provato a „istruire“ il modello su questo tramite testo. È risultato più semplice aggiungere:

  • una verifica;
  • correzione automatica;
  • lavoro tramite parsing XML.

Importante: senza modifiche globali drastiche — solo correzioni mirate.


5. Prestazioni e dimensioni

Un effetto collaterale manifestatosi col tempo è stato il degrado invisibile del prodotto.

L'agente può risolvere il compito, ma nel frattempo:

  • aggiungere una dipendenza pesante;
  • complicare il codice;
  • aumentare la dimensione della build;
  • influire sulle prestazioni.

Per monitorare questo, ho aggiunto:

  • benchmark di base;
  • controllo della dimensione della build.
Monitoraggio delle prestazioni e delle dimensioni di un
Il monitoraggio costante delle metriche previene il degrado „silenzioso“ del prodotto

Non dà una garanzia totale, ma permette almeno di notare per tempo le deviazioni.


6. Pre-commit: filtro rapido (Mercurial/hg o Git)

Parte delle verifiche le ho spostate nel pre-commit:

  • auto-formattazione;
  • linting puntuale;
  • verifiche architettoniche per moduli critici.

L'idea non è „vietare tutto“, ma:

  • catturare rapidamente i problemi di base;
  • non portarli nel repository;
  • non sovraccaricare la revisione con rumore inutile.

Perché funziona

L'essenza dello sviluppo non è cambiata. Test, linter e restrizioni architettoniche sono sempre stati la norma.

È cambiato altro: la velocità con cui appare il codice.

Quando le modifiche vengono generate velocemente e in grandi volumi, il controllo manuale smette di scalare. Ciò che prima si poteva „catturare a occhio“, ora semplicemente non si ha il tempo di processarlo.

In questo contesto, le verifiche automatiche smettono di essere una „buona pratica“ e diventano una necessità di base — semplicemente per mantenere il sistema in uno stato stabile.


Conclusione

Non percepisco questo approccio come universale o obbligatorio per tutti.

Ma nel mio caso ha dato un effetto tangibile — prima di tutto grazie alla riduzione del carico cognitivo.

Non ho più bisogno di:

  • tenere a mente tutte le restrizioni;
  • leggere manualmente ogni modifica;
  • analizzare log enormi;
  • ricontrollare costantemente le basi.

Il sistema se ne occupa, e io intervengo dove ha davvero senso.

Tra i vantaggi pratici che ho notato:

  • iterazioni più stabili senza regressioni casuali;
  • comportamento dell'agente prevedibile grazie al feedback;
  • ciclo di correzione più veloce;
  • meno rumore in fase di revisione;
  • più facile scalare l'uso dell'agente.

Questo non risolve tutti i problemi, ma rende il processo di lavoro con un agente decisamente più gestibile — almeno secondo la mia esperienza.


Link utili

  • Konsist — documentazione aggiornata sul lint architettonico per Kotlin.
  • Spotless — strumento per la formattazione automatica del codice.
  • detekt — analizzatore statico di codice per Kotlin.
  • Mercurial (hg) — sistema di controllo versione.
  • Maestro — piattaforma per l'automazione dei test UI mobile.

Nel prossimo articolo analizzerò come ho aggiunto a tutto questo l'esecuzione automatica di scenari UI e la verifica dell'interfaccia tramite MCP Mobile e Maestro (framework per test UI mobile semplici e dichiarativi).