People have been writing software for quite a long time, so natually, there are some amazing, rock-solid old tools out there, like make and rsync. This extends to diagramming software.
For years, I’ve used Graphviz (created “before 1991”), which lets you write a little declarative program that says what nodes connect to what. Then, you run a compiler that makes the diagram. It figures out for you exactly where the circles (or boxes or other shapes) and lines and labels should go.
Here’s an example program:
digraph {
graph [fontsize=12, fontname=Ubuntu, penwidth=3];
node [fontsize=12, fontname=Ubuntu, shape=circle];
edge [fontsize=12, fontname=Ubuntu];
nodesep=3;
overlap=false;
root=onEstablishPilesContainer;
dim=4;
mode=ipsep;
sep=0.4
bgcolor="#fde725";
// Persisters
PilesPersister -> DeckStore [label="setPart"];
PilesPersister -> DeckStore [label="get"];
deckIdsPersister -> localStorage [label="setItem"];
thingPersister -> localStorage [label="setItem"];
// Stores
DeckCollectionStore;
DeckStore [shape=doublecircle];
activeDeckIdentifier;
PileCollectionStore;
PileStore [shape=doublecircle];
// Responders
onDeckCollectionChange;
onDeckChange [shape=doublecircle];
onPileCollectionChange;
onPileChange [shape=doublecircle];
onEstablishPilesContainer [shape=doublecircle];
DeckCollectionStore -> onDeckCollectionChange;
DeckCollectionStore -> deckIdsPersister [label="write"];
activeDeckIdentifier -> onDeckCollectionChange;
DeckStore -> onDeckChange;
DeckStore -> thingPersister [style=dashed, label="write"];
PileCollectionStore -> onPileCollectionChange;
PileStore -> onPileChange;
PileStore -> thingPersister [style=dashed, label="write"];
PileCollectionStore -> PilesPersister [style=dashed, label="write"]
// Renderers
RenderDeckCollection;
RenderDeck [shape=doublecircle];
RenderPileCollection;
RenderPile [shape=doublecircle];
onDeckCollectionChange -> RenderDeckCollection;
onDeckChange -> RenderDeck;
RenderDeck -> onEstablishPilesContainer [style=dashed, label="(one-time)"];
onEstablishPilesContainer -> RenderPileCollection;
onEstablishPilesContainer -> RenderPile;
onPileCollectionChange -> RenderPileCollection;
onPileChange -> RenderPile;
// Updaters
addDeck -> DeckCollectionStore;
addPile -> PileCollectionStore [label="add"];
RenderDeckCollection -> addDeck [style=dashed];
RenderDeck -> activeDeckIdentifier [style=dashed, label="setPart"];
RenderDeck -> DeckStore [style=dashed, label="setPart"];
RenderDeck -> DeckStore [style=dashed, label="del"];
RenderDeck -> DeckCollectionStore [style=dashed, label="remove"];
RenderPileCollection -> addPile [style=dashed];
RenderPile -> PileCollectionStore [style=dashed, label="remove"];
RenderPile -> PileStore [style=dashed, label="del"];
RenderPile -> PileStore [style=dashed, label="setPart"];
}
That produces this diagram:
Despite knowing Graphviz, I started that diagram drawing it by hand on an e-ink tablet because I’ve been trying lately do stuff away from the computer as much as possible. It was easier than doing it in the likes of Visio or LucidChart, but every change to the subject of the diagram meant a painful redrawing of the layout. I’m diagramming my app for the purpose of finding problems and ways to reduce the complexity, so being able to update the diagram easily is important. With Graphviz, updating is usually a matter of editing a line or two, then running neato
or dot
.
This afternoon, I found Mscgen, a tool specifically for making sequence diagrams. It’s inspired by Graphviz and is more recent (2011), yet is still a command line tool.
Secret room for improvement
Both of these turn out to have browser ports: GraphvizOnline and mscgen_js.
I think I saw that GraphvizOnline existed years before, but I dismissed it as some Emscripten stunt. Only after stumbling on it again recently did I realize how powerful it is. It and mscgen_js make two simple additions to their respective original programs:
- They put a code editor side-by-side with the program output.
- They run the diagram compiler whenever you stop typing and update the image next to the code.
The result is that you can see changes as you try them. With GraphvizOnline, you can even flip through the different compilers (e.g. the one for hierarchies, the one for cyclic graphs, the one for ring diagrams) in a select control. It’s like flipping through Photoshop blending modes and watching the images update.
Without these web apps, to evaluate a change you have to:
- Edit the diagram code in your text editor.
- Switch to a terminal tab (or open a terminal window in vim) and run a command.
- Look at a separate image-viewing app to see the result.
It’s a long loop, but it’s not so bad that I wiped sweat from my brow and thought “There’s gotta be a better way!”
But there was! When that loop down is cut down to…
- Edit the code
…Graphviz and Mscgen explode up into a new plane of extreme usability.
So, JavaScript is good, sometimes.
There is a lot said lately about too many web pages being saddled with unnecessary code, and rightly so. You do not need JavaScript, much less a React, Vue, or Angular app, for a web page that tells you which doctors are in your insurance network or how to reboot a phone.
Even forms don’t require much JavaScript, or even none at all. (I can’t tell you how many times I’ve had to go into DevTools in order to delete or force elements to show up in some fragile web app so that I can pay a bill.) There is a case to be made for a document-oriented web.
Despite their overapplication, web apps have their place. How else could have GraphvizOnline and mscgen_js have been made?
Well, the developers could have wrapped the C core of the original code with GUI code for each of the platforms that web browsers support. They’d build around the original code with GTK, Cocoa, Cocoa Touch, MFC (or whatever is state-of-the-art on Windows these days — WPF?), and several other SDKs.
They would have distribute to each platform. For Apple platforms, the developers would either have to tell people how to bypass Gatekeeper or maybe buy an Apple Developer Program license, and obtain a certificate. For other platforms, they could get it into a package repository or perhaps just put the binary up somewhere, then proceed to convincing users that they should bother to download and execute their app. (This is quite a bit more work than saying “look at this web site.”)
Once those hurdles were leapt, the reports that it doesn’t work on particular variants or configurations of users’ OSes would start coming in.
This, by the way, is why I empathize with complaints about Electron apps taking up a ton of memory, while still being glad that it exists.
Big companies like Salesforce (who now make Slack) and Microsoft (who make VS Code) can actually afford to make a native version of their apps for every platform. (Though attempts to herd them all could lead back to either a large and inefficient shared core or something similar to Electron.)
But that would reduce profits, and Milton Friedman has famously proclaimed that corporations’ sole responsibility is to maximize profit. (And as we all know, Milton Friedman is a god-pharaoh that issues forth waves that compel all to obey from his sarcophagus underneath the University of Chicago.)
For smaller companies, indie developers, and people who just want to make something nice for others, it’s hard to imagine better alternatives, except perhaps offline web apps (which aren’t easy for a lot of users to wrap their heads around — the association between the web and internet connections is strong) or putting all of their proverbial eggs on a specific platform.
I guess all I’m saying is: Web browsers are (sadly?) the closest we’ve ever gotten to a universal app platform, so I hope we can cut away their many excesses and abuses while keeping that part.