Become a member!

Lenguaje DOT de Graphviz: la guía práctica que la documentación oficial no te da

Para desarrolladores, analistas y arquitectos de software que quieren producir diagramas profesionales, claros y visualmente atractivos

🌐
Este artículo también está disponible en otros idiomas:
🇬🇧 English  •  🇮🇹 Italiano  •  🇩🇪 Deutsch  •  🇧🇷 Português

¿Qué es el lenguaje DOT? ¿Qué es Graphviz?

DOT es un lenguaje de descripción de grafos en formato de texto. Permite describir nodos, arcos y atributos visuales usando una sintaxis simple y legible. Es el formato estándar usado por Graphviz.

Graphviz (Graph Visualization Software) es una suite de herramientas open source para la visualización de grafos. Desarrollado originalmente por AT&T Labs, Graphviz lee archivos DOT y genera imágenes en varios formatos (SVG, PNG, PDF). Es la herramienta más usada en el mundo para generar diagramas desde código.

¿Por qué usar DOT y Graphviz en lugar de herramientas gráficas?

  • Versionable: Los archivos DOT son texto puro, perfectos para Git
  • Reproducible: El mismo archivo siempre genera el mismo diagrama
  • Automatizable: Genera diagramas en tu pipeline CI/CD
  • Rápido: Escribe código, no arrastres cajas con el ratón
  • Profesional: Output de alta calidad para documentación técnica

Prefacio

¿Por qué escribí este manual? Porque las guías de DOT y Graphviz existen, pero hablan de todo: biología, química, redes sociales, árboles genealógicos. Cada vez tenía que buscar lo que realmente importa a quienes desarrollamos software. Así que decidí escribir esta guía, reuniendo todo lo que he usado estos años y que tuve que buscar una y otra vez. Me costó algo de trabajo, pero espero que valga la pena. Ahora la comparto.

¿La ventaja de generar diagramas desde código? El archivo DOT vive en el repo, junto al código fuente. Cuando modificas la arquitectura, actualizas el diagrama en el mismo commit, pasa por la misma code review, sigue el mismo flujo. No es un PowerPoint olvidado en algún drive que nadie volverá a abrir. Es parte del proyecto.

Pero hay más. Los archivos DOT son texto, así que puedes generarlos programáticamente. Tu software puede producir diagramas que muestran el estado actual del sistema, las fases de un proceso, el flujo de una petición a través de los microservicios, las dependencias entre módulos cargadas en runtime. He visto equipos generar automáticamente el mapa de migraciones de base de datos, el grafo de feature flags activas, incluso el diagrama de colas de mensajes en tiempo real. Las posibilidades son infinitas.

Si tú también estás cansado de screenshots que envejecen el día después de hacerlos, aquí encontrarás todo lo que necesitas para empezar. Y una vez que domines DOT, descubrirás que los diagramas ya no son documentación estática: se convierten en parte viva de tu sistema.

Daniele Teti
Noviembre 2025


Introducción

En el trabajo diario de quienes desarrollan software (ya seas analista, desarrollador backend, frontend, experto en bases de datos o arquitecto) siempre llega el momento en que un diagrama se vuelve fundamental.

Lo necesitas cuando debes:

  • explicar un flujo complejo,
  • diseñar la pipeline CI/CD,
  • presentar la arquitectura a un nuevo colega,
  • analizar dependencias entre módulos,
  • documentar una base de datos,
  • preparar una presentación técnica,
  • razonar sobre un refactoring,
  • identificar cuellos de botella.

Graphviz y su lenguaje DOT son herramientas ideales: textuales (versionables), rápidas de escribir, visualmente atractivas al renderizar, flexibles.

Instalación de Graphviz

Antes de empezar, asegúrate de tener Graphviz instalado:

  • Windows: Descarga el instalador desde 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 la instalación con dot -V: deberías ver la versión instalada.

Este manual pretende ser la guía definitiva para quienes en el desarrollo de software quieren usar el lenguaje DOT al máximo. Encontrarás:

  • explicación profunda de los atributos con todas las opciones relevantes,
  • ejemplos completos, reutilizables y comentados,
  • mejores prácticas para gráficos profesionales,
  • sugerencias sobre el uso de los motores de layout,
  • escenarios concretos del mundo del desarrollo (diseño, refactoring, análisis, arquitectura).

El lenguaje DOT: bases sólidas

DOT describe grafos con una sintaxis muy simple:

digraph Name {
    nodeA -> nodeB;
}

O sin dirección:

graph Name {
    nodeA -- nodeB;
}

Conceptos clave:

  • nodos: entidades (funciones, objetos, microservicios, tablas de BD)
  • arcos: relaciones, llamadas, flujos
  • atributos: aspecto visual o metadatos

Quick Start: tu primer diagrama en 30 segundos

Prueba inmediatamente online (sin instalar nada)

Todos los ejemplos de este artículo se pueden probar directamente online usando Edotor.net:

  1. Ve a edotor.net

  2. Copia el código DOT de cualquier ejemplo de este artículo

  3. Pégalo en el área izquierda (reemplazando el código existente)

  4. Verás el renderizado inmediato en el área derecha

Es la forma más rápida de experimentar sin instalar Graphviz. Cuando estés listo para producción, instala Graphviz localmente.

Primer ejemplo para probar

Copia este código y pruébalo en 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"];
}

O usa la línea de comandos local

Crea un archivo hello.dot con el código anterior y genera la imagen SVG:

dot -Tsvg hello.dot -o hello.svg

Abre hello.svg en el navegador y verás tu primer flowchart. De aquí en adelante, todo se basa en variaciones y combinaciones de estos conceptos.


Atributos fundamentales (con opciones completas)

Los atributos se pueden aplicar globalmente a:

  • graph
  • node
  • edge
  • a elementos individuales

Ejemplo:

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

    A -> B;
}

A continuación, la lista de los atributos más importantes con sus opciones más útiles en el ámbito del software.


Atributos de los nodos (node)

shape: forma del nodo

Opciones útiles:

  • box
  • ellipse
  • circle
  • diamond (decisiones en flows)
  • record (diagramas de clases, estructuras de datos)
  • plaintext (contenidos completamente personalizados con HTML-label)
  • note
  • folder (disponible en algunas builds)

Ejemplo:

node [shape=box];

Usos típicos:

  • box → módulos de software
  • ellipse → estados
  • diamond → decisiones

style: estilo gráfico

Opciones comunes:

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

Ejemplo:

node [style="filled,rounded"];

fillcolor: color de relleno

Formatos:

  • nombres (ej. "lightgrey")
  • HEX (ej. "#AABBCC")
  • RGB ("#rrggbb")
  • HSL (en algunas builds)

fontname, fontcolor, fontsize

Ej.:

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

margin

Margen interno del nodo.

node [margin="0.2,0.1"];

width y height: dimensiones del nodo

Controlan el tamaño de los nodos. Por defecto Graphviz dimensiona cada nodo según su contenido.

node [width=1.5, height=0.8];

fixedsize: fuerza dimensiones exactas:

  • fixedsize=false (default): el nodo crece para contener el texto y trata width/height como valores mínimos
  • fixedsize=true: el nodo tiene exactamente las dimensiones indicadas en width/height, sea cual sea el contenido
  • fixedsize=shape: se aplica solo a la forma, no a la etiqueta

Casos de uso prácticos:

  • Aspecto uniforme: usa fixedsize=true con width para que todos los nodos tengan el mismo tamaño (imprescindible en diagramas de estado y flowcharts)
  • Tamaño adaptable: deja el default fixedsize=false para que los nodos se adapten a la longitud del texto
  • Solo ancho fijo: combínalo con height para controlar la proporción
// Todos los estados del mismo tamaño (FSM profesional)
node [shape=circle, fixedsize=true, width=0.9];

// Tamaño mínimo, pero puede crecer
node [shape=box, width=1.0, fixedsize=false];

label y xlabel: etiquetas de los nodos

label: la etiqueta estándar, dentro del nodo o junto a él:

A [label="State A"];

Etiquetas multilínea: usa \n para los saltos de línea:

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

xlabel: etiqueta externa, colocada fuera del borde del nodo. Graphviz busca por sí solo la mejor posición para no pisar arcos ni otros nodos. Muy útil en los diagramas de estado, donde quieres que los estados queden limpios:

