DEV Community

Sho Naka
Sho Naka

Posted on • Edited on

Choosing Between Image AI and Code-First Tools for Technical Diagrams: An 8-Tool Comparison

Technical diagrams tend to need repeated rework: a box added, a step reordered, a label changed. When each revision means generating a new image from scratch, small changes are hard to reproduce exactly, and the underlying structure of the diagram is never captured anywhere durable.

TL;DR:
Recurring illustration work fits well into seven scriptable stacks plus one 3D stack, with image-generation AI kept as a fallback for style-first or one-off graphics.

Image AI or code-first tools: how to decide

A useful way to choose between an image-generation model and a code-first tool is to ask three questions before starting:

  • Can this be represented as text or code?
  • Can it be regenerated exactly when requirements change?
  • Does it need raw design freedom, or does it need deterministic structure?

When the answer leans toward "text/code plus deterministic output," a code-first tool is generally the better fit. Image-generation AI remains useful when the goal is style, mood, or a one-off illustration rather than structural accuracy — it is not the right tool for diagrams that need to be reproduced or incrementally edited.

This is not an exhaustive academic survey of every diagramming tool that exists. It is a focused comparison of eight tools that are commonly reached for in technical writing and documentation, organized by what each is actually good at.

An 8-tool decision matrix

A practical matrix for evaluating a new illustration request looks like this:

Tool Best fit Why it's a good fit
Mermaid flow, sequence, architecture notes fastest in markdown-native writing
PlantUML UML-heavy docs strict structure when Mermaid gets too loose
Markmap map-style summaries converts headings directly
Graphviz dependency and direction graphs compact graph semantics
matplotlib numeric visualizations source-of-truth from data tables
Pillow labels, badges, annotations deterministic pixel edits in Python
D3.js node/link or hierarchy interactions data-driven relationship rendering
Blender 3D explanatory graphics stronger structural clarity for complex scenes

Mermaid, matplotlib, and Pillow are the most broadly useful of the eight and the easiest to adopt first — they cover flowcharts, data visualization, and simple annotated graphics with minimal setup. The other five are strong choices for narrower needs: PlantUML for strict UML modeling, Markmap for turning outlines into mind maps, Graphviz for dependency graphs, D3.js for interactive network diagrams, and Blender for 3D explanatory scenes.

Practical snippets

Below are small, runnable snippets for each tool.

1. Mermaid for deterministic flow maps

flowchart LR
  A["User"] --> B["App"]
  B --> C["API"]
  C --> D["Storage"]
  C --> E["Cache"]
Enter fullscreen mode Exit fullscreen mode
npm i -D @mermaid-js/mermaid-cli
Enter fullscreen mode Exit fullscreen mode

Mermaid is well suited for quick, reviewable diagrams because the syntax is fast to read, easy to version-control alongside markdown content, and fast to regenerate.

2. PlantUML for strict structure

@startuml
actor User
participant API
participant DB
User -> API: Request
API -> DB: Query
DB --> API: Result
API --> User: Response
@enduml
Enter fullscreen mode Exit fullscreen mode
java -jar plantuml.jar -tpng architecture.puml
Enter fullscreen mode Exit fullscreen mode

When a diagram should model lifecycle, protocol, or strict roles, PlantUML is a natural next step after Mermaid.

3. Markmap from markdown headings

# Release Plan
## Week 1
### Audit
### Diagram targets
## Week 2
### Implementation
### Regression checks
## Week 3
### Publish preparation
Enter fullscreen mode Exit fullscreen mode
npm i -D markmap-cli
Enter fullscreen mode Exit fullscreen mode

Because it converts existing markdown headings directly, it removes the "learn a separate visual DSL" step for internal notes and outlines.

4. Graphviz for dependency graphs

digraph G {
  rankdir=LR;
  "API" -> "Auth";
  "API" -> "Search";
  "Auth" -> "DB";
  "Search" -> "SearchIndex";
}
Enter fullscreen mode Exit fullscreen mode
dot -Tsvg graph.dot -o graph.svg
Enter fullscreen mode Exit fullscreen mode

Graphviz is a good fit when relationship direction is the only thing that needs to be made obvious.

5. matplotlib for reproducible data visuals

import matplotlib.pyplot as plt

stages = ["Flow", "Auth", "Search", "Storage", "Cache"]
latency = [1.2, 0.7, 2.1, 0.9, 0.4]

plt.figure(figsize=(7, 3.5))
plt.plot(stages, latency, marker="o")
plt.title("Pipeline Latency by Stage")
plt.ylabel("Seconds")
plt.tight_layout()
plt.savefig("pipeline-latency.svg")
Enter fullscreen mode Exit fullscreen mode
uv add matplotlib
Enter fullscreen mode Exit fullscreen mode

