The CLI tool every developer should know: fzf
How much of your day goes on finding things? Tapping the up arrow through command history, running ls and cd in circles, hunting for a file whose name you half remember. It’s a small tax, but you pay it constantly. fzf removes it.
fzf is a command-line fuzzy finder written in Go. Feed it any list, type a few characters, and it filters instantly. It works with your shell, your editor, and anything else that can print lines of text. This post is a practical reference for wiring it into your daily workflow.
What makes fzf special?
The core idea is that you don’t need to remember exact names. Type mnctr and it matches main_controller.go. Type psax and it finds that ps aux incantation from your history. Once you stop needing precise recall, a lot of friction just disappears.
It’s fast too. The Go implementation chews through tens of thousands of lines without noticeable lag, which matters when you’re filtering a large monorepo or years of shell history.
Getting started
Installation is straightforward. On macOS:
brew install fzf
$(brew --prefix)/opt/fzf/install
The install script sets up key bindings for your shell, and it works with bash, zsh, and fish out of the box.
That gives you three shortcuts, and honestly the first one alone justifies the install:
Ctrl+R- Search command historyCtrl+T- Find files in current directoryAlt+C- Change to a subdirectory
Using fzf in your Go projects
Here’s where it gets interesting. fzf reads from stdin and writes your selection to stdout, so you can drop it into the middle of any pipeline. Want to switch between git branches without typing the full name?
git branch | fzf | xargs git checkout
Or find and open a Go file in your editor:
find . -name "*.go" | fzf | xargs code
The matching behaviour is worth understanding even if you never read the source, because it explains why fzf’s results feel right. Conceptually the scorer looks like this:
// fzf uses a scoring algorithm that rewards:
// - Consecutive character matches
// - Matches at word boundaries
// - Matches at the start of the string
// Example of how fuzzy matching works conceptually
func score(pattern, text string) int {
// Higher scores for:
// - "abc" matching "a_b_c" at boundaries
// - "abc" matching "abcdef" at start
// - Consecutive matches like "abc" in "xabcx"
return calculateScore(pattern, text)
}
Integration with vim and neovim
Editor integration is where fzf stops being a nice-to-have. The fzf.vim plugin brings fuzzy finding into your editing workflow.
Add it to your config and you get commands like :Files, :Buffers, and :Rg for ripgrep integration. Finding a Go function across hundreds of files stops being a task and becomes a keystroke.
For neovim users, telescope.nvim takes inspiration from fzf and builds on similar concepts.
tmux integration
If you use tmux, fzf fits right in. You can fuzzy-search through tmux sessions, windows, and panes, and the fzf-tmux command opens fzf in a tmux popup or split.
# Search sessions and switch
tmux list-sessions | fzf | cut -d: -f1 | xargs tmux switch-client -t
Why Go was the right choice
fzf was originally written in Ruby. The author rewrote it in Go for performance, and the payoff was a single binary with no dependencies that runs on any unix system without a runtime.
You’ll notice this pattern across the best CLI tools of the last decade, and it’s no accident. Go’s compilation model makes distribution trivial: users install one file and they’re done. If you’re building CLI tools yourself, it’s a strong argument for Go, and the standard library covers most of what you’ll need.
Practical tips
A few things I’ve picked up from daily use. The --preview flag is the biggest quality-of-life win: seeing file contents as you filter turns fzf from a picker into a browser. It’s also worth defining project-specific fzf commands in your shell config rather than retyping the same pipelines. And the combination of ripgrep and fzf gives you interactive code search across an entire repo:
# Search code and preview matches
rg --line-number . | fzf --preview 'bat --color=always $(echo {} | cut -d: -f1) --highlight-line $(echo {} | cut -d: -f2)'
Wrapping up
fzf does one thing well: it makes finding things fast. Install it, and for one week force yourself to use Ctrl+R instead of the up arrow. Then build your first custom pipeline; the git branch switcher above is a good starting point. Once the “pipe a list in, get a selection out” model clicks, you’ll start spotting places for it everywhere, and going back to a machine without it will feel genuinely strange.