tempo di lettura : 12 minutes

La tua codebase sta crescendo. Le dipendenze tra i moduli si moltiplicano. La documentazione dell’architettura diventa obsoleta prima ancora di essere scritta. E se il tuo build Gradle potesse automaticamente generare diagrammi aggiornati a partire dalla struttura reale del codice? Questo è esattamente ciò che fa il pipeline Graphify + PlantUML Gradle Plugin :`graphify . --no-viz`estratto il Knowledge Graph,`./gradlew generateKnowledgeGraphDiagram`lo trasforma in diagrammi PlantUML. Zero LLM, zero manuale, 100% deterministico.

Il problema: diagrammi sempre in ritardo sul codice

Ogni progetto che supera qualche migliaio di righe conosce questa sindrome:

  1. Si disegna un diagramma di architettura all’inizio del progetto

  2. Il codice evolve, le dipendenze cambiano

  3. Il diagramma diventa una menzogna decorativa

  4. Nessuno lo aggiorna perché è noioso

  5. I nuovi arrivati si basano su di esso e commettono errori

@startuml
skinparam backgroundColor #FEFEFE
skinparam ActivityBackgroundColor #FDEDEC
skinparam ActivityDiamondBackgroundColor #FFF3CD

start

:Diagramme d'architecture créé\n(manuellement, à la main);

:Le code évolue\n(nouvelles classes, nouvelles dépendances);

:Diagramme obsolète\n(ne reflète plus le code);

if (Qui met à jour ?) then (Personne)
    :Documentation mensongère;
    note right: Les nouveaux développeurs\nse basent dessus... et font des erreurs
