Module 2: Agentic Research with the Explore Subagent

Semantic Search vs Grep — Finding by Meaning

Semantic Search vs Grep — Finding by Meaning

Capsule description

So far you've used Explore as a read-only investigation tool. But there's a capability that separates it radically from any traditional search tool: semantic search. While grep searches for exact text, Explore understands meaning. And that difference completely changes how you find code in a codebase.

In this capsule you're going to experience the difference first-hand. You're going to search for the same thing with grep and with Explore, compare results, and understand when to use each. It's not about replacing grep — it's about having two complementary tools and knowing when each one is superior.

The connection with the project is direct: in capsule 05 you're going to answer questions about a codebase using Explore. Many of those questions require semantic search — "where are inputs validated?" isn't something grep solves well. This capsule gives you the ability to find code by what it does, not just by what it's called.


The Problem with Text Search

Why grep isn't enough

grep is an extraordinary tool. It's been the standard for searching text in files for decades. But it has a fundamental limitation: it searches for strings, not concepts.

# You search for where user inputs are validated
grep -r "validate" src/

# Results:
# src/utils/helpers.py:    # validate email format
# src/models/user.py:      def validate_name(self):
# src/tests/test_validation.py: class TestValidate:

You found 3 results. But what if the real validation is in functions called check_params, sanitize_input, ensure_valid, or verify_data? grep doesn't find them because it searches for the word "validate", not the concept of validation.

The gap between text and meaning

# ALL of these functions do input validation
# But none contains the word "validate"

def check_params(request_data: dict) -> bool:
    """Check that the required parameters are present."""
    required = ["name", "email", "age"]
    return all(key in request_data for key in required)

def sanitize_input(raw_text: str) -> str:
    """Clean text of dangerous characters."""
    import html
    return html.escape(raw_text.strip())

def ensure_valid_age(age: int) -> int:
    """Confirm that the age is in a reasonable range."""
    if not 0 < age < 150:
        raise ValueError(f"Age out of range: {age}")
    return age

def verify_email_format(email: str) -> bool:
    """Check that the email has the correct format."""
    import re
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

If you search grep -r "validate", none of these functions show up. But they're all exactly what you need. This is the gap between text search and meaning search.


Semantic Search with Explore

How it works

When you ask Explore something like "where are user inputs validated?", it doesn't search for the word "validate." It understands you're asking about code that verifies, cleans, confirms, or rejects input data. It searches by concept, not by string.

Direct demo: the same question, two tools

# With grep:
$ grep -rn "validat" src/
src/utils/helpers.py:45:    # validate email format
src/models/user.py:23:      def validate_name(self):

# 2 results. Incomplete.
# With Explore in Claude Code:
> "Use Explore to find where user inputs are validated
   in this project"

# Explore finds:
# 1. src/utils/helpers.py:45 - validate_email() — format validation
# 2. src/models/user.py:23 - validate_name() — name validation
# 3. src/middleware/sanitizer.py:12 - sanitize_input() — input cleaning
# 4. src/api/validators.py:8 - check_params() — parameter verification
# 5. src/services/user_service.py:67 - ensure_valid_age() — age range
# 6. src/middleware/auth.py:34 - verify_token() — auth token validation
#
# Explore also reports:
# "Input validation happens in 3 layers:
#  1. Middleware (sanitizer.py) - general cleaning
#  2. API validators (validators.py) - required parameters
#  3. Model level (user.py, user_service.py) - business rules"

# 6 results + layer analysis. Complete.

The difference isn't marginal — it's fundamental. Explore found three times as many results AND provided architectural context.

Progressive example: from simple to complex

Basic level — Search for a functionality:

# Prompt to Claude Code:
> "Use Explore to find where payments are handled
   in this project"

# Explore searches by the meaning "payment handling":
# - src/services/payment_service.py — main payment logic
# - src/api/routes/checkout.py — checkout endpoints
# - src/models/transaction.py — transaction model
# - src/integrations/stripe_client.py — Stripe integration
# - src/utils/currency.py — currency conversion

Intermediate level — Search for a pattern:

# Prompt to Claude Code:
> "Use Explore to find where the retry pattern
   is implemented in this project"

# Explore understands "retry pattern" as a concept:
# - src/utils/retry.py — @retry decorator with exponential backoff
# - src/integrations/api_client.py:89 — manual retry with loop and sleep
# - src/services/email_service.py:34 — send retry with a counter
# - src/config/settings.py:78 — MAX_RETRIES, RETRY_DELAY configured
#
# Note: "There are 2 retry implementations: one centralized
#  (retry.py) and one ad-hoc (api_client.py). The ad-hoc one doesn't
#  use the centralized decorator."

