CUE is the Swiss Army knife of file generation. It is the tool you grab when you need to generate complex JSON, validate YAML, or generally stop configuration files from ruining your life. It slices, it dices, it ensures your integers are actually integers. But guess what else it can be? It turns out, it is also a surprisingly effective *Literate Programming* tool.
This is important because, let’s be honest, the current king of this hill is org-mode. And while org-mode is powerful, it is also a bit of a golden cage. It works perfectly as long as you never leave the Emacs ecosystem. But the moment you try to export a workflow to a colleague who uses VS Code, you realize you have accidentally signed up for vendor lock-in. You want your documentation - your "literate code" - to be portable, not a magic spell that only works inside one specific editor.
The "Frankenstein" Problem
When you write about code - whether it is a blog post, a tutorial, or internal docs - you rarely show one giant, boring file. That puts people to sleep. Instead, you break it down. You show a snippet of the logic. Then a snippet of the config. Then maybe a piece of the CSS. You stitch these disparate body parts together with prose.
The problem is what I call the "Copy-Paste Tribute".
Let's say you write a brilliant tutorial. You copy your working code into the Markdown file. Then, three days later, you realize you named a variable t instead of timeout. You fix it in the source code. But did you fix it in the Markdown file? Probably not. Now your readers are copying broken code, your tutorial is lying to them, and you look like you don't know what you are doing.
CUE solves this by letting us define "parts" and then stitching them together programmatically. It does not just hold the text; it validates that the pieces actually fit. It ensures that the code in your explanation is the exact same code in your final build. It is like having a Lego set where the bricks refuse to click if you are building something structurally unsound 1.
The Basic Engine
So, how do we stop paying this tribute? We treat our document like a build target.
We can use CUE's tool/file to create the document and tool/exec to run commands. The beauty here is that we are not just templating strings; we are defining a dependency graph. If the dependencies (the code snippets) aren't valid, the document doesn't exist.
package example
import (
"tool/file"
"tool/exec"
)
// This is our "build" command. It tells CUE:
// "For every file defined in the 'files' struct, please create it"
command: example: {
for k, v in files {
"(k)": file.Create & {
filename: k
contents: v
}
}
}
// Imagine this is calling a script to get a version number.
// We aren't guessing the version; we are asking the shell for it.
data: exec.Run & {
cmd: ["sh", "-c", "echo '1.2.3'"]
stdout: string
}
// Our final document, stitched together.
// The '(data.stdout)' isn't just a placeholder; it's a promise
// that 'data' will run before this string is finalized.
files: {
"output.md": """
My Awesome Project
Current version: (data.stdout)
"""
}
In a normal editor, that version number 1.2.3 would just be static text I typed in while caffeinated. If the underlying logic changed to 1.2.4, the document would lie to the reader until I manually updated it. With CUE, if data.stdout fails to populate - say, if the shell command crashes - the file simply doesn't generate. The build breaks, protecting the user from bad info.
The Ignition Key: cue cmd
There is a catch, though. If you just stare at the code above or run a standard cue eval, nothing happens. CUE is declarative by nature; it describes the state of the world, it doesn't inherently change it. Your file.Create and exec.Run blocks are just data sitting there, looking pretty.
To actually turn that potential energy into kinetic energy, you need cue cmd.
cue cmd is the built-in task engine. It is the part of CUE that is allowed to have side effects. It looks for those command: blocks we defined earlier and executes the tasks inside them sequentially. It is the crucial bridge between "I want a file" (the definition) and "Here is your file" (the reality). Without invoking cue cmd, your literate programming setup is just a very fancy, inert config file.
Crucial Detail: There is a trap for the uninitiated here. For cue cmd to actually pick up your file, the filename must end with _tool.cue (e.g., build_tool.cue). If you name it just example.cue, CUE will ignore your commands completely, and you will be left wondering why your genius code is doing absolutely nothing.
The Polyglot Pipeline
Once you have the engine running, you might realize that simple shell commands aren't enough. echo '1.2.3' is fine for toddlers, but what if you need to compile a Haskell module or render a diagram?
The real magic happens when you realize CUE is incredibly easy to extend. It isn't limited to its own syntax. You can define your own "renderers" using tool/exec, essentially building a tiny, custom CI runner inside your config file.
Here is how you can teach CUE to speak multiple languages by wrapping standard CLI tools. Note the trick with the Haskell renderer: we aren't just passing raw code; we are wrapping it in a module structure so it compiles standalone.
package example
import (
"tool/exec"
)
// Define our "renderers" -- wrappers around CLI tools.
// Each renderer takes a 'code' string and pipes it into a command.
renderers: pikchr: exec.Run & {
cmd: ["pikchr", "--svg-only", "-"]
code: string
stdin: code
stdout: string
}
renderers: haskell: exec.Run & {
code: string
stdout: string
cmd: ["stack", "runghc"]
// Here we wrap the snippet in a module so it compiles standalone
stdin: """
module Program where
(code)
"""
}
renderers: bash: exec.Run & {
code: string
stdout: string
cmd: ["bash", "-e"]
stdin: code
}
// Just for good measure, ZSH too.
// Why? Because sometimes Bash isn't hipster enough.
renderers: zsh: exec.Run & {
code: string
stdout: string
cmd: ["zsh", "-e"]
stdin: code
}
Now, when I want to include a diagram or a code output, I just pass my snippet through the appropriate renderer. CUE handles the heavy lifting of calling the shell, piping the input via stdin, and capturing the stdout. It turns your documentation into a living program.
Testing the Narrative
The best part? You can "test" your article. By running cue cmd example, CUE actually executes the shell commands, compiles the Haskell, renders the Pikchr diagram, and generates the final files.
If your code snippet has a syntax error, the generation fails. If your Haskell code doesn't compile, the generation fails. If you try to create a Pikchr diagram with invalid coordinates, the generation fails. It turns "writing a blog post" into "passing a CI/CD pipeline".
It is a bit like having a proofreader who is also a very strict compiler 2. It ensures that every diagram in my post was actually rendered from the code right next to it. No stale images, no manual exports, and no "magic" steps I forgot to write down. It is independent of any specific editor ecosystem, effectively solving the "works on my machine" problem for documentation. You aren't just writing; you are engineering the text.
Top comments (0)