else (Quelqu'un)
    :Mise à jour manuelle\n(2-3 heures de travail);
    note right: Déjà obsolète\nà la prochaine PR
endif

stop

@enduml

La domanda non è bisogna avere dei diagrammi? — tutti sanno che sì. La domanda è :chi li mantiene aggiornati?

La risposta: nessuno. Salvo se è automatica.

La soluzione: un pipeline deterministico Knowledge Graph → PlantUML

Il principio è semplice: invece di disegnare i diagrammi a mano, ligenera dalla struttura reale del codice.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

actor Développeur
component "Graphify\n(pip install graphifyy)" as Graphify
collections "graphify-out/graph.json\n(Knowledge Graph)" as KGJSON
component "PlantUML Gradle Plugin\n(generateKnowledgeGraphDiagram)" as Plugin
component "KnowledgeGraphParser" as Parser
component "KnowledgeGraphRenderer" as Renderer
component "PlantumlService\n(validation + rendu PNG)" as PS
collections "diagrams/knowledge-graph/\n(.puml + .png)" as Output

Développeur --> Graphify : graphify . --no-viz
Graphify --> KGJSON : extrait la structure\ndu code source
Développeur --> Plugin : ./gradlew generateKnowledgeGraphDiagram
Plugin --> Parser : parse(graph.json)
Parser --> Plugin : KnowledgeGraph\n(noeuds + arêtes + communautés)
Plugin --> Renderer : render(graph, filters)
Renderer --> Plugin : code PlantUML\ndéterministe
Plugin --> PS : validateSyntax + generateImage
PS --> Output : .puml + .png

note bottom of Plugin
  Pipeline DÉTERMINISTE
  Aucun appel LLM
  Résultat reproductible
end note

@enduml

Due comandi. È tutto.

# Étape 1 : extraire le Knowledge Graph
graphify . --no-viz

# Étape 2 : générer les diagrammes PlantUML
./gradlew generateKnowledgeGraphDiagram

Il risultato? Dei file`.puml` et .png`in`diagrams/knowledge-graph/, versionati in Git, sempre aggiornati con il codice.

Passo 1: Installare Graphify ed estrarre il Knowledge Graph

Installazione

Graphify è uno strumento Python che analizza il tuo codebase e costruisce un grafo di conoscenza strutturato :

# Méthode recommandée
uv tool install graphifyy && graphify install --platform opencode

# Alternative avec pip
pip install graphifyy && graphify install --platform opencode

Configurare le esclusioni

Creare un file`.graphifyignore`alla radice del progetto per escludere i file che non fanno parte della logica di business:

# Secrets — JAMAIS dans le graphe
*-context.yml
*.env

# Fichiers générés
build/
.gradle/

# Tests fonctionnels
src/functionalTest/

Estrarre il Knowledge Graph

graphify . --no-viz

Il flag`--no-viz`Salta la generazione HTML (inutile in un pipeline Gradle). Il risultato è un file.`graphify-out/graph.json`contenente :

  • Nodi: classi, funzioni, file — con il loro tipo e comunità

  • spigoli: relazioni tra nodi (ESTRATTO dal codice, INFERITO dall’LLM)

  • Comunità: raggruppamenti automatici di nodi collegati

Esempio di struttura`graph.json`
{
  "nodes": [
    {"id": "0", "label": "LlmService", "file_type": "code", "community": 0},
    {"id": "1", "label": "ApiKeyPool", "file_type": "code", "community": 0},
    {"id": "2", "label": "PlantumlService", "file_type": "code", "community": 1}
  ],
  "links": [
    {"source": "1", "target": "0", "relation": "uses", "confidence": "EXTRACTED", "weight": 0.9},
    {"source": "0", "target": "2", "relation": "calls", "confidence": "INFERRED", "weight": 0.7}
  ]
}

Il codice sorgente (.kt, .java) viene analizzato localmente da tree-sitter,LLM senza appello. Solo i file di documentazione (.adoc, .md) necessitano una chiamata LLM per l’estrazione semantica. Quindi`--update`Sul codice Kotlin è quasi istantaneo.

Passo 2: Il plugin Gradle trasforma il Knowledge Graph in PlantUML

Architettura del pipeline

Il plugin`com.cheroliv.plantuml`integra un compito`generateKnowledgeGraphDiagram`che trasforma`graph.json`in diagrammi PlantUML in modototalmente deterministico:

@startuml
skinparam backgroundColor #FEFEFE

participant "Gradle" as G
participant "GenerateKnowledgeGraphDiagramTask" as Task
participant "KnowledgeGraphParser" as Parser
participant "KnowledgeGraphRenderer" as Renderer
participant "PlantumlService" as PS

collections "graphify-out/graph.json" as JSON

G -> Task : execute
Task -> JSON : read
Task -> Parser : parse(graph.json)
Parser --> Task : KnowledgeGraph\n(noeuds + arêtes + communautés)
Task -> Renderer : render(graph, filters)
Renderer --> Task : code PlantUML\n(déterministe)
Task -> PS : validateSyntax(plantumlCode)
alt syntaxe valide
    Task -> PS : generateImage → .png
else syntaxe invalide
    Task -> Task : save .puml avec warning
end

@enduml

I componenti interni

Componente Ruolo

KnowledgeGraphParser

Analizza`graph.json`— supporta 3 formati: graphify nativo (nodes+links), eredità (communities), flat. Risolve gli ID numerici in etichette.

KnowledgeGraphRenderer

Trasforma deterministicamente un`KnowledgeGraph`in codice PlantUML. Gruppi per tipo, comunità in package, leggenda automatica.

GenerateKnowledgeGraphDiagramTask

Attività Gradle che orchestra: analizza → renderizza → valida → PNG. Configurabile tramite proprietà Gradle.

kgmodels.kt

Modelli di dati :`KnowledgeGraph`, KnowledgeGraphNode, KnowledgeGraphEdge, KnowledgeGraphCommunity, EdgeType.

PlantumlService

Validazione sintattica + rendering PNG (riutilizzato da tutte le attività del plugin).

Regole di rendering

Il renderer applica convenzioni visive deterministiche :

Tipo di spigolo Notazione PlantUML Significato

EXTRACTED

-→(tratto pieno, nero)

Relazione estratta dal codice sorgente (certezza)

dedotto

..>(tratto tratteggiato)

Relazione inferita dall’LLM (punteggio di fiducia)

ambiguo

--x(linea rossa tratteggiata)

Relazione ambigua (da verificare)

Le comunità sono rese come pacchetti PlantUML con una palette di colori automatica.

Passo 3: Uso quotidiano

Diagramma completo

# Générer le diagramme du Knowledge Graph complet
./gradlew generateKnowledgeGraphDiagram

Uscita :`diagrams/knowledge-graph/knowledge-graph-full.puml`+.png

Filtra per comunità

# Une seule communauté
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0

# Limiter le nombre de noeuds (lisibilité)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.community=community_0 \
  -Pplantuml.kg.maxNodes=15

Filtrare per tipo di spigolo

# Uniquement les relations certaines (EXTRACTED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED

# Relations certaines + inférées
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.edgeTypes=EXTRACTED,INFERRED

Filtra per tipo di nodo e confidenza

# Uniquement les classes de code
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.nodeTypes=code

# Seuil de confiance minimum (pour les INFERRED)
./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.minConfidence=0.7

Directory di output personalizzata

./gradlew generateKnowledgeGraphDiagram \
  -Pplantuml.kg.outputDir=docs/architecture

Riferimento completo delle proprietà

Proprietà Difetto Descrizione

plantuml.kg.community

(tutte)

Filtra le comunità per nome (corrispondenza di sottostringa)

plantuml.kg.edgeTypes

(tutti)

Tipi di spigoli separati da virgole :`EXTRACTED`, INFERRED, AMBIGUOUS

plantuml.kg.minConfidence

0.0

Soglia minima di confidenza per gli archi

plantuml.kg.maxNodes

(illimitato)

Numero massimo di nodi da visualizzare

plantuml.kg.nodeTypes

(tutti)

Tipi di nodi separati da virgole (es.class, code)

plantuml.kg.outputDir

diagrams/knowledge-graph

Directory di output per i file`.puml` et .png

Il pipeline completo in un workflow Gradle

Tipo di flusso di lavoro

@startuml
skinparam backgroundColor #FEFEFE
skinparam ActivityBackgroundColor #E8F4FD
skinparam ActivityDiamondBackgroundColor #FFF3CD
skinparam ActivityBorderColor #2C3E50

start

partition "EXTRACTION" {
    :graphify . --no-viz;
    note right: Analyse le code source\ntree-sitter (local, pas de LLM)

    :graphify-out/graph.json;
    note right: Noeuds + arêtes\n+ communautés
}

partition "GÉNÉRATION" {
    :./gradlew generateKnowledgeGraphDiagram;
    note right: Pipeline déterministe\nparse → render → validate → PNG

    if (Filtres actifs ?) then (Oui)
        :Appliquer filtres\n(community, edgeTypes,\nmaxNodes, nodeTypes);
    else (Non)
        :Diagramme complet;
    endif

    :diagrams/knowledge-graph/\nknowledge-graph-full.puml\nknowledge-graph-full.png;
}

partition "INTÉGRATION" {
    :git add diagrams/knowledge-graph/;
    :git commit;
    note right: Diagrammes versionnés\ntoujours à jour avec le code
}

stop

@enduml

Integrazione nel ciclo di sviluppo

Il pipeline si integra naturalmente nelle fasi chiave dello sviluppo:

@startuml
skinparam backgroundColor #FEFEFE

state "Développement" as dev
state "Extraction\ngraphify . --no-viz" as extract
state "Génération\n./gradlew generateKnowledgeGraphDiagram" as generate
state "Commit\ndiagrammes versionnés" as commit

[*] --> dev
dev --> extract : Code modifié
extract --> generate : graph.json à jour
generate --> commit : .puml + .png générés
commit --> dev : Diagrammes dans le repo

note right of extract
  Quasi instantané sur du code Kotlin
  (tree-sitter, pas de LLM)
end note

note right of generate
  Déterministe
  Pas de LLM
  Résultat reproductible
end note

@enduml

Aggiornamento incrementale

Quando il codice cambia, non ricostruiamo l’intero grafo da zero :

# Mise à jour incrémentale (fichiers modifiés uniquement)
graphify . --update

# Puis régénérer les diagrammes
./gradlew generateKnowledgeGraphDiagram

--update`re-estrae solo i file modificati (rilevati da SHA256 in`graphify-out/cache/)). Sul codice Kotlin, è quasi istantaneo perché tree-sitter lavora localmente senza chiamate LLM.