A [xlabel="State A"];

Ejemplo práctico que compara label y 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 de estados de Graphviz que compara label dentro de los nodos con xlabel colocado fuera

Cuándo usar xlabel:

  • Diagramas de estado: los círculos de los estados quedan visualmente limpios
  • Grafos complejos: menos ruido visual dentro de los nodos
  • Posicionamiento automático: Graphviz encuentra el mejor sitio para la etiqueta
  • Nodos pequeños: cuando una etiqueta interna haría el nodo demasiado grande

Añadir notas y anotaciones a los nodos

Hay varias técnicas para añadir notas explicativas, descripciones o anotaciones a los nodos de un diagrama. Resultan especialmente útiles en mapas mentales, documentación y arquitecturas complejas.

Técnica 1: etiquetas multilínea con \n

La más sencilla: saltos de línea dentro de la etiqueta.

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

Técnica 2: tooltip con el atributo tooltip

Añade un tooltip que aparece al pasar el ratón (funciona en SVG abierto en el navegador):

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

Técnica 3: nodo nota separado, unido con un arco discontinuo

Crea un nodo dedicado a la nota, visualmente distinto de los nodos principales:

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

Técnica 4: etiquetas estructuradas con la forma record

Usa un record para separar el título de la descripción:

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

Ejemplo completo: mapa mental con notas

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"];

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

    // Subconceptos (alrededor del 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];

    // Nodos nota (unidos a los conceptos)
    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];

    // Conexiones principales (ramas del mapa mental)
    Central -- Frontend;
    Central -- Backend;
    Central -- Database;
    Central -- DevOps;

    // Notas unidas con líneas discontinuas (sin flechas)
    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 de la arquitectura de un proyecto con nodos nota que anotan React, Node.js, PostgreSQL y Docker

Cuándo usar cada técnica:

TécnicaIdeal paraVentajasInconvenientes
Multilínea con \nNotas cortas (1-2 líneas)Sencilla, siempre visiblePuede agrandar demasiado los nodos
tooltipExplicaciones largasNo satura el diagramaSolo funciona en SVG interactivo
Nodo nota separadoAnotaciones importantesMuy visible, personalizableAñade complejidad visual
RecordDatos estructuradosSeparación limpiaSintaxis más compleja

Atributos de los arcos (edge)

arrowsize

Escala de la flecha. Default ~1.0

edge [arrowsize=0.8];

arrowhead / arrowtail

Opciones útiles:

  • normal
  • empty (triángulo vacío, muy legible)
  • diamond
  • onormal
  • crow (diagramas ER)
  • tee
  • none

style y color

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

label

Etiqueta del arco.

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

Atributos del grafo (graph)

rankdir

Dirección del layout (solo dot):

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

Ej.:

graph [rankdir=LR];

splines

Controla la forma de los arcos:

  • true (default)
  • false (líneas rectas)
  • polyline
  • ortho (ortogonales, óptimo para diagramas “de arquitecto”)

ranksep, nodesep

Espacios horizontales/verticales.

graph [ranksep=0.8, nodesep=0.6];

¿Cómo definir estilos para grupos de nodos en DOT?

Una de las preguntas más frecuentes al usar Graphviz es: ¿cómo puedo aplicar el mismo estilo a un grupo de nodos sin repetirlo para cada uno?

Basta con listar los nodos en la misma línea seguidos de la definición de atributos. DOT aplicará esos atributos a todos los nodos listados.

Sintaxis para estilizar grupos de nodos

Método 1: redefinir los default de los nodos (recomendado)

// Redefine los default de los nodos antes de cada grupo
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;

Método 2: listar los nodos con los atributos

// Alternativa: nodos separados por punto y coma, el último con los atributos
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: el método 1 es más fiable entre versiones distintas de Graphviz y facilita añadir nodos a una categoría más adelante.

Esta técnica es fundamental cuando tienes diagramas complejos con decenas de nodos que pertenecen a diferentes categorías.

Ejemplo práctico: categorizar nodos por rol

Imagina que debes dibujar una arquitectura con tres tipos de componentes: servicios (cajas azules), bases de datos (cilindros verdes), colas/mensajes (elipses naranjas).

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

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

    // Bases de datos - cilindros verdes
    node [shape=cylinder, style=filled, fillcolor="#E8F5E9"];
    UserDB; OrderDB; SessionCache;

    // Colas de mensajes - elipses naranjas
    node [shape=ellipse, style=filled, fillcolor="#FFF3E0"];
    EmailQueue; SMSQueue; PushQueue;

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

Servicios, bases de datos y colas coloreados por rol: AuthService, UserDB, OrderService, EmailQueue

Qué hace este ejemplo: Define tres categorías de nodos con estilos diferentes en solo tres líneas. Los servicios son cajas azules redondeadas, las bases de datos son cilindros verdes, las colas son elipses naranjas. Cada categoría se reconoce a primera vista.

¿Por qué usar esta técnica?

  1. Código DRY (Don’t Repeat Yourself): No repites shape=box, style=filled para cada nodo
  2. Mantenibilidad: Cambiar el color de todos los servicios requiere una sola modificación
  3. Legibilidad: El código DOT se vuelve auto-documentante (ves inmediatamente qué nodos son servicios, cuáles son DBs)
  4. Escalabilidad: Agregar un nuevo servicio es cuestión de añadir el nombre en la lista

Combinar con atributos de default

Puedes combinar esta técnica con los atributos de default para máxima flexibilidad:

digraph Mixed {
    // Default para todos los nodos
    node [fontname="Segoe UI", fontsize=11];

    // Luego especializas por grupo
    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;
}

Nodos de entrada, validación, proceso, transformación y salida con atributos por defecto compartidos

Qué hace este ejemplo: Define un default común (fuente Segoe UI 11pt) y luego tres grupos: input/output como paralelogramos (convención de flowchart), procesos como cajas, errores como octágonos rojos.


Layout Engines: elegir el correcto

Graphviz no tiene un solo motor de renderizado: ofrece varios, cada uno optimizado para tipos específicos de grafos. Con el engine adecuado obtienes diagramas más legibles y profesionales.

Se especifica el engine usando el flag -K desde CLI:

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

O en el propio archivo DOT:

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

A continuación, los principales engines y cuándo usarlos.


Motores de layout disponibles

dot: layout jerárquico (default)

Qué hace: Dispone los nodos de forma jerárquica, siguiendo la dirección de los arcos. Es el más usado.

Perfecto para:

  • Flowcharts y diagramas de flujo
  • Pipelines CI/CD
  • Call graph (grafo de llamadas entre funciones)
  • Arquitecturas por capas (presentation → business → data)
  • Procesos secuenciales
  • Diagramas de dependencias con dirección clara

Ejemplo de comando:

dot -Tsvg flowchart.dot -o flowchart.svg

Ejemplo visual:

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;
}

Cinco nodos de A a E dispuestos de arriba abajo por el motor jerárquico dot

Qué ves: los nodos, dispuestos en capas jerárquicas claras de arriba abajo. Perfecto para mostrar flujos y dependencias.

Cuándo evitarlo: Si el grafo no tiene una estructura jerárquica clara o contiene muchos ciclos.

neato: layout de fuerza física

Qué hace: Posiciona los nodos simulando fuerzas físicas (repulsión/atracción), generando layouts orgánicos y simétricos.

Útil para:

  • Grafos no dirigidos (sin flechas)
  • Redes conceptuales y mapas mentales
  • Relaciones no jerárquicas entre entidades
  • Grafos pequeños/medianos donde quieres que afloren los clusters naturales

Ejemplo de comando:

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

Ejemplo visual (el mismo grafo de antes, con otro motor):

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;
}

Los mismos cinco nodos de A a E colocados por el motor force-directed neato

Qué ves: los nodos, repartidos de forma orgánica en el plano, con agrupaciones naturales. Perfecto para mapas mentales y redes conceptuales en las que las relaciones no son jerárquicas.

Cuándo evitarlo: Con grafos muy grandes (>100 nodos) o fuertemente direccionales.

fdp: force-directed placement

Qué hace: Similar a neato, pero usa un algoritmo diferente (Fruchterman-Reingold). Generalmente más rápido en grafos medianos.

Útil para:

  • Grafos no dirigidos de tamaño medio
  • Visualizaciones de redes sociales (amistades, conexiones)
  • Análisis de dependencias sin dirección fuerte