Advanced level — Search for an abstract concept:

# Prompt to Claude Code:
> "Use Explore to find possible security vulnerabilities
   in the handling of user data"

# Explore analyzes semantically:
# - src/api/routes/users.py:45 — password logged in debug mode
# - src/middleware/cors.py:12 — CORS allows origin "*" (wildcard)
# - src/utils/crypto.py:23 — uses MD5 for hashing (deprecated, insecure)
# - src/services/user_service.py:89 — concatenated SQL without parameterizing
# - src/config/settings.py:5 — SECRET_KEY hardcoded in the file

Comparison: grep vs Semantic Search

Criteriongrep / ripgrepExplore (semantic)
Searches byExact text / regexMeaning / concept
SpeedMillisecondsSeconds (requires an LLM)
ResultsLines with a matchFiles + context + analysis
False positivesMany (comments, strings, similar names)Few (understands context)
False negativesMany (synonyms, abstractions)Few (understands synonyms)
When to useYou know the exact nameYou know what it does but not what it's called
CostFree, localAPI tokens
MultilingualDoesn't understand languageUnderstands questions in any language

When to use each?

Use grep when:

  • You know the exact name: grep -r "PaymentService" src/
  • You're searching for a literal string: grep -r "TODO:" src/
  • You're searching for a specific import: grep -r "from stripe" src/
  • You need maximum speed (millions of files)
  • You're searching for an exact error: grep -r "Error: connection refused" logs/

Use Explore when:

  • You know what the code does but not what it's called: "where is authentication handled?"
  • You're searching for an abstract concept: "where are there possible memory leaks?"
  • You need context in addition to location: "how does the cache system work?"
  • You're searching for code that implements a pattern: "where is the observer pattern used?"
  • You need to understand relationships: "what components depend on the auth module?"

Key trade-off: grep is free and fast but limited to text. Explore costs tokens and takes longer but understands meaning. In professional practice, you use both: grep for fast exact searches, Explore for deep investigation.


Advanced Semantic Search Techniques

Technique 1: Search by behavior

Instead of searching by name, search by what the code does:

# Instead of: grep -r "cache" src/
# Ask:
> "Use Explore to find code that stores results
   to avoid repeated calculations"

# Finds: functions with memoize, @lru_cache, dict lookups
# that act as cache, Redis calls, and instance variables
# that store previous results — many without the word "cache"

Technique 2: Search by impact

Search for code that affects a specific resource:

# Instead of: grep -r "database\|db\|sql" src/
# Ask:
> "Use Explore to find all code that reads or writes
   to the database"

# Finds: direct queries, ORM calls, migrations,
# seeders, and functions that indirectly cause queries
# through lazy loading

Technique 3: Negative search

Search for the absence of something:

# You can't do this with grep
# Ask:
> "Use Explore to find endpoints that do NOT have
   authentication"

# Finds: public routes that should be private,
# endpoints missing auth middleware, admin routes
# without permission checks

Technique 4: Comparative search

Search for inconsistencies:

# Ask:
> "Use Explore to find functions that handle errors
   differently from the project's dominant pattern"

# Finds: functions that use print() instead of logger,
# that silence exceptions with a bare except,
# or that return None instead of raising

Combining grep and Explore

The professional workflow

The best results come from combining both tools:

# Step 1: Explore for broad investigation
> "Use Explore to understand how the notification
   system works"

# Explore reports:
# "The notification system uses a pub/sub pattern.
#  The publishers are in src/events/, the subscribers
#  in src/handlers/, and the configuration in src/config/
#  notifications.yaml. The main class is EventBus
#  in src/core/event_bus.py"

# Step 2: grep for specific details
$ grep -rn "EventBus" src/
# src/core/event_bus.py:5: class EventBus:
# src/api/routes/orders.py:12: from core.event_bus import EventBus
# src/services/payment_service.py:8: from core.event_bus import EventBus
# ... (exact list of every file that uses it)

# Step 3: Explore for deep analysis
> "Use Explore to analyze whether there are events that are
   published but nobody listens to (dead events)"

The sequence is: Explore (broad view) → grep (exact details) → Explore (deep analysis).

Complete example: investigate the auth system

# 1. General view with Explore
> "Use Explore to explain how
   authentication works in this project"

