We tested how Claude explains methods in the Flask codebase, comparing standard context vs. Nuanced call graph context. Our results demonstrated significant improvements in token usage, cost, and response quality.
Using this issue to document specific experiment scenarios.
Experiment Setup
We selected the full_dispatch_request method from Flask's core request handling pipeline and asked Claude to explain it under two conditions:
- Without Nuanced: Claude searches and reads raw code
- With Nuanced: Claude was provided with the call graph generated by Nuanced
Key Results
| Metric |
Without Nuanced |
With Nuanced |
Improvement |
| Token Usage/Cost |
$0.1579 |
$0.1025 |
35% reduction |
| Response Time |
37.2s |
43.1s |
Comparable |
| Explanation Quality |
Surface-level Missing call relationships |
Comprehensive pipeline explanation Clear control flow understanding |
Significantly better |
Analysis
When using the Nuanced call graph, Claude:
- Required 35% fewer tokens (direct cost savings)
- Provided a more structured and complete explanation of the request handling pipeline
- Clearly identified relationships between components
- Understood the entire flow from preprocessing to error handling to response finalization
Without Nuanced, Claude's explanation was accurate but lacked depth in understanding how the components worked together, focusing more on summarizing the code than explaining its execution flow.
Raw Outputs
without Nuanced (click to expand)
> can you explain the full_dispatch_request method to me?
⏺ I'll find and examine the full_dispatch_request method in the Flask codebase.
⏺ Search(pattern: "full_dispatch_request", include: "*.py")…
⎿ Found 2 files
⏺ Let's look at the implementation in the main Flask application code:
⏺ Read(file_path: "src/flask/app.py")…
⎿ from future import annotations
import collections.abc as cabc
... (+1534 lines)
⏺ Let me look for the full_dispatch_request method which is defined around line 904:
The full_dispatch_request method in Flask is responsible for handling the complete HTTP request-response cycle. Here's what it does:
- It marks the app as having received its first request with self._got_first_request = True
- It wraps the entire request handling process in a try-except block to catch any exceptions
- Within the try block:
- It sends the request_started signal
- Calls preprocess_request() which runs URL value preprocessors and before-request handlers
- If a before-request handler returned a value, it's used as the response
- Otherwise, it calls dispatch_request() to find and execute the view function for the route
- If any exception occurs during preprocessing or dispatching, it's caught and passed to handle_user_exception()
- Finally, it calls finalize_request() on the response value, which:
- Converts the view function's return value to a proper Response object
- Processes the response through any after-request handlers
- Sends the request_finished signal
This method is the main entry point for request handling in Flask and orchestrates the entire request pipeline, including error handling,
before/after request hooks, and view function execution.
With Nuanced (click to expand)
> can you explain the full_dispatch_method to me using its call graph: {
"src.flask.app.Flask.full_dispatch_request": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"src.flask.app.Flask.preprocess_request",
"src.flask.app.Flask.dispatch_request",
"src.flask.app.Flask.handle_user_exception",
"src.flask.app.Flask.finalize_request"
]
},
"src.flask.app.Flask.handle_user_exception": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"src.flask.app.Flask.handle_http_exception",
"src.flask.app.Flask.ensure_sync",
"<builtin>.isinstance"
]
},
"src.flask.app.Flask.preprocess_request": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"<builtin>.reversed",
"src.flask.app.Flask.ensure_sync"
]
},
"src.flask.app.Flask.finalize_request": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"src.flask.app.Flask.process_response",
"src.flask.app.Flask.make_response"
]
},
"src.flask.app.Flask.process_response": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"<builtin>.reversed",
"src.flask.app.Flask.ensure_sync",
"itertools.chain"
]
},
"src.flask.app.Flask.handle_http_exception": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"src.flask.app.Flask.ensure_sync",
"<builtin>.isinstance"
]
},
"src.flask.app.Flask.dispatch_request": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"src.flask.app.Flask.make_default_options_response",
"<builtin>.getattr",
"src.flask.app.Flask.raise_routing_exception",
"src.flask.app.Flask.ensure_sync"
]
},
"src.flask.app.Flask.make_response": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"<builtin>.isinstance",
"<builtin>.type",
"<builtin>.TypeError",
"<builtin>.len",
"<builtin>.callable"
]
},
"src.flask.app.Flask.ensure_sync": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"inspect.iscoroutinefunction",
"src.flask.app.Flask.async_to_sync"
]
},
"src.flask.app.Flask.raise_routing_exception": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"debughelpers.FormDataRoutingRedirect",
"<builtin>.isinstance"
]
},
"src.flask.app.Flask.async_to_sync": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": [
"asgiref.sync.async_to_sync",
"<builtin>.RuntimeError"
]
},
"src.flask.app.Flask.make_default_options_response": {
"filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
"callees": []
}
}
⏺ Let me examine the Flask.full_dispatch_request method and its call graph to explain how it processes requests.
⏺ Read(file_path: "src/flask/app.py")…
⎿ from future import annotations
import collections.abc as cabc
... (+1534 lines)
⏺ Based on the code examination, I can explain the full_dispatch_request method in Flask by following its call graph:
The Flask request handling pipeline centers around full_dispatch_request, which orchestrates the entire request-response cycle. Here's how
it works:
-
full_dispatch_request (line 904-921):
- Marks that the application has received its first request
- Sends the request_started signal
- Calls 4 main methods in sequence:
-
First calls preprocess_request:
- Processes URL values through registered preprocessors
- Executes all registered before_request functions in reverse order
- If any returns a non-None value, it skips to finalize_request directly
-
If preprocessing completes with no early response, calls dispatch_request:
- Checks for routing exceptions and raises them properly
- Handles automatic OPTIONS requests if configured
- Retrieves and calls the appropriate view function for the request
-
If an exception occurs at any point, calls handle_user_exception:
- Determines if the exception is an HTTP exception
- For HTTP exceptions, calls handle_http_exception
- For other exceptions, finds an appropriate error handler or re-raises
-
Finally, calls finalize_request:
- Converts the view function result to a Response using make_response
- Processes the response with process_response, which:
- Runs all registered after_request functions in reverse order
- Saves any session data to the response
- Sends the request_finished signal
The entire flow ensures proper request preprocessing, dispatching, error handling, and response finalization, maintaining Flask's
middleware-like functionality through its various hook points.
Conclusion
Providing AI coding assistants with structured call graph context rather than raw code delivers substantial benefits:
- Lower costs through reduced token usage
- Better understanding of code relationships and execution flow
- Higher quality explanations that focus on how code works, not just what it does
This experiment confirms our thesis that AI coding tools need rich, structured context rather than just more raw code.
We tested how Claude explains methods in the Flask codebase, comparing standard context vs. Nuanced call graph context. Our results demonstrated significant improvements in token usage, cost, and response quality.
Using this issue to document specific experiment scenarios.
Experiment Setup
We selected the
full_dispatch_requestmethod from Flask's core request handling pipeline and asked Claude to explain it under two conditions:Key Results
Missing call relationships
Clear control flow understanding
Analysis
When using the Nuanced call graph, Claude:
Without Nuanced, Claude's explanation was accurate but lacked depth in understanding how the components worked together, focusing more on summarizing the code than explaining its execution flow.
Raw Outputs
without Nuanced (click to expand)
With Nuanced (click to expand)
Conclusion
Providing AI coding assistants with structured call graph context rather than raw code delivers substantial benefits:
This experiment confirms our thesis that AI coding tools need rich, structured context rather than just more raw code.