<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Karam Khoury</title>
    <description>The latest articles on DEV Community by Karam Khoury (@karamkhoury88).</description>
    <link>https://dev.to/karamkhoury88</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4045189%2Fe44cb307-6c08-4fc0-8ff8-6bf3fa1dbfcc.png</url>
      <title>DEV Community: Karam Khoury</title>
      <link>https://dev.to/karamkhoury88</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/karamkhoury88"/>
    <language>en</language>
    <item>
      <title>Give Your .NET REST API an AI Mouth: Adding MCP So Claude and Gemini Can Actually Use It</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Wed, 02 Sep 2026 10:18:11 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/give-your-net-rest-api-an-ai-mouth-adding-mcp-so-claude-and-gemini-can-actually-use-it-3fi4</link>
      <guid>https://dev.to/karamkhoury88/give-your-net-rest-api-an-ai-mouth-adding-mcp-so-claude-and-gemini-can-actually-use-it-3fi4</guid>
      <description>&lt;h3&gt;
  
  
  A production engineer's guide to wrapping an existing ASP.NET Core API with the Model Context Protocol — without tearing out your JWT stack.
&lt;/h3&gt;




&lt;p&gt;Last quarter a product manager dropped a Slack message that a lot of us are getting now: &lt;em&gt;"Can I just ask Claude to pull the open invoices from our system?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We already had the API. Fully built, JWT-secured, battle-tested in production. The problem was never the data — it was that an LLM has no idea our &lt;code&gt;/api/invoices?status=open&lt;/code&gt; endpoint exists, and even if it did, it can't read our OpenAPI spec and authenticate itself.&lt;/p&gt;

&lt;p&gt;The Model Context Protocol (MCP) is the missing adapter. This is how you bolt it onto an API you already own, in an afternoon, and what to do about authentication before security reviews it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Concept: MCP Is an Adapter, Not a Rewrite
&lt;/h2&gt;

&lt;p&gt;MCP is an open protocol that lets AI clients (Claude Desktop, Gemini, Cursor, and others) &lt;strong&gt;discover and call your capabilities as "tools."&lt;/strong&gt; You do not rewrite your API. You stand up a thin MCP server that exposes selected endpoints as tools and forwards the calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   AI Chat (Claude / Gemini)
            │   MCP protocol (JSON-RPC)
            ▼
   ┌──────────────────────┐
   │   MCP Server (.NET)  │  ← [McpServerTool] methods
   │   - GetOpenInvoices  │
   │   - CreateTicket     │
   └─────────┬────────────┘
             │   HttpClient + Bearer/API key
             ▼
   ┌──────────────────────┐
   │  Existing REST API   │  ← unchanged, still JWT-secured
   └──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server is a translator: it speaks JSON-RPC to the model and plain HTTP to your existing API. Your business logic never moves.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Stand Up the MCP Server
&lt;/h3&gt;

&lt;p&gt;Use the official C# SDK (maintained together with Microsoft). Add it to a new minimal ASP.NET Core project so you can host a &lt;strong&gt;remote&lt;/strong&gt; MCP server over Streamable HTTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package ModelContextProtocol.AspNetCore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;WebApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpTransport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;          &lt;span class="c1"&gt;// Streamable HTTP for remote clients&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithToolsFromAssembly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;     &lt;span class="c1"&gt;// discover [McpServerTool] methods&lt;/span&gt;

&lt;span class="c1"&gt;// Typed client to your EXISTING API&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BackendApi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.internal.t1tech.com/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;WebApplication&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                     &lt;span class="c1"&gt;// exposes the /mcp endpoint&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the entire host. &lt;code&gt;MapMcp()&lt;/code&gt; wires up discovery, so any compliant AI client can enumerate your tools.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Turn an Endpoint Into a Tool
&lt;/h3&gt;

&lt;p&gt;A tool is just a method. The attributes and XML-style descriptions are not decoration — the model reads them to decide &lt;em&gt;when&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; to call you. Be explicit; vague descriptions cause hallucinated arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerToolType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceTools&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHttpClientFactory&lt;/span&gt; &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;InvoiceTools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpClientFactory&lt;/span&gt; &lt;span class="n"&gt;httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Returns open (unpaid) invoices for a given customer ID."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetOpenInvoices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The numeric customer identifier."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BackendApi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"api/invoices?customerId=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;status=open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnsureSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice there is no token in that call yet. That is the entire fight, and the next two sections are where production engineering actually happens.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: The JWT Question — Whose Identity Is Calling?
&lt;/h3&gt;

