You're debugging a Rails app. The bug is somewhere in process_payment. You could:
A) Open your IDE, wait for LSP to index, click through 12 files.
B) Paste the whole service into ChatGPT. Hope it doesn't hallucinate the call graph.
C) Run one command:
codetracer process_payment . --mode flow --scope
In under a second: where it's defined, every call site with enclosing method, every assignment, every place the return value goes — across all files.
I chose option C. So I built it.
What is codetracer?
A single-file bash script (~1400 lines) that traces symbols across Ruby and JavaScript/TypeScript codebases. No IDE, no LSP, no AI agent.
Requirements: bash 4+ (or zsh 5+) and ripgrep. That's it.
# Install
curl -fsSL https://raw.githubusercontent.com/mirzalazuardi/codetracer/main/codetracer.sh \
-o /usr/local/bin/codetracer && chmod +x /usr/local/bin/codetracer
The Killer Feature: Case-Variant Expansion
Type a symbol in any naming convention. codetracer finds all forms automatically:
Input: processPayment
Searches simultaneously:
process_payment (snake_case)
PROCESS_PAYMENT (SCREAMING_SNAKE)
processPayment (camelCase)
ProcessPayment (PascalCase)
process-payment (kebab-case)
All these commands produce identical results:
codetracer process_payment
codetracer processPayment
codetracer ProcessPayment
No more "I searched for process_payment but the JS file uses processPayment" moments.
Five Modes for Different Questions
1. Where is it defined?
codetracer PaymentService . --mode def
Finds def, class, module, function, const =, async function, etc.
2. Who calls it?
codetracer send_email . --mode call --scope
Every call site with full scope chain:
app/services/order_service.rb:42: send_email(user, receipt)
scope -> mod Billing > cls OrderService > def complete_order:38
3. How does data flow?
codetracer current_user . --mode flow
Tracks: assignments, passed as argument, returned/yielded, mutations.
4. Which files touch it?
codetracer stripe_key . --mode file
File map with hit counts per file.
5. Everything at once
codetracer order_total . --mode full --scope
NEW: Rails Route Tracing
Just shipped this week. Trace a route through the full request lifecycle:
codetracer --action "OrdersController#refund" ./app
Output:
━━━ ROUTE TRACE: OrdersController#refund ━━━
OrdersController (app/controllers/orders_controller.rb)
├── before_action :authenticate_user! :23 [ApplicationController]
├── before_action :set_order :8
├── def refund :67
│ ├── params: :reason :68 [query]
│ ├── if @order.refundable? :69
│ │ ├── call: RefundService.call :70
│ │ └── enqueue: RefundNotificationJob :75 [async]
│ └── else :77
│ └── render json: errors :78
└── after_action :log_refund_attempt :10
Callbacks, conditionals, params access, service calls, Sidekiq jobs — all in one tree. Perfect for understanding unfamiliar Rails controllers or creating sequence diagrams.
Trace directly from your curl files
Got a directory of curl scripts for testing your API? Point codetracer at them:
# Your existing curl file
cat api_tests/refund.sh
curl -X POST "http://localhost:3000/orders/123/refund?notify=true" \
-H "Content-Type: application/json" \
-d '{"reason": "damaged", "amount": 50.00}'
# Trace it
codetracer --route api_tests/refund.sh ./app
codetracer automatically:
- Extracts the HTTP verb (
POST) and path (/orders/:id/refund) - Converts numeric IDs to
:id(123 → :id) - Extracts query params (
notify) and JSON body keys (reason,amount) - Shows expected params in the trace output
Expected params from curl:
query: notify
body: reason amount
├── def refund
├── params: :reason [query] ← matches!
No more wondering "does this endpoint actually use the params I'm sending?"
Why Not Just Use AI?
I use AI daily. But every question you ask about code structure — "where is this defined?", "who calls this?" — costs tokens, takes seconds, and might be wrong.
codetracer gives those answers instantly, deterministically, for free.
Save Tokens When You Do Use AI
Instead of pasting 500-line files, extract only what matters:
# Get 60 focused lines instead of 2000+ raw lines
codetracer process_payment . --mode flow --scope 2>&1 | head -60
That's a 30x reduction in tokens.
The Scope Chain
This is my favorite feature. Every match shows its full nesting context:
codetracer process_payment . --mode call --scope
fixture_payment_service.rb:40: process_payment(order, order.amount)
scope -> mod Billing:5 > cls PaymentService:6 > def batch_process:38 > blk each:39
You instantly know: this call happens inside an each block, inside batch_process method, inside PaymentService class, inside Billing module.
No clicking through files. No mental stack tracing.
Works on macOS and Linux
Tested on:
- macOS with bash 4+ or zsh 5+
- Ubuntu/Debian
- Arch Linux
# macOS
brew install ripgrep
# Ubuntu
sudo apt install ripgrep
# Optional: fzf for interactive mode, ctags for precise indexing
brew install fzf universal-ctags
Interactive Mode with fzf
codetracer render . --inter
Fuzzy-pick any match, preview context, press Enter to open in your editor.
Real Workflow Example
Debugging an unknown bug:
# 1. Where does this symbol exist?
codetracer process_payment . --mode file
# 2. Read the definition
codetracer process_payment . --mode def --ctags
# 3. Find callers and their context
codetracer process_payment . --mode call --scope
# 4. Trace data through the system
codetracer process_payment . --mode flow --scope --ctx 4
Four commands. Full picture. No IDE required.
Try It
# One-liner install
curl -fsSL https://raw.githubusercontent.com/mirzalazuardi/codetracer/main/codetracer.sh \
-o /usr/local/bin/codetracer && chmod +x /usr/local/bin/codetracer
# Try it
codetracer YourClassName ./your-project --mode def
Links
GitHub: github.com/mirzalazuardi/codetracer
If this looks useful, I'd appreciate a star. And if you have feature requests or find bugs, issues and PRs are welcome.
Built for engineers who prefer a terminal over a GUI and a shell script over a language server.
Top comments (2)
this is clever. i have been burning tokens sending whole files to Claude when really i only need the relevant call chain. been doing poor mans version of this with grep/ctags but it is janky. the token savings argument alone sold me - context windows feel infinite until they suddenly arent and you are paying for it in latency and cost. will try this on a rails monolith i am working on, curious how it handles deep inheritance.
The zero-setup portability is the most compelling angle here — being able to drop a single bash script into a bare Docker container or a CI runner where you can't install gems, language servers, or IDE tooling is a real use case. I've been in that situation before.
Curious whether you've tried pairing this with an LLM coding tool like Claude Code, which does similar structural searching (grep, glob, file reads) but with semantic understanding on top. I wonder if codetracer's deterministic output could be useful as a pre-filter — feeding its focused trace into an LLM prompt rather than raw files — or whether the LLM's own search tools make that redundant in practice?