Il dogfooding: il plugin si documenta da sé

Il plugin PlantUML esiste per trasformare i prompt in diagrammi. Può anche trasformare il Knowledge Graph della sua propre codebase in diagrammi di documentazione. È dogfooding : il plugin consuma il proprio servizio.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "Pipeline normale\n(utente → diagrammi)" as normal {
    [Fichier .prompt] as prompt
    [LlmService\n+ ApiKeyPool] as llm1
    [ProcessPlantumlPromptsTask] as task1
    [Diagramme PNG] as out1

    prompt --> task1
    task1 --> llm1
    llm1 --> out1
}

package "Pipeline Knowledge Graph\n(deterministica, nessun LLM)" as kg {
    [graphify-out/graph.json] as kgjson
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantumlService] as ps
    [Diagramme PNG\n(documentation du plugin)] as out2

    kgjson --> parser
    parser --> renderer
    renderer --> ps
    ps --> out2
}

package "Pipeline Dogfooding\n(LLM → documentazione del plugin)" as dogfood {
    [GraphifyPromptAdapter] as gpa
    [Fichiers .prompt\nauto-générés] as auto_prompt
    [LlmService\n+ ApiKeyPool] as llm2
    [ProcessPlantumlPromptsTask] as task2
    [Diagramme PNG\n(documentation LLM)] as out3

    kgjson --> gpa
    gpa --> auto_prompt
    auto_prompt --> task2
    task2 --> llm2
    llm2 --> out3
}