&lt;p&gt;Your API trusts a JWT. The naive instinct is to bake a service account token into the MCP server. &lt;strong&gt;Do not.&lt;/strong&gt; That collapses every user into one identity and hands the LLM god-mode over your data.&lt;/p&gt;

&lt;p&gt;The correct model: the MCP server is an &lt;strong&gt;OAuth 2.1 Resource Server.&lt;/strong&gt; The AI client authenticates the &lt;em&gt;human&lt;/em&gt;, receives a token, and passes it through. The MCP Authorization spec standardizes this with Protected Resource Metadata (RFC 9728), so the client can discover where to log in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJwtBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://login.t1tech.com/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Audience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mcp-invoice-server"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ...after Build()&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;RequireAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then forward the caller's identity instead of a hardcoded secret. Grab the incoming token from &lt;code&gt;HttpContext&lt;/code&gt; and attach it downstream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;InvoiceTools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;IHttpClientFactory&lt;/span&gt; &lt;span class="n"&gt;httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class="n"&gt;httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreateAuthorizedClientAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BackendApi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTokenAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authorization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AuthenticationHeaderValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the existing API sees the &lt;em&gt;real&lt;/em&gt; user's scopes and roles. Your authorization rules keep working, untouched. The LLM cannot read an invoice the human behind it could not read.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4: Do You Need an API Key Instead? Pick By Client Type
&lt;/h3&gt;

&lt;p&gt;JWT/OAuth is right for interactive chat where a human is present. But not every MCP consumer is a person clicking "Authorize." Choose the scheme by &lt;em&gt;who&lt;/em&gt; connects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remote HTTP server, human in a chat client → OAuth 2.1 with JWT pass-through.&lt;/strong&gt; This is the spec's blessed path. Full user identity, scoped access, revocable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local &lt;code&gt;stdio&lt;/code&gt; server or machine-to-machine automation → API key.&lt;/strong&gt; When there is no interactive login (a scheduled agent, a local dev tool), issue a scoped API key and read it from configuration, never from source.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// API key path — for non-interactive / stdio clients&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Backend:ApiKey"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing Backend:ApiKey."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Api-Key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My recommendation for a team shipping this: &lt;strong&gt;default to OAuth 2.1 JWT pass-through for anything remote&lt;/strong&gt;, and reserve API keys for headless integrations where you can mint &lt;em&gt;narrowly scoped, per-integration&lt;/em&gt; keys and rotate them. Treat a static API key like a password — short TTL, per-client, logged, revocable. Never a single shared secret with full API surface.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Flow: What Actually Happens When Claude Uses Your Tool
&lt;/h3&gt;

&lt;p&gt;Code is abstract until you watch a real request move through it. Here is the end-to-end round trip when a user types &lt;em&gt;"Show me the open invoices for customer 4821"&lt;/em&gt; into Claude.&lt;/p&gt;

&lt;p&gt;But first — the question everyone asks: &lt;strong&gt;where does Claude get that JWT?&lt;/strong&gt; It is not magic and Claude does not "have your token." The MCP client obtains it through a standard OAuth 2.1 handshake the &lt;em&gt;first&lt;/em&gt; time it connects, and this is worth seeing on its own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0a] Claude → MCP Server:  first call, no token
     ← 401 Unauthorized
       WWW-Authenticate: Bearer resource_metadata="https://mcp.t1tech.com/.well-known/..."

[0b] Claude reads Protected Resource Metadata (RFC 9728)
     → learns which Authorization Server (your IdP) issues tokens

[0c] Claude runs OAuth 2.1 Authorization Code + PKCE:
     → opens a browser window
     → USER logs in at login.t1tech.com and clicks "Allow"
     → IdP redirects back with an auth code
     → Claude exchanges code → access_token (JWT) + refresh_token

[0d] Claude securely stores the token and reuses it.
     Refresh happens silently; the login prompt does NOT repeat each message.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The critical point: &lt;strong&gt;the human authenticates directly with your identity provider, not with Claude.&lt;/strong&gt; Claude never sees a password. It receives a scoped, expiring JWT through the same OAuth flow your web frontend would use — and your MCP server, as the Resource Server, advertises where that login lives via the 401 challenge in Step 0a.&lt;/p&gt;

