Become a member!

Linguaggio DOT di Graphviz: la guida pratica che la documentazione ufficiale non ti dà

Per sviluppatori, analisti e software architect che vogliono produrre diagrammi professionali, chiari e belli da vedere

🌐
Questo articolo è disponibile anche in altre lingue:
🇬🇧 English  •  🇪🇸 Español  •  🇩🇪 Deutsch  •  🇧🇷 Português

Cos’è il linguaggio DOT? Cos’è Graphviz?

DOT è un linguaggio di descrizione di grafi in formato testuale. Permette di descrivere nodi, archi e attributi visivi usando una sintassi semplice e leggibile. È il formato standard usato da Graphviz.

Graphviz (Graph Visualization Software) è una suite open source di strumenti per la visualizzazione di grafi. Sviluppato originariamente da AT&T Labs, Graphviz legge file DOT e genera immagini in vari formati (SVG, PNG, PDF). È lo strumento più usato al mondo per generare diagrammi da codice.

Perché usare DOT e Graphviz invece di tool grafici?

  • Versionabile: I file DOT sono testo puro, perfetti per Git
  • Riproducibile: Lo stesso file genera sempre lo stesso diagramma
  • Automatizzabile: Genera diagrammi nella CI/CD pipeline
  • Veloce: Scrivi codice, non trascini box con il mouse
  • Professionale: Output di alta qualità per documentazione tecnica

Prefazione

Perché ho scritto questo manuale? Perché le guide a DOT e Graphviz esistono, ma parlano di tutto: biologia, chimica, reti sociali, alberi genealogici. Ogni volta dovevo cercare quello che serve davvero a chi sviluppa software. Quindi ho deciso di scrivere questa guida, mettendo insieme tutto quello che ho usato in questi anni e che ho dovuto cercare più e più volte. Mi è costata un po' di lavoro, ma spero ne valga la pena. Ora la condivido.

Il vantaggio di generare diagrammi da codice? Il file DOT vive nella repo, insieme al sorgente. Quando modifichi l'architettura, aggiorni il diagramma nello stesso commit, passa dalla stessa code review, segue lo stesso flusso. Non è un PowerPoint dimenticato su un drive che nessuno aprirà mai più. È parte del progetto.

Ma c'è di più. I file DOT sono testo, quindi puoi generarli programmaticamente. Il tuo software può produrre diagrammi che mostrano lo stato attuale del sistema, le fasi di una lavorazione, il flusso di una richiesta attraverso i microservizi, le dipendenze tra moduli caricate a runtime. Ho visto team generare automaticamente la mappa delle migrazioni database, il grafo delle feature flag attive, persino il diagramma delle code di messaggi in tempo reale. Le possibilità sono infinite.

Se anche tu sei stanco di screenshot che invecchiano il giorno dopo averli fatti, qui trovi tutto quello che serve per iniziare. E una volta che padroneggi DOT, scoprirai che i diagrammi non sono più documentazione statica: diventano parte viva del tuo sistema.

Daniele Teti
Novembre 2025


Introduzione

Nel lavoro quotidiano di chi sviluppa software (che tu sia analista, sviluppatore backend, frontend, database expert o architect) arriva sempre il momento in cui un diagramma diventa fondamentale.

Serve quando devi:

  • spiegare un flusso complesso,
  • disegnare la pipeline CI/CD,
  • raccontare l’architettura a un nuovo collega,
  • analizzare dipendenze tra moduli,
  • documentare un database,
  • preparare una presentazione tecnica,
  • ragionare su un refactoring,
  • identificare colli di bottiglia.

Graphviz e il suo linguaggio DOT sono strumenti ideali: testuali (versionabili), veloci da scrivere, belli da renderizzare, flessibili.

Installazione Graphviz

Prima di iniziare, assicurati di avere Graphviz installato:

  • Windows: Scarica l’installer da graphviz.org/download o usa winget install graphviz
  • macOS: brew install graphviz
  • Linux: sudo apt install graphviz (Debian/Ubuntu) o sudo dnf install graphviz (Fedora/RHEL)

Verifica l’installazione con dot -V: dovresti vedere la versione installata.

Questo manuale vuole essere la guida definitiva per chi nel software vuole usare il linguaggio DOT al massimo. Troverai:

  • spiegazione approfondita degli attributi con tutte le opzioni rilevanti,
  • esempi completi, riutilizzabili e commentati,
  • best practices per grafici professionali,
  • suggerimenti sull’uso dei layout engine,
  • scenari concreti dal mondo dello sviluppo (design, refactoring, analisi, architettura).

Il linguaggio DOT: basi solide

DOT descrive grafi con una sintassi molto semplice:

digraph Nome {
    nodoA -> nodoB;
}

Oppure non diretto:

graph Nome {
    nodoA -- nodoB;
}

Concetti chiave:

  • nodi: entità (funzioni, oggetti, microservizi, tabelle DB)
  • archi: relazioni, chiamate, flussi
  • attributi: aspetto visuale o metadati

Quick Start: il tuo primo diagramma in 30 secondi

Testa subito online (senza installare nulla)

Tutti gli esempi di questo articolo sono direttamente testabili online usando Edotor.net:

  1. Vai su edotor.net

  2. Copia il codice DOT di qualsiasi esempio da questo articolo

  3. Incollalo nell’area di sinistra (sostituendo il codice esistente)

  4. Vedi il rendering immediato nell’area di destra

È il modo più veloce per sperimentare senza installare Graphviz. Quando sei pronto per la produzione, installa Graphviz localmente.

Primo esempio da provare

Copia questo codice e testalo su edotor.net:

digraph MyFirstGraph {
    node [shape=box, style=rounded, fillcolor=lightblue, style="rounded,filled"];
    edge [color=blue];

    Start -> Process -> End;
    Process -> Error [style=dashed, label="on failure"];
    Error -> Process [label="retry"];
}

Oppure usa la command line locale

Crea un file hello.dot con il codice sopra e genera l’immagine SVG:

dot -Tsvg hello.dot -o hello.svg

Apri hello.svg nel browser e vedrai il tuo primo flowchart! Da qui in poi, tutto si basa su variazioni e combinazioni di questi concetti.


Attributi fondamentali (con opzioni complete)

Gli attributi possono essere applicati globalmente a:

  • graph
  • node
  • edge
  • a singoli elementi

Esempio:

digraph demo {
    graph [rankdir=LR];
    node [shape=box];
    edge [color=grey];

    A -> B;
}

Di seguito l’elenco degli attributi più importanti con le loro opzioni più utili in ambito software.


Attributi dei nodi (node)

shape: forma del nodo

Opzioni utili:

  • box
  • ellipse
  • circle
  • diamond (decisioni nei flow)
  • record (class diagrams, strutture dati)
  • plaintext (contenuti completamente personalizzati con HTML-label)
  • note
  • folder (disponibile in alcune build)

Esempio:

node [shape=box];

Usi tipici:

  • box → moduli software
  • ellipse → stati
  • diamond → decisioni

style: stile grafico

Opzioni comuni:

  • filled
  • dashed
  • dotted
  • bold
  • rounded
  • combinazioni: "filled,rounded"

Esempio:

node [style="filled,rounded"];

fillcolor: colore di riempimento

Formati:

  • nomi (e.g. "lightgrey")
  • HEX (e.g. "#AABBCC")
  • RGB ("#rrggbb")
  • HSL (in alcune build)

fontname, fontcolor, fontsize

Es.:

node [fontname="Segoe UI", fontsize=12, fontcolor="#333333"];

margin

Margine interno del nodo.

node [margin="0.2,0.1"];

width e height: dimensioni del nodo

Controllano la dimensione dei nodi. Di default Graphviz dimensiona i nodi in automatico in base al contenuto.

node [width=1.5, height=0.8];

fixedsize: forza le dimensioni esatte.

  • fixedsize=false (default): il nodo cresce per contenere il testo, e width/height valgono come dimensioni minime
  • fixedsize=true: il nodo ha esattamente le dimensioni indicate da width/height, qualunque sia il contenuto
  • fixedsize=shape: vale solo per la forma, non per la label

Casi d’uso pratici:

  • Aspetto uniforme: usa fixedsize=true con width per avere tutti i nodi della stessa dimensione (indispensabile nei diagrammi di stato e nei flowchart)
  • Dimensione adattiva: lascia il default fixedsize=false e i nodi si adattano alla lunghezza del testo
  • Solo larghezza fissa: combinalo con height per controllare le proporzioni
// Tutti gli stati della stessa dimensione (FSM professionale)
node [shape=circle, fixedsize=true, width=0.9];

// Dimensione minima, ma può crescere
node [shape=box, width=1.0, fixedsize=false];

label e xlabel: le etichette del nodo

label: l’etichetta standard, mostrata dentro il nodo o accanto:

A [label="State A"];

Label su più righe: usa \n per andare a capo:

A [label="Main Title\nSubtitle or description"];

xlabel: un’etichetta esterna, posizionata fuori dal bordo del nodo. Graphviz trova da solo la posizione migliore per non sovrapporla ad archi e altri nodi. È molto comoda nei diagrammi di stato, dove vuoi tenere puliti i cerchi degli stati:

A [xlabel="State A"];

Esempio pratico che mette a confronto label e xlabel:

digraph LabelComparison {
    graph [rankdir=LR, fontname="Segoe UI"];
    node [shape=circle, fontsize=11, fontname="Segoe UI"];
    edge [fontname="Segoe UI"];

    subgraph cluster_standard {
        label="Using label (internal)";
        style=filled;
        fillcolor="#F5F5F5";

        S1 [label="Idle"];
        S2 [label="Running"];
        S3 [label="Done"];

        S1 -> S2 [label="start"];
        S2 -> S3 [label="finish"];
        S3 -> S1 [label="reset"];
    }

    subgraph cluster_external {
        label="Using xlabel (external)";
        style=filled;
        fillcolor="#F5F5F5";

        X1 [xlabel="Idle"];
        X2 [xlabel="Running"];
        X3 [xlabel="Done"];

        X1 -> X2 [label="start"];
        X2 -> X3 [label="finish"];
        X3 -> X1 [label="reset"];
    }
}

Grafo di stati che confronta la label dentro i nodi con la xlabel posizionata all’esterno

Quando usare xlabel:

  • Diagrammi di stato: i cerchi degli stati restano puliti
  • Grafi complessi: meno confusione visiva dentro i nodi
  • Posizionamento automatico: lasci a Graphviz il compito di trovare il punto migliore per l’etichetta
  • Nodi piccoli: quando una label interna renderebbe il nodo troppo grande

Aggiungere note e annotazioni ai nodi

Ci sono diverse tecniche per aggiungere note, descrizioni o annotazioni ai nodi. Tornano utili soprattutto nelle mappe mentali, nella documentazione e nelle architetture complesse.

Tecnica 1: label su più righe con \n

La strada più semplice: vai a capo dentro la label.

Concept [label="Main Concept\n(This is a note explaining the concept)"];

Tecnica 2: tooltip con l’attributo tooltip

Aggiungi un tooltip che compare al passaggio del mouse (funziona negli SVG aperti nel browser):

Node [label="Cloud Storage", tooltip="Amazon S3, Azure Blob, Google Cloud Storage"];

Tecnica 3: un nodo nota separato, collegato con un arco tratteggiato

Crea un nodo dedicato alla nota, visivamente distinto dai nodi principali:

MainNode [label="User Service"];
Note1 [shape=note, label="Handles authentication\nand user profiles", fillcolor="#FFFACD"];
MainNode -> Note1 [style=dashed, arrowhead=none, color="#CCCCCC"];

Tecnica 4: label strutturate con la shape record

Usa i record per separare il titolo dalla descrizione:

node [shape=record];
Concept [label="{Concept Name|Description or note\labout this concept}"];

Esempio completo: mappa mentale con note

