Automazione documentazione tecnica con LLM: da codice a wiki aziendale senza sforzo

Automazione documentazione tecnica con LLM: da codice a wiki aziendale senza sforzo

A luglio 2025 un'azienda del settore servizi di consulenza fiscale e del lavoro - 35 dipendenti, fatturato annuo nell'ordine dei 5 milioni di euro, gestionale PHP sviluppato internamente in otto anni - mi ha contattato con un problema classico delle PMI italiane: una codebase di circa 120.000 righe di PHP (fra Laravel e moduli custom legacy) senza un singolo file di documentazione tecnica degna di questo nome. Il CTO dell'azienda era appena stato sostituito dopo due anni, il nuovo responsabile IT aveva una pipeline di onboarding di altri due developer in programma, e si era reso conto che portarli al pieno contributo avrebbe richiesto 3-4 mesi ciascuno, quasi interamente dedicati a leggere il codice e interrogare il team storico. Moltiplicato per due persone, significavano 8 mesi di stipendio sprecati in attività di "comprensione pura" che avrebbero dovuto essere sostituite da documentazione leggibile. La richiesta era concreta: "abbiamo bisogno di una wiki che descriva come funziona il sistema, modulo per modulo, in un linguaggio che anche un project manager capisca. Non abbiamo il tempo di scriverla a mano, e anche se l'avessimo il team tecnico non ha le competenze per scrivere documentazione leggibile".