&lt;p&gt;On the .NET side, &lt;code&gt;RequireAuthorization()&lt;/code&gt; already returns the 401. The one extra thing you owe the client is the discovery pointer in Step 0b — register the MCP server as a protected resource so the SDK emits the &lt;code&gt;resource_metadata&lt;/code&gt; challenge and serves the metadata document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Advertises this server as an OAuth 2.1 Resource Server&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceMetadata&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProtectedResourceMetadata&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;AuthorizationServers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://login.t1tech.com/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single registration is what turns "Claude somehow has a token" into a discoverable, spec-compliant login the client can drive on its own.&lt;/p&gt;

&lt;p&gt;Only &lt;em&gt;after&lt;/em&gt; that handshake does the everyday request loop below run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1] USER  →  Claude Chat
    "Show me the open invoices for customer 4821."

[2] Claude → MCP Server:  discovery (once, on connect)
    "What tools do you have?"
    ← [{ name: "GetOpenInvoices",
         description: "Returns open (unpaid) invoices for a customer ID.",
         params: { customerId: int } }]

[3] Claude reasons:
    intent = list open invoices  →  tool = GetOpenInvoices
    extracts argument  →  customerId = 4821

[4] Claude → MCP Server:  tools/call (JSON-RPC)
    Authorization: Bearer &amp;lt;the signed-in user's JWT&amp;gt;
    { "name": "GetOpenInvoices", "arguments": { "customerId": 4821 } }

[5] MCP Server validates the JWT, then forwards downstream:
    GET /api/invoices?customerId=4821&amp;amp;status=open
    Authorization: Bearer &amp;lt;same JWT — user identity preserved&amp;gt;

[6] Existing REST API applies the user's scopes → returns JSON

[7] MCP Server → Claude:  tool result (raw JSON)
    [{ "id": 5567, "amount": 1200.00, "due": "2026-09-15" }, ...]

[8] Claude → USER:
    "Customer 4821 has 2 open invoices totaling $2,050 —
     one due Sep 15 ($1,200) and one due Oct 2 ($850)."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things are worth pausing on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 happens once, not per message.&lt;/strong&gt; The client caches your tool catalog on connect. This is why the &lt;code&gt;[Description]&lt;/code&gt; text is load-bearing — the model chooses tools purely from it, long before your code ever runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 to Step 6 is the whole security story.&lt;/strong&gt; The JWT the human logged in with is the same JWT that reaches your API. Claude never sees a service credential, and it can never retrieve an invoice the signed-in user isn't authorized to see. The model orchestrates; your API still decides.&lt;/p&gt;

&lt;p&gt;If you want to see this concretely, the tool result at Step 7 is exactly the string your &lt;code&gt;GetOpenInvoices&lt;/code&gt; method returned — Claude does the natural-language summarization in Step 8. You return data; the model handles the prose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Impact
&lt;/h2&gt;

&lt;p&gt;The payoff is not "AI hype." It is that a capability you already built becomes usable by a new class of client with near-zero duplication.&lt;/p&gt;

&lt;p&gt;Your team writes one thin tool method per endpoint you want to expose — not a second API. Because identity flows through, your security posture is &lt;em&gt;unchanged&lt;/em&gt;: the same JWT, the same scopes, the same audit trail. And because tools are just decorated C# methods, they unit-test like any other service, and new endpoints become new tools in minutes.&lt;/p&gt;

&lt;p&gt;The long-term maintainability win is that MCP is a stable seam. When you swap Claude for Gemini, or add a third client, nothing changes on your side. You built the adapter once.&lt;/p&gt;




&lt;h2&gt;
  
  
  Actionable Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wrap, don't rewrite.&lt;/strong&gt; Stand up a separate MCP server that forwards to your existing API over &lt;code&gt;HttpClient&lt;/code&gt;. Business logic stays put.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write ruthless tool descriptions.&lt;/strong&gt; The model chooses tools from your &lt;code&gt;[Description]&lt;/code&gt; text. Vague text produces bad calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass the user's identity through — never hardcode a service token.&lt;/strong&gt; Make the MCP server an OAuth 2.1 Resource Server and forward the caller's JWT so existing authorization still applies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose auth by client type.&lt;/strong&gt; OAuth 2.1/JWT for interactive remote clients; scoped, rotatable API keys only for headless &lt;code&gt;stdio&lt;/code&gt; or M2M integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship the smallest surface first.&lt;/strong&gt; Expose read-only tools before write tools, and let least-privilege scopes gate everything the LLM can reach.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>dotnet</category>
      <category>mcp</category>
    </item>
    <item>
      <title>MCP vs RAG: Two AI Buzzwords, Explained Without the Buzzwords</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Thu, 06 Aug 2026 12:33:37 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/mcp-vs-rag-two-ai-buzzwords-explained-without-the-buzzwords-d2h</link>
      <guid>https://dev.to/karamkhoury88/mcp-vs-rag-two-ai-buzzwords-explained-without-the-buzzwords-d2h</guid>
      <description>&lt;p&gt;A few weeks ago, a junior engineer on my team asked me: "Should we use RAG or MCP for this feature?"&lt;/p&gt;