Ejemplo de comando:

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

sfdp: scalable force-directed placement

Qué hace: Versión optimizada de fdp para grafos muy grandes (miles de nodos).

Óptimo para:

  • Análisis de dependencias en codebases complejos
  • Grafo de clases de un proyecto enterprise
  • Redes complejas (infraestructuras, microservicios)
  • Cuando neato o fdp son demasiado lentos

Ejemplo de comando:

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

Tip: Usa sfdp cuando tengas más de 100-200 nodos.

circo: layout circular

Qué hace: Dispone los nodos en círculos concéntricos alrededor de un nodo central.

Ideal para:

  • Visualizar módulos satélite alrededor de un núcleo central
  • Arquitecturas hub-and-spoke
  • Representar componentes que dependen de un servicio central

Ejemplo de comando:

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

Ejemplo visual:

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 y cinco módulos dispuestos en anillo por el motor circo

Qué ves: el nodo central (Core) en el medio y los satélites dispuestos en círculo a su alrededor. Perfecto para arquitecturas hub-and-spoke.

twopi: layout radial

Qué hace: Crea un layout de árbol radial, con el nodo raíz al centro y los niveles que se expanden hacia el exterior.

Perfecto para:

  • Árboles jerárquicos (org chart, sistema de archivos)
  • Taxonomías
  • Mind maps estructurados
  • Visualizar expansiones desde un punto central

Ejemplo de comando:

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

Ejemplo visual:

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=""];
}

Organigrama de empresa que se abre desde el CEO, dibujado por el motor radial twopi

Qué ves: el CEO en el centro, los departamentos (Engineering, Sales, Marketing) en el primer anillo y los miembros de los equipos en el anillo exterior. Una jerarquía radial clara, perfecta para organigramas.

Cómo elegir en la práctica

Tipo de grafoEngine recomendado
Flowchart, pipeline, procesosdot
Arquitecturas en capasdot
Call graph, árbol de dependenciasdot
Red conceptual, brainstormingneato
Red social, grafos medianos no dirigidosfdp
Grafos grandes (>200 nodos)sfdp
Hub central con satélitescirco
Árboles jerárquicos, org charttwopi

Regla práctica: Si tienes flechas y una dirección clara → usa dot. De lo contrario, prueba neato o fdp.


Tipos de diagramas y cuándo usarlos en el día a día de un desarrollador

Esta es la sección más extensa. Para cada tipo de gráfico encontrarás:

  • Cuándo usarlo en la vida real
  • Atributos recomendados
  • Ejemplo completo

Flowchart: entender el comportamiento

En el trabajo cotidiano sucede a menudo que debemos explicar un flujo de decisiones complejo: un procedimiento de validación con múltiples ramas, un proceso de onboarding de usuario, o simplemente el comportamiento de una función con muchos if/else anidados. El flowchart es la herramienta ideal para esto.

Cuándo necesitas un flowchart:

Estás en fase de análisis de requisitos y debes entender todos los casos posibles. Estás debuggeando una lógica que parece haberse vuelto loca y quieres ver visualmente dónde se bifurca el flujo. Debes escribir documentación para un proceso empresarial complejo. Estás haciendo onboarding de un nuevo colega y quieres mostrarle cómo funciona el sistema de autenticación.

El flowchart muestra visualmente las decisiones (rombos), los procesos (rectángulos redondeados), y el flujo lógico (flechas). Es inmediato, claro, universal.

Atributos recomendados para flowcharts profesionales:

Usa rankdir=TB (top-to-bottom) para seguir la convención estándar de los flowcharts. Usa shape=diamond para los nodos de decisión (las condiciones if/else). Usa style=rounded para los pasos de proceso, así se distinguen de los rombos. Usa colores tenues (fillcolor) para resaltar el inicio (verde claro), errores (rojo claro), fin (gris).

Ejemplo 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;
}

Diagrama de flujo básico: inicio, rombo de validación, rama de proceso o error, fin

Qué hace este ejemplo: Parte de un estado inicial (Start), pasa a través de una validación (Check), y se bifurca en dos caminos: éxito (Process) o error (Error). Usa colores para hacer inmediatamente claro qué es positivo y qué negativo.


Grafos de dependencias: entender el software como sistema

Cuando trabajas en un proyecto existente, una de las primeras preguntas que te haces es: “¿Qué depende de qué?” Si debes hacer refactoring de un módulo, quieres saber quién lo usa. Si debes actualizar una librería, quieres entender el impacto en cascada. Si estás diseñando una nueva feature, quieres ver dónde se inserta en la arquitectura existente.

El grafo de dependencias es el mapa de tu sistema. Muestra módulos, servicios, clases o microservicios como nodos, y las dependencias como flechas. Es fundamental para:

Refactoring seguro: Antes de tocar un módulo, ves quién lo llama. Análisis de impacto: Si modificas una API, ves inmediatamente todos los consumidores. Documentación automática: Genera el grafo desde el código (con herramientas como Doxygen, Madge o scripts custom) y mantenlo actualizado. Dependency injection mapping: Visualiza cómo Spring, Angular o .NET inyectan las dependencias.

Atributos útiles:

Usa rankdir=LR (left-to-right) para tener un flujo horizontal, típico de las dependency chains. Usa shape=box para los módulos/servicios. Usa color y penwidth para resaltar dependencias críticas o problemáticas (ej. dependencias circulares en rojo). Usa style=dashed para dependencias opcionales o débiles.

Ejemplo:

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 de dependencias de UI a API y Auth hasta UserService y base de datos

Qué hace este ejemplo: Muestra una arquitectura web clásica: UI llama a API, API depende de Auth y UserService, UserService habla con Database. La dependencia crítica (UserService → Database) está resaltada en rojo con línea gruesa: si la BD cae, todo colapsa.


Arquitectura de software: clusters y capas

Cuando diseñas una arquitectura o documentas la existente, necesitas mostrar agrupamientos lógicos y separación de niveles. Un sistema web típico tiene Presentación, Negocio, Datos. Un proyecto de microservicios tiene límites lógicos (edge, services, datastores). Un sistema legacy podría tener módulos separados por dominio.

El diagrama de arquitectura con clusters te permite:

Visualizar capas separadas: Presentation Layer, Business Layer, Data Layer. Cada capa es una caja de color que contiene sus componentes. Mostrar límites de microservicios: Edge Gateway, Services, Datastores. Cada límite es un cluster. Documentar módulos legacy: Aísla los módulos por responsabilidad (Auth, Orders, Reporting) así queda claro quién hace qué. Presentar la arquitectura a stakeholders: Un diagrama por capas es universal y comprensible incluso para los no técnicos.

Atributos útiles:

Usa subgraph cluster_* para crear agrupamientos visuales. Usa compound=true para permitir arcos que atraviesen los clusters. Usa splines=ortho para líneas ortogonales, típicas de los diagramas “de arquitecto”. Usa shape=cylinder para las bases de datos, así son inmediatamente reconocibles.

Ejemplo de arquitectura en capas:

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;
}

Arquitectura en capas con clusters para presentation, business y data layer

Qué hace este ejemplo: Define tres capas con subgraph cluster_*. Presentation contiene UI, Business contiene dos servicios, Data contiene la BD (con shape=cylinder). Las flechas muestran el flujo: UI → Services → DB. La arquitectura es inmediatamente comprensible.


Mapa de microservicios: entender un ecosistema distribuido

Si trabajas con microservicios, tienes decenas de servicios que se comunican entre sí: API Gateway, servicios de dominio (Orders, Users, Notifications), bases de datos, colas, cachés. Cuando llega un incidente en producción, la primera pregunta es: “¿Quién habla con quién? ¿Dónde está el cuello de botella?”

El mapa de microservicios es tu GPS en el caos distribuido. Te sirve para:

Diseño de la arquitectura: Antes de escribir código, dibuja el mapa. Identifica los límites lógicos (edge, core services, datastores). Documentación de API: Muestra quién llama a qué servicio y a través de qué protocolo (REST, gRPC, eventos). Análisis de rendimiento: Durante un incidente, miras el mapa y entiendes inmediatamente si el problema está en el API Gateway, en un servicio específico, o en la BD compartida. Post-mortem: Después de una caída, el mapa ayuda a reconstruir la cadena de fallos.

Atributos útiles:

Usa cluster para separar los límites lógicos (Edge, Services, Data). Usa shape=cylinder para bases de datos, shape=box para servicios. Usa label en los arcos para especificar el protocolo (HTTP, gRPC, Kafka). Usa shape=plaintext con tabla HTML para servicios complejos con múltiples puertos.

Ejemplo:

digraph Micro {
    // Configuración global (se aplica a todo)
    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"];
    }

    // Edge hacia los servicios
    Gateway -> UserService [label="HTTP"];
    Gateway -> OrderService [label="HTTP"];

    // Servicios hacia los datos
    OrderService -> DB [label="SQL"];
    UserService -> Cache [label="GET/SET"];

    // Servicio a servicio
    NotificationService -> UserService [label="gRPC"];
    OrderService -> NotificationService [label="Event", style=dashed];
}

Mapa de microservicios: API gateway, servicios de pedidos, usuarios y notificaciones, Postgres y Redis

Qué hace este ejemplo: organiza un ecosistema de microservicios en tres capas de colores:

  • Edge Layer (naranja): el API Gateway como punto de entrada
  • Microservices Layer (verde): tres servicios con responsabilidades claras
  • Data Layer (gris): la base de datos Postgres y la caché Redis (cilindros, para distinguirlas a simple vista)

Patrones de comunicación representados:

  • Gateway → servicios: llamadas HTTP (flechas continuas)
  • Servicios → datos: consultas SQL y operaciones sobre la caché
  • Servicio a servicio: llamada síncrona gRPC y evento asíncrono (flecha discontinua)

Los clusters de colores enseñan de inmediato los límites de la arquitectura y las flechas etiquetadas dejan explícitos los protocolos. Ideal para el onboarding, las revisiones de arquitectura o el análisis de un incidente.


Diagramas de estado: modelar comportamientos

Muchos sistemas tienen un comportamiento basado en estados: una petición HTTP puede estar Idle, Loading, Success, Error. Un pedido de e-commerce pasa de Draft → Pending → Confirmed → Shipped. Un sistema embebido tiene estados de encendido, standby, operativo, error. Una interfaz de usuario tiene estados de carga, lista, error.

El diagrama de estados modela estos comportamientos como un grafo: cada nodo es un estado, cada arco es una transición etiquetada con el evento que la causa. Es fundamental para:

Diseñar máquinas de estados: Antes de implementar el patrón state en el código, dibuja el diagrama. Documentar protocolos: Los protocolos de red (TCP, WebSocket, custom) tienen máquinas de estado precisas. El diagrama las hace explícitas. Modelar flujos UI/UX: Cuando diseñas una app, dibuja los estados de la interfaz: carga, lista, error, vacía. Depuración de sistemas embebidos: Si un dispositivo se bloquea en un estado, el diagrama te ayuda a entender qué transiciones faltan.

Relación con la teoría de la informática y los design patterns:

Los diagramas de estado en DOT representan directamente conceptos básicos de la informática y del diseño de software:

  • FSM (Finite State Machine, máquina de estados finitos): un modelo de cálculo con un número finito de estados. El diagrama muestra todos los estados posibles y las transiciones entre ellos. Se usa en compiladores, parsers e implementaciones de protocolos.

  • DFA (Deterministic Finite Automaton, autómata finito determinista): un tipo concreto de FSM en el que cada estado tiene exactamente una transición por símbolo de entrada. Los diagramas de estado son la representación visual de los DFA que se usan en la teoría de lenguajes formales y en los motores de expresiones regulares.

  • State Pattern (design pattern GoF): un patrón de diseño orientado a objetos que permite a un objeto cambiar su comportamiento cuando cambia su estado interno. El diagrama de estados se convierte en el plano para implementar el patrón: cada círculo es una clase que implementa la interfaz State, cada flecha es un método de transición.

Un diagrama de estados, además de documentar, es una especificación formal que se puede traducir directamente a código (State Pattern), usar para validar (DFA) o analizar para comprobar su corrección (teoría de las FSM).

Atributos útiles:

Usa shape=circle para los estados (convención estándar de las FSM). Usa rankdir=LR para layout horizontal, típico de los diagramas de estado. Usa label en los arcos para mostrar el evento que causa la transición. Usa shape=doublecircle para estados finales/terminales. Usa fixedsize=true con width para que todos los círculos tengan el mismo tamaño y el diagrama quede coherente.

Cómo representar los estados inicial y final (estándar FSM/DFA):

Según las convenciones de las máquinas de estados finitos y de los DFA:

  • Estado inicial: usa shape=point con un width pequeño (por ejemplo 0.2) para obtener un punto negro que marca el punto de entrada
  • Estado final/de aceptación: usa shape=doublecircle para obtener el borde de doble círculo que indica los estados terminales o de aceptación
  • Estados normales: usa shape=circle con fixedsize=true en todos los estados intermedios (así tienen un aspecto uniforme)

Ejemplo completo con la notación FSM correcta:

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"];

    // Estado inicial (punto negro)
    START [shape=point, width=0.2, fixedsize=true];

    // Estado final (doble círculo)
    END [shape=doublecircle, fixedsize=true, width=0.9];

    // Estados normales
    Idle; Loading; Ready; Error;

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

Diagrama de estados con Idle, Loading, Ready y Error y transiciones etiquetadas

Qué hace este ejemplo: modela el ciclo de vida de una petición asíncrona siguiendo las convenciones FSM/DFA:

  • START (shape=point): el punto negro que indica el punto de entrada de la máquina de estados
  • END (shape=doublecircle): el doble círculo que indica el estado de aceptación/terminal
  • Estados normales (Idle, Loading, Ready, Error): círculos uniformes (fixedsize=true, width=0.9) que representan los estados intermedios
  • Transiciones etiquetadas: flechas que muestran los eventos que provocan el cambio de estado

La máquina arranca en START, entra en Idle y pasa a Loading. Desde Loading puede tener éxito (→ Ready) o fallar (→ Error). Un error se puede resetear para volver a Idle. El éxito lleva al estado final END.

Del diagrama al código (implementación del State Pattern):

Este diagrama se traduce directamente al State Pattern:

// Cada círculo se convierte en una clase 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 { ... }

// Las transiciones se convierten en implementaciones de métodos
class LoadingState implements State {
    void success() {
        context.setState(new ReadyState());
    }
    void fail() {
        context.setState(new ErrorState());
    }
}

El diagrama hace de documentación y, a la vez, de especificación para la implementación.


Diagramas de clases con record

Cuando diseñas un sistema orientado a objetos, o quieres documentar el dominio de una aplicación, el diagrama de clases es el estándar. Muestra clases con atributos y métodos, y las relaciones entre ellas (herencia, composición, dependencia).

DOT no es UML, pero con shape=record obtienes algo muy similar y perfectamente legible. Es útil para:

Diseño inicial: Antes de escribir el código, dibuja las clases principales del dominio. Identifica atributos, métodos, relaciones. Refactoring: Cuando debes reestructurar un módulo, dibuja el estado actual y el deseado. Compara los dos diagramas. Domain-Driven Design (DDD): Modela las entidades, value objects, agregados. El diagrama ayuda a visualizar los límites del dominio. Documentación: Genera el diagrama automáticamente desde el código (con herramientas como Doxygen) y mantenlo actualizado.

Atributos útiles:

Usa shape=record para crear cajas con secciones separadas (nombre clase | atributos | métodos). Usa fontname="Courier New" o una fuente monoespaciada para que parezca código. Usa arrowhead=onormal para herencia (flecha vacía, estándar UML). Usa \l (backslash-l) para alinear el texto a la izquierda dentro de los records.

Ejemplo:

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"];
}

Diagrama de clases con shape record: la clase Employee hereda de Person

Qué hace este ejemplo: Define dos clases: Person (con atributos nombre, edad, método greet) y Employee (con id, rol, método work). Person es la superclase de Employee (flecha con arrowhead=onormal, estándar UML para la herencia). El \l alinea el texto a la izquierda dentro de los records.


Diagramas ER profesionales con HTML-label

Si trabajas con bases de datos, tarde o temprano debes dibujar el esquema de las tablas: claves primarias, foreign keys, relaciones 1:N o N:N. El diagrama Entity-Relationship (ER) es el estándar para esto.

DOT soporta tablas HTML dentro de los nodos con shape=plaintext, permitiéndote crear diagramas ER limpios y profesionales. Es fundamental para:

Diseño de bases de datos: Antes de escribir las migraciones, dibuja el esquema. Identifica las entidades, los atributos, las relaciones. Valida el diseño con el equipo. Data modeling: Cuando diseñas un nuevo módulo, parte del modelo de datos. El diagrama ER te ayuda a razonar sobre normalización y performance. Reverse engineering: Cuando heredas una BD legacy sin documentación, genera el diagrama ER desde la BD misma (con herramientas como SchemaSpy o pg_dump + script) para entender la estructura. Documentación: El diagrama ER es comprensible incluso para los no desarrolladores (product managers, analistas de negocio).

Atributos útiles:

Usa shape=plaintext para habilitar HTML label. Usa <TABLE> HTML para crear boxes estructurados con encabezado (nombre tabla) y filas (campos). Usa arrowhead=crow para relaciones 1:N (estándar de los diagramas ER). Usa label="1:N" en los arcos para hacer explícita la cardinalidad.

Ejemplo:

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"];
}

Diagrama ER con tablas HTML-label: User y Order en relación uno a muchos

Qué hace este ejemplo: Define dos tablas (User y Order) usando HTML. Cada tabla tiene un encabezado en negrita y filas para los campos. La flecha con arrowhead=crow y label="1:N" muestra la relación: un User tiene muchos Order (clave externa user_id en Order).


Diagramas de pipelines CI/CD

Si trabajas en un equipo que hace integración y despliegue continuos, tienes una pipeline que ejecuta pasos automáticos: build, test, análisis estático, packaging, deploy, monitoreo. Cuando algo se rompe, o cuando haces onboarding de un nuevo developer, necesitas un diagrama que muestre toda la pipeline.

El diagrama CI/CD visualiza el flujo automático desde el commit hasta el deploy. Es útil para:

DevOps y SRE: Documentar la pipeline existente. Identificar cuellos de botella (¿qué paso requiere más tiempo?). Onboarding de equipo: Un nuevo developer mira el diagrama y entiende inmediatamente qué sucede después de un git push. Optimización: ¿Quieres paralelizar algunos pasos? El diagrama te muestra cuáles dependen de cuáles. Debugging: ¿La pipeline falla? El diagrama te ayuda a entender en qué paso y por qué (flecha punteada para los reintentos).

Atributos útiles:

Usa rankdir=LR para flujo horizontal (típico de las pipelines). Usa shape=box con style=filled para los pasos, coloreándolos por tipo (build=azul, test=verde, deploy=rojo). Usa style=dotted con label="retry" para mostrar mecanismos de reintento automático. Usa label en los arcos para indicar condiciones (ej. “only on master branch”).

Ejemplo:

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 de código a build, test con retry, package, deploy y monitor

Qué hace este ejemplo: Muestra una pipeline clásica: Code → Build → Test → Package → Deploy → Monitor. La flecha punteada de Test a Build muestra un reintento automático en caso de fallo de los tests. Es un flujo lineal, inmediatamente comprensible.


Reducir la complejidad: mapas conceptuales y análisis

No todos los diagramas deben ser jerárquicos o direccionales. A veces necesitas visualizar relaciones conceptuales sin una estructura fija: durante un brainstorming, cuando haces diseño colectivo con el equipo, o cuando quieres mapear las dependencias conceptuales entre áreas tecnológicas.

El mapa conceptual no tiene un “inicio” o un “fin”: es una red de nodos conectados orgánicamente. Es útil para:

Brainstorming: Partes de una idea central y agregas nodos conectados mientras discutes con el equipo. El mapa crece naturalmente. Diseño colectivo: Durante una sesión de diseño, mapea los componentes y sus relaciones. Aún no sabes la jerarquía, pero sabes que “Backend habla con Database y API”. Análisis de dependencias conceptuales: Quieres entender qué áreas tecnológicas están conectadas (ej. Security → Logging → Observability). Documentación de alto nivel: Para stakeholders no técnicos, un mapa conceptual es más accesible que un grafo jerárquico.

Atributos útiles:

Usa el engine neato o fdp en lugar de dot, para obtener un layout orgánico basado en fuerzas físicas. Usa shape=ellipse para nodos conceptuales (no procesos). Usa style=filled con colores tenues para agrupamientos visuales. Usa grafos no dirigidos (graph en lugar de digraph) si las relaciones son bidireccionales.

Ejemplo con engine neato:

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

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

Mapa conceptual que conecta backend, base de datos, API, seguridad, logging y observabilidad


Evitar superposiciones: overlap y splines

Uno de los problemas más comunes cuando se dibujan grafos complejos es la superposición de flechas, textos y nodos. Graphviz ofrece atributos específicos para controlar este comportamiento y hacer los diagramas más legibles.

El problema: superposiciones no deseadas

Cuando tienes muchos nodos y flechas, especialmente con layouts jerárquicos o con splines=ortho, las flechas pueden superponerse a los labels de los clusters o a los nodos. Aquí un ejemplo típico 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;
}

Tres clusters de componentes con arcos que cruzan los nodos, antes de corregir overlap

Problema: Con splines=ortho las flechas pueden atravesar los labels de los clusters haciendo el diagrama confuso.

La solución: overlap y splines

Graphviz ofrece varios atributos para resolver este problema:

overlap: evita superposiciones entre nodos

graph [overlap=false];

Opciones principales:

  • false o voronoi: evita superposiciones (mejor calidad, más lento)
  • scale: escala el grafo para evitar superposiciones
  • scalexy: escala con proporciones diferentes en X e Y
  • true (default): permite superposiciones

splines: controla la forma de los arcos

graph [splines=true];

Opciones:

  • true o spline: curvas suaves que evitan nodos (recomendado)
  • curved: arcos ligeramente curvos
  • polyline: líneas quebradas
  • ortho: líneas ortogonales (¡puede causar superposiciones!)
  • line o false: líneas rectas

sep: margen extra entre elementos

graph [sep="+0.2"];

Agrega margen entre nodos y arcos (en pulgadas). El + significa “agregar al valor por defecto”.

ranksep y nodesep: espaciado entre niveles y nodos

graph [ranksep=0.8, nodesep=0.6];

Aumenta el espacio vertical (ranksep) y horizontal (nodesep) entre los elementos.

Ejemplo mejorado

Aquí el mismo grafo con atributos optimizados para evitar superposiciones:

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;
}

Los mismos tres clusters redibujados limpios tras configurar overlap y splines

Resultado: Las flechas ahora evitan los nodos y los labels, el grafo es más espacioso y legible. Los colores de fondo ayudan a distinguir los clusters.

Cuándo usar qué

EscenarioAtributos recomendados
Diagramas complejos con muchos nodosoverlap=false, splines=true
Grafos jerárquicos (flowcharts, arquitecturas)ranksep=0.8, nodesep=0.6, splines=true
Grafos con clustersoverlap=false, sep="+0.2"
Diagramas “de arquitecto” con líneas rectassplines=polyline (evita ortho)
Grafos pequeños y simplesdefault (no hace falta modificar)

Regla práctica: Comienza siempre con overlap=false y splines=true si tienes más de 10 nodos o usas clusters.

Ejemplo completo con todas las mejores prácticas

Aquí un ejemplo real que combina todos los atributos recomendados: arquitectura de microservicios con 4 capas, muchos nodos y arcos, clusters coloreados, espacios optimizados:

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"];
    }

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

    // Del gateway a los servicios
    LoadBalancer -> Gateway;
    Gateway -> AuthService [label="gRPC"];
    Gateway -> UserService [label="REST"];
    Gateway -> OrderService [label="REST"];

    // Dependencias entre servicios
    OrderService -> PaymentService [label="API call"];
    UserService -> AuthService [label="validate"];
    PaymentService -> AuthService [label="validate"];

    // Acceso a los datos
    AuthService -> UsersDB;
    UserService -> UsersDB;
    UserService -> Cache [style=dashed, label="cache"];
    OrderService -> OrdersDB;
    OrderService -> Cache [style=dashed, label="cache"];
}

Línea de comandos:

dot -Tsvg overlap_complete.dot -o overlap_complete.svg

Sistema completo en capas de web UI y load balancer a gateway, servicios y bases de datos

Qué demuestra este ejemplo:

  • overlap=false: Ninguna superposición entre nodos, incluso con 13 nodos y 4 clusters
  • splines=true: Flechas curvas que evitan elegantemente nodos y labels
  • sep="+0.25": Margen extra que mantiene todo legible
  • ranksep=1.0, nodesep=0.7: Espaciado generoso entre capas y nodos
  • Clusters coloreados: Cada capa tiene un color distintivo para identificación inmediata
  • Labels en los arcos: Protocolos (HTTPS, gRPC, REST) y tipo de conexión explícitos
  • Style dashed para cache: Dependencias opcionales visualizadas diferentemente
  • Cylinder shape para DB: Bases de datos inmediatamente reconocibles

Esta es la plantilla perfecta para documentar arquitecturas complejas de forma profesional y legible.


Color Schemes: paletas profesionales listas para usar

Graphviz incluye color schemes predefinidos basados en paletas profesionales de ColorBrewer. En lugar de elegir colores manualmente, puedes usar paletas probadas para legibilidad, accesibilidad y profesionalismo.

Cómo funcionan los color schemes

Usa el atributo colorscheme para seleccionar una paleta, luego usa los números 1-9 (o más, depende del esquema) en lugar de los códigos hex:

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

Los color schemes están organizados en categorías:

  • Qualitative (set1, set2, set3, pastel1, pastel2, dark2, paired, accent): para categorías distintas
  • Sequential (blues3-9, greens3-9, reds3-9, purples3-9, oranges3-9): para valores progresivos
  • Diverging (rdylgn3-11, spectral3-11, rdbu3-11): para datos con punto central

Referencia completa: graphviz.org/docs/attrs/colorscheme/

Ejemplos prácticos con color schemes profesionales

Cada ejemplo usa el mismo diagrama (proceso de 5 pasos) pero con paletas diferentes. Compara y elige el más adecuado para tu caso de uso.

Ejemplo 1: Set39 (cualitativo, vívido)

Óptimo para distinguir categorías diferentes, presentaciones coloridas, dashboards.

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];
}

Línea de comandos:

dot -Tsvg colorscheme_set39.dot -o colorscheme_set39.svg

Flujo de proceso de cinco pasos coloreado con el esquema Brewer set39

Ejemplo 2: Pastel19 (cualitativo, suave)

Colores pastel para documentación técnica, wikis, posts de blog. Menos impactante pero más legible a largo plazo.

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];
}

Línea de comandos:

dot -Tsvg colorscheme_pastel.dot -o colorscheme_pastel.svg

Flujo de proceso de cinco pasos coloreado con un esquema Brewer pastel

Ejemplo 3: Blues9 (secuencial, progresión)

Ideal para mostrar intensidad creciente, prioridades, fases de maduración.

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;
}

Línea de comandos:

dot -Tsvg colorscheme_blues.dot -o colorscheme_blues.svg

Flujo de proceso de cinco pasos degradado con el esquema secuencial blues

Ejemplo 4: RdYlGn9 (divergente, semáforo)

Perfecto para estados de éxito/warning/error, health checks, monitoreo.

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"];
}

Línea de comandos:

dot -Tsvg colorscheme_rdylgn.dot -o colorscheme_rdylgn.svg

Estados de un servicio de idle a error coloreados con el esquema rojo-amarillo-verde rdylgn

Ejemplo 5: Paired12 (cualitativo, parejas)

Usa pares de colores coordinados. Óptimo para comparaciones, versiones A/B, relaciones.

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];
}

Línea de comandos:

dot -Tsvg colorscheme_paired.dot -o colorscheme_paired.svg

Flujos de las versiones 1.x y 2.x lado a lado con el esquema de colores paired

Ejemplo 6: Accent8 (cualitativo, contrastes fuertes)

Máximo contraste entre elementos. Para resaltar diferencias importantes.

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];
}

Línea de comandos:

dot -Tsvg colorscheme_accent.dot -o colorscheme_accent.svg

Pipeline de datos input, parse, transform, validate, output resaltada con el esquema accent

Cuándo usar qué color scheme

EscenarioColor Scheme Recomendado
Categorías diferentes, presentacionesset39, set28, dark28
Documentación técnica, wikispastel19, pastel28
Progresión, prioridades, nivelesblues9, greens9, purples9, oranges9
Estados (ok/warning/error)rdylgn9, rdylbu9, spectral9
Comparaciones, versiones A/Bpaired12, paired11
Máximo contrasteaccent8, set39
Accesibilidad (daltonismo)set2, dark2 (ColorBrewer safe)

Combinar color schemes

Puedes usar esquemas diferentes para nodos y arcos:

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

Tip: Prueba siempre los colores con ColorBrewer para verificar accesibilidad e imprimibilidad.


Técnicas avanzadas: ports, ranks, compound edges

Ports en records: conecta arcos a campos específicos de un record usando la sintaxis node:port:

ClassA:field1 -> ClassB:field2;

Forzar nodos al mismo nivel: usa rank=same para posicionar múltiples nodos en la misma fila horizontal:

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

Compound edges entre clusters: conecta clusters enteros en lugar de nodos individuales con lhead y ltail:

edge [lhead=cluster_B, ltail=cluster_A];

Arcos ortogonales: crea arcos con ángulos rectos para un estilo “de arquitecto”:

graph [splines=ortho];

Estilo profesional: consejos prácticos

  • Usa paletas coherentes.
  • Evita gradientes demasiado brillantes.
  • Usa Inter, Arial o Roboto para legibilidad.
  • Usa ortho para arcos “de arquitecto”.
  • Mantén márgenes adecuados (nodesep, ranksep).
  • Prefiere SVG para calidad en blogs.
  • Mantén los archivos DOT versionados.

Estilos gráficos listos para usar

Aquí encuentras 5 variantes estilísticas para el mismo diagrama de estados, listas para copiar y adaptar a tus diagramas. Cada estilo define colores, fuentes, tamaños y formas para crear un look coherente.

Estilo 1: Corporate Blue (profesional, formal)

Paleta azul/gris, fuente Segoe UI, estilo limpio para presentaciones corporativas.

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"];
}

Línea de comandos:

dot -Tsvg style_corporate.dot -o style_corporate.svg

Máquina de estados Idle, Processing y Complete con el estilo corporate azul

Estilo 2: Dark Mode (moderno, tech)

Fondo oscuro, texto claro, paleta verde/cian para UIs modernas y herramientas para desarrolladores.

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"];
}

Línea de comandos:

dot -Tsvg style_dark.dot -o style_dark.svg

La misma máquina de estados con el tema oscuro sobre fondo oscuro

Estilo 3: Warm Minimal (suave, legible)

Paleta cálida naranja/beige, sans-serif, óptimo para documentación técnica.

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"];
}

Línea de comandos:

dot -Tsvg style_warm.dot -o style_warm.svg

La misma máquina de estados con el estilo warm en tonos naranja

Estilo 4: Monochrome (elegante, imprimible)

Blanco y negro, gradaciones de gris, perfecto para impresión y documentación formal.

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"];
}

Línea de comandos:

dot -Tsvg style_mono.dot -o style_mono.svg

La misma máquina de estados con el estilo monocromo, solo grises y negro

Estilo 5: Vibrant Gradient (creativo, impactante)

Colores vívidos con degradados, óptimo para presentaciones y slides visualmente llamativas.

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"];
}

Línea de comandos:

dot -Tsvg style_vibrant.dot -o style_vibrant.svg

La misma máquina de estados con el estilo vibrant y colores saturados

Cómo usar estos estilos

Copia el bloque graph, node, edge del estilo que prefieras y aplícalo a tus diagramas. Luego puedes personalizar:

  • Colores: reemplaza los códigos hex con tu paleta
  • Fuentes: usa fontname="FontName" (Arial, Helvetica, Courier, Times, Verdana, Consolas)
  • Tamaños: fontsize para texto, penwidth para grosor de bordes y flechas
  • Shape: box, ellipse, circle, diamond, cylinder, record
  • Degradados: usa fillcolor="color1:color2" con gradientangle (solo algunos formatos de salida)

Notas sobre compatibilidad de fuentes

