I’ve been using my na and doing command-line tools daily for years, but lately they’d started feeling sluggish. When na next
was taking over a second to show me my next actions from a tiny 5KB file, I knew it was time to dig in and fix things.
In case you’re unfamiliar, na
is a command line tool for adding and listing per-project todos, and doing
is a CLI for tracking what you were doing, with export features, time tracking, tagging, and other usefuly features.
The Problem
Both tools had developed performance issues that made them occasionally frustrating to use. The na next
command was consistently running at 1.3+ seconds with wildly variable performance—sometimes 200ms, sometimes 500ms for identical inputs. Meanwhile, doing recent
was sometimes taking over 3 seconds to display a simple list of recent entries.
This wasn’t acceptable for tools meant for quick, frequent use.
The Investigation
I built custom benchmarking systems for both gems (NA_BENCHMARK=1
and DOING_BENCHMARK=1
) to see where the time was going. The results were eye-opening.
For na
, the culprits were:
- Theme loading happening repeatedly for every action
- Regex compilation on every color template use
- Pager overhead adding 300-479ms to small outputs
- Git integration running system calls by default
- Heavy gems loading at startup
For doing
, the pager was the main bottleneck, consuming about 600ms of the 3+ second runtime.
The Fixes
- Caching everything
- I pre-compiled regex patterns, cached theme data, and implemented template caching to eliminate repeated processing.
- Smart pagination
- Both tools now skip the pager entirely for small outputs. For
na
, that’s anything under 2000 characters or 50 lines. For doing
, it’s anything under 150% of terminal height (letting you scroll up half a page for reasonable outputs).
- Lazy loading
- Made git integration opt-in with a
--repo-top
flag in na
, and deferred heavy gem loading until actually needed.
- Batch processing
- Generate all strings first, then apply regex highlighting once instead of repeatedly.
The Results
The improvements were dramatic:
- na: From 1300ms+ down to ~6ms (99.5% faster)
- doing: From 3+ seconds down to 0.73 seconds (78% faster)
Both tools now feel genuinely responsive again. The na next
command is nearly instantaneous, and doing recent
displays results in under a second.
Bonus Fixes
While I was at it, I fixed several test failures that had been nagging me:
- Color module method definitions causing
NoMethodError
- ANSI escape sequence handling in exports
- String extension methods for color attributes
- Cleaned up export output (no more unwanted
\e[0m
characters)
Both tools are back to being the responsive, daily-use utilities they were meant to be. You can find more details in the doing wiki and there’s full documentation for na
on the na project page, if you’re curious.