&lt;p&gt;He said it like the two were competing options. Like picking between React and Vue.&lt;/p&gt;

&lt;p&gt;They're not. One is about &lt;strong&gt;giving an AI knowledge&lt;/strong&gt;. The other is about &lt;strong&gt;giving an AI hands&lt;/strong&gt;. Once you see that difference, the buzzwords stop being scary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Concept, in One Line Each
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RAG (Retrieval-Augmented Generation):&lt;/strong&gt; Before the AI answers you, it goes and &lt;em&gt;reads&lt;/em&gt; relevant documents first, then writes its answer based on what it just read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP (Model Context Protocol):&lt;/strong&gt; A standard way for an AI to &lt;em&gt;connect to and use&lt;/em&gt; outside tools — like your calendar, your database, or your file system — instead of just talking about them.&lt;/p&gt;

&lt;p&gt;Here's the simplest way to picture it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RAG:  Question -&amp;gt; Search your documents -&amp;gt; Read the best matches -&amp;gt; Answer
MCP:  Question -&amp;gt; Call a real tool (email, calendar, database) -&amp;gt; Get a real result -&amp;gt; Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RAG makes the AI &lt;strong&gt;smarter about what it knows&lt;/strong&gt;.&lt;br&gt;
MCP makes the AI &lt;strong&gt;capable of doing something&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where They're Similar
&lt;/h3&gt;

&lt;p&gt;Both exist to fix the same core problem: an AI, by itself, only knows what it was trained on, up to a certain date. It can't see your company's internal wiki. It can't see today's stock price. It can't see the email that just landed in your inbox.&lt;/p&gt;

&lt;p&gt;Both RAG and MCP are ways of feeding an AI &lt;em&gt;fresh, outside information&lt;/em&gt; at the moment you ask your question, instead of relying only on what it memorized during training.&lt;/p&gt;

&lt;p&gt;That's the whole family resemblance. After that, they split in very different directions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where They're Different
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RAG is about reading. MCP is about acting.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;RAG&lt;/th&gt;
&lt;th&gt;MCP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it does&lt;/td&gt;
&lt;td&gt;Finds and reads relevant text&lt;/td&gt;
&lt;td&gt;Connects to and operates a tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use&lt;/td&gt;
&lt;td&gt;"Answer using our documents"&lt;/td&gt;
&lt;td&gt;"Book this meeting" or "Update this record"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;Better, more accurate answers&lt;/td&gt;
&lt;td&gt;Real actions with real side effects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example source&lt;/td&gt;
&lt;td&gt;A PDF, a knowledge base, a set of notes&lt;/td&gt;
&lt;td&gt;A calendar app, an email inbox, a database&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Think of RAG as a &lt;strong&gt;very fast research assistant&lt;/strong&gt;. You ask a question, they run to the library, grab the three most relevant pages, and hand them to you before you even finish your coffee.&lt;/p&gt;

&lt;p&gt;Think of MCP as a &lt;strong&gt;universal remote control&lt;/strong&gt;. It doesn't know anything by itself — but it can press the right buttons on whichever device you point it at: your calendar, your CRM, your ticketing system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where To Use Each
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use RAG when the problem is "the AI doesn't know this."&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A customer support bot that needs to answer questions using your product manuals.&lt;/li&gt;
&lt;li&gt;A legal assistant that needs to reference your actual contracts, not generic legal knowledge.&lt;/li&gt;
&lt;li&gt;An internal chatbot that answers HR questions using your company's real policy documents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use MCP when the problem is "the AI needs to do this."&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An assistant that checks your real calendar and books a meeting for you.&lt;/li&gt;
&lt;li&gt;A support agent that actually creates a ticket in your helpdesk system, not just describes one.&lt;/li&gt;
&lt;li&gt;A coding assistant that reads your real files and runs your real tests, instead of guessing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use both when you need an AI that is both well-informed and useful.&lt;/strong&gt; A great example: a customer support AI that uses RAG to pull the correct refund policy from your documents, then uses MCP to actually process the refund in your billing system.&lt;/p&gt;




&lt;h3&gt;
  
  
  A Real-Life Example
&lt;/h3&gt;

&lt;p&gt;Imagine you run an online store and you build an AI assistant for customer support.&lt;/p&gt;

&lt;p&gt;A customer asks: &lt;em&gt;"What's your return policy for items bought during the sale, and can you start my return?"&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG&lt;/strong&gt; kicks in first. It searches your actual return policy documents, finds the section about sale items (which usually has different rules than regular items), and pulls the exact, correct answer instead of a generic guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt; kicks in next. It connects to your order management system, finds the customer's real order, and actually starts the return process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without RAG, the AI might confidently give the wrong policy. Without MCP, the AI could only &lt;em&gt;tell&lt;/em&gt; the customer how to start a return, not &lt;em&gt;actually&lt;/em&gt; start it. Together, the customer gets a correct answer and a completed action, in one conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Impact for Your Team
&lt;/h2&gt;

&lt;p&gt;Getting this distinction right changes how you scope a project.&lt;/p&gt;

&lt;p&gt;If a stakeholder says "make the AI smarter about our documents," that's a RAG problem — you're building a search-and-retrieve pipeline over your content.&lt;/p&gt;

&lt;p&gt;If they say "make the AI actually do things in our systems," that's an MCP problem — you're building integrations and giving the AI safe, well-defined tools to call.&lt;/p&gt;

&lt;p&gt;Confusing the two leads to wasted sprints: teams building elaborate document search when what the business actually wanted was for the AI to update a record, or vice versa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actionable Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;RAG = knowledge.&lt;/strong&gt; Use it when the AI needs facts it wasn't trained on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP = capability.&lt;/strong&gt; Use it when the AI needs to perform a real action in a real system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They're not rivals.&lt;/strong&gt; Most serious AI products end up using both together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope your project by the verb.&lt;/strong&gt; "Answer using X" → RAG. "Do X" → MCP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start small.&lt;/strong&gt; Pick one document set for RAG, or one tool for MCP, prove it works, then expand.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you strip away the acronyms, both ideas are things you already understand: look it up before you answer, and use the right tool for the job.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>mcp</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Concurrency vs Parallelism: A Simple Guide</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Tue, 04 Aug 2026 06:44:36 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/concurrency-vs-parallelism-a-simple-guide-33l9</link>
      <guid>https://dev.to/karamkhoury88/concurrency-vs-parallelism-a-simple-guide-33l9</guid>
      <description>&lt;h2&gt;
  
  
  Concurrency: juggling tasks
&lt;/h2&gt;

&lt;p&gt;Think of concurrency like a chef in a busy kitchen. The chef doesn't cook all dishes at the exact same time — they chop some vegetables, stir a pot, check the oven, and go back to chopping. They make progress on multiple tasks by switching between them quickly.&lt;/p&gt;

&lt;p&gt;In computing, concurrency means a system can handle multiple tasks overlapping in time, but not necessarily running at the same instant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tasks take turns (like a single CPU core switching between processes).&lt;/li&gt;
&lt;li&gt;Useful when tasks involve waiting (downloading files, handling user input).&lt;/li&gt;
&lt;li&gt;Common in applications like web servers that manage many users at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parallelism: doing tasks side by side
&lt;/h2&gt;

&lt;p&gt;Now imagine two chefs in the same kitchen. One chops vegetables while the other grills meat — both working at the same time.&lt;/p&gt;

