ryboe/q: print-style debugging for Go programs
Quick and dirty debugging output for tired Go programmers
At a glance
- What is it?
- ryboe/q replaces scattered fmt.Printf calls with pretty-printed values written to $TMPDIR/q. It fits Go programmers who want a quick inspection view without adding logging services to the debugging loop.
- Who is it for?
- Adopt ryboe/q when a Go developer needs a lightweight local view of expressions and structs during development. Do not choose it as a substitute for a structured logging, tracing, or production observability system.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 40 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why ryboe/q sends Go values to a dedicated file
ryboe/q targets a narrow problem: inspecting Go values while code is running without formatting a long fmt.Printf expression each time. Its public call is q.Q, followed by the values or expressions to inspect. According to the README, q.Q pretty-prints variables and expressions, expands structs for easier inspection, and adds color to the output.
The output destination is the main operational distinction. Rather than mixing debug lines into the standard output used by the application, q writes to $TMPDIR/q. That separation can make local debugging easier to read when a program already uses stdout for application data. It does not turn q into a conventional logger: the documented workflow is to watch one fixed file in another terminal.
The intended users are Go programmers who value a short call site over configuration. Python programmers may also recognize the approach because the README describes ryboe/q as a Go port of zestyping's q module. The library is therefore closer to a debugging view than to a debugging protocol. It does not document breakpoints, stepping, watches, process attachment, or remote sessions.
The q.Q call and the $TMPDIR/q viewing loop
Basic use consists of importing q and passing expressions to q.Q. The README gives this compact form:
import "q"
...
q.Q(a, b, c)The import path shown in the usage example is the short package path q. The repository's go.mod identifies the module as github.com/ryboe/q, so the example reflects a particular source placement rather than a general module-mode import rule. The install instructions clone the repository into the GOPATH source directory, which explains that short import path.
For a useful local session, the program writes q output to $TMPDIR/q and a second terminal tails that file. The README recommends a dedicated terminal because repeated calls append debug information there. The shell function creates the file with a Q LOG marker if necessary, then follows its last 100 lines. If $TMPDIR is empty, it falls back to /tmp/q.
The documentation says q.Q is safe for concurrent use. That claim covers calls from concurrent Go code, but it does not say that readers can interpret output as a transaction log or correlate each value with a request. Multiple goroutines can produce interleaved debugging information, and the README does not document sequence numbers, caller metadata, filtering, or per-package files.
Install ryboe/q through the documented GOPATH layout
The README does not provide a go install command. Its documented installation method clones the repository into the active GOPATH under src/q. Run this in a shell where go env GOPATH resolves to the location used by the development environment:
git clone https://github.com/ryboe/q "$(go env GOPATH)"/src/qThe repository declares module github.com/ryboe/q and Go version 1.26.0 in go.mod. Confirm that the local Go toolchain meets that file before compiling the package. No dependency installation command is documented, although go.mod lists github.com/kr/pretty and other module dependencies. The repository also contains a vendor directory, but the README does not give separate vendoring instructions, so the article does not prescribe a build mode that is not stated there.
After the clone, the first program-level use is the q.Q call shown earlier. Launch the program in one terminal, then add the documented shell function to the shell configuration and source that configuration in another terminal. The function clears the screen, initializes the log if needed, and follows the file. That separate viewer is part of the intended workflow rather than an optional logging feature.
A local q.Q debugging session with qq and rmqq
The README supplies qq and rmqq as shell functions. Add them to the shell configuration so each debugging session can reopen the q log. The exact documented definitions are:
qq() {
clear
logpath="$TMPDIR/q"
if [[ -z "$TMPDIR" ]]; then
logpath="/tmp/q"
fi
if [[ ! -f "$logpath" ]]; then
echo 'Q LOG' > "$logpath"
fi
tail -100f -- "$logpath"
}After running the program and invoking qq, the terminal should display Q LOG if the file was newly created, followed by debug output as q.Q receives values. The command follows only the last 100 lines. A direct tail -f $TMPDIR/q is also possible, but the README recommends the function because it handles the empty-TMPDIR case and creates the destination.
rmqq removes the existing q file and then calls qq, which creates a fresh log and begins following it:
rmqq() {
logpath="$TMPDIR/q"
if [[ -z "$TMPDIR" ]]; then
logpath="/tmp/q"
fi
if [[ -f "$logpath" ]]; then
rm "$logpath"
fi
qq
}Because rmqq deletes the file, use it only when the previous local output is no longer needed. The README does not document automatic log rotation, size limits, or cleanup outside these commands.
Editor snippets for inserting q.Q debug calls
ryboe/q's short API is designed for repeated ad hoc insertion. The README supplies editor snippets so that typing qq expands into q.Q($1) // DEBUG. This reduces typing and leaves an explicit marker that the call is temporary debugging code.
Visual Studio Code users are directed to Preferences > User Snippets > Go. Zed users can add the snippet to ~/.config/zed/snippets/go.json. The documented VS Code and Zed entry is:
"qq": {
"prefix": "qq",
"body": "q.Q($1) // DEBUG",
"description": "Pretty-print to $TMPDIR/q",
}Sublime Text uses Tools > Developer > New Snippet and a Go-scoped snippet. JetBrains GoLand uses Settings > Editor > Live Templates, with qq as the abbreviation and q.Q($END$) // DEBUG as the template text. For SirVer/ultisnips in Vim or NeoVim, :UltiSnipsEdit opens the editor used to add the snippet:
snippet qq "qq"
q.Q(${1:})
${2}
endsnippetThe exact configuration mechanism varies by editor, but the generated call follows the same pattern. Snippets do not add lifecycle management. Calls still need to be removed before code is shipped, and a snippet marked // DEBUG can remain long after the debugging session unless a developer reviews the diff.
Where q.Q differs from fmt.Printf and structured logging
The direct alternative is fmt.Printf. A fmt.Printf call is part of the Go standard library, writes to standard output, and requires the programmer to choose verbs, separators, and formatting details. q.Q offers a shorter debug call, pretty-printed values, visible struct contents, and a dedicated file. That convenience comes at the cost of output control. The README does not document custom destinations, file rotation, severity levels, structured fields, timestamps, or sinks for long-term retention.
Python programmers have a closer conceptual alternative in zestyping's q module, from which the README says this Go implementation was ported. Both approaches center on inspecting values through a concise debugging call. The Go port writes to $TMPDIR/q and uses q.Q, so Python instructions would not transfer directly.
Another practical alternative is writing a small project-specific debug helper around log.Printf or fmt.Fprintf. That takes more setup but can define an application-owned destination and output format. q is the better fit for rapid local inspection when its fixed-file workflow is acceptable. It is the wrong tool when debug records must be retained across machines, routed through a logging service, queried by fields, or correlated with production events.
ryboe/q maintenance, Go requirements, and MIT adoption
The repository is not archived. Its last push was on 2026-09-13, and the release list includes v1.0.26 dated 2026-03-08, following v1.0.25 and v1.0.24. Those facts establish recent repository activity and a versioned release history, but they do not prove the scope of future support or the responsiveness of an individual issue. The README does not present a support policy or compatibility matrix beyond the Go version in go.mod.
go.mod requires Go 1.26.0 and the package depends on github.com/kr/pretty v0.3.1, with kr/text and rogpeppe/go-internal listed indirectly. This raises an adoption constraint for teams pinned to older Go toolchains. The upgrade path is versioned, yet the README does not document a migration procedure, backward-compatibility policy, or release automation.
The repository identifies its license as MIT and includes a LICENSE file. Teams that redistribute the package or incorporate it into a product should review that license text against their own release and attribution obligations. The license does not by itself answer whether the fixed local-file behavior meets operational logging requirements.
Editorial conclusion
Adopt ryboe/q when a Go developer needs a lightweight local view of expressions and structs during development. Do not choose it as a substitute for a structured logging, tracing, or production observability system. Before adoption, verify that the Go toolchain satisfies go.mod, that $TMPDIR/q fits the local debugging workflow, and that your editor snippet is available on the intended workstation.
Frequently asked questions
Where does ryboe/q write its debugging output?
The README states that q.Q writes to $TMPDIR/q rather than noisy-ass stdout. If $TMPDIR is empty, the documented shell functions use /tmp/q.
How do I install and start using ryboe/q in Go?
The documented install method clones https://github.com/ryboe/q into $(go env GOPATH)/src/q. Basic use imports q and calls q.Q(a, b, c), then a second terminal follows $TMPDIR/q.
Is ryboe/q safe to call from concurrent Go code?
The README explicitly says q.Q() is safe for concurrent use. It does not claim that output from concurrent calls forms a structured or transactionally ordered event stream.
Which editors have documented ryboe/q snippets?
The README provides snippets or setup directions for VS Code, Zed, Sublime Text, JetBrains GoLand, Emacs, and Vim or NeoVim with SirVer/ultisnips. The generated body follows the q.Q($1) // DEBUG pattern.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ryboe-q)Was this useful?