graph MindMapWithNotes {
    graph [layout=fdp, K=0.6, fontname="Segoe UI"];
    node [shape=box, style="rounded,filled", fillcolor="#E3F2FD", fontsize=11, fontname="Segoe UI"];
    edge [color="#888888", fontname="Segoe UI"];

    // Concetto principale (al centro)
    Central [label="Project\nArchitecture", fillcolor="#BBDEFB", fontsize=14, width=2.0, height=1.0, pin=true, pos="0,0!"];

    // Sotto-concetti (attorno al centro)
    Frontend [label="Frontend\nReact + TypeScript", width=1.8];
    Backend [label="Backend\nNode.js + Express", width=1.8];
    Database [label="Database\nPostgreSQL", width=1.5];
    DevOps [label="DevOps\nDocker + CI/CD", width=1.5];

    // Nodi nota (agganciati ai concetti)
    FrontendNote [shape=note, label="Uses Redux\nfor state mgmt", fillcolor="#FFFACD", fontsize=10];
    BackendNote [shape=note, label="RESTful API\n+ WebSocket", fillcolor="#FFFACD", fontsize=10];
    DatabaseNote [shape=note, label="PostgreSQL\n+ migrations", fillcolor="#FFFACD", fontsize=10];
    DevOpsNote [shape=note, label="Automated\ndeployments", fillcolor="#FFFACD", fontsize=10];

    // Collegamenti principali (i rami della mappa mentale)
    Central -- Frontend;
    Central -- Backend;
    Central -- Database;
    Central -- DevOps;

    // Note agganciate con linee tratteggiate (senza frecce)
    Frontend -- FrontendNote [style=dashed, color="#CCCCCC"];
    Backend -- BackendNote [style=dashed, color="#CCCCCC"];
    Database -- DatabaseNote [style=dashed, color="#CCCCCC"];
    DevOps -- DevOpsNote [style=dashed, color="#CCCCCC"];
}

Grafo dell’architettura di un progetto con nodi nota che annotano React, Node.js, PostgreSQL e Docker

Quando usare ciascuna tecnica:

TecnicaIdeale perProContro
\n su più righeNote brevi (1-2 righe)Semplice, sempre visibilePuò gonfiare troppo i nodi
tooltipSpiegazioni lungheNon affolla il diagrammaFunziona solo negli SVG interattivi
Nodo nota separatoAnnotazioni importantiMolto visibile, personalizzabileAggiunge complessità visiva
Record HTMLDati strutturatiSeparazione pulitaSintassi più complessa

Attributi degli archi (edge)

arrowsize

Scala della freccia. Default ~1.0

edge [arrowsize=0.8];

arrowhead / arrowtail

Opzioni utili:

  • normal
  • empty (triangolo vuoto, molto leggibile)
  • diamond
  • onormal
  • crow (diagrammi ER)
  • tee
  • none

style e color

edge [style=dashed, color="#888888"];

label

Etichetta dell’arco.

A -> B [label="calls"];

Attributi del grafo (graph)

rankdir

Direzione del layout (solo dot):

  • TB (top → bottom)
  • BT (bottom → top)
  • LR (left → right)
  • RL (right → left)

Es.:

graph [rankdir=LR];

splines

Controlla la forma degli archi:

  • true (default)
  • false (linee dritte)
  • polyline
  • ortho (ortogonali, ottimo per diagrammi “da architetto”)

ranksep, nodesep

Spazi orizzontali/verticali.

graph [ranksep=0.8, nodesep=0.6];

Come definire stili per gruppi di nodi in DOT?

Una delle domande più frequenti quando si usa Graphviz è: come posso applicare lo stesso stile a un gruppo di nodi senza ripeterlo per ciascuno?

La risposta è semplice: elenca i nodi sulla stessa riga seguiti dalla definizione degli attributi. DOT applicherà quegli attributi a tutti i nodi elencati.

Sintassi per stilizzare gruppi di nodi

Metodo 1: ridefinire i default dei nodi (consigliato)

// Ridefinisci i default dei nodi prima di ogni gruppo
node [shape=box, style=filled, fillcolor="#E8F4F8"];
A; B; C;

node [shape=ellipse, style=filled, fillcolor="#FFF4E6", fontname="Segoe UI"];
D; E; F;

node [shape=diamond, style=filled, fillcolor="#FFE8E8"];
G; H; I;

Metodo 2: elencare i nodi con gli attributi

// Alternativa: nodi separati da punto e virgola, l'ultimo con gli attributi
A; B; C [shape=box, style=filled, fillcolor="#E8F4F8"];
D; E; F [shape=ellipse, style=filled, fillcolor="#FFF4E6"];
G; H; I [shape=diamond, style=filled, fillcolor="#FFE8E8"];

Nota: il Metodo 1 si comporta in modo più affidabile fra le diverse versioni di Graphviz, e rende più facile aggiungere in seguito altri nodi a una categoria.

Questa tecnica diventa indispensabile quando hai diagrammi complessi con decine di nodi che appartengono a categorie diverse.

Esempio pratico: categorizzare nodi per ruolo

Immagina di dover disegnare un’architettura con tre tipi di componenti: servizi (box blu), database (cilindri verdi), code/messaggi (ellissi arancioni).

digraph Architecture {
    graph [rankdir=LR, nodesep=0.8, fontname="Segoe UI"];
    node [fontname="Segoe UI"];
    edge [fontname="Segoe UI"];

    // Servizi - box blu
    node [shape=box, style="filled,rounded", fillcolor="#E3F2FD"];
    AuthService; UserService; OrderService; NotificationService;

    // Database - cilindri verdi
    node [shape=cylinder, style=filled, fillcolor="#E8F5E9"];
    UserDB; OrderDB; SessionCache;

    // Code messaggi - ellissi arancioni
    node [shape=ellipse, style=filled, fillcolor="#FFF3E0"];
    EmailQueue; SMSQueue; PushQueue;

    // Relazioni
    AuthService -> SessionCache;
    UserService -> UserDB;
    OrderService -> OrderDB;
    OrderService -> EmailQueue;
    NotificationService -> EmailQueue;
    NotificationService -> SMSQueue;
    NotificationService -> PushQueue;
}

Servizi, database e code colorati per ruolo: AuthService, UserDB, OrderService, EmailQueue

Cosa fa questo esempio: Definisce tre categorie di nodi con stili diversi in sole tre righe. I servizi sono box blu arrotondati, i database sono cilindri verdi, le code sono ellissi arancioni. Ogni categoria è immediatamente riconoscibile visivamente.

Perché usare questa tecnica?

  1. Codice DRY (Don’t Repeat Yourself): Non ripeti shape=box, style=filled per ogni nodo
  2. Manutenibilità: Cambiare il colore di tutti i servizi richiede una sola modifica
  3. Leggibilità: Il codice DOT diventa auto-documentante (vedi subito quali nodi sono servizi, quali DB)
  4. Scalabilità: Aggiungere un nuovo servizio è questione di aggiungere il nome nella lista

Combinare con attributi di default

Puoi combinare questa tecnica con gli attributi di default per massima flessibilità:

digraph Mixed {
    // Default per tutti i nodi
    node [fontname="Segoe UI", fontsize=11];

    // Poi specializzi per gruppo
    Input; Validation [shape=parallelogram, fillcolor="#B3E5FC", style=filled];
    Process; Transform [shape=box, fillcolor="#C8E6C9", style=filled];
    Output; Export [shape=parallelogram, fillcolor="#FFCCBC", style=filled];
    Error [shape=octagon, fillcolor="#FFCDD2", style=filled];

    Input -> Validation -> Process -> Transform -> Output -> Export;
    Validation -> Error;
    Process -> Error;
}

Nodi input, validazione, processo, trasformazione e output stilizzati con attributi di default condivisi

Cosa fa questo esempio: Definisce un default comune (font Segoe UI 11pt) e poi tre gruppi: input/output come parallelogrammi (convenzione flowchart), processi come box, errori come ottagoni rossi.


Layout Engines: scegliere quello giusto

Graphviz non ha un solo motore di rendering: ne offre diversi, ognuno ottimizzato per tipologie specifiche di grafo. Scegliere l’engine giusto significa ottenere diagrammi più leggibili e professionali.

Si specifica l’engine usando il flag -K da CLI:

dot -Kdot -Tsvg file.dot -o output.svg
dot -Kneato -Tsvg file.dot -o output.svg

Oppure nel file DOT stesso:

graph G {
    layout=neato;
    // ...
}

Di seguito i principali engine e quando usarli.


Motori di layout disponibili

dot: layout gerarchico (default)

Cosa fa: Dispone i nodi in modo gerarchico, seguendo la direzione degli archi. È il più usato.

Perfetto per:

  • Flowchart e diagrammi di flusso
  • Pipeline CI/CD
  • Call graph (grafo delle chiamate tra funzioni)
  • Architetture a layer (presentation → business → data)
  • Processi sequenziali
  • Diagrammi di dipendenza con direzione chiara

Esempio di comando:

dot -Tsvg flowchart.dot -o flowchart.svg

Esempio visivo:

digraph DotExample {
    graph [rankdir=TB, fontname="Segoe UI"];
    node [shape=box, style="rounded,filled", fillcolor="#E3F2FD", fontname="Segoe UI"];

    A -> B -> C;
    A -> D -> C;
    B -> E;
    D -> E;
}

Cinque nodi da A a E disposti dall’alto in basso dal motore gerarchico dot

Cosa vedi: nodi disposti in livelli gerarchici ben distinti, dall’alto verso il basso. Perfetto per mostrare flussi e dipendenze.

Quando evitarlo: Se il grafo non ha una struttura gerarchica chiara o contiene molti cicli.

neato: layout a forza fisica

Cosa fa: Posiziona i nodi simulando forze fisiche (repulsione/attrazione), generando layout organici e simmetrici.

Utile per:

  • Grafi non diretti (senza frecce)
  • Reti concettuali e mappe mentali
  • Relazioni non gerarchiche tra entità
  • Grafi piccoli/medi dove vuoi evidenziare cluster naturali

Esempio di comando:

dot -Kneato -Tsvg concepts.dot -o concepts.svg

Esempio visivo (lo stesso grafo di prima, con un altro motore):

graph NeatoExample {
    graph [layout=neato, fontname="Segoe UI"];
    node [shape=ellipse, style=filled, fillcolor="#FFF4E6", fontname="Segoe UI"];

    A -- B -- C;
    A -- D -- C;
    B -- E;
    D -- E;
}

Gli stessi cinque nodi da A a E posizionati dal motore force-directed neato

Cosa vedi: nodi distribuiti in modo organico nello spazio 2D, con raggruppamenti naturali. Perfetto per mappe mentali e reti concettuali, dove le relazioni non sono gerarchiche.

Quando evitarlo: Con grafi molto grandi (>100 nodi) o fortemente direzionali.

fdp: force-directed placement

Cosa fa: Simile a neato, ma usa un algoritmo diverso (Fruchterman-Reingold). Generalmente più veloce su grafi medi.

Utile per:

  • Grafi non diretti di dimensioni medie
  • Visualizzazioni di social network (amicizie, collegamenti)
  • Analisi di dipendenze senza direzione forte

Esempio di comando:

dot -Kfdp -Tsvg network.dot -o network.svg

sfdp: scalable force-directed placement

Cosa fa: Versione ottimizzata di fdp per grafi molto grandi (migliaia di nodi).

Ottimo per:

  • Analisi delle dipendenze su codebase complesse
  • Grafo delle classi di un progetto enterprise
  • Reti complesse (infrastrutture, microservizi)
  • Quando neato o fdp sono troppo lenti

Esempio di comando:

dot -Ksfdp -Tsvg dependencies.dot -o dependencies.svg

Tip: Usa sfdp quando hai più di 100-200 nodi.

circo: layout circolare

Cosa fa: Dispone i nodi in cerchi concentrici attorno a un nodo centrale.

Ideale per:

  • Visualizzare moduli satellite attorno a un core centrale
  • Architetture hub-and-spoke
  • Rappresentare componenti che dipendono da un servizio centrale

Esempio di comando:

dot -Kcirco -Tsvg modules.dot -o modules.svg

Esempio visivo:

graph CircoExample {
    graph [layout=circo, fontname="Segoe UI"];
    node [shape=circle, style=filled, fillcolor="#E8F5E9", fontname="Segoe UI"];

    Core -- Module1;
    Core -- Module2;
    Core -- Module3;
    Core -- Module4;
    Core -- Module5;
    Module1 -- Module2;
    Module3 -- Module4;
}

Nodo Core e cinque moduli disposti ad anello dal motore circo