Las fuentes deben estar instaladas en el sistema donde generas las imágenes:

  • Fuentes universales (funcionan en todas partes): Arial, Helvetica, Times, Times-Roman, Courier
  • Fuentes modernas (verificar disponibilidad): Verdana, Consolas, Roboto, Inter
  • Windows: la mayoría de las fuentes ya están instaladas
  • macOS: excelente soporte para fuentes estándar
  • Linux: instala fonts-liberation o fonts-dejavu para tener equivalentes de Arial/Helvetica
  • CI/CD: usa fuentes básicas o incluye las fuentes en el contenedor Docker

Si una fuente no está disponible, Graphviz usa un fallback (normalmente Times). Para estar seguro, usa siempre fuentes básicas o prueba la generación en el entorno de producción.


Workflow: cómo integrar Graphviz en el trabajo real

  1. Pon los archivos .gv en el repositorio.

  2. Genera los SVG automáticamente en la CI:

    dot -Tsvg diagram.gv -o diagram.svg
    
  3. Incluye los SVG en la documentación (README, wiki, blog).

  4. Actualiza los diagramas en cada refactoring.

  5. Versiona los archivos DOT y revisa sus diff como harías con el código.


Troubleshooting: errores comunes y soluciones

Error: “syntax error in line X near…”

  • Causa: Sintaxis DOT no válida
  • Solución: Revisa si faltan puntos y coma o hay llaves sin cerrar o comillas sin pareja
  • Ejemplo: A -> B debe terminar con ; → A -> B;

Error: “Warning: Unable to find font…”

  • Causa: La fuente especificada no está instalada en el sistema
  • Solución: Usa fuentes universales (Arial, Helvetica, Times) o instala la fuente requerida
  • Verificar fuentes disponibles: dot -v muestra las fuentes disponibles

El diagrama es demasiado grande/pequeño

  • Solución 1: Agrega graph [size="8,6"] para limitar dimensiones (en pulgadas)
  • Solución 2: Usa graph [ratio=compress] para comprimir automáticamente
  • Solución 3: Genera PNG con DPI personalizado: dot -Tpng -Gdpi=150 file.dot -o file.png

Las flechas se superponen a los nodos

  • Solución: Agrega graph [overlap=false, splines=true] (ver sección “Evitar superposiciones”)

Los nodos están todos alineados horizontalmente en lugar de verticalmente

  • Solución: Usa graph [rankdir=TB] para top-to-bottom (el default es LR = left-to-right)

El cluster no aparece

  • Causa: Nombre del cluster no comienza con cluster_
  • Solución: Renombra subgraph mygroup a subgraph cluster_mygroup

Output SVG demasiado grande (tamaño de archivo)

  • Causa: SVG con muchos elementos
  • Solución 1: Usa PNG en lugar de SVG para grafos muy complejos
  • Solución 2: Optimiza con svgo: svgo input.svg -o output.svg

El grafo no se genera (sin output, sin error)

  • Causa: Comando incorrecto o redirección equivocada
  • Verificación: Usa -v para verbose: dot -v -Tsvg input.dot -o output.svg
  • Test rápido: echo "digraph{A->B}" | dot -Tsvg > test.svg

Los labels de los arcos no se ven

  • Causa: Labels demasiado largos o fuente demasiado pequeña
  • Solución: Aumenta fontsize en los edges o usa \n para dividir labels en múltiples líneas

Buenas prácticas para un código DOT limpio

Don’t Repeat Yourself (DRY)

Define los atributos una sola vez, al nivel superior, en lugar de repetirlos en cada elemento.

❌ Mal (repetitivo):

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

✅ Bien (DRY):

digraph {
    // Se define una vez y vale para todos
    graph [fontname="Segoe UI"];
    node [fontname="Segoe UI", shape=box];
    edge [fontname="Segoe UI"];

    A; B; C;  // Heredan todos los atributos
}

Principio clave: usa las declaraciones graph, node y edge para fijar los default. Sobrescríbelos solo cuando un elemento concreto necesite atributos distintos.

Usa los comentarios

Añade comentarios para explicar las partes complejas:

digraph {
    // Estilo global
    graph [rankdir=LR, fontname="Segoe UI"];

    // Componentes principales
    A -> B;

    // Camino de gestión de errores
    B -> Error [style=dashed, color=red];
}

Organiza los grafos grandes

Agrupa las declaraciones relacionadas:

digraph {
    // === Configuración ===
    graph [rankdir=TB];
    node [shape=box];

    // === Servicios ===
    ServiceA; ServiceB; ServiceC;

    // === Bases de datos ===
    DB1 [shape=cylinder];
    DB2 [shape=cylinder];

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

Plantillas DOT + comandos Graphviz (CLI)

Cada plantilla incluye:

  1. snippet DOT

  2. comando CLI para generar la imagen

Por coherencia uso el formato SVG, pero puedes reemplazar -Tsvg con:

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

Y puedes guardar el input DOT en template.dot o pasarlo por un pipe.


Flowchart

Plantilla 1: Proceso básico

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;
}

Línea de comandos:

dot -Tsvg FlowBasic.dot -o FlowBasic.svg

Salida de la plantilla: proceso lineal de inicio a validación, persistencia y fin

Plantilla 2: Bifurcación (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;
}

Línea de comandos:

dot -Tsvg FlowIfElse.dot -o FlowIfElse.svg

Salida de la plantilla: diagrama if/else con rombo de validez y dos ramas

Plantilla 3: Proceso con secciones

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;
}

Línea de comandos:

dot -Tsvg FlowSections.dot -o FlowSections.svg

Salida de la plantilla: gestión de la petición dividida en clusters input, processing y output

State Machine

Plantilla 4: Máquina de estados básica

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"];
}

Línea de comandos:

dot -Tsvg StateMachine.dot -o StateMachine.svg

Salida de la plantilla: máquina de estados con Idle, Loading, Success, Error y retry

Plantilla 5: Estados anidados

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"];
}

Línea de comandos:

dot -Tsvg NestedStates.dot -o NestedStates.svg

Salida de la plantilla: estados anidados, clusters Ready y Active con pause y resume

Sequence Diagram (DOT)

Plantilla 6: Secuencia horizontal

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"];
}

Línea de comandos:

dot -Tsvg Sequence.dot -o Sequence.svg

Salida de la plantilla: secuencia de login horizontal entre client, API, AuthService y DB

Plantilla 7: Secuencia con activaciones

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"];
}

Línea de comandos:

dot -Tsvg SequenceActivation.dot -o SequenceActivation.svg

Salida de la plantilla: secuencia de login con activaciones entre usuario, frontend, backend y DB

Dependency Graph

Plantilla 8: Módulos

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

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

Línea de comandos:

dot -Tsvg DependencyTree.dot -o DependencyTree.svg

Salida de la plantilla: árbol de dependencias de App a módulos y librerías

Plantilla 9: Microservicios

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

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

Línea de comandos:

dot -Tsvg MicroservicesDep.dot -o MicroservicesDep.svg

Salida de la plantilla: dependencias de microservicios del gateway a auth, pedidos, pagos y banco

Architecture Diagram

Plantilla 10: Por capas

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;
}

Línea de comandos:

dot -Tsvg Layered.dot -o Layered.svg

Salida de la plantilla: arquitectura en capas con UI, API, servicios, base de datos y cache

Plantilla 11: Hexagonal

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;
}

Línea de comandos:

dot -Tsvg Hexagonal.dot -o Hexagonal.svg

Salida de la plantilla: arquitectura hexagonal con core domain, puertos y adaptadores

ER Diagrams

Plantilla 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"];
}

Línea de comandos:

dot -Tsvg ER_OneToMany.dot -o ER_OneToMany.svg

Salida de la plantilla: diagrama ER de User y Order en relación 1:N

Plantilla 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;
}

Línea de comandos:

dot -Tsvg ER_ManyToMany.dot -o ER_ManyToMany.svg

Salida de la plantilla: relación N:N entre Student y Course mediante la tabla Enroll

Call Graph

Plantilla 14: Básico

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

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

Línea de comandos:

dot -Tsvg CallGraph.dot -o CallGraph.svg

Salida de la plantilla: call graph de main a init, loadConfig, readFile y parseJson

Plantilla 15: Con categorías

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;
}

Línea de comandos:

dot -Tsvg CategorizedCalls.dot -o CategorizedCalls.svg