# Explore reports: JWT with refresh tokens, middleware
# in src/middleware/auth.py, user model in src/models/,
# login endpoint in src/api/routes/auth.py

# 2. Grep to find all the protected routes
$ grep -rn "@require_auth\|@login_required" src/api/
# Exact list of 23 protected endpoints

# 3. Explore to find gaps
> "Use Explore to find endpoints in src/api/routes/
   that access user data but don't have an authentication
   decorator"

# Explore finds 2 endpoints without protection that should
# have it — this is a real security finding

Connection with the Project

In the Module Project (capsule 05) you're going to answer specific questions about a codebase using Explore. Many of those questions are semantic by nature:

  • "How does a login request flow from the endpoint to the database?" — requires a behavior search
  • "Where are user inputs validated?" — requires a concept search
  • "What dependencies does the payments module have?" — requires a relationship search

Without semantic search, these questions require grep + manually reading dozens of files. With Explore, you get direct answers with context.


Troubleshooting

Problem 1: Explore doesn't find what I'm looking for

Cause: The prompt is too vague or uses different terminology from the codebase.

Solution: Be more specific and try synonyms:

# Vague:
> "Use Explore to find the security"

# Specific:
> "Use Explore to find where user permissions
   are checked before accessing resources"

Problem 2: Explore returns too many results

Cause: The question is too broad.

Solution: Narrow it down by directory or module:

# Too broad:
> "Use Explore to find error handling"

# Narrowed:
> "Use Explore to find error handling
   in src/api/routes/ — specifically HTTP errors"

Problem 3: grep is faster for my case

Cause: You're searching for an exact string you know.

Solution: Use grep. Not everything requires semantic search:

# For this, grep is superior:
grep -rn "from fastapi import" src/
# Instant, exact, complete result

Problem 4: I don't know whether to use grep or Explore

Cause: You haven't defined whether you're searching for text or a concept.

Solution: Ask yourself this question: "Do I know the exact name of what I'm looking for?"

  • Yes → grep
  • No, but I know what it does → Explore

Problem 5: Explore interprets my question incorrectly

Cause: Ambiguity in the prompt.

Solution: Add context:

# Ambiguous:
> "Use Explore to find the model"

# With context:
> "Use Explore to find the data model
   (ORM/database model) for users, not the machine
   learning model"

Exercises

Exercise 1: Identify the right tool (Easy)

For each search, decide whether you'd use grep or Explore and explain why:

  1. Find all the files that import requests
  2. Find where rate limiting is implemented
  3. Find the definition of the UserService class
  4. Find possible SQL injections
  5. Find all the TODOs in the project
See solution
  1. grep — searches for the exact string from requests import / import requests
  2. Explore — "rate limiting" can be implemented as a decorator, middleware, or counter without using that phrase
  3. grep — searches for the exact string class UserService
  4. Explore — SQL injection shows up as string concatenation in queries, f-strings with SQL, etc. — there's no single string to search for
  5. grep — searches for the exact string TODO

General rule: if you can write the regex, use grep. If you need to describe the concept, use Explore.

Exercise 2: Reformulate for semantic search (Easy)

Convert these grep searches into semantic questions for Explore:

  1. grep -r "try.*except" src/
  2. grep -r "sleep\|time.sleep" src/
  3. grep -r "os.environ\|getenv" src/
See solution
  1. grep "try.*except" → "Use Explore to find functions that handle errors and exceptions, including those that use conditionals to detect errors without try/except"
  2. grep "sleep" → "Use Explore to find code that introduces delays, waits, or throttling, including asyncio.sleep, polling loops, and rate limiters"
  3. grep "os.environ" → "Use Explore to find where environment configurations are read, including environment variables, .env files, config files, and secrets managers"

Note: the semantic version finds more because it includes synonyms and variants that grep doesn't capture.

Exercise 3: Combined search (Medium)

You have a Django project and you need to understand the permission system. Design a 3-step sequence combining grep and Explore:

See solution
# Step 1: Explore for a general view
> "Use Explore to explain how the permission
   system works in this Django project. Does it use Django's
   built-in system, a package like django-guardian,
   or a custom implementation?"

# Step 2: grep to map exact usage
$ grep -rn "@permission_required\|has_perm\|user_passes_test" src/
$ grep -rn "PermissionMixin\|BasePermission" src/

# Step 3: Explore to find gaps
> "Use Explore to find views in src/views/
   that access sensitive data but don't check
   permissions in any way"