Cosa vedi: il nodo centrale (Core) in mezzo, con i satelliti disposti in cerchio attorno. Perfetto per le architetture hub-and-spoke.

twopi: layout radiale

Cosa fa: Crea un layout ad albero radiale, con il nodo radice al centro e i livelli che si espandono verso l’esterno.

Perfetto per:

  • Alberi gerarchici (org chart, file system)
  • Tassonomie
  • Mind map strutturate
  • Visualizzare espansioni da un punto centrale

Esempio di comando:

dot -Ktwopi -Tsvg tree.dot -o tree.svg

Esempio visivo:

digraph TwopiExample {
    graph [layout=twopi, ranksep=2.0, fontname="Segoe UI"];
    node [shape=box, style="rounded,filled", fillcolor="#FFEBEE", fontsize=11, width=1.0, height=0.5, fontname="Segoe UI"];
    edge [color="#666666", fontname="Segoe UI"];

    CEO [label="CEO", fillcolor="#FFCDD2"];

    CEO -> Engineering [label=""];
    CEO -> Sales [label=""];
    CEO -> Marketing [label=""];

    Engineering -> Dev1 [label=""];
    Engineering -> Dev2 [label=""];

    Sales -> Rep1 [label=""];
    Sales -> Rep2 [label=""];

    Marketing -> Designer [label=""];
    Marketing -> Writer [label=""];
}

Organigramma aziendale che si irradia dal CEO, disegnato dal motore radiale twopi

Cosa vedi: il CEO al centro, i reparti (Engineering, Sales, Marketing) nel primo anello e le persone dei team in quello esterno. Una gerarchia radiale chiara, perfetta per gli organigrammi.

Come scegliere in pratica

Tipo di grafoEngine consigliato
Flowchart, pipeline, processidot
Architetture layereddot
Call graph, dependency treedot
Rete concettuale, brainstormingneato
Social network, grafi medi non direttifdp
Grafi grandi (>200 nodi)sfdp
Hub centrale con satelliticirco
Alberi gerarchici, org charttwopi

Regola pratica: Se hai frecce e una direzione chiara → usa dot. Altrimenti prova neato o fdp.


Tipologie di grafici e quando usarli nella vita reale di un developer

Questa è la sezione più corposa. Per ogni tipo di grafico troverai:

  • Quando usarlo nella vita reale
  • Attributi consigliati
  • Esempio completo

Flowchart: capire il comportamento

Nel lavoro quotidiano capita spesso di dover spiegare un flusso decisionale complesso: una procedura di validazione con molteplici branch, un processo di onboarding utente, o semplicemente il comportamento di una funzione con tanti if/else annidati. Il flowchart è lo strumento ideale per questo.

Quando ti serve un flowchart:

Sei in fase di analisi requisiti e devi capire tutte le casistiche possibili. Stai debuggando una logica che sembra impazzita e vuoi vedere visivamente dove il flusso si biforca. Devi scrivere documentazione per un processo aziendale complesso. Stai facendo onboarding di un nuovo collega e vuoi mostrargli come funziona il sistema di autenticazione.

Il flowchart mostra visivamente le decisioni (rombi), i processi (rettangoli arrotondati), e il flusso logico (frecce). È immediato, chiaro, universale.

Attributi consigliati per flowchart professionali:

Usa rankdir=TB (top-to-bottom) per seguire la convenzione standard dei flowchart. Usa shape=diamond per i nodi decisionali (le condizioni if/else). Usa style=rounded per gli step di processo, così si distinguono dai rombi. Usa colori tenui (fillcolor) per evidenziare start (verde chiaro), errori (rosso chiaro), fine (grigio).

Esempio completo:

digraph Flow {
    graph [rankdir=TB, nodesep=0.6];
    node [fontname="Segoe UI"];

    Start   [shape=oval, style=filled, fillcolor="#C1F2C7"];
    Check   [shape=diamond, label="Valid input?"];
    Process [shape=box, style="filled,rounded", fillcolor="#F0F4FF"];
    Error   [shape=box, fillcolor="#FFEAEA", style=filled];
    End     [shape=oval, fillcolor="#DDDDDD", style=filled];

    Start -> Check;
    Check -> Process [label="yes"];
    Check -> Error   [label="no"];
    Process -> End;
}

Flowchart di base: inizio, rombo di validazione input, ramo processo o errore, fine

Cosa fa questo esempio: Parte da uno stato iniziale (Start), passa attraverso una validazione (Check), e si biforca in due percorsi: successo (Process) o errore (Error). Usa colori per rendere immediatamente chiaro cosa è positivo e cosa negativo.


Grafi di dipendenza: capire il software come sistema

Quando lavori su un progetto esistente, una delle prime domande che ti fai è: “Cosa dipende da cosa?” Se devi fare refactoring di un modulo, vuoi sapere chi lo usa. Se devi aggiornare una libreria, vuoi capire l’impatto a cascata. Se stai progettando una nuova feature, vuoi vedere dove si inserisce nell’architettura esistente.

Il grafo delle dipendenze è la mappa del tuo sistema. Mostra moduli, servizi, classi, o microservizi come nodi, e le dipendenze come frecce. È fondamentale per:

Refactoring sicuro: Prima di toccare un modulo, vedi chi lo chiama. Analisi di impatto: Se modifichi un’API, vedi immediatamente tutti i consumatori. Documentazione automatica: Genera il grafo dal codice (con tool come Doxygen, Madge, o script custom) e mantienilo aggiornato. Dependency injection mapping: Visualizza come Spring, Angular o .NET iniettano le dipendenze.

Attributi utili:

Usa rankdir=LR (left-to-right) per avere un flusso orizzontale, tipico delle dependency chain. Usa shape=box per i moduli/servizi. Usa color e penwidth per evidenziare dipendenze critiche o problematiche (es. dipendenze circolari in rosso). Usa style=dashed per dipendenze opzionali o deboli.

Esempio:

digraph Deps {
    graph [rankdir=LR];
    node [shape=box, style=filled, fillcolor="#F7FAFF", fontname="Segoe UI"];
    edge [color="#555555"];

    UI -> API;
    API -> Auth;
    API -> UserService;
    UserService -> Database [color="#FF5555", penwidth=2, label="critical"];
}

Grafo delle dipendenze da UI ad API e Auth fino a UserService e database

Cosa fa questo esempio: Mostra una classica architettura web: UI chiama API, API dipende da Auth e UserService, UserService parla col Database. La dipendenza critica (UserService → Database) è evidenziata in rosso con linea spessa: se il DB va giù, tutto crolla.


Architettura software: cluster e layer

Quando progetti un’architettura o documenti quella esistente, hai bisogno di mostrare raggruppamenti logici e separazione dei livelli. Un sistema web tipico ha Presentation, Business, Data. Un progetto a microservizi ha boundary logici (edge, services, datastores). Un sistema legacy potrebbe avere moduli separati per dominio.

Il diagramma di architettura con cluster ti permette di:

Visualizzare layer separati: Presentation Layer, Business Layer, Data Layer. Ogni layer è un box colorato che contiene i suoi componenti. Mostrare boundary dei microservizi: Edge Gateway, Services, Datastores. Ogni boundary è un cluster. Documentare moduli legacy: Isola i moduli per responsabilità (Auth, Orders, Reporting) così è chiaro chi fa cosa. Presentare l’architettura a stakeholder: Un diagramma layered è universale e comprensibile anche dai non-tecnici.

Attributi utili:

Usa subgraph cluster_* per creare raggruppamenti visivi. Usa compound=true per permettere archi che attraversano i cluster. Usa splines=ortho per linee ortogonali, tipiche dei diagrammi “da architetto”. Usa shape=cylinder per i database, così sono immediatamente riconoscibili.

Esempio architettura layered:

digraph Architecture {
    graph [
        rankdir=TB,
        ranksep=0.8,
        nodesep=0.6,
        overlap=false,
        splines=true,
        sep="+0.2"
    ];
    node [shape=box, style="rounded,filled", fillcolor="#F0F4FF", fontname="Segoe UI"];

    subgraph cluster_presentation {
        label="Presentation Layer";
        style=filled;
        color=lightgrey;
        fillcolor="#E8F4F8";
        UI;
    }

    subgraph cluster_business {
        label="Business Layer";
        style=filled;
        color=lightgrey;
        fillcolor="#FFF4E6";
        ServiceA; ServiceB;
    }

    subgraph cluster_data {
        label="Data Layer";
        style=filled;
        color=lightgrey;
        fillcolor="#F0F0F0";
        DB [shape=cylinder, fillcolor="#D0E8FF"];
    }

    UI -> ServiceA;
    UI -> ServiceB;
    ServiceA -> DB;
    ServiceB -> DB;
}

Architettura a livelli con cluster per presentation, business e data layer

Cosa fa questo esempio: Definisce tre layer con subgraph cluster_*. Presentation contiene UI, Business contiene due servizi, Data contiene il DB (con shape=cylinder). Le frecce mostrano il flusso: UI → Services → DB. L’architettura è immediatamente comprensibile.


Microservices Map: capire un ecosistema distribuito

Se lavori con microservizi, hai decine di servizi che comunicano tra loro: API Gateway, servizi di dominio (Orders, Users, Notifications), database, code, cache. Quando arriva un incident in produzione, la prima domanda è: “Chi parla con chi? Dov’è il collo di bottiglia?”

La mappa dei microservizi è il tuo GPS nel caos distribuito. Ti serve per:

Design architetturale: Prima di scrivere codice, disegna la mappa. Identifica i boundary logici (edge, core services, datastores). Documentazione API: Mostra chi chiama quale servizio e attraverso quale protocollo (REST, gRPC, eventi). Analisi di performance: Durante un incident, guardi la mappa e capisci subito se il problema è nell’API Gateway, in un servizio specifico, o nel DB condiviso. Post-mortem: Dopo un down, la mappa aiuta a ricostruire la catena di fallimento.

Attributi utili:

Usa cluster per separare i boundary logici (Edge, Services, Data). Usa shape=cylinder per database, shape=box per servizi. Usa label sugli archi per specificare il protocollo (HTTP, gRPC, Kafka). Usa shape=plaintext con HTML table per servizi complessi con porte multiple.

Esempio:

digraph Micro {
    // Impostazioni globali (valgono per tutto)
    graph [rankdir=LR, splines=true, nodesep=1.2, ranksep=1.8, overlap=false, sep="+0.4", fontname="Segoe UI"];
    node [shape=box, style="rounded,filled", fillcolor="#E3F2FD", fontname="Segoe UI", fontsize=11];
    edge [color="#555555", arrowsize=0.8, fontsize=10, fontcolor="#333333", fontname="Segoe UI"];

    subgraph cluster_gateway {
        label="Edge Layer";
        fillcolor="#FFF3E0";
        color="#FF9800";
        Gateway [label="API Gateway", fillcolor="#FFE0B2"];
    }

    subgraph cluster_services {
        label="Microservices Layer";
        fillcolor="#E8F5E9";
        color="#4CAF50";
        OrderService [label="Order Service", fillcolor="#C8E6C9"];
        UserService [label="User Service", fillcolor="#C8E6C9"];
        NotificationService [label="Notification Service", fillcolor="#C8E6C9"];
    }

    subgraph cluster_data {
        label="Data Layer";
        fillcolor="#F5F5F5";
        color="#9E9E9E";
        DB [shape=cylinder, label="Postgres\nDB", fillcolor="#BBDEFB"];
        Cache [shape=cylinder, label="Redis\nCache", fillcolor="#FFCCBC"];
    }

    // Dall'edge ai servizi
    Gateway -> UserService [label="HTTP"];
    Gateway -> OrderService [label="HTTP"];

    // Dai servizi ai dati
    OrderService -> DB [label="SQL"];
    UserService -> Cache [label="GET/SET"];

    // Fra servizi
    NotificationService -> UserService [label="gRPC"];
    OrderService -> NotificationService [label="Event", style=dashed];
}

Mappa dei microservizi: API gateway, servizi ordini, utenti e notifiche, Postgres e Redis

Cosa fa questo esempio: organizza un ecosistema di microservizi in tre layer colorati:

  • Edge Layer (arancione): l’API Gateway come punto d’ingresso
  • Microservices Layer (verde): tre servizi con responsabilità ben distinte
  • Data Layer (grigio): il database Postgres e la cache Redis (cilindri, per distinguerli a colpo d’occhio)

I pattern di comunicazione che si vedono:

  • Gateway → servizi: chiamate HTTP (frecce continue)
  • Servizi → dati: query SQL e operazioni sulla cache
  • Fra servizi: una chiamata sincrona gRPC e un evento asincrono (freccia tratteggiata)

I cluster colorati mostrano subito i confini architetturali, e le etichette sugli archi rendono espliciti i protocolli. Perfetto per l’onboarding, le review architetturali o l’analisi di un incident.


Diagrammi di stato: modellare comportamenti

Molti sistemi hanno un comportamento a stati: una richiesta HTTP può essere Idle, Loading, Success, Error. Un ordine e-commerce passa da Draft → Pending → Confirmed → Shipped. Un sistema embedded ha stati di accensione, standby, operativo, errore. Un’interfaccia UI ha stati di caricamento, pronta, errore.

Il diagramma di stato modella questi comportamenti come un grafo: ogni nodo è uno stato, ogni arco è una transizione etichettata con l’evento che la causa. Ti serve per:

Progettare macchine a stati: Prima di implementare lo state pattern nel codice, disegna il diagramma. Documentare protocolli: I protocolli di rete (TCP, WebSocket, custom) hanno state machine precise. Il diagramma le rende esplicite. Modellare UI/UX flow: Quando progetti un’app, disegna gli stati dell’interfaccia: caricamento, pronta, errore, vuota. Debug di sistemi embedded: Se un device si blocca in uno stato, il diagramma ti aiuta a capire quali transizioni mancano.

Il legame con la teoria dell’informatica e con i design pattern:

I diagrammi di stato in DOT rappresentano direttamente concetti fondamentali dell’informatica e della progettazione software:

  • FSM (Finite State Machine): un modello di calcolo con un numero finito di stati. Il diagramma mostra tutti gli stati possibili e le transizioni fra di essi. Si usa in compilatori, parser e implementazioni di protocolli.

  • DFA (Deterministic Finite Automaton): un tipo particolare di FSM in cui ogni stato ha esattamente una transizione per ciascun simbolo in ingresso. I diagrammi di stato sono la rappresentazione visiva dei DFA che si usano nella teoria dei linguaggi formali e nei motori di espressioni regolari.

  • State Pattern (design pattern GoF): un pattern object-oriented che permette a un oggetto di cambiare comportamento quando cambia il suo stato interno. Il diagramma di stato diventa il progetto da cui implementare il pattern: ogni cerchio è una classe che implementa l’interfaccia State, ogni freccia è un metodo di transizione.

Un diagramma di stato è più di una pagina di documentazione: è una specifica formale che puoi tradurre direttamente in codice (State Pattern), usare per la validazione (DFA) o analizzare per verificarne la correttezza (teoria delle FSM).

Attributi utili:

Usa shape=circle per gli stati (la convenzione standard delle FSM). Usa rankdir=LR per un layout orizzontale, tipico dei diagrammi di stato. Usa label sugli archi per mostrare l’evento che causa la transizione. Usa shape=doublecircle per gli stati finali/terminali. Usa fixedsize=true insieme a width per avere cerchi tutti della stessa dimensione, a vantaggio della coerenza visiva.

Rappresentare stati iniziali e finali (lo standard FSM/DFA):

Secondo le convenzioni delle macchine a stati finiti e dei DFA:

  • Stato iniziale: usa shape=point con una width piccola (per esempio 0.2) per ottenere il punto nero che indica l’ingresso
  • Stato finale/di accettazione: usa shape=doublecircle per il bordo a doppio cerchio che indica gli stati terminali/di accettazione
  • Stati normali: usa shape=circle con fixedsize=true per tutti gli stati intermedi (così hanno tutti lo stesso aspetto)

Esempio completo con la notazione FSM corretta:

digraph States {
    graph [rankdir=LR, fontname="Segoe UI"];
    node [shape=circle, fontsize=12, fixedsize=true, width=0.9, fontname="Segoe UI"];
    edge [fontname="Segoe UI"];

    // Stato iniziale (punto nero)
    START [shape=point, width=0.2, fixedsize=true];

    // Stato finale (doppio cerchio)
    END [shape=doublecircle, fixedsize=true, width=0.9];

    // Stati normali
    Idle; Loading; Ready; Error;

    // Transizioni
    START -> Idle;
    Idle -> Loading   [label="start"];
    Loading -> Ready  [label="success"];
    Loading -> Error  [label="fail"];
    Error -> Idle     [label="reset"];
    Ready -> END      [label="finish"];
}

Diagramma di stati con Idle, Loading, Ready ed Error e transizioni etichettate

Cosa fa questo esempio: modella il ciclo di vita di una richiesta asincrona seguendo le convenzioni FSM/DFA:

  • START (shape=point): il punto nero che indica l’ingresso nella macchina a stati
  • END (shape=doublecircle): il doppio cerchio che indica lo stato di accettazione/terminale
  • Stati normali (Idle, Loading, Ready, Error): cerchi uniformi (fixedsize=true, width=0.9) che rappresentano gli stati intermedi
  • Transizioni etichettate: frecce che mostrano gli eventi che fanno cambiare stato

La macchina parte da START, entra in Idle e poi passa a Loading. Da Loading può andare a buon fine (→ Ready) o fallire (→ Error). Da Error un reset riporta a Idle. Il successo porta allo stato finale END.

Dal diagramma al codice (implementazione dello State Pattern):

Questo diagramma si traduce direttamente nello State Pattern:

// Ogni cerchio diventa una classe State
interface State {
    void start();
    void success();
    void fail();
    void reset();
    void finish();
}

class IdleState implements State { ... }
class LoadingState implements State { ... }
class ReadyState implements State { ... }
class ErrorState implements State { ... }

// Le transizioni diventano implementazioni dei metodi
class LoadingState implements State {
    void success() {
        context.setState(new ReadyState());
    }
    void fail() {
        context.setState(new ErrorState());
    }
}

Il diagramma fa da documentazione e, allo stesso tempo, da specifica per l’implementazione.


Diagrammi delle classi con record

Quando progetti un sistema object-oriented, o vuoi documentare il dominio di un’applicazione, il diagramma delle classi è lo standard. Mostra classi con attributi e metodi, e le relazioni tra loro (ereditarietà, composizione, dipendenza).

DOT non è UML, ma con shape=record ottieni qualcosa di molto simile e perfettamente leggibile. È utile per:

Progettazione iniziale: Prima di scrivere il codice, disegna le classi principali del dominio. Identifica attributi, metodi, relazioni. Refactoring: Quando devi ristrutturare un modulo, disegna lo stato attuale e quello desiderato. Confronta i due diagrammi. Domain-Driven Design (DDD): Modella le entità, value object, aggregati. Il diagramma aiuta a visualizzare i boundary del dominio. Documentazione: Genera il diagramma automaticamente dal codice (con tool come Doxygen) e mantienilo aggiornato.

Attributi utili:

Usa shape=record per creare box con sezioni separate (nome classe | attributi | metodi). Usa fontname="Courier New" o monospace per renderlo simile a codice. Usa arrowhead=onormal per ereditarietà (freccia vuota, standard UML). Usa \l (backslash-l) per allineare il testo a sinistra dentro i record.

Esempio:

digraph Classes {
    node [shape=record, fontname="Segoe UI"];

    Person [label="{Person|name: string\l age: int\l|greet()}"];
    Employee [label="{Employee|id: int\l role: string\l|work()}"];

    Person -> Employee [arrowhead="onormal"];
}

Diagramma delle classi con shape record: la classe Employee eredita da Person

Cosa fa questo esempio: Definisce due classi: Person (con attributi nome, età, metodo greet) e Employee (con id, ruolo, metodo work). Person è la superclasse di Employee (freccia con arrowhead=onormal, standard UML per l’ereditarietà). Il \l allinea il testo a sinistra dentro i record.


Diagrammi ER professionali con HTML-label

Se lavori con database, prima o poi devi disegnare lo schema delle tabelle: chiavi primarie, foreign key, relazioni 1:N o N:N. Il diagramma Entity-Relationship (ER) è lo standard per questo.

DOT supporta tabelle HTML dentro i nodi con shape=plaintext, permettendoti di creare diagrammi ER puliti e professionali. È fondamentale per:

Progettazione database: Prima di scrivere le migration, disegna lo schema. Identifica le entità, gli attributi, le relazioni. Valida il design con il team. Data modeling: Quando progetti un nuovo modulo, parti dal modello dati. Il diagramma ER ti aiuta a ragionare su normalizzazione e performance. Reverse engineering: Quando erediti un DB legacy senza documentazione, genera il diagramma ER dal DB stesso (con tool come SchemaSpy o pg_dump + script) per capire la struttura. Documentazione: Il diagramma ER è comprensibile anche dai non-developer (product manager, analisti business).

Attributi utili:

Usa shape=plaintext per abilitare HTML label. Usa <TABLE> HTML per creare box strutturati con header (nome tabella) e righe (campi). Usa arrowhead=crow per relazioni 1:N (standard dei diagrammi ER). Usa label="1:N" sugli archi per rendere esplicita la cardinalità.

Esempio:

digraph ER {
    node [shape=plaintext];

    User [label=<
        <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
            <TR><TD><B>User</B></TD></TR>
            <TR><TD>id PK</TD></TR>
            <TR><TD>email</TD></TR>
        </TABLE>
    >];

    Order [label=<
        <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
            <TR><TD><B>Order</B></TD></TR>
            <TR><TD>id PK</TD></TR>
            <TR><TD>user_id FK</TD></TR>
        </TABLE>
    >];

    User -> Order [label="1:N", arrowhead="crow"];
}

Diagramma ER con tabelle HTML-label: User e Order in relazione uno a molti

Cosa fa questo esempio: Definisce due tabelle (User e Order) usando HTML. Ogni tabella ha un header in grassetto e righe per i campi. La freccia con arrowhead=crow e label="1:N" mostra la relazione: un User ha molti Order (chiave esterna user_id in Order).


Diagrammi CI/CD Pipeline

Se lavori in un team che fa continuous integration e deployment, hai una pipeline che esegue step automatici: build, test, analisi statica, packaging, deploy, monitoraggio. Quando qualcosa si rompe, o quando fai onboarding di un nuovo developer, serve un diagramma che mostri l’intera pipeline.

Il diagramma CI/CD visualizza il flusso automatico dal commit al deploy. È utile per:

DevOps e SRE: Documentare la pipeline esistente. Identificare colli di bottiglia (quale step richiede più tempo?). Onboarding team: Un nuovo developer guarda il diagramma e capisce subito cosa succede dopo un git push. Ottimizzazione: Vuoi parallelizzare alcuni step? Il diagramma ti mostra quali dipendono da quali. Debugging: La pipeline fallisce? Il diagramma ti aiuta a capire a che step e perché (freccia tratteggiata per i retry).

Attributi utili:

Usa rankdir=LR per flusso orizzontale (tipico delle pipeline). Usa shape=box con style=filled per gli step, colorandoli per tipo (build=blu, test=verde, deploy=rosso). Usa style=dotted con label="retry" per mostrare meccanismi di retry automatico. Usa label sugli archi per indicare condizioni (es. “only on master branch”).

Esempio:

digraph CICD {
    graph [rankdir=LR];
    node [shape=box, style=filled, fillcolor="#F8FBFF"];

    Code -> Build -> Test -> Package -> Deploy -> Monitor;

    Test -> Build [style=dotted, label="retry"];
}

Pipeline CI/CD da codice a build, test con retry, package, deploy e monitor

Cosa fa questo esempio: Mostra una pipeline classica: Code → Build → Test → Package → Deploy → Monitor. La freccia tratteggiata da Test a Build mostra un retry automatico in caso di fallimento dei test. È un flusso lineare, immediatamente comprensibile.


Sottrarre complessità: mappe concettuali e analisi

Non tutti i diagrammi devono essere gerarchici o direzionali. A volte hai bisogno di visualizzare relazioni concettuali senza una struttura fissa: durante un brainstorming, quando fai design collettivo con il team, o quando vuoi mappare le dipendenze concettuali tra aree tecnologiche.

La mappa concettuale non ha un “inizio” o una “fine”: è una rete di nodi collegati organicamente. È utile per:

Brainstorming: Parti da un’idea centrale e aggiungi nodi collegati mentre discuti con il team. La mappa cresce naturalmente. Design collettivo: Durante una sessione di design, mappa i componenti e le loro relazioni. Non sai ancora la gerarchia, ma sai che “Backend parla con Database e API”. Analisi delle dipendenze concettuali: Vuoi capire quali aree tecnologiche sono collegate (es. Security → Logging → Observability). Documentazione di alto livello: Per stakeholder non-tecnici, una mappa concettuale è più accessibile di un grafo gerarchico.

Attributi utili:

Usa engine neato o fdp invece di dot, per ottenere un layout organico basato su forze fisiche. Usa shape=ellipse per nodi concettuali (non processi). Usa style=filled con colori tenui per raggruppamenti visivi. Usa grafi non diretti (graph invece di digraph) se le relazioni sono bidirezionali.

Esempio con engine neato:

graph Concepts {
    layout=neato;
    node [shape=ellipse, style=filled, fillcolor="#EFEFFF"];

    Backend -- Database;
    Backend -- API;
    API -- Security;
    Security -- Logging;
    Logging -- Observability;
}

Mappa concettuale che collega backend, database, API, sicurezza, logging e osservabilità


Evitare sovrapposizioni: overlap e splines

Uno dei problemi più comuni quando si disegnano grafi complessi è la sovrapposizione di frecce, testi e nodi. Graphviz offre attributi specifici per controllare questo comportamento e rendere i diagrammi più leggibili.

Il problema: sovrapposizioni indesiderate

Quando hai molti nodi e frecce, specialmente con layout gerarchici o con splines=ortho, le frecce possono sovrapporsi ai label dei cluster o ai nodi. Ecco un esempio tipico del problema:

digraph OverlapProblem {
    graph [rankdir=TB, splines=ortho];
    node [shape=box, style=rounded];

    subgraph cluster_a {
        label="Component A";
        A1; A2;
    }

    subgraph cluster_b {
        label="Component B";
        B1; B2;
    }

    subgraph cluster_c {
        label="Component C";
        C1; C2;
    }

    A1 -> B1;
    A2 -> B2;
    B1 -> C1;
    B2 -> C2;
    A1 -> C1;
}

Tre cluster di componenti con archi che attraversano i nodi, prima di correggere overlap

Problema: Con splines=ortho le frecce possono attraversare i label dei cluster rendendo il diagramma confuso.

La soluzione: overlap e splines

Graphviz offre diversi attributi per risolvere questo problema:

overlap: evita sovrapposizioni tra nodi

graph [overlap=false];

Opzioni principali:

  • false o voronoi: evita sovrapposizioni (migliore qualità, più lento)
  • scale: scala il grafo per evitare sovrapposizioni
  • scalexy: scala con proporzioni diverse su X e Y
  • true (default): permette sovrapposizioni

splines: controlla la forma degli archi

graph [splines=true];

Opzioni:

  • true o spline: curve smooth che evitano nodi (raccomandato)
  • curved: archi leggermente curvi
  • polyline: linee spezzate
  • ortho: linee ortogonali (può causare sovrapposizioni!)
  • line o false: linee dritte

sep: margine extra tra elementi

graph [sep="+0.2"];

Aggiunge margine tra nodi e archi (in pollici). Il + significa “aggiungi al valore di default”.

ranksep e nodesep: spaziatura tra livelli e nodi

graph [ranksep=0.8, nodesep=0.6];

Aumenta lo spazio verticale (ranksep) e orizzontale (nodesep) tra gli elementi.

Esempio migliorato

Ecco lo stesso grafo con attributi ottimizzati per evitare sovrapposizioni:

digraph OverlapSolved {
    graph [
        rankdir=TB,
        ranksep=0.8,
        nodesep=0.6,
        overlap=false,
        splines=true,
        sep="+0.2"
    ];
    node [shape=box, style=rounded];

    subgraph cluster_a {
        label="Component A";
        style=filled;
        fillcolor="#E8F4F8";
        A1; A2;
    }

    subgraph cluster_b {
        label="Component B";
        style=filled;
        fillcolor="#FFF4E6";
        B1; B2;
    }

    subgraph cluster_c {
        label="Component C";
        style=filled;
        fillcolor="#F0F0F0";
        C1; C2;
    }

    A1 -> B1;
    A2 -> B2;
    B1 -> C1;
    B2 -> C2;
    A1 -> C1;
}

Gli stessi tre cluster ridisegnati puliti dopo aver impostato overlap e splines

Risultato: Le frecce ora evitano i nodi e i label, il grafo è più spazioso e leggibile. I colori di sfondo aiutano a distinguere i cluster.

Quando usare cosa

ScenarioAttributi consigliati
Diagrammi complessi con molti nodioverlap=false, splines=true
Grafi gerarchici (flowchart, architetture)ranksep=0.8, nodesep=0.6, splines=true
Grafi con clusteroverlap=false, sep="+0.2"
Diagrammi “da architetto” con linee drittesplines=polyline (evita ortho)
Grafi piccoli e semplicidefault (non serve modificare)

Regola pratica: Inizia sempre con overlap=false e splines=true se hai più di 10 nodi o usi cluster.

Esempio completo con tutte le best practices

Ecco un esempio reale che combina tutti gli attributi consigliati: architettura a microservizi con 4 layer, molti nodi e archi, cluster colorati, spazi ottimizzati:

digraph CompleteExample {
    graph [
        rankdir=TB,
        ranksep=1.0,
        nodesep=0.7,
        overlap=false,
        splines=true,
        sep="+0.25"
    ];
    node [shape=box, style="rounded,filled", fillcolor="#F0F4FF", fontname="Segoe UI", fontsize=11];
    edge [color="#555555", arrowsize=0.8];

    subgraph cluster_frontend {
        label="Frontend Layer";
        style=filled;
        fillcolor="#E8F4F8";
        color="#5A9FD4";

        WebUI [label="Web UI"];
        MobileApp [label="Mobile App"];
    }

    subgraph cluster_api {
        label="API Gateway Layer";
        style=filled;
        fillcolor="#FFF4E6";
        color="#E8A87C";

        Gateway [label="API Gateway"];
        LoadBalancer [label="Load Balancer"];
    }

    subgraph cluster_services {
        label="Microservices Layer";
        style=filled;
        fillcolor="#F0F8E8";
        color="#90C290";

        AuthService [label="Auth Service"];
        UserService [label="User Service"];
        OrderService [label="Order Service"];
        PaymentService [label="Payment Service"];
    }

    subgraph cluster_data {
        label="Data Layer";
        style=filled;
        fillcolor="#F5F5F5";
        color="#999999";

        UsersDB [shape=cylinder, fillcolor="#D0E8FF", label="Users DB"];
        OrdersDB [shape=cylinder, fillcolor="#D0E8FF", label="Orders DB"];
        Cache [shape=box, fillcolor="#FFE8D0", label="Redis Cache"];
    }

    // Dal frontend al gateway
    WebUI -> LoadBalancer [label="HTTPS"];
    MobileApp -> LoadBalancer [label="HTTPS"];

    // Dal gateway ai servizi
    LoadBalancer -> Gateway;
    Gateway -> AuthService [label="gRPC"];
    Gateway -> UserService [label="REST"];
    Gateway -> OrderService [label="REST"];

    // Dipendenze fra servizi
    OrderService -> PaymentService [label="API call"];
    UserService -> AuthService [label="validate"];
    PaymentService -> AuthService [label="validate"];

    // Accesso ai dati
    AuthService -> UsersDB;
    UserService -> UsersDB;
    UserService -> Cache [style=dashed, label="cache"];
    OrderService -> OrdersDB;
    OrderService -> Cache [style=dashed, label="cache"];
}

Command line:

dot -Tsvg overlap_complete.dot -o overlap_complete.svg

Sistema completo a livelli da web UI e load balancer a gateway, servizi e database

Cosa dimostra questo esempio:

  • overlap=false: Nessuna sovrapposizione tra nodi, anche con 13 nodi e 4 cluster
  • splines=true: Frecce curve che evitano elegantemente nodi e label
  • sep="+0.25": Margine extra che mantiene tutto leggibile
  • ranksep=1.0, nodesep=0.7: Spaziatura generosa tra layer e nodi
  • Cluster colorati: Ogni layer ha un colore distintivo per identificazione immediata
  • Label sugli archi: Protocolli (HTTPS, gRPC, REST) e tipo di connessione espliciti
  • Style dashed per cache: Dipendenze opzionali visualizzate diversamente
  • Cylinder shape per DB: Database immediatamente riconoscibili

Questo è il template perfetto per documentare architetture complesse in modo professionale e leggibile.


Color Schemes: palette professionali pronte all’uso

Graphviz include color schemes predefiniti basati su palette professionali di ColorBrewer. Invece di scegliere colori manualmente, puoi usare palette testate per leggibilità, accessibilità e professionalità.

Come funzionano i color schemes

Usa l’attributo colorscheme per selezionare una palette, poi usa i numeri 1-9 (o più, dipende dallo schema) invece dei codici hex:

node [colorscheme=set39, fillcolor=1, color=2];

I color schemes sono organizzati in categorie:

  • Qualitative (set1, set2, set3, pastel1, pastel2, dark2, paired, accent): per categorie distinte
  • Sequential (blues3-9, greens3-9, reds3-9, purples3-9, oranges3-9): per valori progressivi
  • Diverging (rdylgn3-11, spectral3-11, rdbu3-11): per dati con punto centrale

Riferimento completo: graphviz.org/docs/attrs/colorscheme/

Esempi pratici con color schemes professionali

Ogni esempio usa lo stesso diagramma (processo a 5 step) ma con palette diverse. Confronta e scegli quello più adatto al tuo caso d’uso.

Esempio 1: Set39 (qualitativo, vivace)

Ottimo per distinguere categorie diverse, presentazioni colorate, dashboard.

digraph ProcessSet39 {
    graph [rankdir=LR, bgcolor=white];
    node [shape=box, style="rounded,filled", colorscheme=set39, fontname=Arial];
    edge [colorscheme=set39, penwidth=2];

    Start [fillcolor=1, label="Start"];
    Validate [fillcolor=2, label="Validate"];
    Process [fillcolor=3, label="Process"];
    Store [fillcolor=4, label="Store"];
    Notify [fillcolor=5, label="Notify"];

    Start -> Validate [color=1];
    Validate -> Process [color=2];
    Process -> Store [color=3];
    Store -> Notify [color=4];
}

Command line:

dot -Tsvg colorscheme_set39.dot -o colorscheme_set39.svg

Flusso di processo in cinque passi colorato con lo schema Brewer set39

Esempio 2: Pastel19 (qualitativo, soft)

Colori pastello per documentazione tecnica, wiki, blog post. Meno impattante ma più leggibile a lungo.

digraph ProcessPastel {
    graph [rankdir=LR, bgcolor=white];
    node [shape=box, style="rounded,filled", colorscheme=pastel19, fontname=Arial, fontcolor="#333333"];
    edge [colorscheme=dark28, penwidth=2];

    Start [fillcolor=1, label="Start"];
    Validate [fillcolor=3, label="Validate"];
    Process [fillcolor=5, label="Process"];
    Store [fillcolor=7, label="Store"];
    Notify [fillcolor=9, label="Notify"];

    Start -> Validate [color=1];
    Validate -> Process [color=3];
    Process -> Store [color=5];
    Store -> Notify [color=7];
}

Command line:

dot -Tsvg colorscheme_pastel.dot -o colorscheme_pastel.svg

Flusso di processo in cinque passi colorato con uno schema Brewer pastel

Esempio 3: Blues9 (sequential, progressione)

Ideale per mostrare intensità crescente, priorità, fasi di maturazione.

digraph ProcessBlues {
    graph [rankdir=LR, bgcolor=white];
    node [shape=box, style="rounded,filled", colorscheme=blues9, fontname=Arial];
    edge [color="#2171B5", penwidth=2];

    Start [fillcolor=2, fontcolor=black, label="Start"];
    Validate [fillcolor=4, fontcolor=white, label="Validate"];
    Process [fillcolor=6, fontcolor=white, label="Process"];
    Store [fillcolor=8, fontcolor=white, label="Store"];
    Notify [fillcolor=9, fontcolor=white, label="Notify"];

    Start -> Validate -> Process -> Store -> Notify;
}

Command line:

dot -Tsvg colorscheme_blues.dot -o colorscheme_blues.svg

Flusso di processo in cinque passi sfumato con lo schema sequenziale blues

Esempio 4: RdYlGn9 (diverging, semaforo)

Perfetto per stati di successo/warning/errore, health checks, monitoring.

digraph ProcessStatus {
    graph [rankdir=LR, bgcolor=white];
    node [shape=box, style="rounded,filled", colorscheme=rdylgn9, fontname=Arial, fontcolor=black];
    edge [penwidth=2, color="#666666"];

    Idle [fillcolor=5, label="Idle\n(neutral)"];
    Starting [fillcolor=7, label="Starting\n(ok)"];
    Running [fillcolor=9, label="Running\n(good)"];
    Warning [fillcolor=4, label="Warning"];
    Error [fillcolor=1, label="Error"];

    Idle -> Starting -> Running;
    Running -> Warning [style=dashed];
    Warning -> Error [style=dashed];
    Running -> Idle [label="stop"];
}

Command line:

dot -Tsvg colorscheme_rdylgn.dot -o colorscheme_rdylgn.svg

Stati di un servizio da idle a errore colorati con lo schema rosso-giallo-verde rdylgn

Esempio 5: Paired12 (qualitativo, coppie)

Usa coppie di colori coordinati. Ottimo per confronti, versioni A/B, relazioni.

digraph ProcessPaired {
    graph [rankdir=TB, bgcolor=white];
    node [shape=box, style="rounded,filled", colorscheme=paired12, fontname=Arial];
    edge [colorscheme=paired12, penwidth=2];

    subgraph cluster_v1 {
        label="Version 1.x";
        style=filled;
        fillcolor="#F0F0F0";
        V1_Start [fillcolor=1, label="Start v1"];
        V1_Process [fillcolor=1, label="Process v1"];
        V1_End [fillcolor=1, label="End v1"];
        V1_Start -> V1_Process -> V1_End;
    }

    subgraph cluster_v2 {
        label="Version 2.x";
        style=filled;
        fillcolor="#F8F8F8";
        V2_Start [fillcolor=2, label="Start v2"];
        V2_Process [fillcolor=2, label="Process v2"];
        V2_End [fillcolor=2, label="End v2"];
        V2_Start -> V2_Process -> V2_End;
    }

    V1_Start -> V2_Start [label="upgrade", color=4, style=dashed];
}

Command line:

dot -Tsvg colorscheme_paired.dot -o colorscheme_paired.svg

Flussi delle versioni 1.x e 2.x affiancati con lo schema di colori paired

Esempio 6: Accent8 (qualitativo, contrasti forti)

Massimo contrasto tra elementi. Per evidenziare differenze importanti.

digraph ProcessAccent {
    graph [rankdir=LR, bgcolor="#F5F5F5"];
    node [shape=box, style="rounded,filled", colorscheme=accent8, fontname=Arial, fontcolor=black];
    edge [colorscheme=accent8, penwidth=2];

    Input [fillcolor=1, label="Input"];
    Parse [fillcolor=2, label="Parse"];
    Transform [fillcolor=3, label="Transform"];
    Validate [fillcolor=4, label="Validate"];
    Output [fillcolor=5, label="Output"];

    Input -> Parse [color=1];
    Parse -> Transform [color=2];
    Transform -> Validate [color=3];
    Validate -> Output [color=4];
    Validate -> Parse [color=6, label="retry", style=dashed];
}

Command line:

dot -Tsvg colorscheme_accent.dot -o colorscheme_accent.svg

Pipeline dati input, parse, transform, validate, output evidenziata con lo schema accent

Quando usare quale color scheme

ScenarioColor Scheme Consigliato
Categorie diverse, presentazioniset39, set28, dark28
Documentazione tecnica, wikipastel19, pastel28
Progressione, priorità, livelliblues9, greens9, purples9, oranges9
Stati (ok/warning/error)rdylgn9, rdylbu9, spectral9
Confronti, versioni A/Bpaired12, paired11
Massimo contrastoaccent8, set39
Accessibilità (daltonismo)set2, dark2 (ColorBrewer safe)

Combinare color schemes

Puoi usare schemi diversi per nodi e archi:

node [colorscheme=pastel19, fillcolor=3];
edge [colorscheme=dark28, color=2];

Tip: Testa sempre i colori con ColorBrewer per verificare accessibilità e stampabilità.


Tecniche avanzate: ports, ranks, compound edges

Ports nei record. Collega archi a campi specifici di un record usando la sintassi nodo:porta:

ClassA:field1 -> ClassB:field2;

Forzare nodi allo stesso livello. Usa rank=same per posizionare più nodi sulla stessa riga orizzontale:

{ rank=same; A; B; C; }

Compound edges tra cluster. Collega cluster interi invece di singoli nodi con lhead e ltail:

edge [lhead=cluster_B, ltail=cluster_A];

Archi ortogonali. Crea archi con angoli retti per uno stile “da architetto”:

graph [splines=ortho];

Stile professionale: consigli pratici

  • Usa palette coerenti.
  • Evita gradienti troppo brillanti.
  • Usa Inter, Arial o Roboto per leggibilità.
  • Usa ortho per archi “da architetto”.
  • Mantieni margini adeguati (nodesep, ranksep).
  • Preferisci SVG per qualità nei blog.
  • Mantieni i file DOT versionati.

Stili grafici pronti all’uso

Qui trovi 5 varianti stilistiche per lo stesso state diagram, pronte da copiare e adattare ai tuoi diagrammi. Ogni stile definisce colori, font, dimensioni e forme per creare un look coerente.

Stile 1: Corporate Blue (professionale, formale)

Palette blu/grigio, font Segoe UI, stile pulito per presentazioni aziendali.

digraph CorporateBlue {
    graph [
        rankdir=LR,
        bgcolor="#F8F9FA",
        fontname="Segoe UI",
        fontsize=12
    ];
    node [
        shape=box,
        style="rounded,filled",
        fillcolor="#E3F2FD",
        color="#1976D2",
        fontname="Segoe UI",
        fontsize=11,
        fontcolor="#1565C0",
        penwidth=2
    ];
    edge [
        color="#1976D2",
        fontname="Segoe UI",
        fontsize=10,
        fontcolor="#424242"
    ];

    Idle [label="Idle"];
    Processing [label="Processing"];
    Complete [label="Complete"];

    Idle -> Processing [label="start"];
    Processing -> Complete [label="finish"];
    Complete -> Idle [label="reset"];
    Processing -> Idle [label="cancel"];
}

Command line:

dot -Tsvg style_corporate.dot -o style_corporate.svg

Macchina a stati Idle, Processing e Complete resa nello stile corporate blu

Stile 2: Dark Mode (moderno, tech)

Sfondo scuro, testo chiaro, palette verde/ciano per UI moderne e developer tools.

digraph DarkMode {
    graph [
        rankdir=LR,
        bgcolor="#1E1E1E",
        fontname="Consolas",
        fontsize=12
    ];
    node [
        shape=box,
        style="rounded,filled",
        fillcolor="#2D2D30",
        color="#00D9FF",
        fontname="Consolas",
        fontsize=11,
        fontcolor="#E0E0E0",
        penwidth=2
    ];
    edge [
        color="#00D9FF",
        fontname="Consolas",
        fontsize=10,
        fontcolor="#B0B0B0"
    ];

    Idle [label="Idle"];
    Processing [label="Processing"];
    Complete [label="Complete"];

    Idle -> Processing [label="start"];
    Processing -> Complete [label="finish"];
    Complete -> Idle [label="reset"];
    Processing -> Idle [label="cancel"];
}

Command line:

dot -Tsvg style_dark.dot -o style_dark.svg

La stessa macchina a stati resa con il tema scuro su sfondo scuro

Stile 3: Warm Minimal (soft, leggibile)

Palette calda arancio/beige, sans-serif, ottimo per documentazione tecnica.

digraph WarmMinimal {
    graph [
        rankdir=LR,
        bgcolor="#FFFBF5",
        fontname="Segoe UI",
        fontsize=12
    ];
    node [
        shape=box,
        style="rounded,filled",
        fillcolor="#FFE8CC",
        color="#FF8C42",
        fontname="Segoe UI",
        fontsize=11,
        fontcolor="#6B4423",
        penwidth=1.5
    ];
    edge [
        color="#FF8C42",
        fontname="Segoe UI",
        fontsize=10,
        fontcolor="#8B5A3C",
        penwidth=1.5
    ];

    Idle [label="Idle"];
    Processing [label="Processing"];
    Complete [label="Complete"];

    Idle -> Processing [label="start"];
    Processing -> Complete [label="finish"];
    Complete -> Idle [label="reset"];
    Processing -> Idle [label="cancel"];
}

Command line:

dot -Tsvg style_warm.dot -o style_warm.svg

La stessa macchina a stati resa nello stile warm con toni arancio

Stile 4: Monochrome (elegante, stampabile)

Bianco e nero, gradazioni di grigio, perfetto per stampa e documentazione formale.

digraph Monochrome {
    graph [
        rankdir=LR,
        bgcolor="white",
        fontname="Segoe UI",
        fontsize=12
    ];
    node [
        shape=box,
        style="rounded,filled",
        fillcolor="#F5F5F5",
        color="#333333",
        fontname="Segoe UI",
        fontsize=11,
        fontcolor="#000000",
        penwidth=2
    ];
    edge [
        color="#333333",
        fontname="Segoe UI",
        fontsize=10,
        fontcolor="#666666",
        penwidth=1.5
    ];

    Idle [label="Idle"];
    Processing [label="Processing"];
    Complete [label="Complete"];

    Idle -> Processing [label="start"];
    Processing -> Complete [label="finish"];
    Complete -> Idle [label="reset"];
    Processing -> Idle [label="cancel"];
}

Command line:

dot -Tsvg style_mono.dot -o style_mono.svg

La stessa macchina a stati resa nello stile monocromatico, solo grigi e nero

Stile 5: Vibrant Gradient (creativo, impattante)

Colori vivaci con sfumature, ottimo per presentazioni e slide visivamente accattivanti.

digraph VibrantGradient {
    graph [
        rankdir=LR,
        bgcolor="#FAFAFA",
        fontname="Segoe UI",
        fontsize=12
    ];
    node [
        shape=box,
        style="rounded,filled",
        fillcolor="#A8E6CF:#56CCF2",
        gradientangle=90,
        color="#2D6A9F",
        fontname="Segoe UI",
        fontsize=11,
        fontcolor="#1A3A52",
        penwidth=2.5
    ];
    edge [
        color="#9B59B6",
        fontname="Segoe UI",
        fontsize=10,
        fontcolor="#5B3A72",
        penwidth=2
    ];

    Idle [label="Idle"];
    Processing [label="Processing", fillcolor="#FFD93D:#FF6B9D", gradientangle=90];
    Complete [label="Complete", fillcolor="#6BCF7F:#4ECDC4", gradientangle=90];

    Idle -> Processing [label="start"];
    Processing -> Complete [label="finish"];
    Complete -> Idle [label="reset"];
    Processing -> Idle [label="cancel"];
}

Command line:

dot -Tsvg style_vibrant.dot -o style_vibrant.svg

La stessa macchina a stati resa nello stile vibrant con colori saturi

Come usare questi stili

Copia il blocco graph, node, edge dello stile che preferisci e applicalo ai tuoi diagrammi. Puoi poi personalizzare:

  • Colori: sostituisci i codici hex con la tua palette
  • Font: usa fontname="NomeFont" (Arial, Helvetica, Courier, Times, Verdana, Consolas)
  • Dimensioni: fontsize per testo, penwidth per spessore bordi e frecce
  • Shape: box, ellipse, circle, diamond, cylinder, record
  • Gradienti: usa fillcolor="color1:color2" con gradientangle (solo alcuni output format)

Note sulla compatibilità dei font

I font devono essere installati sul sistema dove generi le immagini:

  • Font universali (funzionano ovunque): Arial, Helvetica, Times, Times-Roman, Courier
  • Font moderni (verificare disponibilità): Verdana, Consolas, Roboto, Inter
  • Windows: la maggior parte dei font sono già installati
  • macOS: ottimo supporto per font standard
  • Linux: installa fonts-liberation o fonts-dejavu per avere Arial/Helvetica equivalenti
  • CI/CD: usa font base o includi i font nel container Docker

Se un font non è disponibile, Graphviz usa un fallback (solitamente Times). Per essere sicuro, usa sempre font base o testa la generazione sull’ambiente di produzione.


Workflow: come integrare Graphviz nel lavoro reale

  1. Metti i file .gv nella repo.

  2. Genera gli SVG automaticamente nella CI:

    dot -Tsvg diagram.gv -o diagram.svg
    
  3. Inietta gli SVG nella documentazione (README, wiki, blog).

  4. Aggiorna i diagrammi ad ogni refactoring.

  5. Usa differ e versioning dei file DOT come faresti col codice.


Troubleshooting: errori comuni e soluzioni

Errore: “syntax error in line X near…”

  • Causa: Sintassi DOT non valida
  • Soluzione: Controlla punto e virgola mancanti, parentesi graffe non chiuse, virgolette non matchate
  • Esempio: A -> B deve terminare con ; → A -> B;

Errore: “Warning: Unable to find font…”

  • Causa: Il font specificato non è installato sul sistema
  • Soluzione: Usa font universali (Arial, Helvetica, Times) o installa il font richiesto
  • Verifica font disponibili: dot -v mostra i font disponibili

Il diagramma è troppo grande/piccolo

  • Soluzione 1: Aggiungi graph [size="8,6"] per limitare dimensioni (in pollici)
  • Soluzione 2: Usa graph [ratio=compress] per comprimere automaticamente
  • Soluzione 3: Genera PNG con DPI custom: dot -Tpng -Gdpi=150 file.dot -o file.png

Le frecce si sovrappongono ai nodi

  • Soluzione: Aggiungi graph [overlap=false, splines=true] (vedi sezione “Evitare sovrapposizioni”)

I nodi sono tutti allineati orizzontalmente invece che verticalmente

  • Soluzione: Usa graph [rankdir=TB] per top-to-bottom (default è LR = left-to-right)

Il cluster non appare

  • Causa: Nome cluster non inizia con cluster_
  • Soluzione: Rinomina subgraph mygroup in subgraph cluster_mygroup

Output SVG troppo grande (file size)

  • Causa: SVG con molti elementi
  • Soluzione 1: Usa PNG invece di SVG per grafi molto complessi
  • Soluzione 2: Ottimizza con svgo: svgo input.svg -o output.svg

Grafo non si genera (nessun output, nessun errore)

  • Causa: Comando errato o redirect sbagliato
  • Verifica: Usa -v per verbose: dot -v -Tsvg input.dot -o output.svg
  • Test rapido: echo "digraph{A->B}" | dot -Tsvg > test.svg

Le label degli archi non si vedono

  • Causa: Label troppo lunghe o font troppo piccolo
  • Soluzione: Aumenta fontsize sugli edge o usa \n per spezzare label su più righe

Best practice per un codice DOT pulito

Don’t Repeat Yourself (DRY)

Definisci gli attributi una volta sola al livello più alto, invece di ripeterli su ogni elemento.

❌ Male (ripetitivo):

digraph {
    node [fontname="Segoe UI"];
    A [fontname="Segoe UI", shape=box];
    B [fontname="Segoe UI", shape=box];
    C [fontname="Segoe UI", shape=box];
}

✅ Bene (DRY):

digraph {
    // Definito una volta, vale per tutti
    graph [fontname="Segoe UI"];
    node [fontname="Segoe UI", shape=box];
    edge [fontname="Segoe UI"];

    A; B; C;  // Ereditano tutti gli attributi
}

Principio chiave: usa le dichiarazioni graph, node ed edge per impostare i default. Sovrascrivili solo quando un elemento specifico ha bisogno di attributi diversi.

Usa i commenti

Aggiungi commenti per spiegare le sezioni più complesse:

digraph {
    // Stile globale
    graph [rankdir=LR, fontname="Segoe UI"];

    // Componenti principali
    A -> B;

    // Percorso di gestione degli errori
    B -> Error [style=dashed, color=red];
}

Organizza i grafi grandi

Raggruppa le dichiarazioni correlate:

digraph {
    // === Configurazione ===
    graph [rankdir=TB];
    node [shape=box];

    // === Servizi ===
    ServiceA; ServiceB; ServiceC;

    // === Database ===
    DB1 [shape=cylinder];
    DB2 [shape=cylinder];

    // === Connessioni ===
    ServiceA -> DB1;
    ServiceB -> DB2;
}

Template DOT + Comandi Graphviz (CLI)

Ogni template include:

  1. snippet DOT

  2. comando CLI per generare l’immagine

Per coerenza uso il formato SVG, ma puoi sostituire -Tsvg con:

  • -Tpng
  • -Tpdf
  • -Tjpg
  • -Tgif

E puoi salvare l’input DOT in template.dot oppure usare pipe.


Flowchart

Template 1: Processo base

digraph FlowBasic {
    graph [rankdir=TB];
    node [shape=rectangle, style=rounded, fontsize=12];

    Start [label="Start", shape=circle];
    Step1 [label="Input validation"];
    Step2 [label="Process request"];
    Step3 [label="Persist data"];
    End [label="End", shape=doublecircle];

    Start -> Step1 -> Step2 -> Step3 -> End;
}

Command line:

dot -Tsvg FlowBasic.dot -o FlowBasic.svg

Output del template: processo lineare da start a validazione, persistenza e fine

Template 2: Branching (if/else)

digraph FlowIfElse {
    graph [rankdir=TB];
    node [fontsize=12, style=rounded];

    Start [shape=circle];
    Check [shape=diamond, label="Is valid?"];
    A [label="Handle valid case"];
    B [label="Handle error"];
    End [shape=doublecircle];

    Start -> Check;
    Check -> A [label="Yes"];
    Check -> B [label="No"];
    A -> End;
    B -> End;
}

Command line:

dot -Tsvg FlowIfElse.dot -o FlowIfElse.svg

Output del template: flowchart if/else con rombo di validità e due rami

Template 3: Processo con sezioni

digraph FlowSections {
    graph [rankdir=TB];
    node [fontsize=11, style=rounded];

    subgraph cluster_input {
        label="Input Stage";
        color=lightgrey;
        style=filled;

        A1 [label="Receive request"];
        A2 [label="Validate payload"];
        A1 -> A2;
    }

    subgraph cluster_processing {
        label="Processing Stage";
        color=lightblue;
        style=filled;

        P1 [label="Transform data"];
        P2 [label="Apply business rules"];
        P1 -> P2;
    }

    subgraph cluster_output {
        label="Output Stage";
        color=lightyellow;
        style=filled;

        O1 [label="Persist"];
        O2 [label="Return response"];
        O1 -> O2;
    }

    A2 -> P1 -> O1;
}

Command line:

dot -Tsvg FlowSections.dot -o FlowSections.svg

Output del template: gestione della richiesta divisa in cluster input, processing e output

State Machine

Template 4: State machine base

digraph StateMachine {
    graph [rankdir=LR];
    node [shape=circle, fontsize=12];

    Idle;
    Loading;
    Error;
    Success;

    Idle -> Loading [label="start"];
    Loading -> Success [label="ok"];
    Loading -> Error [label="fail"];
    Error -> Idle [label="retry"];
}

Command line:

dot -Tsvg StateMachine.dot -o StateMachine.svg

Output del template: macchina a stati con Idle, Loading, Success, Error e retry

Template 5: Stati annidati

digraph NestedStates {
    graph [rankdir=LR];
    node [fontsize=11];

    subgraph cluster_ready {
        label="Ready state";
        style=dashed;
        R1 [shape=circle, label="Idle"];
        R2 [shape=circle, label="Primed"];
        R1 -> R2 [label="prepare"];
    }

    subgraph cluster_active {
        label="Active state";
        style=dashed;
        A1 [shape=circle, label="Running"];
        A2 [shape=circle, label="Paused"];
        A1 -> A2 [label="pause"];
        A2 -> A1 [label="resume"];
    }

    R2 -> A1 [label="activate"];
}

Command line:

dot -Tsvg NestedStates.dot -o NestedStates.svg

Output del template: stati annidati, cluster Ready e Active con pause e resume

Sequence Diagram (DOT)

Template 6: Sequence orizzontale

digraph Sequence {
    graph [rankdir=LR];
    node [shape=box, fontsize=11];

    Client -> API [label="POST /login"];
    API -> AuthService [label="Check credentials"];
    AuthService -> DB [label="Query user"];
    DB -> AuthService [label="Result"];
    AuthService -> API [label="Token"];
    API -> Client [label="200 OK"];
}

Command line:

dot -Tsvg Sequence.dot -o Sequence.svg

Output del template: sequenza di login orizzontale fra client, API, AuthService e DB

Template 7: Sequence con attivazioni

digraph SequenceActivation {
    graph [rankdir=LR];
    node [shape=box, style=rounded, fontsize=11];

    User -> Frontend [label="Login"];
    Frontend -> Backend [label="POST /login"];
    Backend -> Backend [label="validate()"];
    Backend -> DB [label="SELECT user"];
    DB -> Backend [label="row found"];
    Backend -> Frontend [label="JWT"];
    Frontend -> User [label="Welcome"];
}

Command line:

dot -Tsvg SequenceActivation.dot -o SequenceActivation.svg

Output del template: sequenza di login con attivazioni fra utente, frontend, backend e DB

Dependency Graph

Template 8: Moduli

digraph DependencyTree {
    graph [rankdir=TB];
    node [shape=box, style=rounded, fontsize=12];

    App -> ModuleA;
    App -> ModuleB;
    ModuleA -> LibA;
    ModuleA -> LibB;
    ModuleB -> LibB;
}

Command line:

dot -Tsvg DependencyTree.dot -o DependencyTree.svg

Output del template: albero delle dipendenze da App a moduli e librerie

Template 9: Microservizi

digraph MicroservicesDep {
    graph [rankdir=LR];
    node [shape=box, style=rounded, fontsize=11];

    Gateway -> Auth;
    Gateway -> Orders;
    Auth -> UsersDB;
    Orders -> ProductsService;
    Orders -> Payments;
    Payments -> BankAPI;
}

Command line:

dot -Tsvg MicroservicesDep.dot -o MicroservicesDep.svg

Output del template: dipendenze fra microservizi dal gateway ad auth, ordini, pagamenti e banca

Architecture Diagram

Template 10: Layered

digraph Layered {
    graph [rankdir=TB];
    node [shape=box, style=rounded, fontsize=12];

    subgraph cluster_presentation {
        label="Presentation Layer";
        style=filled;
        color=lightyellow;
        UI;
        API;
    }

    subgraph cluster_business {
        label="Business Layer";
        style=filled;
        color=lightblue;
        Services;
    }

    subgraph cluster_data {
        label="Data Layer";
        style=filled;
        color=lightgrey;
        DB;
        Cache;
    }

    UI -> API -> Services -> DB;
    Services -> Cache;
}

Command line:

dot -Tsvg Layered.dot -o Layered.svg

Output del template: architettura a livelli con UI, API, servizi, database e cache

Template 11: Esagonale

digraph Hexagonal {
    graph [rankdir=LR];
    node [shape=box, style=rounded, fontsize=11];

    AppCore [label="Core Domain"];
    PortIn [label="Inbound Ports"];
    PortOut [label="Outbound Ports"];
    AdapterIn [label="Inbound Adapters"];
    AdapterOut [label="Outbound Adapters"];
    DB [label="Database"];
    UI [label="Frontend/UI"];

    UI -> AdapterIn -> PortIn -> AppCore;
    AppCore -> PortOut -> AdapterOut -> DB;
}

Command line:

dot -Tsvg Hexagonal.dot -o Hexagonal.svg

Output del template: architettura esagonale con core domain, porte e adapter

