v5.0
live available
devtools guide 06 Oct 2026

Seven code search tools, twelve planted files, and the defaults that never looked

ripgrep found five of twelve. GNU grep found nine. The gap is not accuracy, it is which files each tool decided were worth opening, and every one of those decisions has an off switch.

By Purvansh Parmar Checked 2026-10-06 Read 8 min Entries 7

No matches is the most trusted line a terminal prints. You grep for a token, nothing comes back, and you write the ticket saying the token is gone. That inference is wrong for six of the seven tools below, because every one of them narrows the file set before it reads a byte, and none of them tells you what it skipped. I planted one string in twelve places inside a small repo and ran each tool the way a person actually types it. The best default missed three of the twelve. The worst missed eight.

How this was checkedBuilt a 12 trap repo with one unique token planted in a gitignored build dir, a dotfile, a hidden dir, node_modules, an unknown extension, a file with a NUL byte, a UTF-16LE file, a 2 MB minified line, a symlinked dir, an untracked file and a gzip member. Ran seven search tools with the command you would actually type. Default recall ranged from 4 of 12 to 9 of 12, and only ripgrep and ugrep could be argued up to 12.

What I compared on

01 GNU grep 3.7

The one already installed, with no opinion about your repository layout.

Wins
The question is whether a string exists anywhere under this path, and you want the answer uncontaminated by someone else's idea of which directories are boring. Nothing here is hidden from you by default.
Loses
It takes 177 ms where ripgrep takes 9, and on the binary file it prints binary file matches with no line and no column, which tells you a file is interesting but not why.
9 of 12 default10 of 12 with -Ra177 ms on 255 MB

02 ugrep 7.8.5

A grep-compatible searcher that reads archives and transcodes encodings.

Wins
You need one tool that can be talked into reading everything, including the inside of a tarball, without a second pass or a pipeline. Its scan rate is the highest here: 255 MB in 54 ms, against ripgrep's 41 ms for the same bytes under -uuu.
Loses
Four separate flags to open the four doors, and the one for hidden files is a bare dot, which nobody remembers. The ugrep 7.x packages also want glibc 2.38, so on Ubuntu 22.04 I had to build it from source.
8 of 12 default12 of 12 with -R -. -z54 ms on 255 MB3,305 stars, 8 open issues

03 sift 0.9.2

A Go searcher that respects .gitignore only when you ask it to with --git.

Wins
It is the only tool here that printed the reason for a skip. An error on stderr naming --blocksize beats silence, and silence is what the other six gave me every single time they walked past a file.
Loses
The 262,144 byte line limit is a real default that bit on real minified JS, and no amount of flags gets it to the UTF-16 file. At 1,654 stars with 45 open issues, you are also betting on a small project.
8 of 12 default10 of 12 max262,144 byte line cap62 ms on 255 MB

04 ack 3.9.0

A single-file Perl script that filters by a built-in table of file types.

Wins
Zero install beyond a curl and a chmod, which is the whole pitch, and it ran on a box where the ugrep deb would not load. Its ignore list is auditable in one command, so you can see exactly what it decided not to read.
Loses
No flag turns binary scanning on, so the NUL byte file is permanently invisible, and the only way to reach the minified file is --ignore-ack-defaults, which discards the type table you came for. 109 ms, slowest of the seven after grep.
6 of 12 default9 of 12 max28 default ignore-dir rules109 ms on 255 MB

05 git grep 2.34.1

Searches the index, which means tracked files and nothing else.

Wins
You want the question scoped to code that is actually committed, which is the right scope for a refactor and the only scope that matters in CI. It is also six times faster than ripgrep because the index already told it the file list.
Loses
A leaked credential lives in an untracked .env.local roughly always, and that is the one file git grep structurally cannot see. --untracked --no-exclude-standard fixes it and nobody types that twice.
5 of 12 default9 of 12 max6 ms on 255 MB0 untracked files read

06 ripgrep 15.2.0

Rust searcher that applies .gitignore, hidden-file and binary filters before reading.

Wins
The filters are stacked on one escalating flag, so -u drops gitignore, -uu adds hidden files, -uuu adds binaries. One letter, repeated, in the order you usually want them. That is the best interface in this list by a distance.
Loses
The default is the most aggressive filter of the seven, and it is silent about it. Three of its five misses were files a person would obviously want searched, and the 9 ms headline is mostly the 237 MB it never opened.
5 of 12 default12 of 12 with -uuu -z -L9 ms default41 ms under -uuu

07 the_silver_searcher 2.2.0

The C searcher that made gitignore-aware search normal, now mostly coasting.

Wins
It is in every distro's repository under a name people already know, and it reads the minified line that sift refuses. If a box has ag and nothing else, 18 ms for the common case is still fine.
Loses
Last pushed in June 2024 with 564 open issues against 27,125 stars, and -u costs it 8x: 18 ms becomes 149 ms. Everything it does, ripgrep does with better defaults and a smaller gap beween fast and complete.
4 of 12 default10 of 12 max18 ms default149 ms under -u564 open issues

Side by side

DefaultCeiling255 MB treeCannot reach
GNU grep 3.79 / 1210 / 12177 msUTF-16, archives
ugrep 7.8.58 / 1212 / 1254 msnothing
sift 0.9.28 / 1210 / 1262 msUTF-16, archives
ack 3.9.06 / 129 / 12109 msbinary, UTF-16, archives
git grep 2.34.15 / 129 / 126 mssymlinks, UTF-16, archives
ripgrep 15.2.05 / 1212 / 129 msnothing
ag 2.2.04 / 1210 / 1218 msUTF-16, archives

Verdict

Install ripgrep 15.2.0. Then learn two commands rather than one, because the tool is answering two different questions and only one of them suits the defaults. Plain rg for where is this defined: its filters are right nearly every time there, and the 9 ms is real. rg -uuu -z -L for is this string anywhere under here, which earns its own muscle memory. ugrep ties at 12 of 12 and scans faster per byte, but four unrelated flags lose to one letter typed three times. What has to stop is reading a silent rg as proof. On this rig that reading was wrong seven times out of twelve.

Sources