In sei settimane ho costruito una pipeline di automazione che legge la codebase PHP, ne estrae contesto strutturale (classi, metodi, relazioni fra moduli, flussi di esecuzione), e lo invia a Claude API con prompt calibrati per produrre tre livelli di documentazione: overview per non-tecnici (pagine da una pagina ciascuna che descrivono cosa fa ogni modulo in italiano piano), architettura per tecnici junior (pagine che spiegano come i moduli si interconnettono, con diagrammi ASCII), riferimento API per developer (documentazione di ogni endpoint e di ogni classe con esempi d'uso). Al termine della pipeline, le tre classi di documentazione vengono pubblicate automaticamente su Confluence (potrebbe essere Notion, Wiki.js o Outline - la scelta dipende dal cliente), con struttura gerarchica e link incrociati. Il costo operativo effettivo dell'intera pipeline è intorno ai 2 euro al mese di chiamate API, perché la pipeline è incrementale: rigenera solo la documentazione dei file che cambiano fra un deploy e l'altro, non l'intera codebase ogni volta. L'onboarding dei due nuovi developer, partiti a settembre 2025, è durato 6 settimane ciascuno invece dei 3-4 mesi preventivati - un risparmio cumulato di circa 8-10 settimane di stipendio pagato per "leggere codice e chiedere al team storico".

Perché la documentazione scritta a mano fallisce sistematicamente nelle PMI

La domanda legittima prima di proporre una soluzione automatica è: perché non scrivere la documentazione a mano, una volta? La risposta è che quasi tutte le aziende ci hanno provato e hanno fallito, per ragioni strutturali non legate alle buone intenzioni del team. La prima ragione è che scrivere documentazione è un'attività che compete con tutto il resto: quando c'è una deadline di feature, la documentazione è la prima cosa che salta. La seconda è che la documentazione invecchia rapidamente - un documento accurato oggi è potenzialmente obsoleto fra tre mesi se il codice sottostante è stato modificato, e nessuno ha memoria o disciplina per aggiornarla sistematicamente. La terza è che il pubblico della documentazione è eterogeneo: quello che serve al project manager (flussi di business, responsabilità dei moduli) è diverso da quello che serve al developer junior (come chiamare un'API interna) che è diverso da quello che serve al nuovo CTO (architettura, decisioni storiche, debito tecnico accumulato).

L'approccio tradizionale in un'azienda strutturata è di avere un technical writer dedicato, ma nel contesto PMI italiano questo è fuori budget. L'approccio "scrivi doc inline ai metodi" (PHPDoc, docstrings) produce materiale di riferimento utile per il developer ma inutile per il project manager. L'approccio "scrivi documentazione di architettura nei README di ogni modulo" parte con buone intenzioni ma in sei mesi i README sono disallineati rispetto al codice. L'automazione con LLM attacca esattamente questa disciplina mancante: la documentazione viene rigenerata automaticamente a ogni deploy, quindi non può invecchiare, e viene prodotta in più livelli di lettura, quindi serve pubblici diversi. Non è un sostituto di una buona architettura informativa, è un moltiplicatore che rende quella architettura sostenibile senza disciplina umana continua.

Questa impostazione è coerente con la più generale strategia di automazione di processi IT con LLM, che ho descritto in più dettaglio in un articolo complementare sulla costruzione di MCP server personalizzati per integrare Claude con strumenti aziendali come database, API gestionali, CMS e sistemi di ticketing. Il pattern comune è: l'LLM non sostituisce il lavoro umano di progettazione, sostituisce il lavoro umano di esecuzione ripetitiva che nessuno vuole fare.

L'architettura della pipeline: estrazione, generazione, pubblicazione

La pipeline si articola in tre stadi distinti, ognuno con responsabilità chiare. Il primo stadio è l'estrazione del contesto dalla codebase. Uno script PHP con analisi statica (AST parsing via nikic/PHP-Parser) legge ogni file, estrae namespace, nome classe, metodi pubblici, firma dei parametri con tipi, commenti PHPDoc esistenti, uses di altre classi (per ricostruire dipendenze). Questa fase produce, per ogni file PHP, un JSON strutturato di "metadata" che è compatto (tipicamente 5-15 KB per file invece dei 50-500 KB del sorgente completo), deterministic (lo stesso input produce lo stesso output), e sufficiente per generare documentazione di alto livello.

Un estratto dello script di estrazione:

<?php
// scripts/extract-context.php - semplificato
use PhpParser\ParserFactory;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Node;

class ContextExtractor extends NodeVisitorAbstract
{
    public array $context = [];
    private string $currentClass = '';
    private string $currentNamespace = '';

    public function enterNode(Node $node)
    {
        if ($node instanceof Node\Stmt\Namespace_) {
            $this->currentNamespace = $node->name->toString();
        }

        if ($node instanceof Node\Stmt\Class_) {
            $this->currentClass = $this->currentNamespace . '\\' . $node->name;
            $this->context[$this->currentClass] = [
                'type' => 'class',
                'doc' => $this->extractDoc($node),
                'extends' => $node->extends?->toString(),
                'implements' => array_map(fn($i) => $i->toString(), $node->implements),
                'methods' => [],
                'uses' => [],
            ];
        }

        if ($node instanceof Node\Stmt\ClassMethod && $node->isPublic()) {
            $params = [];
            foreach ($node->params as $param) {
                $params[] = [
                    'name' => $param->var->name,
                    'type' => $param->type ? $this->typeToString($param->type) : 'mixed',
                    'default' => $param->default ? 'yes' : null,
                ];
            }
            $this->context[$this->currentClass]['methods'][] = [
                'name' => $node->name->toString(),
                'doc' => $this->extractDoc($node),
                'params' => $params,
                'returnType' => $node->returnType ? $this->typeToString($node->returnType) : null,
                'lineCount' => $node->getEndLine() - $node->getStartLine(),
            ];
        }
    }

    private function extractDoc(Node $node): ?string
    {
        $comment = $node->getDocComment();
        return $comment ? $comment->getText() : null;
    }

    // ... helper methods
}

// Main
$parser = (new ParserFactory())->createForNewestSupportedVersion();
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('app/'));

foreach ($files as $file) {
    if (!$file->isFile() || $file->getExtension() !== 'php') continue;

    $code = file_get_contents($file->getRealPath());
    $ast = $parser->parse($code);

    $extractor = new ContextExtractor();
    $traverser = new NodeTraverser();
    $traverser->addVisitor($extractor);
    $traverser->traverse($ast);

    $outputPath = 'artifacts/context/' . str_replace(['/', '.'], '_', $file->getRelativePathname()) . '.json';
    file_put_contents($outputPath, json_encode($extractor->context, JSON_PRETTY_PRINT));
}

Il secondo stadio è la generazione della documentazione via Claude API. Uno script Python (o PHP) legge i context JSON prodotti dallo stadio 1, e per ciascuno chiama Claude con un prompt strutturato che chiede di produrre un markdown specifico al livello richiesto. Il prompt varia per livello:

PROMPT per overview non-tecnica:
Sei un technical writer che produce documentazione per project manager
non-tecnici di una software house. Ti passo l'analisi statica di un file PHP.
Produci una descrizione di mezza pagina che risponda a:
1. Cosa fa questo modulo nel linguaggio del business (non tecnico)?
2. Quali dipendenze ha su altri moduli?
3. Quando un project manager dovrebbe preoccuparsi di questo modulo?
Usa italiano piano, nessun termine tecnico non spiegato, massimo 400 parole.
Output: markdown.

PROMPT per riferimento tecnico:
Sei un developer senior che produce documentazione API per altri developer.
Ti passo l'analisi statica di un file PHP. Produci documentazione markdown che:
1. Elenca ogni metodo pubblico con firma completa
2. Per ogni metodo descrive brevemente lo scopo e i parametri
3. Indica esempi d'uso quando desumibili dalla signature
Usa italiano tecnico, stile compatto tipo JavaDoc.
Output: markdown.

Il terzo stadio è la pubblicazione su Confluence (o sistema wiki equivalente). Uno script chiama l'API REST di Confluence per creare o aggiornare pagine, mantenendo la gerarchia pensata (spazio root, sotto-spazi per livello di documentazione, pagine per modulo). La chiave per evitare duplicazioni è identificare ogni pagina con un metadata univoco (ad esempio il path del file sorgente) e fare upsert condizionale. Il REST API di Confluence è documentato ufficialmente da Atlassian per l'integrazione con sistemi esterni e supporta publishing programmatico.

Stai cercando un Consulente Informatico esperto per automatizzare la generazione di documentazione tecnica su codebase PHP legacy o su nuovi progetti Laravel/Symfony, con calibrazione per pubblici eterogenei e pubblicazione automatica su Confluence o wiki aziendali? Nel mio profilo professionale trovi l'esperienza concreta su LLM automation, pipeline CI/CD per documentazione, integrazione con strumenti di knowledge management aziendale.

L'incrementalità: generare solo ciò che è cambiato

Il pattern naïve sarebbe di rigenerare tutta la documentazione a ogni deploy. Su 120.000 righe di PHP sparse in ~850 file, questo significherebbe ~850 chiamate API per deploy, circa 50-80 euro di costo LLM per run. Ripetuto per release quotidiane, diventa una spesa significativa per un'attività di commodity.

La pipeline diventa economicamente sostenibile grazie al pattern di generazione incrementale: ogni run rigenera solo i file che sono cambiati dal run precedente. L'implementazione è questa: lo stadio 1 salva un hash SHA256 di ogni file sorgente; prima di inviare il context a Claude, verifica se l'hash è cambiato rispetto al precedente run (file docs-generation-manifest.json che mantiene timestamp + hash per ogni file). Se l'hash è identico, la pagina Confluence esistente è ancora valida e lo step viene skippato. Se è cambiato, il context va alla LLM e la pagina viene aggiornata.

In pratica, dopo il run iniziale completo (costoso - ~850 chiamate, ma singolo evento), i run successivi processano solo il diff. Su una codebase con ciclo di deploy tipico di PMI (3-5 file modificati per giorno di sviluppo attivo), ogni run costa 3-5 chiamate API, che sono pochi centesimi. Da qui i 2 euro al mese di costo operativo effettivo.

Il pattern ha un edge case: se una classe "stabile" viene modificata indirettamente (ad esempio una classe parent cambia, e la documentazione della classe child ne dipende concettualmente anche se il file child non è toccato), il sistema non rileva il cambio. La soluzione pragmatica è un job settimanale che forza la rigenerazione di tutto (overhead di ~850 chiamate una volta alla settimana, ~80 euro al mese di costo addizionale). Ho valutato con il cliente se questo overhead valesse la pena; abbiamo optato per un full refresh mensile invece che settimanale, basato sulla nostra stima che disallineamenti concettuali in questa tipologia di codebase siano rari.

Il prompt engineering: come ottenere documentazione che qualcuno vuole leggere

Il fattore più determinante della qualità finale è la formulazione dei prompt. Nei primi giorni di sviluppo ho sprecato quattro iterazioni producendo documentazione tecnicamente corretta ma noiosa - una descrizione in prosa delle firme dei metodi che nessuno avrebbe letto. Il cambio di direzione è arrivato aggiungendo al prompt vincoli narrativi espliciti.

Per l'overview non-tecnica, il prompt finale contiene direttive come: "immagina di spiegare al tuo project manager cosa fa questo modulo alla pausa caffè, in meno di 3 minuti, senza che lui si annoi. Usa analogie concrete dal mondo del business, non metafore astratte. Se il modulo gestisce fatturazione, parla di fatture; se gestisce preventivi, parla di preventivi; non parlare mai di 'oggetti' o 'interfacce' o 'astrazioni'. Finisci con una frase che dice al project manager quando questo modulo è rilevante per lui (es. quando un cliente ha un problema con la fatturazione elettronica; quando vogliamo aggiungere un nuovo tipo di preventivo)."

Per il riferimento tecnico, il prompt finale è: "stile JavaDoc ma in italiano. Per ogni metodo, una frase che dice cosa fa, una che chiarisce quando si usa, un esempio d'uso se desumibile dalla firma. Se la firma include parametri con nomi non autoesplicativi (es. 'x', 'data', 'tmp'), segnala esplicitamente che il codice ha debito tecnico di naming e suggerisci rinominazione - non tentare di inventare significati che non sono nel codice."

L'ultima direttiva è la più importante: segnala limiti e debito tecnico. La documentazione automatica onesta è quella che dice "questo metodo ha parametri mal nominati, raccomando rinomina" invece di fingere chiarezza che non c'è. Questa disciplina trasforma la pipeline in un co-pilot per il team, non in un generatore di contenuto cosmetico.

Verifica e correzione: il punto dove l'umano resta indispensabile

L'LLM non è perfetto. Anche con prompt calibrati, produce occasionalmente descrizioni sbagliate (capisce male la semantica di un metodo, inventa dipendenze che non esistono, applica metafore business non appropriate al dominio specifico). Il pattern di verifica che ho introdotto è di due livelli.

Primo livello: verifica automatica della coerenza strutturale. Uno script post-generazione legge la markdown prodotta e verifica che faccia riferimento solo a classi e metodi che esistono davvero nel context estratto allo stadio 1. Se la documentazione dice "il modulo X usa la classe Y" ma Y non è mai importata da X secondo l'AST, questo è un segnale di allucinazione LLM. Il report di anomalie viene postato come commento sulla PR di aggiornamento docs in Confluence, per triage umano.

Secondo livello: review umana periodica. Una volta al mese, un developer senior del team dedica 2 ore a sfogliare la documentazione più consultata (analizzata con le metriche di view di Confluence), legge spot-check su 10-15 pagine, e corregge eventuali errori direttamente in Confluence come suggerimenti al prompt. Il pattern è che i suggerimenti diventano parte del prompt template per le prossime generazioni, in modo che l'LLM impari dai feedback umani. Questo è un ciclo di miglioramento continuo che dopo 3-4 mesi raggiunge un livello di qualità stabile in cui le correzioni sono rare.

Questo pattern di review iterativa è lo stesso che descrivo nel mio articolo sulla costruzione di una pipeline di code review automatizzata con LLM su GitHub/GitLab per aziende italiane - l'LLM funziona meglio quando ha un umano nel loop che corregge periodicamente, non quando viene lasciato in autonomia totale.

I limiti onesti: quando la pipeline non produce valore

La pipeline automatica non sostituisce completamente il lavoro di architettura informativa. Ci sono tre categorie di documentazione che restano meglio scritte a mano. Prima: le decisioni architetturali storiche (perché abbiamo scelto PostgreSQL invece di MySQL nel 2021, perché il modulo di reporting è stato estratto come microservizio, perché non abbiamo adottato React ma Inertia.js). Queste sono informazioni non desumibili dal codice corrente e richiedono memoria del contesto passato che solo un umano ha. Seconda: i runbook operativi per situazioni di emergenza (come ripristinare il sistema se il database crasha, come identificare un data breach, chi chiamare alle 3 di notte). Queste sono procedure che richiedono precisione e rigore editoriale, non prosa generativa. Terza: la documentazione di onboarding per specifici ruoli (cosa deve sapere un junior nei primi 30 giorni, quali ambienti configurare, a chi chiedere cosa). Questa richiede conoscenza della struttura organizzativa aziendale che l'LLM non ha.

Per il cliente della consulenza fiscale, la pipeline automatica gestisce il 70% della documentazione necessaria (overview moduli, riferimenti API, architettura di base). Il 30% rimanente è scritto a mano dal team interno, con cadenza più rilassata perché il lavoro volumetrico più pesante è già coperto. La combinazione dei due approcci è sostenibile - mentre solo documentazione manuale non lo era, e solo automazione non coprirebbe le parti critiche del knowledge management.

Il ROI a otto mesi: quanto ha risparmiato l'azienda

A otto mesi dal rollout della pipeline, le metriche del cliente sono queste. Tempo medio di onboarding di nuovo developer: 6 settimane (vs 12-16 settimane pre-documentazione). Risparmio cumulativo sui due onboarding gestiti in questa finestra: circa 9 settimane di stipendio full-time, equivalenti a ~12.000 euro. Tempo medio di risoluzione di un ticket di supporto che richiede comprensione di un modulo del sistema: 45 minuti (vs 2.5 ore pre-documentazione, misurato su 47 ticket campione). Risparmio cumulativo: circa 50 ore di lavoro del team di supporto nei primi 6 mesi, equivalenti a ~1.800 euro. Costo totale della pipeline: 2 euro/mese × 8 mesi + 2 giornate-uomo/mese di review umana × 8 mesi × ~300 euro/giornata = ~4.816 euro. ROI netto a 8 mesi: ~9.000 euro di risparmio netto, con pipeline che continua a girare senza richiesta di effort aggiuntivo.

Il beneficio meno quantificabile è la qualità percepita del lavoro del team. Il CTO del cliente mi ha riferito che durante un audit tecnico da parte di un cliente enterprise, la presenza di documentazione strutturata e aggiornata è stata specificamente apprezzata e citata come "segnale di maturità operativa". Un audit passato con osservazioni positive si traduce in rinnovo contratto confermato - valore economico diretto, difficile da stimare in anticipo ma reale. Il pattern è descritto nel mio articolo sull'allineamento NIS2 per software house con processi interni adeguati in 6 mesi, dove la documentazione è uno dei sette deliverable del pacchetto di compliance finale.

Se gestisci un progetto PHP/Laravel o Symfony con codebase ampia e documentazione carente, e stai subendo il costo dell'onboarding lento o della confusione interna sui flussi del sistema, oppure stai per affrontare un audit tecnico da parte di un cliente enterprise e sai che la scarsa documentazione è un red flag, contattami per una valutazione: in una settimana di lavoro analizzo la struttura della tua codebase, disegno la pipeline di generazione documentazione con Claude API calibrata per tre livelli di pubblico, imposto l'integrazione con Confluence (o il sistema wiki che usi), e ti consegno il primo deliverable di documentazione completa entro la seconda settimana - con costo operativo trascurabile e review umana leggera una volta al mese.

Ultima modifica: