What you'll build: A browser-based tool where developers paste C# or Blazor code and ask "why" questions. The AI returns a structured 4-block answer — The Decision, Why This Pattern, If You Used The Alternative (with real runnable code), and When You'd Choose Differently. Includes client-side concept chip detection, session tracking, follow-up question generation, and related concept lookup.

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 not Action<T>?
  • Why fetch data in OnInitializedAsync and not a field initializer?
  • Why AddScoped and not AddSingleton for this service?
  • Why Expression<Func<T,bool>> and not just Func<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.
Key Takeaways
  • 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.