note "Stesso LlmService, stesso ApiKeyPool,\nstesso PlantumlService\n— ZERO duplicazioni" as N

@enduml

Attività Gradle associate :

Compito LLM ? Descrizione

generateKnowledgeGraphDiagram

No

Trasforma`graph.json`in PlantUML (deterministico)

generateDiagramDocs

Sì

Genera dei`.prompt`dal grafo, li elabora tramite LLM (dogfooding)

# Documentation déterministe (rapide, pas de LLM)
./gradlew generateKnowledgeGraphDiagram

# Documentation LLM (plus riche, consomme des tokens)
./gradlew generateDiagramDocs

Perché è deterministico (e perché conta)

Il punto chiave del pipeline`generateKnowledgeGraphDiagram` : non chiama nessun LLM. La trasformazione`graph.json`PlantUML è una funzione pura.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

rectangle "Pipeline DETERMINISTICA\n(generateKnowledgeGraphDiagram)" as det {
    [graph.json] as json
    [KnowledgeGraphParser] as parser
    [KnowledgeGraphRenderer] as renderer
    [PlantUML code] as puml

    json --> parser : parse
    parser --> renderer : KnowledgeGraph
    renderer --> puml : render (fonction pure)
}

rectangle "Pipeline LLM\n(processPlantumlPrompts)" as llm {
    [.prompt] as prompt
    [LlmService] as llmSvc
    [ChatModel] as model
    [PlantUML code] as puml2

    prompt --> llmSvc
    llmSvc --> model : API call
    model --> puml2 : réponse non-déterministe
}

note bottom of det
Stesso input → stesso output
Nessuna latenza di rete
Nessun costo in token
Riproducibile in CI
end note

note bottom of llm
Stesso input → output variabile
Latenza di rete (1-5s)
Costo in token
Richiede una chiave API
end note

@enduml

I vantaggi concreti :

Vantaggio Impatto

Riproducibilità

Anche`graph.json`→ lo stesso diagramma. Esattamente. Ogni volta.

Zero costo

Nessuna chiamata LLM = nessun token = nessuna fattura API.

Zero latenza

Parse + render richiede ~100ms, non 1-5 secondi.

Compatibile CI

Nessuna chiave API necessaria. Nessun test flaky dovuto a una risposta LLM variabile.

versionabile

Le .puml`generato è del testo. Lo possiamo`diff, il revisore in PR, versionarlo in Git.

Diagrammi del pipeline stesso

Per chiudere il cerchio, ecco il diagramma del pipeline come sarebbe generato dal plugin:

@startuml
skinparam backgroundColor #FEFEFE

participant "Développeur" as Dev
participant "Gradle" as G
participant "GenerateKnowledgeGraphDiagramTask" as Task
participant "KnowledgeGraphParser" as Parser
participant "KnowledgeGraphRenderer" as Renderer
participant "PlantumlService" as PS
collections "graphify-out/graph.json" as JSON
collections "diagrams/knowledge-graph/" as Out

Dev -> G : ./gradlew generateKnowledgeGraphDiagram
G -> Task : execute
Task -> JSON : read
Task -> Parser : parse(graph.json)
Parser --> Task : KnowledgeGraph

alt filtres actifs
    Task -> Task : appliquer filtres\n(community, edgeTypes, maxNodes…)
end

Task -> Renderer : render(graph, filters)
Renderer --> Task : code PlantUML

Task -> PS : validateSyntax()
alt valide
    Task -> PS : generateImage()
    PS --> Out : .puml + .png
else invalide
    Task -> Out : .puml (avec warning)
end

@enduml

Integrazione nella governance del progetto