The logic: Explore gives the big picture, grep gives the exact numbers, Explore finds what's missing.

Exercise 4: Negative search (Medium)

Write 3 questions for Explore that search for the absence of something (things that should exist but don't):

See solution
  1. "Use Explore to find public functions in src/api/ that don't have docstrings"
  2. "Use Explore to find database models that don't have validation on their fields"
  3. "Use Explore to find endpoints that accept user input but don't sanitize it before processing it"

Why this matters: negative search is impossible with grep (you can't search for something that isn't there). It's one of the most valuable capabilities of semantic search for code auditing and vulnerability detection.

Exercise 5: Real case — Onboarding with mixed search (Hard)

You're onboarding to a 15K-line Python project. Design an investigation plan of 10 searches (a mix of grep and Explore) to understand:

  • How authentication works
  • Where the entry points are
  • What database it uses and how it's accessed
See solution
# Authentication (4 searches):
1. Explore: "How does authentication work in this project?
   JWT, sessions, OAuth, API keys?"
2. grep: grep -rn "SECRET_KEY\|JWT\|token" src/config/
3. Explore: "Which endpoints require authentication and which are public?"
4. grep: grep -rn "@auth\|@login\|@require" src/api/

# Entry points (3 searches):
5. grep: grep -rn "if __name__\|app.run\|uvicorn" src/
6. Explore: "What are all the entry points of this application?
   Include CLI, web, workers, and scheduled tasks"
7. grep: grep -rn "@app.route\|@router" src/

# Database (3 searches):
8. Explore: "What database does this project use and how does it connect?
   Does it use an ORM or raw queries?"
9. grep: grep -rn "DATABASE\|SQLALCHEMY\|psycopg\|pymongo" src/
10. Explore: "Are there raw SQL queries in the project? If so,
    is any of them vulnerable to SQL injection?"

Pattern: each area uses Explore for the general view and grep for the exact data. This produces a complete map in minutes, not hours.

Exercise 6: Build your cheatsheet (Hard)

Create a quick reference table with 10 common search scenarios, indicating for each: recommended tool, exact prompt/command, and what kind of result you expect.

See solution
ScenarioToolCommand/PromptExpected result
Find a class definitiongrepgrep -rn "class ClassName" src/Exact line + file
Understand data flowExplore"How does data flow from input to the DB?"Textual flow diagram
Find a module's importsgrepgrep -rn "from module import" src/List of files
Find vulnerabilitiesExplore"Are there security vulnerabilities in src/api/?"List with context
Find TODOsgrepgrep -rn "TODO|FIXME|HACK" src/List with lines
Understand error handlingExplore"How does this project handle errors? Is there a consistent pattern?"Pattern analysis
Find tests of a functiongrepgrep -rn "test_function_name" tests/Test files
Find dead codeExplore"Are there functions that are defined but never called?"List of dead code
Find config valuesgrepgrep -rn "KEY_NAME" src/config/Exact values
Understand dependenciesExplore"What does module X depend on? What depends on X?"Dependency graph

Principle: grep for what you can name, Explore for what you can describe.


Summary

In this capsule you learned:

  • grep searches for text, Explore searches for meaning — it's the fundamental difference between the two tools
  • Explore finds synonyms and variants that grep can't: check_params, sanitize_input, ensure_valid are all "validation" to Explore
  • Semantic search has 4 advanced techniques: by behavior, by impact, negative, and comparative
  • Negative search is exclusive to Explore — you can't search for the absence of something with grep
  • The professional workflow combines both: Explore (broad view) → grep (exact details) → Explore (deep analysis)
  • The decision rule is simple: do you know the exact name? grep. Do you know what it does but not what it's called? Explore

Next capsule: Exploration Patterns — Top-Down, Dependency-Following, Feature-Tracing. You're going to learn three systematic strategies to investigate codebases with Explore, each optimized for a different type of question.


Additional Resources

  1. Claude Code Documentation - Subagents - Official reference on how subagents work, including Explore
  2. ripgrep (rg) - Documentation - The modern alternative to grep, faster and with better UX
  3. The Art of Searching Code - Sourcegraph Blog - Perspectives on searching code at scale
  4. Semantic Code Search - Papers with Code - Research on semantic code search
  5. grep vs ripgrep vs ag - Benchmarks - Comparison of text search tools
  6. How LLMs Understand Code - Anthropic Research - How language models understand source code

Module 2, Capsule 03 — Refactoring & Legacy Code with Claude Code Guide