This post covers the full build: the server route, the system prompt engineering, the chip detection logic, the answer parsing, and the session tracking. Everything is live at rrskillverse.in/tools/code-decision-explainer.
The Problem No Tool Currently Solves
Every existing AI code tool answers "what" — what does this code do? GitHub Copilot, ChatGPT, Cursor — all excellent at explaining syntax. But intermediate developers hit a completely different wall.
Here is the real scenario. A developer is studying an employee dashboard built in Blazor. They see this in the code:
// Program.cs
builder.Services.AddScoped<IEmployeeService, FakeEmployeeService>();
// EmployeeDashboard.razor
@inject IEmployeeService EmployeeService
They know what an interface is. They can read the syntax. What they cannot figure out is: why inject the interface and not the concrete class? Why not write @inject FakeEmployeeService EmployeeService? It would compile. It would run. What breaks if you skip the interface?
The answer involves Dependency Inversion, testability, swappability in DI registrations, and the specific coupling problem that injecting concrete types creates. No existing tool explains that combination in a way that is specific to this code and this context. They either give a textbook definition of interfaces or a generic explanation that does not address the question a developer actually has.
The same pattern repeats constantly in .NET and Blazor development:
- Why
EventCallback<T>and notAction<T>? - Why fetch data in
OnInitializedAsyncand not a field initializer? - Why
AddScopedand notAddSingletonfor this service? - Why
Expression<Func<T,bool>>and not justFunc<T,bool>?
These are all decision questions. They require a senior developer's reasoning, not a reference definition. That is the gap this tool fills.
How the Tool Works
The tool has three input surfaces and four output blocks.
Input 1: Code textarea. The developer pastes any C# class, Blazor component, or .NET snippet. As they type, a debounced function runs detectChips() against the pasted code using regex patterns.
Input 2: Concept chips. The chip detection function identifies known architectural patterns — interface declarations, EventCallback usage, generic constraints, DI registrations, lifecycle methods, CancellationTokens. For each pattern found, it surfaces a pre-written "why" question as a tappable chip. Tapping a chip fills the question field and triggers the explain call immediately.
Input 3: Manual question field. For questions the chip detector does not cover. The developer types a free-form question like "why is IsSelected a Parameter instead of a private bool?"
The AI response is parsed into four structured blocks and rendered in colour-coded cards:
- The Decision (purple) — what was chosen in plain English
- Why This Pattern (teal) — the actual reasoning
- If You Used The Alternative (amber) — real runnable code showing what breaks
- When You'd Choose Differently (coral) — the honest answer on when the alternative is fine
After the first answer, three follow-up questions are generated and shown as chips. A session panel builds up a list of concepts the developer has asked about, with a confidence indicator and a copy button.
The System Prompt — Engineering the AI Reasoning
The system prompt is the most important part of this tool. The constraint that makes it work:
The developer already knows the SYNTAX. They do NOT need definitions.
They need to understand WHY a specific pattern was chosen in a specific context.
This single line forces every response to be decision-focused. Without it, the AI defaults to explaining what an interface is. With it, every response addresses why this particular interface was used in this particular injection point.
The "show alternative as real runnable code" instruction is equally critical. This is the structure the model follows for every answer:
**THE DECISION**
One to two sentences. What was chosen and what it means in plain English.
**WHY THIS PATTERN**
Two to four sentences. The actual reasoning — what problem it solves,
what benefit it provides here specifically.
**IF YOU USED THE ALTERNATIVE**
Show the actual alternative code (short snippet, max 8 lines).
Then one sentence explaining what breaks or becomes harder.
**WHEN YOU'D CHOOSE DIFFERENTLY**
One to two sentences. The honest answer — when the alternative is fine.
Forcing the AI to demonstrate the alternative concretely makes the trade-off tangible. Developers can look at the alternative, understand what changes, and decide whether the original pattern is justified in their specific case. That is fundamentally more useful than "without an interface you lose flexibility."
Building It — Key Implementation Points
The Server Route
The route lives in server.js and uses the shared callAzure() helper. It handles two modes: standard explain and follow-up generation, controlled by the followupMode flag in the request body.
app.post('/api/code-decision-explain', aiToolLimiter, async (req, res) => {
const { code, question, includeAnalogy, followupMode } = req.body;
if (followupMode) {
// Lightweight call — max 200 tokens, returns JSON array of 3 strings
const raw = await callAzure([{ role: 'user', content: followupPrompt }], 200);
const followups = JSON.parse(raw.trim());
return res.json({ followups });
}
// Main explain call — 1200 tokens, structured 4-block response
const answer = await callAzure([
{ role: 'system', content: systemPrompt },
{ role: 'user', content: 'Code:
' + code + '
Question: ' + question }
], 1200);
res.json({ answer });
});
Chip Detection
The chip detection runs entirely client-side on every keystroke (debounced 400ms). It uses regex to match known .NET and Blazor patterns and surfaces pre-written questions for each:
function detectChips(code) {
var chips = [];
if (/interfaces+I[A-Z]/.test(code) || /@injects+I[A-Z]/.test(code))
chips.push({ label: 'Why interface and not a class?',
q: 'Why was an interface used here instead of injecting the concrete class directly?' });
if (/EventCallback/.test(code))
chips.push({ label: 'Why EventCallback and not Action?',
q: 'Why is EventCallback<T> used here instead of Action<T> or Func<T>?' });
// ... 8 more patterns ...
if (chips.length === 0 && code.trim().length > 20)
chips.push({ label: 'Explain the design decisions',
q: 'What are the key design decisions in this code and why were they made?' });
return chips.slice(0, 5);
}
The fallback chip — "Explain the design decisions" — ensures the tool always has something useful to offer, even when the pasted code does not match any known pattern.
Answer Parsing and 4-Block Rendering
The AI returns the four blocks delimited by bold markers. The parser splits the response on these markers and renders each section into its colour-coded block. Code blocks inside the response (triple-backtick fences) are detected, HTML-escaped, and wrapped in a monospace div with white-space: pre-wrap.
Follow-Up Generation
After each answer, a second small API call generates three follow-up questions. This call runs asynchronously so the answer is visible immediately while the follow-ups load in the background:
// Show answer immediately
answerContent.innerHTML = renderAnswer(answer);
// Follow-ups arrive asynchronously
callFollowups(code, question).then(function(followups) {
if (followups.length > 0) renderFollowups(followups);
});
Session Tracking
Every question adds an entry to the session panel with a visual confidence dot. The session can be copied as plain text — useful for a developer reviewing what they asked during a study session.
Try It Live
The tool is live at rrskillverse.in/tools/code-decision-explainer. Try these three questions with the built-in examples:
- Load the Interface + DI example → tap "Why interface and not a class?"
- Load the EventCallback example → tap "Why EventCallback and not Action?"
- Load the Repository example → tap "Why Expression<Func> not just Func?"
What Comes Next
- C++, Python, SQL support. The chip detection patterns and question set need extending per language. The system prompt adjusts to match the language context.
- Saving sessions to Supabase. Right now sessions only persist for the browser tab. Adding a user token and Supabase row would let developers revisit their question history.
- Weekly "most asked decisions" post. Session logs (anonymised) can drive a weekly blog post: "The 5 .NET decisions developers asked about most this week." Pure signal from real developer confusion.
- The gap between "what code does" and "why it was written this way" is the most under-served problem in developer tooling today.
- A single constraint in the system prompt — "they know the syntax, explain the decision" — completely changes the quality of AI responses.
- Forcing the AI to show real alternative code (not pseudocode) makes the trade-off tangible and testable.
- Client-side chip detection with regex is fast, free, and surfaces the exact questions developers have without requiring them to articulate the confusion themselves.
- Follow-up generation as a separate lightweight call (200 tokens) keeps the main response fast while adding genuine learning depth.
- The 4-block format — Decision / Why / Alternative / When Different — is a reusable structure for any tool that needs to explain architectural choices rather than syntax.