TL;DR:
fdreplacesfindwith simpler syntax, much faster execution, .gitignore-awareness by default, and colourised outputripgrep(rg) replacesgrepwith significantly faster performance, built-in file type filtering, .gitignore support, and better output formatting- Both are written in Rust and available on every platform via
brew,apt,scoop,cargo - Neither requires learning new concepts — if you know
findandgrep, you already understand fd and ripgrep, the syntax is just cleaner
Most developers learn find and grep early and keep using them until they retire. They work. They’re on every system. Muscle memory is a real thing.
The case for switching to fd and ripgrep isn’t about correctness — the original tools are correct. It’s about the small frictions that add up: find syntax that never gets intuitive no matter how many times you look it up, grep that happily searches your node_modules folder and the .git directory, and speed that becomes noticeable on large codebases.
Here’s the practical case for both tools.
fd — A More Sensible find
Installation
# macOS
brew install fd
# Ubuntu/Debian
sudo apt install fd-find
# Note: the binary is fdfind on Debian/Ubuntu to avoid conflict with an existing fd package
# alias fd=fdfind in your shell config
# Windows
scoop install fd
winget install sharkdp.fd
# From source
cargo install fd-find
The basic difference
find syntax:
find . -name "*.ts" -type f
find . -name "*.ts" -not -path "*/node_modules/*" -type f
find . -mtime -1 -type f
fd syntax:
fd --extension ts
fd --extension ts # node_modules excluded automatically
fd --changed-within 1d
The pattern in fd is a regex (or substring match if no regex characters) applied to the filename. You don’t prefix with -name, you don’t repeat the directory, and .gitignore entries (including node_modules) are excluded by default.
Most useful fd patterns
Find all TypeScript files:
fd -e ts
fd -e ts -e tsx
Find files matching a pattern in the name:
fd "test" # Files with "test" in name
fd "test" src/ # Scoped to src/
fd "\.test\." src/ # Regex: files like "foo.test.ts"
Find recently modified files:
fd --changed-within 2h # Last 2 hours
fd --changed-within 7d # Last week
Find directories only:
fd -t d config # Directories named config
Execute a command on each result:
fd -e json --exec cat {} # Print each JSON file
fd -e log --exec rm {} # Delete log files
fd -e ts --exec wc -l {} # Line count of each TS file
Include ignored files (when you need to search node_modules or .git):
fd --no-ignore pattern
fd --hidden pattern # Include hidden files/dirs
fd --hidden --no-ignore pattern # Include everything
Pipe into other tools:
fd -e ts | head -20 # First 20 TypeScript files
fd -e ts | xargs grep "TODO" # Search for TODO in all TS files
fd -e md | wc -l # Count markdown files
Why the defaults matter
The fact that fd ignores .gitignore entries by default changes the experience in a meaningful way for most development workflows. Running fd pattern in a Node.js project doesn’t produce hundreds of matches from node_modules. Running it in a Python project skips __pycache__ and venv. You get results for code you actually care about.
Hidden files (dotfiles) are also excluded by default, which is usually right — you rarely want .git/COMMIT_EDITMSG in your search results.
ripgrep — grep That Respects Your Time
Installation
# macOS
brew install ripgrep
# Ubuntu/Debian
sudo apt install ripgrep
# Binary is rg
# Windows
scoop install ripgrep
winget install BurntSushi.ripgrep.MSVC
# From source
cargo install ripgrep
The basic difference
grep to search for a pattern in TypeScript files, excluding node_modules:
grep -r "useState" . --include="*.ts" --include="*.tsx" --exclude-dir=node_modules --exclude-dir=.git
rg equivalent:
rg "useState" -t ts
The .gitignore handling, directory exclusions, and file type selection are built in and on by default.
File type filtering
ripgrep has built-in definitions for dozens of file types:
rg "import" -t js # JavaScript files
rg "SELECT" -t sql # SQL files
rg "TODO" -t py # Python files
rg "error" -t rust # Rust files
rg "test" -t go # Go files
# List all built-in type definitions
rg --type-list
You can add custom types:
rg --type-add "web:*.{html,css,js,ts}" "className" -t web
Output control
# Files with matches (no content)
rg -l "pattern"
# Count matches per file
rg -c "pattern"
# Case insensitive
rg -i "pattern"
# Fixed string (no regex interpretation)
rg -F "exact.string"
# Context lines (before/after)
rg -C 3 "pattern" # 3 lines before and after
rg -A 2 "pattern" # 2 lines after
rg -B 2 "pattern" # 2 lines before
Multiline matching
# Match patterns that span multiple lines
rg -U "function\s+\w+\([^)]*\)\s*\{"
Including/excluding paths
rg "pattern" src/ # Only in src/
rg "pattern" -g "*.test.ts" # Only test files
rg "pattern" -g "!*.test.ts" # Exclude test files
rg "pattern" --no-ignore # Include gitignored files
rg "pattern" --hidden # Include hidden files
Replacing output
ripgrep can output results in a format that’s easy to pipe into editors:
# JSON output (useful for scripting)
rg "pattern" --json | jq '.data.lines.text'
# Null-terminated for xargs
rg -l0 "pattern" | xargs -0 sed -i 's/old/new/g'
Why ripgrep is faster
ripgrep uses several approaches that make it significantly faster than GNU grep on most codebases:
- SIMD (vectorised) string matching — processes multiple bytes per CPU cycle on supported hardware
- Parallel directory traversal — searches multiple directories concurrently
- Memory-mapped file reading — skips file-by-file buffering overhead for large files
- Smart encoding handling — detects binary files early and skips them rather than reading through them
On a codebase with a million lines of code and a cold file cache, the difference between grep -r and rg can be 10–50x depending on hardware. On warm caches (second search) the gap narrows but rg is consistently faster.
Combining fd and ripgrep
They compose well together:
# Search for a pattern only in recently modified TypeScript files
fd -e ts --changed-within 7d --exec rg "TODO: deprecated" {}
# Find all config files and search for a database URL
fd "config" -e json -e yaml | xargs rg "DATABASE_URL"
# Count total matches across file types
fd -e ts -e tsx | xargs rg -c "useEffect" | awk -F: '{sum += $2} END {print sum}'
Both tools also integrate well with fzf for interactive selection:
# Interactive file finder with preview
fd -t f | fzf --preview 'cat {}'
# Interactive content search with preview
rg --line-number "" | fzf --delimiter ':' --preview 'cat {1}' --preview-window '+{2}-5'
Shell Aliases and Configuration
In your .bashrc or .zshrc:
# Make fd work if installed as fdfind (Debian/Ubuntu)
which fdfind &>/dev/null && alias fd=fdfind
# Common aliases
alias rgs='rg -l' # List files with matches
alias rgf='rg --fixed-strings' # Fixed string search
# If you want to use rg as your default grep for interactive use
# (don't alias grep itself — some scripts depend on POSIX grep behaviour)
alias grep='rg' # Optional, be aware of behavioural differences
Both tools respect a config file for persistent defaults:
ripgrep: ~/.config/ripgrep/rc (or $RIPGREP_CONFIG_PATH)
# ~/.config/ripgrep/rc
--max-columns=150
--max-columns-preview
--smart-case
fd doesn’t have a config file but respects .gitignore and .ignore files in the directory tree.
Editor Integration
Neovim: telescope.nvim uses ripgrep for its live grep functionality. Install ripgrep and it works automatically. The fd binary powers file finder in Telescope too.
VS Code: ripgrep is bundled with VS Code and used for the built-in search. You’re already using it — the option to use an external ripgrep binary is available in settings.
Helix: Uses ripgrep for global search by default.
Emacs: consult-ripgrep provides live ripgrep search in minibuffer. projectile uses fd for file finding.
Zed: Uses ripgrep for project search. Configured automatically if installed.
For most editors, the tools are most powerful not through the editor’s built-in integration but by dropping into a terminal and running them directly — especially for cross-project searches, bulk operations, or scripted analysis.
The Two-Minute Install
If you haven’t installed these yet:
# macOS
brew install fd ripgrep
# Ubuntu/Debian
sudo apt install fd-find ripgrep
echo "alias fd=fdfind" >> ~/.bashrc && source ~/.bashrc
# Then test:
fd -e md # Should list markdown files
rg "TODO" -t ts # Should search TypeScript for TODOs
Five minutes of experimenting with your own codebase will demonstrate more than any benchmark.