I've been looking into a more structured approach to prompt engineering that moves away from prose and toward call graph planning. Specifically, I'm fascinated by how developers like Dillon Mulroy and Rin (r17x) have been using call stacks to build technical specs. Instead of a paragraph of text, they use a hierarchy of function calls. This makes it incredibly easy to review the AI's logic and spot potential failures before a single line of actual code is written.
The Effect TypeScript Mental Model #
If you want to take this to a professional level, you should look at adopting a design thinking model based on the Effect TypeScript library. Effect is essentially a standard library that makes asynchronous code, error handling, and dependency injection 100% type-safe and predictable.
When you force an LLM to plan using an "Effect-style" mental model, you are essentially forcing it to categorize every single step into three specific channels:
A (The Happy Path): This forces the AI to map out the core domain logic. It focuses purely on what the function returns and how data moves through the system, without getting distracted by edge cases initially.E (Failure Modes): This is where most AI plans fail. Instead of saying "handle errors," you force the AI to categorize errors into Retry (transient issues), Escape (recoverable/expected errors), and Die (actual bugs or panics). This mental model is universal across almost all programming languages.R (Requirements/Dependencies): This identifies exactly what a function needs to run. By declaring dependencies upfront, you prevent the AI from creating "hidden" dependencies that make testing a nightmare.
Implementing Call Graph Planning #
The workflow looks something like this:
X β Graph β Effect
β β β β β
β β β β ββ what each node needs
β β β βββββ where the graph breaks
β β ββββββββ what flows through nodes
β β
β ββ nodes = functions, edges = data flow
β
ββ the problem: what youβre trying to build
Instead of asking an LLM to "Plan a user authentication system," you should instruct it to generate a call graph using the A, E, and R framework. This forces the model to think linearly.
A standard production call graph might look like this:
Production:
HTTP Handler β UserService.getUser β UserRepo.findById β PostgresDB
And a test implementation would look like this:
Tests:
HTTP Handler β UserService.getUser β UserRepoMock
Why this actually works for LLM agents #
When I benchmark different LLM agents for complex coding tasks, the ones that perform best are the ones that can maintain a high level of structural integrity. By requiring a call graph, you gain three massive advantages in your AI workflow:
-
Data Flow Mapping (A): You can immediately see the relationship between functions and the structure of the final output.
-
Error Identification (E): You catch where the logic might breakβdeciding whether to retry or fall backβduring the planning phase rather than the debugging phase.
-
Dependency Injection (R): You ensure every function declares its requirements openly, making the resulting code much easier to unit test.
The real win here is the speed of the code review. If the call graph is convoluted or the error handling is missing, you can correct the AI immediately. It is much cheaper to fix a flawed graph than it is to refactor 200 lines of broken TypeScript.
Next Cloudinary's credit system is a massive trap for anyone running β