ER Diagrams

Template 12: 1:N

digraph ER_OneToMany {
    graph [rankdir=LR];
    node [shape=record, fontsize=11];

    User [label="{User|id PK|name|email}"];
    Order [label="{Order|id PK|user_id FK|total}"];

    User -> Order [label="1:N"];
}

Command line:

dot -Tsvg ER_OneToMany.dot -o ER_OneToMany.svg

Output del template: diagramma ER di User e Order in relazione 1:N

Template 13: N:N

digraph ER_ManyToMany {
    graph [rankdir=LR];
    node [shape=record, fontsize=11];

    Student [label="{Student|id PK|name}"];
    Course [label="{Course|id PK|title}"];
    Enroll [label="{Enroll|student_id FK|course_id FK}"];

    Student -> Enroll;
    Course -> Enroll;
}

Command line:

dot -Tsvg ER_ManyToMany.dot -o ER_ManyToMany.svg

Output del template: relazione N:N fra Student e Course tramite la tabella Enroll

Call Graph

Template 14: Base

digraph CallGraph {
    graph [rankdir=TB];
    node [shape=box, style=rounded, fontsize=11];

    main -> init;
    main -> loadConfig;
    loadConfig -> readFile;
    readFile -> parseJson;
}

Command line:

dot -Tsvg CallGraph.dot -o CallGraph.svg

Output del template: call graph da main a init, loadConfig, readFile e parseJson

Template 15: Con categorie

digraph CategorizedCalls {
    graph [rankdir=TB];
    node [shape=box, style=rounded, fontsize=11];

    subgraph cluster_io {
        label="I/O Functions";
        color=lightgrey;
        readFile;
        writeFile;
    }

    subgraph cluster_logic {
        label="Business Logic";
        color=lightblue;
        compute;
        validate;
    }

    main -> compute -> validate;
    compute -> readFile;
    validate -> writeFile;
}

Command line:

dot -Tsvg CategorizedCalls.dot -o CategorizedCalls.svg

Output del template: call graph con funzioni di I/O e business logic in cluster separati

Network Diagram

Template 16: Base

digraph Network {
    graph [rankdir=LR];
    node [shape=box, style=rounded, fontsize=11];

    Client -> LoadBalancer;
    LoadBalancer -> AppServer1;
    LoadBalancer -> AppServer2;
    AppServer1 -> DB;
    AppServer2 -> DB;
}

Command line:

dot -Tsvg Network.dot -o Network.svg

Output del template: rete dal client al load balancer, due app server e DB

Template 17: Con protocolli

digraph NetworkProto {
    graph [rankdir=LR];
    node [shape=box, fontsize=11];

    Client -> API [label="HTTPS"];
    API -> Auth [label="gRPC"];
    API -> Orders [label="REST"];
    Orders -> DB [label="TCP"];
}

Command line:

dot -Tsvg NetworkProto.dot -o NetworkProto.svg

Output del template: archi di rete etichettati con i protocolli HTTPS, gRPC, REST e TCP

Timeline / Roadmap

Template 18: Timeline

digraph Timeline {
    graph [rankdir=LR];
    node [shape=box, style=rounded, fontsize=11];

    Start -> Milestone1 -> Milestone2 -> Milestone3 -> Release;
}

Command line:

dot -Tsvg Timeline.dot -o Timeline.svg

Output del template: timeline di progetto da start a tre milestone fino al rilascio

Template 19: Roadmap

digraph Roadmap {
    graph [rankdir=LR];
    node [shape=box, fontsize=11];

    subgraph cluster_backend {
        label="Backend";
        B1 [label="Auth module"];
        B2 [label="Payments"];
    }

    subgraph cluster_frontend {
        label="Frontend";
        F1 [label="Login UI"];
        F2 [label="Dashboard"];
    }

    B1 -> B2;
    F1 -> F2;
}

Command line:

dot -Tsvg Roadmap.dot -o Roadmap.svg

Output del template: roadmap con tracce backend e frontend, auth, pagamenti, login UI, dashboard

Mind Map

Template 20: Mind map

graph MindMap {
    layout=twopi;
    rankdir=LR;
    node [shape=box, style=rounded, fontsize=11];

    Central -- Idea1;
    Central -- Idea2;
    Central -- Idea3;
    Idea2 -- Sub1;
    Idea2 -- Sub2;
}

Command line:

dot -Ktwopi -Tsvg MindMap.dot -o MindMap.svg

Output del template: mappa mentale radiale con nodo centrale, tre idee e sotto-idee

Component Diagram

Template 21: Components

digraph Components {
    graph [rankdir=LR];
    node [shape=component, fontsize=11];

    UI -> API;
    API -> Service;
    Service -> DB;
}

Command line:

dot -Tsvg Components.dot -o Components.svg

Output del template: diagramma dei componenti che collega UI, API, servizio e database

Class Diagram

Template 22: UML class-like

digraph Classes {
    graph [rankdir=TB];
    node [shape=record, fontsize=11];

    Person [label="{Person|name:string|age:int}"];
    Student [label="{Student|grade:int}"];
    Person -> Student;
}

Command line:

dot -Tsvg Classes.dot -o Classes.svg

Output del template: diagramma delle classi stile UML con Student che eredita da Person

Layouts

Template 23: Orizzontale

digraph Horizontal {
    graph [rankdir=LR];
    node [shape=box, style=rounded];
}

Command line:

dot -Tsvg Horizontal.dot -o Horizontal.svg

Output del template: scheletro vuoto da sinistra a destra, pronto da riempire con nodi

Template 24: Verticale

digraph Vertical {
    graph [rankdir=TB];
    node [shape=box, style=rounded];
}

Command line:

dot -Tsvg Vertical.dot -o Vertical.svg

Output del template: scheletro vuoto dall’alto in basso, pronto da riempire con nodi

Template 25: Circolare

graph Circular {
    layout=circo;
    node [shape=box, style=rounded];
}

Command line:

dot -Kcirco -Tsvg Circular.dot -o Circular.svg

Output del template: scheletro vuoto con layout circo, pronto da riempire con nodi

Stili professionali

Template 26: Modern theme

digraph Modern {
    graph [rankdir=LR];
    node [
        shape=box,
        style="rounded,filled",
        fillcolor="#eef3f8",
        color="#6a8bbf",
        fontsize=11
    ];
    edge [color="#6a8bbf"];

    A -> B -> C;
}

Command line:

dot -Tsvg Modern.dot -o Modern.svg

Output del template: tre nodi A, B, C nel tema moderno arrotondato e pieno

Template 27: Tema Scuro

digraph Dark {
    bgcolor="#1e1e1e";
    node [
        shape=box,
        style="rounded,filled",
        fillcolor="#333333",
        fontcolor="white",
        color="#777777",
        fontsize=11
    ];
    edge [color="#999999"];

    A -> B -> C;
}

Command line:

dot -Tsvg Dark.dot -o Dark.svg

Output del template: tre nodi A, B, C nel tema scuro su sfondo scuro


Risorse utili

Documentazione ufficiale

Strumenti online

Integrazioni e librerie

  • Graphviz Visual Editor (VSCode): Estensione per preview live in Visual Studio Code
  • PlantUML: plantuml.com: Usa Graphviz per rendering di UML
  • Mermaid: mermaid.js.org: Alternativa JavaScript-based a Graphviz per grafici in Markdown
  • Python graphviz: graphviz.readthedocs.io: Libreria Python per generare grafi programmaticamente
  • Go graphviz: github.com/goccy/go-graphviz: Binding Go per Graphviz

Gallery ed esempi

Community e supporto

Tool complementari


FAQ: Domande frequenti su DOT e Graphviz

Come si crea un diagramma con Graphviz?

Crea un file con estensione .dot contenente la descrizione del grafo, poi esegui dot -Tsvg file.dot -o output.svg. Il comando genera un’immagine SVG dal codice DOT.

Qual è la differenza tra digraph e graph in DOT?

digraph crea un grafo diretto (con frecce), graph crea un grafo non diretto (con linee senza freccia). Usa -> per archi diretti, -- per archi non diretti.

Come cambio la direzione del layout in Graphviz?

Usa l’attributo rankdir: TB (top-to-bottom, default), BT (bottom-to-top), LR (left-to-right), RL (right-to-left). Esempio: graph [rankdir=LR];

Come creo un flowchart con decisioni (rombi) in DOT?

Usa shape=diamond per i nodi decisionali: Decision [shape=diamond, label="Valido?"];

Come raggruppo nodi in un box (cluster) in Graphviz?

Usa subgraph cluster_nome { ... }. Il prefisso cluster_ è obbligatorio per visualizzare il raggruppamento.

Come creo diagrammi ER (Entity-Relationship) con DOT?

Usa shape=plaintext con HTML label per creare tabelle strutturate, e arrowhead=crow per le relazioni 1:N.

Come evito sovrapposizioni tra frecce e nodi in Graphviz?

Aggiungi graph [overlap=false, splines=true, sep="+0.2"]; per evitare collisioni.

Qual è il layout engine migliore per il mio diagramma?

  • dot: flowchart, pipeline, gerarchie (default)
  • neato/fdp: grafi non diretti, mappe concettuali
  • circo: strutture hub-and-spoke
  • twopi: alberi radiali, org chart

Come genero PNG invece di SVG con Graphviz?

Cambia il flag -T: dot -Tpng file.dot -o output.png. Puoi anche specificare DPI: dot -Tpng -Gdpi=150.

Posso usare Graphviz online senza installarlo?

Sì! Usa edotor.net o GraphvizOnline per testare codice DOT nel browser.

Come integro Graphviz nella mia CI/CD pipeline?

Installa Graphviz nell’ambiente CI (es. apt install graphviz in Docker), poi aggiungi uno step che esegue dot -Tsvg *.dot. Versiona i file .dot e genera le immagini automaticamente.

Come creo diagrammi di stato (state machine) con DOT?

Usa shape=circle per gli stati, shape=point per lo stato iniziale, shape=doublecircle per lo stato finale. Etichetta gli archi con gli eventi: Idle -> Loading [label="start"];

Come applico lo stesso stile a più nodi senza ripeterlo?

Elenca i nodi sulla stessa riga seguiti dagli attributi: A; B; C [shape=box, fillcolor=lightblue, style=filled];

DOT supporta i colori HEX?

Sì! Usa codici HEX come fillcolor="#E3F2FD" o nomi predefiniti come fillcolor=lightblue.

Come creo diagrammi UML con Graphviz?

Usa shape=record per class diagram: Class [label="{NomeClasse|attributo:tipo|metodo()}"];. Per UML completo considera PlantUML che usa Graphviz internamente.


Articolo scritto da Daniele Teti, danieleteti.it

Fatti chiave

Manuale pratico del linguaggio DOT di Graphviz per sviluppatori, analisti e software architect, con template pronti all'uso.

  • DOT è un linguaggio testuale per descrivere grafi; Graphviz, sviluppato originariamente da AT&T Labs, genera SVG, PNG o PDF dai file DOT
  • Installazione su Windows (winget install graphviz), macOS (brew install graphviz) e Linux (apt o dnf); dot -V verifica l'installazione
  • Spiega gli attributi di nodi, archi e grafo, label contro xlabel, le note sui nodi e come definire stili per gruppi di nodi con gli attributi di default
  • Layout engine a confronto: dot (gerarchico, default), neato e fdp (force-directed), sfdp, circo (circolare), twopi (radiale); si scelgono con -K o con l'attributo layout
  • Tipi di diagrammi dal lavoro reale: flowchart, grafi di dipendenze, architetture a livelli, mappe di microservizi, diagrammi di stato, diagrammi delle classi con record, diagrammi ER con HTML-label, pipeline CI/CD, mappe concettuali
  • Le sovrapposizioni si evitano con graph [overlap=false, splines=true, sep="+0.2"]
  • Color scheme Brewer mostrati: set39, pastel19, blues9, rdylgn9, paired12, accent8
  • Cinque stili grafici pronti: corporate, dark, warm, mono, vibrant
  • 27 template DOT, ognuno con il comando Graphviz da riga di comando e l'SVG risultante
  • Workflow consigliato: file .gv nel repository e SVG generati in CI; editor online citati: edotor.net e GraphvizOnline

Comments