The CLI tool every developer should know: fzf
Ever spent minutes scrolling through your command history looking for that one command you ran last week? Or digging through directories trying to find a file? There’s a better way. It’s called fzf.
fzf is a command-line fuzzy finder written in Go. You type a few characters, and it instantly filters through thousands of items. It works with your shell, your editor, and pretty much anything that outputs text.
What makes fzf special?
The magic is in the fuzzy matching algorithm. You don’t need to remember exact names. Type mnctr and it’ll match main_controller.go. Type psax and it’ll find that ps aux command from your history.
It’s fast too. Really fast. The Go implementation processes tens of thousands of lines without breaking a sweat. This matters when you’re searching through large codebases or extensive command histories.
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. It works with bash, zsh, and fish out of the box.
Once installed, you get three powerful shortcuts:
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. You can pipe any output to fzf. Want to quickly switch between git branches?
git branch | fzf | xargs git checkout
Or find and open a Go file in your editor:
find . -name "*.go" | fzf | xargs code
You can also use fzf programmatically. Since it’s written in Go, you might want to understand how it handles input. The core matching logic is elegant:
// 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
fzf really shines with editor integration. The fzf.vim plugin brings fuzzy finding to your editing workflow.
Add it to your config and you get commands like :Files, :Buffers, and :Rg for ripgrep integration. Finding that Go function across hundreds of files becomes instant.
For neovim users, telescope.nvim takes inspiration from fzf and builds on similar concepts.
tmux integration
If you use tmux, fzf integrates beautifully. You can fuzzy-search through tmux sessions, windows, and panes. 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. The result? A single binary with no dependencies. It runs on any unix system without requiring a runtime.
This is a pattern we see often in successful CLI tools. Go’s compilation model makes distribution simple. Users don’t need to install anything extra. If you’re building CLI tools, this approach is worth considering. The standard library has everything you need for most use cases.
Practical tips
A few things I’ve learned from using fzf daily:
- Preview files - Use
--previewto see file contents as you search - Custom bindings - Set up project-specific fzf commands in your shell config
- Combine with ripgrep -
rg+fzfis incredibly powerful for code search
# 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 solves a simple problem really well. It makes finding things fast. Whether you’re navigating command history, searching files, or building custom workflows, it’s a tool that pays back the small time investment to learn it.
Give it a try. After a week, you’ll wonder how you worked without it.