&lt;p&gt;That's parallelism: tasks actually running simultaneously, using multiple resources (like multiple CPU cores).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tasks run truly at the same time.&lt;/li&gt;
&lt;li&gt;Requires hardware support (multi-core processors, GPUs).&lt;/li&gt;
&lt;li&gt;Used for heavy computation (video encoding, scientific simulations).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-world examples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency:&lt;/strong&gt; a chat app handling thousands of messages — it switches between users so fast it seems instant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelism:&lt;/strong&gt; a weather-forecast model running calculations across multiple CPUs to predict storms faster.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to use each
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;concurrency&lt;/strong&gt; when tasks involve waiting (I/O, network requests).&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;parallelism&lt;/strong&gt; when you need brute-force speed (data processing, rendering).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt; = managing multiple tasks efficiently (even on a single core).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelism&lt;/strong&gt; = executing multiple tasks at the same time (needs multiple cores).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are essential in modern software, and understanding them helps you write faster, more responsive programs.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by Karam Khoury — Lead Software Engineer (.NET &amp;amp; Azure) with 14+ years building secure, scalable fintech systems. More at &lt;a href="https://karamkhoury.me" rel="noopener noreferrer"&gt;karamkhoury.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>parallelism</category>
      <category>concurrency</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>AWS Introduction for Beginner Software Engineers</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Tue, 04 Aug 2026 06:39:39 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/aws-introduction-for-beginner-software-engineers-4ec5</link>
      <guid>https://dev.to/karamkhoury88/aws-introduction-for-beginner-software-engineers-4ec5</guid>
      <description>&lt;p&gt;As a software engineer starting with AWS, you might feel overwhelmed by all the services. Let me break down the key components with real-world examples to help you get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. DNS &amp;amp; How AWS Route 53 Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Users can't remember IP addresses like &lt;code&gt;54.210.167.101&lt;/code&gt; for your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; AWS Route 53 (DNS service) maps &lt;code&gt;yourdomain.com&lt;/code&gt; to your servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt; Your startup has servers in Ohio and Frankfurt. Route 53 automatically sends European users to Frankfurt and routes US traffic to Ohio — faster load times for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Networking: VPC &amp;amp; Subnets
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC:&lt;/strong&gt; Your private cloud network (like an office building).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public subnet:&lt;/strong&gt; For web servers (needs internet access).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private subnet:&lt;/strong&gt; For databases (blocked from direct internet access).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internet Gateway:&lt;/strong&gt; Connects public subnets to the internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAT Gateway:&lt;/strong&gt; Lets private subnets reach the internet (one-way).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt; In your e-commerce app, the web server runs in a public subnet while the database sits in a private subnet.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Storing &amp;amp; Delivering Static Files (S3 + CloudFront)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon S3:&lt;/strong&gt; Stores images, videos, and logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFront (CDN):&lt;/strong&gt; Caches content globally for faster loading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt; Your blog hosts images in S3; CloudFront caches them in 450+ locations, so a reader in Tokyo gets images from Japan instead of Virginia — cutting load time by ~50%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Enable S3 Versioning to recover accidentally deleted files.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Running Backend Services
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AWS Lambda (Serverless)&lt;/strong&gt; — best for event-driven tasks. &lt;em&gt;Example:&lt;/em&gt; a photo app resizes images automatically on upload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EC2 (Virtual Servers)&lt;/strong&gt; — best for full control (custom OS, legacy apps). &lt;em&gt;Example:&lt;/em&gt; hosting a Java monolith with specific dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECS/EKS (Containers)&lt;/strong&gt; — best for microservices (Docker/Kubernetes). &lt;em&gt;Example:&lt;/em&gt; a food-delivery app with separate services for orders, payments, and tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Databases: Picking the Right One
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon RDS (SQL):&lt;/strong&gt; Structured data with relationships — e.g. user accounts + orders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB (NoSQL):&lt;/strong&gt; High-speed, scalable lookups — e.g. a ride-sharing app tracking real-time driver locations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aurora (High-Performance SQL):&lt;/strong&gt; Low-latency apps — e.g. stock-trading platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Adding AI/ML to Your App
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock (pre-trained AI):&lt;/strong&gt; e.g. a customer-support chatbot trained on your FAQ docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SageMaker (custom ML models):&lt;/strong&gt; e.g. a recommendation engine ("users who bought X also liked Y").&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Security Essentials
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC:&lt;/strong&gt; Controls how your networking works — what can connect to what, what reaches the internet, and what stays private.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NACLs (Network ACLs):&lt;/strong&gt; A firewall for an entire subnet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Groups:&lt;/strong&gt; A firewall for each specific instance or service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM (Identity &amp;amp; Access Management):&lt;/strong&gt; Controls who can access what.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Golden rule — least privilege:&lt;/strong&gt; a Lambda function should only access the one DynamoDB table it needs; backup processes get read-only access; only DevOps can reach production databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Monitoring &amp;amp; Debugging
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch:&lt;/strong&gt; Tracks performance metrics and logs (EC2 CPU, Lambda errors). &lt;em&gt;Example:&lt;/em&gt; alert when database CPU hits 90%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudTrail:&lt;/strong&gt; Audit logs ("who changed IAM policies?", "who deleted an S3 bucket?") — critical for compliance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Imagine building "Twitter Lite":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Route 53 routes users to the nearest server.&lt;/li&gt;
&lt;li&gt;CloudFront + S3 deliver profile pictures fast.&lt;/li&gt;
&lt;li&gt;EC2 or Lambda runs the backend API.&lt;/li&gt;
&lt;li&gt;DynamoDB stores tweets for instant loading.&lt;/li&gt;
&lt;li&gt;CloudWatch alerts you if something breaks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next steps for beginners
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hands-on:&lt;/strong&gt; try the AWS Free Tier (12 months free).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First project:&lt;/strong&gt; deploy a static website on S3 + CloudFront.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep dive:&lt;/strong&gt; learn Lambda + DynamoDB.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Written by Karam Khoury — Lead Software Engineer (.NET &amp;amp; Azure) with 14+ years building secure, scalable fintech systems. More at &lt;a href="https://karamkhoury.me" rel="noopener noreferrer"&gt;karamkhoury.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Oops, I Committed a Secret: A Calm Guide to Scrubbing Git History</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Fri, 24 Jul 2026 12:00:25 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/oops-i-committed-a-secret-a-calm-guide-to-scrubbing-git-history-38ll</link>
      <guid>https://dev.to/karamkhoury88/oops-i-committed-a-secret-a-calm-guide-to-scrubbing-git-history-38ll</guid>
      <description>&lt;p&gt;You just finished a great feature. You're in the flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Fixed the bug!"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you see it, staring back from the GitHub UI: your live API key. 💀&lt;/p&gt;

