The Modern Toolkit
The Modern 2026 Toolkit
A senior Go engineer is defined by their tools. Using standard grep/find is slow. Upgrade your terminal.
General Tools (Rust-powered, Go-essential)
These aren’t written in Go, but every Go dev uses them. 1. ripgrep (rg): The fastest text search. * rg "func main" -t go: Find main in Go files only. 2. fd: Faster find. * fd -e go: Find all Go files. 3. fzf: Fuzzy finder. Pipe fd into fzf to open files instantly. 4. jq: JSON processor. Crucial for debugging API responses or logs (zap/slog JSON output).
Go-Specific Tools
gopsutil
If you are building system tools (agents, monitors), github.com/shirou/gopsutil is the standard for reading CPU/Memory/Disk stats cross-platform.
go fix Modernizers (New in 1.26)
Go 1.26 extends go fix modernizers and adds inline directives for local fixes.
go fix -modernize ./...Examples of what it fixes: * Replaces manual loops with slices.Contains. * Updates older interface{} to any. * Updates deprecated package usage.
Inline fix for one location:
//go:fix inline
func has(xs []string, x string) bool {
for _, v := range xs {
if v == x {
return true
}
}
return false
}govulncheck
The official vulnerability scanner.
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...It tells you if your code actually calls the vulnerable function in a dependency. This drastically reduces false positives compared to generic scanners (Dependabot).
Workflow Integration
Don’t run these manually. Put them in a Makefile or Taskfile (Task is a modern Make alternative written in Go).
# Taskfile.yaml
tasks:
audit:
cmds:
- go vet ./...
- staticcheck ./...
- govulncheck ./...Worked example
slices / maps modern stdlib helpers vs hand-rolled loops.
Save as util.go and util_test.go. Then:
go mod init example
go test -v// util.go
package main
import (
"maps"
"slices"
)
func has(xs []string, x string) bool {
return slices.Contains(xs, x)
}
func cloneMap(m map[string]int) map[string]int {
return maps.Clone(m)
}// util_test.go
package main
import "testing"
func TestHas(t *testing.T) {
xs := []string{"a", "b", "c"}
if !has(xs, "b") || has(xs, "z") {
t.Fatal("contains mismatch")
}
}
func TestCloneMap(t *testing.T) {
m := map[string]int{"x": 1}
c := cloneMap(m)
c["x"] = 9
if m["x"] != 1 {
t.Fatal("clone should be independent")
}
}Expected output:
=== RUN TestHas
--- PASS: TestHas (0.00s)
=== RUN TestCloneMap
--- PASS: TestCloneMap (0.00s)
PASS
More examples
Table of “grep-friendly” symbols your toolkit will find.
package main
import "fmt"
// AuditTarget is intentionally distinctive for rg demos.
func AuditTarget() string { return "ok" }
func main() {
fmt.Println(AuditTarget())
}# in a shell with ripgrep installed:
# rg 'func AuditTarget' -t go
go run .Runnable example
Note: Tools like
rg,staticcheck, andgovulncheckare external CLIs. This stdlib demo captures the modernizer idea: replace a hand-rolled loop withslices.Contains, and rungo vet-friendly code undergo test.
Save as modern.go and modern_test.go. Then:
go mod init example
go test -v
go vet ./...// modern.go
package main
import "slices"
// legacyHas is the pre-slices style many codebases still have.
func legacyHas(xs []string, x string) bool {
for _, v := range xs {
if v == x {
return true
}
}
return false
}
// modernHas is the 1.21+ equivalent (what go fix -modernize targets).
func modernHas(xs []string, x string) bool {
return slices.Contains(xs, x)
}// modern_test.go
package main
import "testing"
func TestHas(t *testing.T) {
xs := []string{"go", "rust", "zig"}
if !legacyHas(xs, "go") || !modernHas(xs, "go") {
t.Fatal("expected to find go")
}
if legacyHas(xs, "c") || modernHas(xs, "c") {
t.Fatal("did not expect c")
}
if legacyHas(xs, "rust") != modernHas(xs, "rust") {
t.Fatal("implementations diverged")
}
}Expected output:
=== RUN TestHas
--- PASS: TestHas (0.00s)
PASS
What to notice: Stdlib slices / maps replace a lot of utility code. Keep a small go test + go vet loop before pulling heavier linters. govulncheck still needs a separate install—run it in CI on real modules.
Try next: Run go fix -modernize ./... on a module that still uses interface{} and manual contains loops. Pipe JSON logs through jq in your shell.