Nel nostro progetto, questo pipeline si integra in una strategia di gestione del contesto EAGER/LAZY per l’agente IA. Il Knowledge Graph sostituisce la documentazione dell’architettura manuale con un grafo strutturato, queryable e aggiornato automaticamente.

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "EAGER\n(toujours chargé)" as eager {
    [PROMPT_REPRISE.adoc\n(contexte session)] as pr
    [INDEX.adoc\n(vue d'ensemble)] as idx
    [GRAPH_REPORT.adoc\n(~50 lignes)] as gr
}

package "LAZY — Stratégie\n(sur demande)" as lazy_strat {
    [Méthodologies] as meth
    [Archives sessions] as sessions
}

package "LAZY — Graphify\n(requêtes ciblées)" as lazy_graph {
    [graph.json] as gj
    [Queries\n(query/path/explain)] as queries
}

package "Pipeline PlantUML\n(auto-généré)" as puml {
    [generateKnowledgeGraphDiagram] as kgTask
    [generateDiagramDocs] as ddTask
    [Diagrammes PNG] as diagrams
}

Agent --> eager : Lit en début de session
Agent ..> lazy_strat : Charge si type détecté
Agent ..> lazy_graph : Query si besoin structurel
kgTask --> diagrams : Déterministe
ddTask --> diagrams : Via LLM

note right of gr
  Condensé du Knowledge Graph
  God nodes + communautés
  Remplace ECOSYSTEM_OVERVIEW
  (225 lignes → 50 lignes)
end note

@enduml

Il matrimonio dei due sistemi è complementare:

  • La strategia gestisce il QUAND e il COMMENT— governance, flusso di lavoro, archiviazione

  • Graphify gestisce il COSA e il DOVE— struttura del codice, relazioni, query mirate

_ La strategia di sessione gestisce ilquando et le COME(governance, workflow, soglie), Graphify gestisce ilCHE et le dove(struttura del codice, relazioni, query mirate). Il pipeline PlantUML gestisce ilCon cosa?(diagrammi deterministici, versionati, sempre aggiornati). _

Impostazione: checklist in 5 minuti

# Fase Ordine

1

Installa Graphify

uv tool install graphifyy

2

Configurare le esclusioni

Creare`.graphifyignore`

3

Estrarre il Knowledge Graph

graphify . --no-viz

4

Generare i diagrammi

./gradlew generateKnowledgeGraphDiagram

5

Committa i risultati

git add diagrams/knowledge-graph/

# Script complet en 5 commandes
uv tool install graphifyy
cat > .graphifyignore << 'EOF'
*-context.yml
*.env
build/
.gradle/
EOF
graphify . --no-viz
./gradlew generateKnowledgeGraphDiagram
git add graphify-out/GRAPH_REPORT.adoc graphify-out/graph.json diagrams/knowledge-graph/

Trappole e mitigazioni

Trappola Descrizione Mitigazione

Grafo troppo denso

Un grande progetto genera centinaia di nodi illeggibili

usare`-Pplantuml.kg.maxNodes=30`e filtrare per comunità

Esclusioni troppo larghe

Troppi file in`.graphifyignore`riduce il valore del grafo

Iniziare escludendo solo credentials e build

Direzione delle frecce

Gli archi INFERRED possono avere una direzione ambigua

Filtra per`-Pplantuml.kg.edgeTypes=EXTRACTED`per le relazioni certe solo

Grafo obsoleto

Il codice cambia ma il grafo non viene ricostruito

Utilizzare`graphify . --update`regolarmente o il hook git`graphify hook install`

Costo aggiornato

Gli aggiornamenti incrementali sono quasi gratuiti (tree-sitter local)

Solo i docs (.adoc) consumano dei token LLM

Ciò che si ottiene alla fine

@startuml
skinparam backgroundColor #FEFEFE

rectangle "PRIMA" as avant {
    card "Diagrammi disegnati a mano\nSempre obsoleti\nNessuno li aggiorna" as av1 #FDEDEC
}

rectangle "DOPO" as apres {
    card "Diagrammi generati automaticamente\nSempre aggiornati con il codice\nVersionati in Git\nDeterministici e riproducibili" as ap1 #E8F8E8
}

avant --> apres : graphify . --no-viz\n+ ./gradlew generateKnowledgeGraphDiagram

@enduml

I benefici concreti:

Beneficio Dettaglio

Documentazione sempre aggiornata

I diagrammi riflettono il codice corrente, non uno snapshot manuale

Zero sforzo di manutenzione

I diagrammi si rigenerano a ogni build

Riduzione del debito tecnico

Non c’è più bisogno di mantenere i diagrammi a mano

Contesto visivo per i nuovi

Un nuovo sviluppatore comprende l’architettura osservando i diagrammi

Potenziale fine-tuning

Le coppie (sottografo → diagramma) sono esempi di addestramento IA

Validazione automatica

`PlantumlService.validateSyntax()`verifica ogni diagramma generato

Onboarding rapido

5 diagrammi = vista completa dell’architettura

_ Chi ha un _perché può sopportare tutti i come. __

Il perché : diagrammi sempre aggiornati. Il come : due comandi in una pipeline Gradle.

Articoli correlati