&lt;p&gt;We've all been there. Here's the calm, correct order of operations — from "quick save" for a mistake you caught instantly, to the deep clean for a secret that's been buried in history for months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: Rotate the secret first (yes, before anything else)
&lt;/h2&gt;

&lt;p&gt;This is the step people skip, and it's the only one that's non-negotiable. &lt;strong&gt;The moment a secret hits a remote, assume it's compromised.&lt;/strong&gt; Bots scrape public GitHub for credentials within &lt;em&gt;seconds&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So before you touch history:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Revoke / rotate&lt;/strong&gt; the leaked key, token, or password at its source (AWS, Stripe, your DB, wherever).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Then&lt;/em&gt; clean the repo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cleaning history without rotating is like changing the locks after posting the old key online. Rotate first, always.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 1: You caught it before pushing (the quick save)
&lt;/h2&gt;

&lt;p&gt;If the secret is only in your &lt;strong&gt;last, un-pushed commit&lt;/strong&gt;, this is trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stop tracking the file and ignore it going forward&lt;/span&gt;
git &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt; .env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# Fold the fix into the last commit&lt;/span&gt;
git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. Nothing left the building. (Still rotate the key if it was ever real.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 2: It's deep in history, or already pushed (the deep clean)
&lt;/h2&gt;

&lt;p&gt;Once the secret is several commits back — or on the remote — you need to rewrite history. The modern, recommended tool is &lt;strong&gt;&lt;code&gt;git filter-repo&lt;/code&gt;&lt;/strong&gt; (Git's own docs now steer you here instead of the old &lt;code&gt;filter-branch&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install (Python)&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;git-filter-repo

&lt;span class="c"&gt;# Option A: remove a whole file from ALL history&lt;/span&gt;
git filter-repo &lt;span class="nt"&gt;--path&lt;/span&gt; config/secrets.yml &lt;span class="nt"&gt;--invert-paths&lt;/span&gt;

&lt;span class="c"&gt;# Option B: redact a specific string everywhere it appears&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'AKIAIOSFODNN7EXAMPLE==&amp;gt;REDACTED'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; replacements.txt
git filter-repo &lt;span class="nt"&gt;--replace-text&lt;/span&gt; replacements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer &lt;strong&gt;BFG Repo-Cleaner&lt;/strong&gt; if you like a faster, simpler tool for the common cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bfg &lt;span class="nt"&gt;--delete-files&lt;/span&gt; id_rsa
bfg &lt;span class="nt"&gt;--replace-text&lt;/span&gt; passwords.txt

&lt;span class="c"&gt;# Expire the old refs and garbage-collect&lt;/span&gt;
git reflog expire &lt;span class="nt"&gt;--expire&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git gc &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--aggressive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Then: force-push and warn your team
&lt;/h2&gt;

&lt;p&gt;History rewriting changes commit hashes, so you must overwrite the remote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ This rewrites shared history. Tell every collaborator to &lt;strong&gt;re-clone or hard-reset&lt;/strong&gt; — if someone merges an old local branch, the secret comes right back. And remember: on GitHub, the old commit can still be reachable via its hash / cached views for a while, which is exactly why &lt;strong&gt;Step 0 (rotation)&lt;/strong&gt; is what actually protects you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevention: never live on the edge again
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.gitignore&lt;/code&gt; first.&lt;/strong&gt; Add &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt;, &lt;code&gt;secrets.*&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; the first commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment variables / a secret manager&lt;/strong&gt; (Azure Key Vault, AWS Secrets Manager, Doppler) — secrets never touch the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A pre-commit scanner&lt;/strong&gt; so a secret is blocked &lt;em&gt;before&lt;/em&gt; it's committed:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# gitleaks catches secrets in staged changes&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;gitleaks
gitleaks protect &lt;span class="nt"&gt;--staged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire that into a pre-commit hook and the "cold sweat" moment mostly disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rotate the secret&lt;/strong&gt; (non-negotiable, do it first).&lt;/li&gt;
&lt;li&gt;Un-pushed? &lt;code&gt;git commit --amend&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In history? &lt;code&gt;git filter-repo&lt;/code&gt; or BFG, then &lt;code&gt;git push --force&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tell your team to re-clone.&lt;/li&gt;
&lt;li&gt;Add a pre-commit scanner so it never happens again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How do you handle secrets in your workflow — pre-commit hooks, or living life on the edge? 👇&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by Karam Khoury — Lead Software Engineer (.NET &amp;amp; Azure) with 14+ years building secure, scalable fintech systems. More at &lt;a href="https://karamkhoury.me" rel="noopener noreferrer"&gt;karamkhoury.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>security</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>System Design: The 6 Core Load Balancing Algorithms, Explained</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Fri, 24 Jul 2026 11:58:19 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/system-design-the-6-core-load-balancing-algorithms-explained-2gc</link>
      <guid>https://dev.to/karamkhoury88/system-design-the-6-core-load-balancing-algorithms-explained-2gc</guid>
      <description>&lt;p&gt;Stop guessing how your traffic scales — understand the routing mechanics. Here's how the six core algorithms actually dictate traffic distribution, and when to reach for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Round Robin
&lt;/h2&gt;

&lt;p&gt;Distributes requests sequentially across the server pool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Dealing a deck of cards — Server A gets request 1, Server B gets 2, Server C gets 3, then the cycle repeats. Best for identical, stateless servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Weighted Round Robin
&lt;/h2&gt;

&lt;p&gt;Sequential routing that accounts for unequal hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Server A has 64 GB RAM, Server B has 32 GB. Assign weight 2 to A and 1 to B, and A receives exactly twice as many requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Least Connections
&lt;/h2&gt;

&lt;p&gt;Routes traffic to the server with the fewest active, open sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Joining the supermarket checkout with the fewest people. Ideal for long-lived connections — WebSockets, heavy database queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. IP Hash
&lt;/h2&gt;

&lt;p&gt;Uses a hash of the client's IP to guarantee the same client hits the same server every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A user at 192.168.1.50 is always routed to Server C. Mandatory for legacy stateful apps that store session data in-memory rather than a distributed cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Least Response Time
&lt;/h2&gt;

&lt;p&gt;A hybrid: lowest number of active connections combined with the lowest average response time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Joining the line that has both the fewest people &lt;em&gt;and&lt;/em&gt; the fastest cashier. Essential when server performance degrades unpredictably.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Adaptive (Resource-Based)
&lt;/h2&gt;

&lt;p&gt;Routes based on real-time hardware telemetry from an agent on each server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; The load balancer queries CPU, memory, and I/O wait in real time, and avoids any server spiking above 80% utilization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Choose your algorithm based on your infrastructure and state management — not trends. Build systems that scale predictably.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by Karam Khoury — Lead Software Engineer (.NET &amp;amp; Azure), building secure, scalable fintech systems. More at &lt;a href="https://karamkhoury.me" rel="noopener noreferrer"&gt;karamkhoury.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