Salida de la plantilla: call graph con funciones de I/O y lógica de negocio en clusters separados

Network Diagram

Plantilla 16: Básico

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

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

Línea de comandos:

dot -Tsvg Network.dot -o Network.svg

Salida de la plantilla: red del cliente al load balancer, dos app servers y DB

Plantilla 17: Con protocolos

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"];
}

Línea de comandos:

dot -Tsvg NetworkProto.dot -o NetworkProto.svg

Salida de la plantilla: conexiones de red etiquetadas con los protocolos HTTPS, gRPC, REST y TCP

Timeline / Roadmap

Plantilla 18: Timeline

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

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

Línea de comandos:

dot -Tsvg Timeline.dot -o Timeline.svg

Salida de la plantilla: línea de tiempo del proyecto de inicio a tres hitos y release

Plantilla 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;
}

Línea de comandos:

dot -Tsvg Roadmap.dot -o Roadmap.svg

Salida de la plantilla: roadmap con pistas backend y frontend, auth, pagos, login UI, dashboard

Mind Map

Plantilla 20: Mapa mental

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

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

Línea de comandos:

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

Salida de la plantilla: mapa mental radial con nodo central, tres ideas y subideas

Component Diagram

Plantilla 21: Componentes

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

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

Línea de comandos:

dot -Tsvg Components.dot -o Components.svg

Salida de la plantilla: diagrama de componentes que encadena UI, API, servicio y base de datos

Class Diagram

Plantilla 22: Estilo UML

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

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

Línea de comandos:

dot -Tsvg Classes.dot -o Classes.svg

Salida de la plantilla: diagrama de clases estilo UML con Student que hereda de Person

Layouts

Plantilla 23: Horizontal

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

Línea de comandos:

dot -Tsvg Horizontal.dot -o Horizontal.svg

Salida de la plantilla: esqueleto vacío de izquierda a derecha, listo para añadir nodos

Plantilla 24: Vertical

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

Línea de comandos:

dot -Tsvg Vertical.dot -o Vertical.svg

Salida de la plantilla: esqueleto vacío de arriba abajo, listo para añadir nodos

Plantilla 25: Circular

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

Línea de comandos:

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

Salida de la plantilla: esqueleto vacío con layout circo, listo para añadir nodos

Estilos profesionales

Plantilla 26: Tema moderno

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

    A -> B -> C;
}

Línea de comandos:

dot -Tsvg Modern.dot -o Modern.svg

Salida de la plantilla: tres nodos A, B, C con el tema moderno redondeado y relleno

Plantilla 27: Tema oscuro

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

    A -> B -> C;
}

Línea de comandos:

dot -Tsvg Dark.dot -o Dark.svg

Salida de la plantilla: tres nodos A, B, C con el tema oscuro sobre fondo oscuro


Recursos útiles

Documentación oficial

Herramientas online

Integraciones y librerías

  • Graphviz Visual Editor (VSCode): Extensión para preview en vivo en Visual Studio Code
  • PlantUML: plantuml.com: usa Graphviz para renderizado de UML
  • Mermaid: mermaid.js.org: alternativa basada en JavaScript a Graphviz para gráficos en Markdown
  • Python graphviz: graphviz.readthedocs.io: librería Python para generar grafos programáticamente
  • Go graphviz: github.com/goccy/go-graphviz: binding Go para Graphviz

Galerías y ejemplos

Comunidad y soporte

Herramientas complementarias


FAQ: Preguntas frecuentes sobre DOT y Graphviz

¿Cómo creo un diagrama con Graphviz?

Crea un archivo con extensión .dot que contenga la descripción del grafo, luego ejecuta dot -Tsvg file.dot -o output.svg. El comando genera una imagen SVG desde el código DOT.

¿Cuál es la diferencia entre digraph y graph en DOT?

digraph crea un grafo dirigido (con flechas), graph crea un grafo no dirigido (con líneas sin flecha). Usa -> para arcos dirigidos, -- para arcos no dirigidos.

¿Cómo cambio la dirección del layout en Graphviz?

Usa el atributo rankdir: TB (top-to-bottom, por defecto), BT (bottom-to-top), LR (left-to-right), RL (right-to-left). Ejemplo: graph [rankdir=LR];

¿Cómo creo un flowchart con decisiones (rombos) en DOT?

Usa shape=diamond para los nodos de decisión: Decision [shape=diamond, label="Valid?"];

¿Cómo agrupo nodos en una caja (cluster) en Graphviz?

Usa subgraph cluster_name { ... }. El prefijo cluster_ es obligatorio para visualizar el agrupamiento.

¿Cómo creo diagramas ER (Entity-Relationship) con DOT?

Usa shape=plaintext con HTML label para crear tablas estructuradas, y arrowhead=crow para las relaciones 1:N.

¿Cómo evito superposiciones entre flechas y nodos en Graphviz?

Agrega graph [overlap=false, splines=true, sep="+0.2"]; para evitar colisiones.

¿Cuál es el mejor layout engine para mi diagrama?

  • dot: flowcharts, pipelines, jerarquías (por defecto)
  • neato/fdp: grafos no dirigidos, mapas conceptuales
  • circo: estructuras hub-and-spoke
  • twopi: árboles radiales, org charts

¿Cómo genero PNG en lugar de SVG con Graphviz?

Cambia el flag -T: dot -Tpng file.dot -o output.png. También puedes especificar DPI: dot -Tpng -Gdpi=150.

¿Puedo usar Graphviz online sin instalarlo?

¡Sí! Usa edotor.net o GraphvizOnline para probar código DOT en tu navegador.

¿Cómo integro Graphviz en mi pipeline CI/CD?

Instala Graphviz en tu entorno de CI (ej. apt install graphviz en Docker) y añade un paso que ejecute dot -Tsvg *.dot. Versiona los archivos .dot y genera las imágenes automáticamente.

¿Cómo creo diagramas de estado (state machine) con DOT?

Usa shape=circle para los estados, shape=point para el estado inicial, shape=doublecircle para el estado final. Etiqueta los arcos con los eventos: Idle -> Loading [label="start"];

¿Cómo aplico el mismo estilo a múltiples nodos sin repetirlo?

Lista los nodos en la misma línea seguidos de los atributos: A; B; C [shape=box, fillcolor=lightblue, style=filled];

¿DOT soporta colores HEX?

¡Sí! Usa códigos HEX como fillcolor="#E3F2FD" o nombres predefinidos como fillcolor=lightblue.

¿Cómo creo diagramas UML con Graphviz?

Usa shape=record para diagramas de clases: Class [label="{ClassName|attribute:type|method()}"];. Para UML completo considera PlantUML que usa Graphviz internamente.


Artículo escrito por Daniele Teti, danieleteti.it

Datos clave

Manual práctico del lenguaje DOT de Graphviz para desarrolladores, analistas y arquitectos de software, con plantillas listas para usar.

  • DOT es un lenguaje de texto para describir grafos; Graphviz, desarrollado originalmente en AT&T Labs, genera SVG, PNG o PDF a partir de archivos DOT
  • Instalación en Windows (winget install graphviz), macOS (brew install graphviz) y Linux (apt o dnf); dot -V verifica la instalación
  • Explica los atributos de nodos, arcos y grafo, label frente a xlabel, las notas en los nodos y cómo definir estilos para grupos de nodos con atributos por defecto
  • Layout engines comparados: dot (jerárquico, por defecto), neato y fdp (force-directed), sfdp, circo (circular), twopi (radial); se eligen con -K o con el atributo layout
  • Tipos de diagramas del trabajo real: diagramas de flujo, grafos de dependencias, arquitecturas en capas, mapas de microservicios, diagramas de estados, diagramas de clases con record, diagramas ER con HTML-label, pipelines CI/CD, mapas conceptuales
  • Las superposiciones se evitan con graph [overlap=false, splines=true, sep="+0.2"]
  • Esquemas de color Brewer mostrados: set39, pastel19, blues9, rdylgn9, paired12, accent8
  • Cinco estilos gráficos listos: corporate, dark, warm, mono, vibrant
  • 27 plantillas DOT, cada una con su comando Graphviz de línea de comandos y el SVG resultante
  • Workflow recomendado: archivos .gv en el repositorio y SVG generados en CI; editores online citados: edotor.net y GraphvizOnline

Comments