For this kind of visual, AI image generation is generally the wrong tool.
Data-driven charts should be generated from the underlying data, not approximated visually.

6. Pillow for labels and annotations

from PIL import Image, ImageDraw, ImageFont

canvas = Image.new("RGB", (640, 200), "#1f2d3d")
draw = ImageDraw.Draw(canvas)
draw.rectangle((20, 40, 620, 160), outline="#f4d03f", width=3)
draw.text((40, 80), "Deployment Checklist", fill="#ffffff")
canvas.save("badge-note.png")
Enter fullscreen mode Exit fullscreen mode
uv add Pillow
Enter fullscreen mode Exit fullscreen mode

Pillow is a solid choice for simple, repeatable badges and annotations, where consistency across many images matters more than illustration variety.

7. D3.js for flexible network diagrams

import { JSDOM } from "jsdom";
import * as d3 from "d3";
import fs from "node:fs";

const width = 540;
const height = 360;
const nodes = [{id: "A"}, {id: "B"}, {id: "C"}];
const links = [{source: "A", target: "B"}, {source: "B", target: "C"}];

const dom = new JSDOM("<!doctype html><body></body>");
const body = d3.select(dom.window.document.body);
const svg = body.append("svg").attr("viewBox", `0 0 ${width} ${height}`);

const simulation = d3.forceSimulation(nodes)
  .force("link", d3.forceLink(links).id(d => d.id).distance(110))
  .force("charge", d3.forceManyBody().strength(-220))
  .force("center", d3.forceCenter(width / 2, height / 2));

simulation.tick(80);

svg.selectAll("line")
  .data(links)
  .join("line")
  .attr("x1", d => d.source.x)
  .attr("y1", d => d.source.y)
  .attr("x2", d => d.target.x)
  .attr("y2", d => d.target.y);

svg.selectAll("circle")
  .data(nodes)
  .join("circle")
  .attr("cx", d => d.x)
  .attr("cy", d => d.y)
  .attr("r", 18);

fs.writeFileSync("network.svg", body.html());
Enter fullscreen mode Exit fullscreen mode
npm i d3 jsdom
Enter fullscreen mode Exit fullscreen mode

As relationship density grows, D3 offers finer control over layout and interaction than static diagram tools typically provide.

8. Blender for 3D explanatory scenes

import bpy

bpy.ops.wm.read_factory_settings(use_empty=True)
camera = bpy.data.objects["Camera"]
camera.location = (4, -6, 3)
camera.data.lens = 40
cube = bpy.ops.mesh.primitive_cube_add(size=2, location=(0, 0, 1))
sphere = bpy.ops.mesh.primitive_uv_sphere_add(radius=0.6, location=(2, 0, 0.6))

bpy.ops.render.render(write_still=True, filepath="infra-overview.png")
Enter fullscreen mode Exit fullscreen mode
blender --background --python render_scene.py
Enter fullscreen mode Exit fullscreen mode

Blender is worth reaching for in cases where shape and spatial composition are themselves part of the explanation — for example, illustrating physical or architectural structure in three dimensions.

A general rule of thumb

This decision framework can be summarized as "draw from intent, not from prompt":

If a diagram has underlying structure, express it in text or code and regenerate it from that source. Reserve image-generation AI for final polish or style-first deliverables where exact reproducibility is not the goal.

Applying this consistently helps avoid a common source of friction: knowing what a diagram is supposed to communicate but being unable to reproduce the same output twice through prompting alone.

Trying this in practice

  1. Choose one section of a document or article that currently relies on an image-generation-AI file for its diagram.
  2. Classify it into one of the 8 matrix buckets above.
  3. Replace it with the smallest corresponding snippet from this article.
  4. Change one parameter or piece of text and regenerate, then compare that to re-prompting an image model.

As a general guide:

  • Mermaid for control-flow logic
  • PlantUML for strict protocol or class views
  • Markmap for knowledge maps
  • Graphviz for dependency direction
  • matplotlib for numbers
  • Pillow for labels and badges
  • D3.js for link-heavy visuals
  • Blender for 3D structure-only cases

A reasonable starting point is to replace one recurring illustration with Mermaid or matplotlib first, and leave the rest of the workflow unchanged until that habit is established.

Code-first tools make visuals reproducible in the same way source code does: the diagram is only ever as far away as the file that generates it. The technical accuracy of the tool details above has been independently verified; readers should adapt the specific commands and snippets to their own environment.

Top comments (0)