<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[System Shogun]]></title><description><![CDATA[Everything about system design, software architecture and AI in the cloud.]]></description><link>https://systemshogun.com</link><image><url>https://substackcdn.com/image/fetch/$s_!EGQ-!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5048a823-9128-48de-ac3a-c0e4d523f053_800x800.png</url><title>System Shogun</title><link>https://systemshogun.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 30 Apr 2026 13:17:38 GMT</lastBuildDate><atom:link href="https://systemshogun.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[System Shogun]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[systemshogun@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[systemshogun@substack.com]]></itunes:email><itunes:name><![CDATA[Kaloyan Drenski]]></itunes:name></itunes:owner><itunes:author><![CDATA[Kaloyan Drenski]]></itunes:author><googleplay:owner><![CDATA[systemshogun@substack.com]]></googleplay:owner><googleplay:email><![CDATA[systemshogun@substack.com]]></googleplay:email><googleplay:author><![CDATA[Kaloyan Drenski]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Build a Smart Call Center with AI on Azure]]></title><description><![CDATA[Architecture & PoC of a smart call center on Microsoft Azure with LangGraph]]></description><link>https://systemshogun.com/p/build-a-smart-call-center-with-ai</link><guid isPermaLink="false">https://systemshogun.com/p/build-a-smart-call-center-with-ai</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Fri, 17 Apr 2026 12:51:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/9NWp9DCUgC8" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Customer calls are one of the most valuable sources of business insight.</p><p>The question is how well you capture and use that information.</p><p>What if every call could be turned into structured data and used by agentic AI systems where agents actively handle tasks, fix issues, and support decision-making?</p><p>There are many ways to do it, but here&#8217;s a simple architecture on Azure:</p><p>&#120284;&#120315;&#120308;&#120306;&#120320;&#120321;&#120310;&#120316;&#120315; &amp; &#120291;&#120319;&#120316;&#120304;&#120306;&#120320;&#120320;&#120310;&#120315;&#120308;</p><p>Audio &#8594; Blob &#8594; Event Grid &#8594; Queue &#8594; Worker</p><p>&#120296;&#120315;&#120305;&#120306;&#120319;&#120320;&#120321;&#120302;&#120315;&#120305;&#120310;&#120315;&#120308;</p><p>- Content Understanding (turns audio into structured data)</p><p>&#120279;&#120302;&#120321;&#120302; &amp; &#120293;&#120306;&#120321;&#120319;&#120310;&#120306;&#120323;&#120302;&#120313;</p><p>- PostgreSQL (chat history, other structured data)</p><p>- Azure AI Search (RAG)</p><p>&#120276;&#120284;</p><p>- Microsoft Foundry + LangGraph (multi-agent orchestration)</p><p>&#120296;&#120284; / &#120276;&#120291;&#120284;</p><p>- Container Apps (exposes the system to users / clients)</p><p>Nothing fancy individually, but together, it turns raw calls into something agents can query, analyze, and act on in real workflows.</p><p>Full walkthrough + POC app:</p><div id="youtube2-9NWp9DCUgC8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;9NWp9DCUgC8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/9NWp9DCUgC8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Build AI Apps with Microsoft Foundry, Microsoft Agent Framework, and PostgreSQL]]></title><description><![CDATA[Learn how to build awesome AI apps with Microsoft Foundry, Microsoft Agent Framework, and PostgreSQL]]></description><link>https://systemshogun.com/p/build-ai-apps-with-microsoft-foundry</link><guid isPermaLink="false">https://systemshogun.com/p/build-ai-apps-with-microsoft-foundry</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 21 Mar 2026 05:47:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/eldhIGRLxIQ" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div id="youtube2-eldhIGRLxIQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;eldhIGRLxIQ&quot;,&quot;startTime&quot;:&quot;1s&quot;,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/eldhIGRLxIQ?start=1s&amp;rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Winter Is Coming… Guard Your Architecture in the AI Era]]></title><description><![CDATA[Practical Steps to Protect Your Architecture Long-Term in the AI Era]]></description><link>https://systemshogun.com/p/winter-is-coming-guard-your-architecture</link><guid isPermaLink="false">https://systemshogun.com/p/winter-is-coming-guard-your-architecture</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sun, 01 Mar 2026 06:29:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1e8b18fe-3f77-40cd-9be5-f58f0861741e_586x882.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a Solution Architect, my daily job consists of working closely with the business to understand their needs, gathering both functional and non-functional requirements, and designing solutions that translate those requirements into reliable, scalable systems.</p><p>Lately, a big part of my job has been figuring out how to leverage AI&#8217;s benefits without compromising the overall solution architecture and turning the system into unreliable, hard to maintain mess. Many teams are being pushed to deliver faster using AI, often under tighter deadlines. If this isn&#8217;t handled carefully, it can quickly lead to architectural erosion, growing complexity, and systems that you want to stay away from.</p><p>Teams are already feeling the impact. When guardrails are missing, structure erodes and boundaries blur. We&#8217;re even seeing large systems struggle under constant rushed changes with AI. Frequent Windows update issues are a reminder of how fragile complex software (especially written with AI) can become when complexity grows faster than control.</p><p>The challenge isn&#8217;t speed versus quality. It&#8217;s learning how to move fast without weakening the architecture that keeps the system stable.</p><p>In today&#8217;s post, I&#8217;m focusing specifically on greenfield projects. I&#8217;m going to walk through a few practical steps you can follow to ensure AI accelerates your development without slowly destroying your solution and making it unmaintainable.</p><p>I will cover brownfield systems separately, because the strategy there is different and deserves its own discussion.</p><h2>Build Good Architecture from the Start (Greenfield)</h2><p>If you&#8217;re working on a greenfield project, you have a rare advantage as you control the foundation. There&#8217;s no legacy, no accidental complexity, no historical shortcuts. That also means the responsibility is entirely yours.</p><p>Start with clear understanding of functional and non-functional requirements. Design system boundaries intentionally. Define coding standards, layering rules, and architectural principles before the first large batch of AI-generated code enters the repository.</p><p>AI can help you build and move faster but only when you have stable foundations.</p><p>In greenfield projects, the discipline you apply at the beginning determines maintainability years later.</p><h2>We&#8217;ve Got the Architecture&#8230; Now It&#8217;s Time to Protect It</h2><p>Designing a solid architecture is only the first step. Once development begins and AI starts generating code at scale, protection becomes critical.</p><p>Architecture should not live only in your head or in a diagram. It needs to be documented, enforced, and protected with automated checks, review processes, and clear boundaries.</p><p>Now let&#8217;s see some basic steps that will help us protect the solution from architectural erosion.</p><h2>Use GitHub Copilot to Protect the Architecture</h2><p>AI should not only generate code. It should help enforce the architecture you designed. Instead of treating AI as a shortcut to write tons of code, treat it as an extension of your governance model as well.</p><p>If you give AI clear rules, boundaries, and responsibilities, it can become part of your quality control system rather than a source of entropy.</p><p>A recent 2025 study (<a href="https://arxiv.org/pdf/2507.11538">https://arxiv.org/pdf/2507.11538</a>) showed that as the number of instructions increases, AI models start missing requirements and skipping constraints. In other words, the more rules you throw at them, the more likely they are to ignore some of them. <strong>You should always keep this in mind.</strong></p><p>This has direct implications for how we document architecture and even how we use the tools that I am about to show you in a second.</p><p>If your repository contains hundreds of loosely structured rules, long narrative explanations, and scattered constraints, the model will not reliably follow all of them. Overloading it with instructions reduces compliance.</p><p>That is why architectural guidance for AI must be:</p><ul><li><p>Short</p></li><li><p>Explicit</p></li><li><p>Structured</p></li><li><p>Prioritized</p></li></ul><p>Concise, well-organized rules outperform long, dense documents. If we want AI to protect the architecture, we must write instructions that it can realistically follow.</p><p>Now let&#8217;s look at some easy-to-follow steps that will help us protect the architectural integrity of our solution.</p><h2>Custom Instructions</h2><p>Start with a <code>copilot-instructions.md</code> file. This document defines your project&#8217;s coding standards and architectural rules in a concise format.</p><p>Include things like:</p><ul><li><p>Folder structure expectations</p></li><li><p>Layering rules</p></li><li><p>Dependency constraints</p></li><li><p>Naming conventions</p></li><li><p>Required patterns</p></li></ul><p>When Copilot reads this before generating code, it aligns with your architecture instead of inventing its own structure.</p><p><strong>Keep it short. If it&#8217;s too long, it won&#8217;t be followed consistently.</strong></p><p>Here&#8217;s an example of such a document.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;13fc6e62-0caf-4e34-ad2d-81a155248a1e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># Project: Protect Arch Demo

This project follows **Clean Architecture** principles. All contributions &#8212; human or AI &#8212; must respect the layered dependency rules.

## Architecture Layers (innermost &#8594; outermost)

1. **Domain** (`src/Domain`) &#8212; Entities and repository interfaces. Zero dependencies on other project layers.
2. **Application** (`src/Application`) &#8212; Use cases / business logic. Depends only on Domain.
3. **Infrastructure** (`src/Infrastructure`) &#8212; Data access, external services. Depends on Domain and Application.
4. **Api** (`src/Api`) &#8212; HTTP layer, DI composition root. Depends on Application and Infrastructure.

## Dependency Rules &#8212; NEVER violate these

| Layer          | May reference               | Must NEVER reference             |
| -------------- | --------------------------- | -------------------------------- |
| Domain         | (nothing)                   | Application, Infrastructure, Api |
| Application    | Domain                      | Infrastructure, Api              |
| Infrastructure | Domain, Application         | Api                              |
| Api            | Application, Infrastructure | &#8212;                                |

## Coding Guidelines

- Use **C# 12** with file-scoped namespaces.
- Keep Domain entities as plain POCOs &#8212; no framework dependencies.
- Repository **interfaces** live in `Domain/Interfaces`; **implementations** live in `Infrastructure/Repositories`.
- Use cases live in `Application/UseCases` and accept interfaces via constructor injection.
- The Api project is the only place that configures DI and middleware &#8212; never register services elsewhere.
- All new code must pass the architecture tests in `tests/ArchitectureTests` (run with `dotnet test`).
</code></pre></div><h2>Agent Skills</h2><p>Agent Skills let you package reusable capabilities, like instructions, scripts, templates, and supporting resources that Copilot can load when needed. Only the skill&#8217;s <strong>name and description</strong> are available by default. The full instructions and resources are loaded <strong>on demand</strong>, when the task matches the skill&#8217;s purpose.</p><p>This is critical for two reasons:</p><ol><li><p>It keeps context clean and lightweight.</p></li><li><p>It prevents instruction overload, which reduces model reliability.</p></li></ol><p>A skill can encapsulate workflows such as:</p><ul><li><p>Architecture validation</p></li><li><p>Test generation</p></li><li><p>Refactoring checks</p></li><li><p>Security review</p></li><li><p>Deployment validation</p></li></ul><p>Unlike custom instructions, which are always applied and define your structural rules, Agent Skills are task-specific. They activate only when relevant.</p><p>Here&#8217;s an example of a skill for clean architecture review:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;4efb8ca0-4edf-43b5-99a2-b3cefcbc1123&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">---
name: clean-architecture-review
description: 'Reviews .NET code for Clean Architecture dependency violations. Use when reviewing code changes, pull requests, or validating that new code respects layer boundaries in a Clean Architecture project.'
---

# Clean Architecture Review

You are a specialist in Clean Architecture for .NET projects. When this skill is activated, perform a thorough review of the code for dependency rule violations.

## Architecture Layers

The project uses four layers (innermost &#8594; outermost):

1. **Domain** (`src/Domain`) &#8212; Entities, value objects, repository interfaces. **Zero** dependencies on other layers.
2. **Application** (`src/Application`) &#8212; Use cases and business logic. Depends only on Domain.
3. **Infrastructure** (`src/Infrastructure`) &#8212; Data access, external services. Depends on Domain and Application.
4. **Api** (`src/Api`) &#8212; HTTP endpoints, DI composition root. Depends on Application and Infrastructure.

## Review Checklist

For each source file, check the following:

### 1. Layer Identification

Determine which layer a file belongs to based on its path prefix:

- `src/Domain/` &#8594; Domain
- `src/Application/` &#8594; Application
- `src/Infrastructure/` &#8594; Infrastructure
- `src/Api/` &#8594; Api

### 2. Forbidden Dependencies

Check all `using` directives and type references:

| Layer          | Forbidden Dependencies           |
| -------------- | -------------------------------- |
| Domain         | Application, Infrastructure, Api |
| Application    | Infrastructure, Api              |
| Infrastructure | Api                              |
| Api            | (no restrictions)                |

### 3. Structural Rules

- Interfaces in `Domain/Interfaces/` must **only** be interfaces
- Repository implementations must live in `Infrastructure/Repositories/`
- Use cases must live in `Application/UseCases/`
- DI registration must only happen in `src/Api/Program.cs`

## How to Run Verification

Use the [architecture test script](./run-arch-tests.ps1) to execute automated verification:

```powershell
.\run-arch-tests.ps1
```

Or run directly:

```
dotnet test tests/ArchitectureTests --verbosity normal
```

## Output Format

Report findings as:

```
&#9989; PASS &#8212; No architecture violations found
```

or

```
&#10060; VIOLATION in [file path]
   Layer: [layer name]
   References: [forbidden namespace]
   Rule: [which rule is broken]
   Fix: [how to fix it]
```</code></pre></div><h2>Custom Agents</h2><p>Custom agents let you define AI roles with clear responsibilities.</p><p>Instead of one generic assistant, you can create:</p><ul><li><p>A Developer Agent</p></li><li><p>An Architecture Review Agent</p></li><li><p>A Testing Agent</p></li><li><p>A Documentation Agent</p></li></ul><p>You can generate code with one agent and hand it off to another for validation.</p><p>That handoff mirrors real engineering teams. Responsibilities are separated. Validation is intentional. AI becomes structured, not improvisational.<br><br>Here&#8217;s an example of an <strong>Architecture Guardian </strong>agent:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;15ae9641-8a2e-4f49-8156-b36bb7f199ee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">---
name: arch-guardian
description: 'Architecture Guardian &#8212; reviews code for Clean Architecture violations'
tools:
  - search
  - read/readFile
---

# Architecture Guardian

You are the **Architecture Guardian** for this project. Your sole purpose is to protect the Clean Architecture boundaries.

## Your Personality

You are a vigilant castle guard. The architecture is your castle, and each layer is a wall. You never let a dependency sneak through the wrong gate.

## What You Do

When asked to review code or validate the architecture:

1. **Identify the layer** each file belongs to based on its path:
   - `src/Domain/` &#8594; Domain layer
   - `src/Application/` &#8594; Application layer
   - `src/Infrastructure/` &#8594; Infrastructure layer
   - `src/Api/` &#8594; Api layer

2. **Check `using` statements and references** for forbidden dependencies:
   - Domain &#8594; must NOT reference Application, Infrastructure, or Api
   - Application &#8594; must NOT reference Infrastructure or Api
   - Infrastructure &#8594; must NOT reference Api

3. **Check structural rules**:
   - Interfaces in `Domain/Interfaces/` must be interfaces (not classes)
   - Repository implementations must live in `Infrastructure/Repositories/`
   - Use cases must live in `Application/UseCases/`
   - DI registration must only happen in `src/Api/`

4. **Run the architecture tests** to confirm:

   ```
   dotnet test tests/ArchitectureTests --verbosity normal
   ```

5. **Report findings** clearly &#8212; list each violation with the file, line, and which rule was broken.

## Important

- NEVER suggest code that violates these rules.
- If asked to write code, always place it in the correct layer.
- If you find violations, provide corrected code that moves the logic to the proper layer.
</code></pre></div><h2>Hooks (Preview)</h2><p>Hooks allow you to execute custom logic automatically when specific events occur during AI-assisted workflows. This feature is currently in preview, but it introduces an important architectural capability.</p><p>Instead of relying only on instructions, you can attach enforcement mechanisms to lifecycle events inside the development flow.</p><p>Hooks can trigger scripts or commands at defined moments &#8212; for example before or after AI generates code.</p><p>You can use hooks to:</p><ul><li><p>Validate that required architectural patterns are present after generation</p></li><li><p>Run boundary-check scripts when a new file is created</p></li><li><p>Enforce dependency direction rules automatically</p></li><li><p>Trigger linting or architectural validation commands</p></li><li><p>Block unsafe patterns before changes are finalized</p></li></ul><p>Hooks turn architecture from documentation into execution.</p><p>Instead of trusting that AI follows your rules, you verify compliance programmatically.</p><p>Even though the feature is still in preview, it represents a major step toward embedding architectural governance directly into AI-assisted development workflows.<br></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tYKY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tYKY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 424w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 848w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1272w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tYKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png" width="537" height="232" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:232,&quot;width&quot;:537,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16263,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/189433015?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tYKY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 424w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 848w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1272w, https://substackcdn.com/image/fetch/$s_!tYKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67955cfb-948d-4a5d-9b04-8acc6a6d7dce_537x232.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;18499ab5-adfa-47d0-a996-6cf218dd5e8d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">{
  "hooks": {
    "PostToolUse": [
      {
        "type": "command",
        "command": "bash .github/hooks/scripts/arch-test.sh",
        "windows": "powershell -ExecutionPolicy Bypass -File .github/hooks/scripts/arch-test.ps1",
        "timeout": 60
      }
    ]
  }
}
</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;5229e91c-541f-41c7-933d-d4165c0f8a43&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">
#!/usr/bin/env pwsh
# PostToolUse hook &#8212; runs architecture tests after Copilot edits files
# Only triggers when the tool is "editFiles" (i.e. Copilot wrote code)

$input_json = [Console]::In.ReadToEnd() | ConvertFrom-Json

$tool = $input_json.tool_name

# Only run after file-editing tools
if ($tool -notin @("editFiles", "create_file", "replace_string_in_file", "write_to_file", "insert_edit")) {
    # Not a file edit &#8212; skip silently
    @{ continue = $true } | ConvertTo-Json
    exit 0
}

# Run architecture tests
$testResult = dotnet test tests/ArchitectureTests --no-restore --verbosity quiet 2&gt;&amp;1

if ($LASTEXITCODE -ne 0) {
    $output = @{
        continue           = $true
        hookSpecificOutput = @{
            hookEventName     = "PostToolUse"
            additionalContext = "ARCHITECTURE VIOLATION DETECTED! The architecture tests failed after your edit. Please review and fix the dependency rule violation. Test output: $($testResult -join "`n")"
        }
    }
    $output | ConvertTo-Json -Depth 3
    exit 0
}

@{ continue = $true } | ConvertTo-Json
exit 0</code></pre></div><h2>GitHub Copilot Code Review Agent</h2><p>Generating code is only half of the equation. The real protection happens during review.</p><p>GitHub Copilot&#8217;s code review agent brings AI into the pull request workflow. Instead of using AI only at the moment of creation, you can use it as an automated reviewer that analyzes changes before they reach your main branch.</p><p>From an architectural perspective, this is critical.</p><p>The review agent can:</p><ul><li><p>Detect violations of architectural boundaries</p></li><li><p>Flag missing required patterns</p></li><li><p>Identify risky dependency changes</p></li><li><p>Suggest improvements aligned with project conventions</p></li><li><p>Surface potential security or maintainability issues</p></li></ul><p>This transforms AI from a code accelerator into a quality gate participant.</p><p>When combined with branch protection rules and required status checks, the Copilot review agent becomes part of your governance layer. Every pull request passes through automated architectural scrutiny before human approval.</p><p>This mirrors how mature engineering organizations operate:</p><ul><li><p>Developer writes code</p></li><li><p>Automated systems validate it</p></li><li><p>Reviewers enforce standards</p></li><li><p>Only then does it reach production</p></li></ul><p>In a greenfield project, integrating AI into the review stage is one of the most effective ways to prevent architectural erosion while still benefiting from rapid generation.</p><h2>AI-Governed Systems in 2026 and Onward</h2><p>This is what a modern solution should start to look like.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ib1y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ib1y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 424w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 848w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1272w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ib1y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png" width="585" height="890" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:890,&quot;width&quot;:585,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79873,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/189433015?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ib1y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 424w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 848w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1272w, https://substackcdn.com/image/fetch/$s_!ib1y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2702f053-3e39-4b69-bc66-cd580dee15da_585x890.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Not just <code>src</code> and <code>tests</code>, but explicit architectural guardrails built into the repository itself. Dedicated folders for agents, skills, hooks, prompts, and enforcement scripts. Architecture is not something we design at the beginning and then forget &#8212; it needs mechanisms that continuously enforce boundaries as the system evolves. And what better way to achieve that than a well-structured AI governance process with a human in the loop?</p><h2>Final Thoughts</h2><p>None of the steps discussed here are complex or exotic. They are practical, easy to follow, and accessible to any team starting a greenfield project.</p><ul><li><p>Define your architecture clearly in the beginning.</p></li><li><p>Document it concisely.</p></li><li><p>Use custom instructions to encode your standards.</p></li><li><p>Leverage Agent Skills and Custom Agents to structure AI behavior.</p></li><li><p>Add hooks to automatically validate architectural rules when different AI agent events occur.</p></li><li><p>Enforce everything through pull requests, automated tests, and quality gates.</p></li></ul><p>If a team wants to move fast with AI while still building a system that remains understandable, maintainable, and scalable years from now, these steps are a great starting point.</p>]]></content:encoded></item><item><title><![CDATA[What Happens When You Open ChatGPT in Your Browser?]]></title><description><![CDATA[From the initial request in your browser to the response]]></description><link>https://systemshogun.com/p/what-happens-when-you-open-chatgpt</link><guid isPermaLink="false">https://systemshogun.com/p/what-happens-when-you-open-chatgpt</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 07 Feb 2026 09:00:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!U-a6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you type <strong>chat.com</strong> in your browser and press <strong>Enter</strong>, a lot happens in milliseconds. In today&#8217;s post I am going to try and explain it with a system diagram that I&#8217;ve built for this purpose.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U-a6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U-a6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 424w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 848w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1272w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U-a6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png" width="1456" height="858" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:858,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:254367,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186829366?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!U-a6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 424w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 848w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1272w, https://substackcdn.com/image/fetch/$s_!U-a6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F586ca2eb-4144-4bfe-84b3-0532006c4975_1847x1088.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>DNS Lookup</h3><p>Domain names exist so humans can remember websites more easily. It&#8217;s much easier to remember <strong>chat.com</strong> than <strong>104.18.41.60</strong>, right? But since every computer on the internet communicates using <strong>IP addresses</strong>, we need a way to translate a domain name into an IP address. That&#8217;s where <strong>DNS</strong> comes in.</p><p>Your browser first asks a simple question:<br><strong>&#8220;What&#8217;s the IP address of chat.com?&#8221;</strong></p><p>It checks caches (browser, OS, router, ISP).<br><br>If there&#8217;s no cached answer, it queries <a href="https://www.cloudflare.com/learning/dns/what-is-dns/">DNS </a>by traversing the DNS hierarchy: first the root servers, then the top-level domain servers (.com), and finally the authoritative name server, which contains the IP address for chat.com.</p><p>This IP is <strong><a href="https://en.wikipedia.org/wiki/Anycast">Anycast</a></strong>, meaning the same IP is advertised from many locations globally.</p><h4>What is Anycast?</h4><p>Anycast is a network addressing and routing method in which incoming requests can be routed to multiple different locations, or &#8220;nodes.&#8221; With Anycast, the same IP address is advertised from servers in multiple geographic locations. This approach is commonly used by CDNs, such as <strong>Cloudflare</strong>, and provides benefits related to security, reliability, and performance.</p><p>For example, a hacker performing a <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS </a>attack would require significantly more resources to have a meaningful impact, because the traffic is distributed across many servers and locations rather than concentrated on a single endpoint (<a href="https://en.wikipedia.org/wiki/Unicast">Unicast</a>).</p><p>And of course, another benefit of CDNs in general is also enabled by Anycast. You are routed as close as possible to resources such as images, videos, and text. Basically, Anycast routes your traffic (via BGP, as explained later) to the nearest server that can handle your request. So, if you open <strong>chat.com</strong>, you will hit a server that is closest to you and therefore experience lower latency and overall good performance. For example, if you are in France, your request will not be routed to the US, but rather to a nearby location, such as Paris.</p><p>So, at the end of the DNS lookup process, we get an IP address such as <strong>104.18.41.60</strong>, which belongs to <strong>Cloudflare</strong>, and not directly to OpenAI (ChatGPT).</p><h3>Border Gateway Protocol (BGP)</h3><p>BGP is the routing protocol of the internet. A hero almost no one talks about, yet it is the protocol that keeps the internet reliable by selecting paths between networks based on routing policies. To better understand how this works, let&#8217;s briefly talk about <a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">autonomous systems</a>.</p><h4>Autonomous systems</h4><p>The internet is a network of hundreds of thousands of smaller networks called <strong>autonomous systems (ASes)</strong>, each operated by a single organization (e.g., Cloudflare) and consisting of its own set of routers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wFD5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wFD5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 424w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 848w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1272w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wFD5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png" width="619" height="426" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d064e517-6bd1-4b81-902c-6fa348b48282_619x426.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:426,&quot;width&quot;:619,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52747,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186829366?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wFD5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 424w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 848w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1272w, https://substackcdn.com/image/fetch/$s_!wFD5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd064e517-6bd1-4b81-902c-6fa348b48282_619x426.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Basically, what BGP does is select a route from source to destination based on routing policies between autonomous systems, rather than simply choosing the path with the fewest hops. In practice, these policies can include factors such as business relationships, costs, and traffic agreements between ASes. But at a high level, the idea is simple: BGP determines how traffic moves between networks on the internet.</p><h3>TCP</h3><p>Once we have the source IP, destination IP, and port (443 in our case, because that&#8217;s what TLS uses), we are ready to start a TCP connection. This is handled underneath by the operating system (OS). The OS creates a TCP connection by adding the required headers&#8212;such as source IP, destination IP, and ports&#8212;and sending the packets over the network.</p><p>The process is a three-step procedure (commonly called the <strong><a href="https://www.geeksforgeeks.org/computer-networks/tcp-3-way-handshake-process/">three-way handshake</a></strong>), where the client and the server acknowledge each other and establish an ongoing connection between them.</p><h3>TLS</h3><p>Since we don&#8217;t want our chat messages to be visible to third parties, we establish an <strong>encrypted channel</strong> over TCP. This is done using <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a>, which relies on symmetric encryption algorithms such as <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES </a>to encrypt all data exchanged between the client and the server, including the chat messages. This works by negotiating a <a href="https://www.cloudflare.com/learning/ssl/what-is-a-session-key/">symmetric session key</a> that is then used by both parties for encryption and decryption.</p><h3>HTTP</h3><p>Now that we have a secure (TLS) and ongoing (TCP) session, we are ready to start communicating with ChatGPT. To do that, we rely on the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview">HTTP protocol</a>. Since we want to load the ChatGPT page in our browser, we first send an HTTP GET request to <strong>chat.com</strong>.</p><p>All of this communication is encrypted using the negotiated symmetric key, and the request is sent not directly to ChatGPT&#8217;s infrastructure, but to <strong>Cloudflare</strong>, because Cloudflare sits in front of ChatGPT to protect its infrastructure, provide CDN capabilities, and handle traffic at the edge for the duration of the session. Remember, you never communicate directly with ChatGPT&#8217;s web servers. </p><p>Once the request is received and validated, it is forwarded to one of ChatGPT&#8217;s many web servers, which processes it. The web server then returns the website resources&#8212;such as HTML, CSS, JavaScript, and images&#8212;to Cloudflare&#8217;s servers, which in turn send the response back to your browser.</p><h3>Browser</h3><p>When the browser receives the response from Cloudflare, it parses the HTML, CSS, and JavaScript files and builds the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">DOM</a>. The final step is rendering the page, where the browser renders the visual elements&#8212;that&#8217;s how you see ChatGPT in your browser.</p><h3>Thank you</h3><p>Now you know what happens when you open ChatGPT in your browser and all the steps that take place in milliseconds, almost like magic. If you found this post useful, please subscribe and share it with others.</p>]]></content:encoded></item><item><title><![CDATA[A Simple Architecture That Works for 90% of Applications]]></title><description><![CDATA[You&#8217;ll also learn how requests travel across the internet using the BGP protocol and autonomous systems that are rarely talked about.]]></description><link>https://systemshogun.com/p/a-simple-architecture-that-works</link><guid isPermaLink="false">https://systemshogun.com/p/a-simple-architecture-that-works</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 31 Jan 2026 07:36:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/J-GgXhU5iXE" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we&#8217;ll go over the system architecture that&#8217;s used in ~90% of the applications today. If you want a slightly deeper dive, please consider watching this short video that I made.</p><div id="youtube2-J-GgXhU5iXE" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;J-GgXhU5iXE&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/J-GgXhU5iXE?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h3>THE BASICS&#8230;</h3><p>Let&#8217;s start with the basics. A typical architecture usually has a <strong>Client</strong> for the user interface (UI), a <strong>Server </strong>for the business logic, and a <strong>Database</strong> to store state.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0zu1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0zu1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 424w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 848w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1272w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0zu1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png" width="1456" height="565" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:565,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:155767,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186052838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0zu1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 424w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 848w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1272w, https://substackcdn.com/image/fetch/$s_!0zu1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff905380d-121c-4f3f-bdcc-dfc01e70d38e_1637x635.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is an architecture we still see today, and it was predominant in the early days of the internet. However, it has a <strong>major flaw</strong> &#8212; it lacks mechanisms that improve <strong>security</strong> and <strong>availability</strong>.</p><p>Since there&#8217;s nothing in front of your web server to protect it, your web server is publicly visible and an easy target for all kinds of attacks, most notably<a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/"> distributed denial-of-service (DDoS)</a> attacks.</p><p>Another major issue is the lack of availability. What happens, for example, if your single web server goes down? Your clients won&#8217;t be happy, and your business might be at risk.</p><p>Of course, whether the lack of availability mechanisms is an issue or not depends entirely on the type of application you have. If this is just a simple web app containing your CV, for example, then this is okay. If this is banking software or a medical system, it is not.</p><p>Security, on the other hand, should never be a compromise when your system is exposed to the internet. Yes, a hacked banking system is far more damaging than someone hacking your CV website, but even a personal attack can affect you, your finances, and your reputation.</p><p>Let&#8217;s now fix these issues.</p><h3>MEET THE REVERSE PROXY</h3><p>There&#8217;s one service that helps mitigate the flaws of the architecture described above. Some call it a <strong>reverse proxy</strong>, others a <strong>load balancer</strong> or <strong>gateway</strong>. And while they are not always the same thing, they usually solve overlapping problems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!530O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!530O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 424w, https://substackcdn.com/image/fetch/$s_!530O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 848w, https://substackcdn.com/image/fetch/$s_!530O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1272w, https://substackcdn.com/image/fetch/$s_!530O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!530O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png" width="1204" height="844" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:844,&quot;width&quot;:1204,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:139987,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186052838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!530O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 424w, https://substackcdn.com/image/fetch/$s_!530O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 848w, https://substackcdn.com/image/fetch/$s_!530O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1272w, https://substackcdn.com/image/fetch/$s_!530O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F205412bb-cebf-4cd2-9f7c-1b07d2890f08_1204x844.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That&#8217;s it. One service that will improve your security tremendously and help you reach higher availability. But wait a minute, we now have <strong>two </strong>web servers, instead of one. Yes, that&#8217;s because we do want to utilize the <strong>load balancing </strong>feature of our reverse proxy. </p><p>Now, unlike in the first example above, <strong>if and when</strong> your server goes down, for example <strong>Server A</strong>, all the traffic will be automatically redirected to <strong>Server B</strong>. And just like that your business will no longer suffer and thus you&#8217;ve improved your overall availability.</p><p>And now you have protection from attacks, load balancing, caching, and TLS/SSL termination&#8212;features that usually come built in with these services in the cloud.</p><h3>Internet Routing</h3><p>And finally, I&#8217;m going to spend just a short amount of time on internet routing and try to answer the question of how a request reaches your load balancer in the first place.</p><p>Many people think it&#8217;s all thanks to the<a href="https://www.cloudflare.com/learning/dns/what-is-dns/"> Domain Name System (DNS)</a>, and while the DNS is extremely important part, there&#8217;s another protocol, without which the internet simply won&#8217;t work and that&#8217;s the <a href="https://aws.amazon.com/what-is/border-gateway-protocol/">Border Gateway Protocol</a> which basically finds the best route for your request based on latency, cost, geographic location, and others.</p><p>What we call &#8220;the internet&#8221; is a network of many large networks connected together, called autonomous systems. An <a href="https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/">autonomous system (AS)</a> is a large network&#8212;or a group of networks&#8212;operated under a single routing policy, and every device connected to the internet ultimately connects through one of these systems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yO6X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yO6X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 424w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 848w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yO6X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png" width="1456" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:276441,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/186052838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yO6X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 424w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 848w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1272w, https://substackcdn.com/image/fetch/$s_!yO6X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03b84134-c343-4b56-befe-81b8ddd7393a_3019x1286.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each autonomous system uses BGP to announce which IP address ranges it is responsible for and which other autonomous systems it connects to. BGP routers collect this information from ASes around the world and build routing tables that determine how traffic should flow from one AS to another. When a packet arrives, a router consults its routing table to decide which AS the packet should be forwarded to next.</p><p>Because there are so many autonomous systems globally, BGP routers constantly update their routing tables. As networks go offline, new networks come online, or ASes change the IP ranges they advertise, these changes are propagated via BGP so routers can continuously adjust their routing decisions.</p><p>I simplified the diagram above, but if you&#8217;ve read this far, I hope you now have a better idea of what happens when you visit a website&#8212;or your own app&#8212;and how and why it all happens so fast. I&#8217;m definitely going to make a video and a blog post on BGP and how it works, and I&#8217;ll write about it here as well, so I&#8217;ll keep you posted.</p><p>Thank you for reading this.</p>]]></content:encoded></item><item><title><![CDATA[My Experiment Building Distributed Online Store in Aspire and .NET]]></title><description><![CDATA[How I built a distributed e-commerce application using Aspire and .NET]]></description><link>https://systemshogun.com/p/my-experiment-building-distributed</link><guid isPermaLink="false">https://systemshogun.com/p/my-experiment-building-distributed</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Wed, 21 Jan 2026 06:41:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!a_w7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lately, I&#8217;ve been experimenting around with <a href="https://aspire.dev/">Aspire </a>by building a simple e-commerce distributed application that consists of a few microservices: Payments, Orders, Notifications, Invoices, Catalog, Shipping, Cart, and a single Web UI. </p><p>Each one of the microservice has its own SQL database (except for Cart, which uses Redis), all of which containerized. The idea of my experiment was to see how easy it really makes the life of a developer that&#8217;s been assigned to work on a distributed system with lots of moving parts.</p><p>Here&#8217;s what the solution structure looks like.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a_w7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a_w7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 424w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 848w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1272w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a_w7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png" width="673" height="627" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:627,&quot;width&quot;:673,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55584,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/184958773?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!a_w7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 424w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 848w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1272w, https://substackcdn.com/image/fetch/$s_!a_w7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b28fef8-84a5-42ec-b05f-15f40ec956e2_673x627.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>And here&#8217;s the architecture diagram that represents the action flow.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qui8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qui8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 424w, https://substackcdn.com/image/fetch/$s_!qui8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 848w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1272w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qui8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png" width="1456" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:367705,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/184958773?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qui8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 424w, https://substackcdn.com/image/fetch/$s_!qui8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 848w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1272w, https://substackcdn.com/image/fetch/$s_!qui8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74f87d72-fc06-4ea4-960e-832da97ebda2_3034x1334.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The flow starts with the user paying for the items they have in their shopping cart (Redis). Once the payment goes through, a call to Order microservice creates a new order with all the required information. Once the new order is created in the database the service pushes a new <strong>OrderCreated</strong> event to notify the whole system. Now, this is just a quick and dirty. In a real scenario we should consider something like <a href="https://microservices.io/patterns/data/saga.html">The Saga Pattern</a> to ensure that we don&#8217;t leave our system in a corrupt state (e.g., the insert in the order database failed, but we pushed an event for the new order)</p><p>To implement the event-based communication flow I used a library called <a href="https://particular.net/nservicebus">NServiceBus</a>. It is a paid solution, but available for free as long as your solution is not reaching a production environment (e.g., free for Dev, Test, QA, UAT).</p><p>Now, what exactly is <strong>Aspire</strong>?</p><p>Think of Aspire as a production-ready platform (from Microsoft) that allows you to build, test, deploy, debug, and run distributed applications. It provides built-in tooling for local development, containerization, orchestration, and observability, making it much easier to work with distributed systems.</p><p>Aspire really makes things so much easier. For example, see how easily I&#8217;ve added <a href="https://www.rabbitmq.com/">RabbitMQ</a> as a messaging broker to my solution.</p><pre><code>// RabbitMQ
var messaging = builder
    .AddRabbitMQ("messaging")
    .WithManagementPlugin(15672)
    .WithLifetime(ContainerLifetime.Persistent);</code></pre><p>Here&#8217;s my SQL database.</p><pre><code>// SQL Server
var sql = builder
    .AddSqlServer("sql")
    .WithLifetime(ContainerLifetime.Persistent);</code></pre><p>And that&#8217;s my Redis cache.</p><pre><code>// Redis
var cartcache = builder.AddRedis("cartcache")
    .WithLifetime(ContainerLifetime.Persistent);</code></pre><p>And this is how I&#8217;m creating the Orders microservice, its own SQL database, and then injecting the RabbitMQ messaging that we declared above to be used in it for event-based communication.</p><pre><code>// Catalog
var catalogDb = sql.AddDatabase("CatalogDb", "eCommerceDemo.Catalog");
var catalog = builder.AddProject&lt;Projects.eCommerceDemo_Catalog&gt;("catalog")
    .WithReference(catalogDb)
    .WaitFor(catalogDb)
    .WithReference(messaging)
    .WaitFor(messaging);</code></pre><p>Tell me that isn&#8217;t easy!</p><p>If you&#8217;re curious about how this looks in action you should check my latest video where I give a detailed walkthrough. You&#8217;ll see how a single order creation triggers other services and we&#8217;ll even send an email.</p><div id="youtube2-1EAZrxnOXiA" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;1EAZrxnOXiA&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/1EAZrxnOXiA?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[You Probably Don't Need SignalR in .NET 10 (use Server-Sent Events)]]></title><description><![CDATA[Learn how to implement Server-Sent Events in .NET 10 (code demo)]]></description><link>https://systemshogun.com/p/you-probably-dont-need-signalr-in</link><guid isPermaLink="false">https://systemshogun.com/p/you-probably-dont-need-signalr-in</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Wed, 24 Dec 2025 15:47:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ZZBx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Do you need SignalR?</h3><p>If your app only needs <strong>server-to-browser</strong> notifications, then you really don&#8217;t need the heavy <a href="https://dotnet.microsoft.com/en-us/apps/aspnet/signalr">SignalR</a> and all the packages, rules and protocols it comes with. You need <a href="https://en.wikipedia.org/wiki/Server-sent_events">Server-Sent events</a>, and in .NET 10 they are extremely easy to implement. </p><p>In today&#8217;s blog post I&#8217;m going to show you how you can get started.</p><p>Today&#8217;s demo shows how to build real-time UI updates in .NET 10 <strong>without SignalR</strong>, using plain Server-Sent Events. An Azure Function simulates a weather sensor writing data to the database, while a Minimal API streams the updates live to a Blazor WASM frontend.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZZBx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZZBx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 424w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 848w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1272w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png" width="1456" height="357" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:357,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:93322,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZZBx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 424w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 848w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1272w, https://substackcdn.com/image/fetch/$s_!ZZBx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc97dbf54-f195-496b-a65a-c9a8d761cf95_2160x530.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>The solution consists of three projects:</p><ul><li><p>Blazor WASM (for UI)</p></li><li><p>.NET 10 Web API (for SSE)</p></li><li><p>.NET 10 Function App (for updating SQL database with fictious weather data)</p></li><li><p>SQL Database (for storing sensor / weather data)</p></li></ul><p>Required packages (for database manipulation):</p><ul><li><p>Microsoft.EntityFrameworkCore</p></li><li><p>Microsoft.EntityFrameworkCore.SqlServer</p></li></ul><p>I&#8217;m only going to share core code snippets here. See the full solution on GitHub <a href="https://github.com/kaldren/ServerSentEventsDemo">here</a>. </p><h3>Database</h3><p>Our database has only one table called <code>WeatherForecasts</code> with basic structure (<code>Id </code>for PK, <code>CapturedAt</code>, <code>TemperatureC </code>and <code>Summary</code>)</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LCKK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LCKK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 424w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 848w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1272w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LCKK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png" width="1456" height="257" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:257,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16022,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LCKK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 424w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 848w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1272w, https://substackcdn.com/image/fetch/$s_!LCKK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5bd0d2-71de-4db6-89d8-bf2e557ea9a1_1515x267.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Function App (SSE.WeatherSensor)</h3><p>Our Function App runs every 10 seconds and inserts weather data into the database, simulating a live sensor.</p><p>Here&#8217;s what the table looks like after it&#8217;s populated with weather data generated by the Function App.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MztH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MztH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 424w, https://substackcdn.com/image/fetch/$s_!MztH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 848w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1272w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MztH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png" width="701" height="345" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:345,&quot;width&quot;:701,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31533,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MztH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 424w, https://substackcdn.com/image/fetch/$s_!MztH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 848w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1272w, https://substackcdn.com/image/fetch/$s_!MztH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0538052b-09ed-497b-8c85-92fc86fe5955_701x345.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>API (SSE.API)</h3><p>This is the API endpoint that implements SSE. It polls the database every 5 seconds and streams the latest weather data to connected clients (our Blazor WASM).</p><pre><code>app.MapGet("api/weatherforecast", (HttpContext context, SSEDbContext dbContext) =&gt;
{

    async IAsyncEnumerable&lt;WeatherForecast&gt; GetWeatherForecastStream([EnumeratorCancellation] CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var latestForecast = await dbContext.WeatherForecasts
                .OrderByDescending(wf =&gt; wf.CapturedAt)
                .FirstOrDefaultAsync(cancellationToken);

            if (latestForecast != null)
            {
                yield return latestForecast;
            }

            await Task.Delay(5000, cancellationToken);
        }
    }

    if (context.Request.Headers["Accept"] == "text/event-stream")
    {
        return Results.ServerSentEvents(GetWeatherForecastStream(context.RequestAborted));
    }
    else
    {
        return Results.BadRequest("Unsupported Accept header. Use 'text/event-stream'.");
    }
});</code></pre><h3>UI (SSE.Web)</h3><p>This is our client app that subscribes to the SSE endpoint and renders the live weather updates in real time.</p><pre><code>@page "/weather"
@using System.Net.ServerSentEvents
@using System.Text.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@inject HttpClient Http

&lt;h3&gt;Live Weather Sensor Feed&lt;/h3&gt;

@if (forecast == null &amp;&amp; string.IsNullOrEmpty(errorMessage))
{
    &lt;p&gt;&lt;em&gt;Waiting for sensor data...&lt;/em&gt;&lt;/p&gt;
}
else {
    &lt;table class="table"&gt;
        &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;Time (Local)&lt;/th&gt;
                &lt;th&gt;Temp (C)&lt;/th&gt;
                &lt;th&gt;Summary&lt;/th&gt;
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;

            &lt;tr style="border: 2px solid black;"&gt;
                &lt;td&gt;@forecast.CapturedAt.ToLocalTime().ToString("HH:mm:ss")&lt;/td&gt;
                &lt;td&gt;@forecast.TemperatureC&#176;C&lt;/td&gt;
                &lt;td&gt;@forecast.Summary&lt;/td&gt;
            &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
}


@if (!string.IsNullOrEmpty(errorMessage))
{
    &lt;div class="alert alert-danger"&gt;@errorMessage&lt;/div&gt;
}

@code {
    private WeatherForecast forecast;
    private string? errorMessage;

    public class WeatherForecast
    {
        public int Id { get; set; }
        public DateTimeOffset CapturedAt { get; set; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
    }

    protected override async Task OnInitializedAsync()
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:7194/api/weatherforecast");
            request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/event-stream"));

            using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
            using var stream = await response.Content.ReadAsStreamAsync();

            var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
            var parser = SseParser.Create(stream, (type, data) =&gt;
                JsonSerializer.Deserialize&lt;WeatherForecast&gt;(data, options));

            await foreach (var item in parser.EnumerateAsync())
            {
                if (item.Data is not null)
                {
                    forecast = item.Data;

                    await InvokeAsync(StateHasChanged);
                }
            }
        }
        catch (Exception ex)
        {
            errorMessage = $"Connection lost: {ex.Message}";
            await InvokeAsync(StateHasChanged);
        }
    }
}</code></pre><h3>Demo</h3><p>Here&#8217;s the whole solution in action.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hvIz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hvIz!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hvIz!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif" width="1456" height="444" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:444,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6177494,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hvIz!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!hvIz!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f5bdaa0-b817-4cb8-895a-cee7c346c687_2584x788.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>TL;DR:</strong> If your use case is <strong>server-to-browser real-time updates only</strong>, you probably don&#8217;t need SignalR &#8212; SSE is simpler, cheaper, and easier to maintain. Make sure to consider it for your next project.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n9V2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n9V2!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n9V2!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12372827,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182508428?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n9V2!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 424w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 848w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1272w, https://substackcdn.com/image/fetch/$s_!n9V2!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8912e69f-389d-443d-a7e0-1ccb77b8c305_2584x788.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div>]]></content:encoded></item><item><title><![CDATA[Stop Building Monoliths! Build Modular Monoliths Instead]]></title><description><![CDATA[Intro to Modular Monolith Architecture]]></description><link>https://systemshogun.com/p/stop-building-monoliths-build-modular</link><guid isPermaLink="false">https://systemshogun.com/p/stop-building-monoliths-build-modular</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 22 Dec 2025 07:18:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BWwR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we&#8217;ll talk about <strong>Modular Monolith architecture</strong> &#8212; what it is, the problems it solves, and why I believe it&#8217;s the right starting point for many modern applications. We&#8217;ll also walk through a few diagrams and a real demo app I&#8217;ve built to see how this looks in an actual codebase.</p><h2>What is a Modular Monolith?</h2><p>A Modular Monolith is a software architecture that runs as a <strong>single process</strong>, just like a traditional monolith. The difference is that instead of one large, tangled codebase, the application is <strong>explicitly split into well-defined modules</strong>.</p><p>Each module represents a <strong>separate domain</strong> and owns its own logic. The boundaries are clear, enforced in code, and intentional &#8212; not just folders pretending to be architecture, but separate class libraries.</p><h2>What is a Module?</h2><p>A module is a <strong>self-contained logical unit</strong> that represents a specific business capability (for example, Payments or Reviews). Its sole purpose is to model that specific business domain &#8212; exactly how you would design a microservice.</p><p>Each module behaves like a <strong>separate application</strong> focused on a single business use case. It has its own API layer and its own data store (a separate schema or even a separate database). In other words, it&#8217;s an independent application that happens to live <strong>inside the monolith</strong>, and it communicates with other modules <strong>only through its public API</strong>.</p><p>And this is the key point: even though all modules run inside a <strong>single process</strong>, they are designed so that, if needed, they can later be <strong>extracted into independent microservices</strong> with minimal friction.</p><p>Here&#8217;s how a typical module looks like.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0ypK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0ypK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 424w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 848w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1272w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0ypK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png" width="1041" height="185" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:185,&quot;width&quot;:1041,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19759,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0ypK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 424w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 848w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1272w, https://substackcdn.com/image/fetch/$s_!0ypK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b9bc912-5849-449c-91bc-e3830eb4c78f_1041x185.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Each module exposes its own <strong>public API</strong>, and that is the <strong>only</strong> way other modules are allowed to access its logic. This is extremely important because it enforces <strong>loose coupling</strong>, making modules easier to extract later &#8212; which is at the core of the Modular Monolith&#8217;s benefits.</p><p>In addition, every module owns its <strong>own data store</strong> &#8212; either a separate schema within a shared database or a completely separate database. Data ownership is strict and enforced at the module boundary.</p><p>In short, think of each module as an independent API that just happens to live inside the same application.</p><h2>Why use Modular Monolith?</h2><p>A Modular Monolith is powerful because it lets you <strong>start simple</strong>, while still enforcing <strong>clear boundaries</strong> that allow the system to scale later if &#8212; and only if &#8212; it actually becomes necessary.</p><p>You get the simplicity of a monolith today, but by designing proper modules, you&#8217;re deliberately preparing for a future where parts of the system may need to be <strong>distributed into microservices</strong>. If that time comes, the transition is far less painful than trying to break apart a traditional, tightly coupled monolith.</p><p>Yes, you <em>can</em> start directly with a microservices architecture. But in most cases, that&#8217;s a <strong>bad idea</strong>, even when you&#8217;re convinced you&#8217;ll eventually need it (this is exactly what <a href="https://martinfowler.com/bliki/MonolithFirst.html">Monolith First by Martin Fowler</a> argues).</p><p>By building a Modular Monolith, you get several concrete benefits that help you <strong>ship faster and at lower cost</strong> &#8212; which, in today&#8217;s enterprise world, is non-negotiable (everyone wants it yesterday):</p><ul><li><p><strong>Easy to start with</strong> &#8212; it&#8217;s still a monolith</p></li><li><p><strong>Simple CI/CD</strong> &#8212; a single deployable unit</p></li><li><p><strong>Clear boundaries</strong> &#8212; making future migration to microservices significantly easier</p></li><li><p><strong>Easier to scale when needed</strong> &#8212; both technically and organizationally</p></li><li><p><strong>Better maintainability and team ownership</strong> &#8212; teams work on modules without stepping on each other</p></li></ul><h2>Real Estate Platform</h2><p>Let&#8217;s take a fictional real estate platform as an example &#8212; a place where people list their homes for others to buy. In this scenario, a Modular Monolith solution could look like this.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BWwR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BWwR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 424w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 848w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1272w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BWwR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png" width="1456" height="664" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:664,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132900,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BWwR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 424w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 848w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1272w, https://substackcdn.com/image/fetch/$s_!BWwR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8582683b-9ffa-4f96-a996-2d851c2485a9_1492x680.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At this point, it&#8217;s important to understand what this diagram <strong>does not</strong> represent.</p><p>This is <strong>not</strong> a microservices architecture. All modules run inside a <strong>single process</strong>, are deployed together, and share the same runtime. There is no network communication between them, no service discovery, no distributed tracing, and no operational overhead.</p><p>Yet, from a <strong>design perspective</strong>, each module behaves as if it were a microservice.</p><p>Each module:</p><ul><li><p>Owns its <strong>business logic</strong></p></li><li><p>Exposes a <strong>public API</strong></p></li><li><p>Owns its <strong>data</strong></p></li><li><p>Has <strong>no direct access</strong> to other modules&#8217; internals or databases</p></li></ul><p>This is what gives the Modular Monolith its power: <strong>strong boundaries without distribution</strong>.</p><h2>Module-to-Module Communication</h2><p>So how do modules talk to each other? Let&#8217;s take <strong>Listings</strong> and <strong>Reviews</strong> as an example.</p><p>Imagine we&#8217;re building a page that needs to display a specific listing (for example, a house on XYZ Street) along with all the reviews people have left for it.</p><p>The Listings module should not reach directly into the Reviews database, and the Reviews module should not query Listings data directly. Instead, communication happens strictly through each module&#8217;s public API.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZOWt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZOWt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 424w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 848w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1272w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png" width="1402" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1402,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105371,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZOWt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 424w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 848w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1272w, https://substackcdn.com/image/fetch/$s_!ZOWt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a6b8cdf-252e-4955-8337-0082f299cc69_1402x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The application first calls the <strong>Listings</strong> module to fetch the listing data by <code>listingId</code>. Then, using the same identifier, it calls the <strong>Reviews</strong> module through its public API to retrieve all reviews associated with that listing.</p><p>Both modules:</p><ul><li><p>Access <strong>only their own database</strong></p></li><li><p>Expose <strong>explicit read models</strong> through their APIs</p></li><li><p>Remain completely unaware of each other&#8217;s internal implementation</p></li></ul><p>Even though these calls happen <strong>inside the same process</strong>, they are treated exactly like service-to-service calls. No shared repositories. No cross-module ORM access.</p><p>This discipline is what keeps the system modular.</p><p>If, at some point, the Reviews module needs to be extracted into a separate microservice, this flow remains largely unchanged &#8212; the in-process API call simply becomes a network call. The contract stays the same.</p><p>This is the core idea behind Modular Monolith communication: <strong>design for distribution, without paying the distribution cost upfront</strong>.</p><h2>Modular Monolith Solution</h2><p>To make this concrete, here&#8217;s how this looks in a <strong>real codebase</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B6LO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B6LO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 424w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 848w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1272w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B6LO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png" width="366" height="923" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f650946b-1737-4bc6-97b2-c79912b53294_366x923.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:923,&quot;width&quot;:366,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:45515,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B6LO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 424w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 848w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1272w, https://substackcdn.com/image/fetch/$s_!B6LO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff650946b-1737-4bc6-97b2-c79912b53294_366x923.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The solution is structured around a <strong>Modules</strong> folders, where each module represents a distinct business capability &#8212; Accounts, Listings, Reviews, and so on.</p><p>Each module is fully self-contained and separate class library that follows the same internal structure built around <a href="https://devblogs.microsoft.com/ise/next-level-clean-architecture-boilerplate/">Clean Architecture</a>:</p><ul><li><p><strong>API</strong> &#8212; the module&#8217;s public surface, exposed to other modules</p></li><li><p><strong>Application</strong> &#8212; use cases, orchestration, and business workflows</p></li><li><p><strong>Domain</strong> &#8212; core business logic and domain models</p></li><li><p><strong>Infrastructure</strong> &#8212; data access, external integrations, persistence</p></li></ul><p>This is not accidental. Every module is treated as a <strong>mini application</strong> with clear ownership and explicit boundaries.</p><p>The <strong>Shared</strong> projects contain only truly cross-cutting concerns &#8212; things like the database abstraction, shared kernel primitives, or mediator implementation (a custom one I built since <a href="https://www.jimmybogard.com/automapper-and-mediatr-going-commercial/">MediatR</a> is now commercial). There is no shared business logic between modules.</p><p>At the top level, there&#8217;s a single <strong>API host</strong> that wires everything together and a single <strong>Web UI</strong> consuming the system &#8212; reinforcing the fact that this is still a <strong>single deployable unit</strong>, running in a <strong>single process</strong>.</p><p>This structure enforces discipline:</p><ul><li><p>Modules don&#8217;t reach into each other&#8217;s internals</p></li><li><p>Data access stays inside module boundaries</p></li><li><p>Communication happens only through public APIs</p></li></ul><p>And most importantly, if one of these modules ever needs to become a standalone microservice, most of the work is already done &#8212; the boundaries, contracts, and responsibilities are already in place.</p><p>That&#8217;s the real power of a Modular Monolith: <strong>microservice-level design, without microservice-level complexity</strong>.</p><p>Lastly, let&#8217;s take a deeper look at one of the modules &#8212; the <strong>Listings</strong> module.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WnON!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WnON!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 424w, https://substackcdn.com/image/fetch/$s_!WnON!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 848w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1272w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WnON!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png" width="424" height="759" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:759,&quot;width&quot;:424,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41516,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/182301692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WnON!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 424w, https://substackcdn.com/image/fetch/$s_!WnON!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 848w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1272w, https://substackcdn.com/image/fetch/$s_!WnON!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a36129e-3287-4b53-a4ef-6432f9f0c6ba_424x759.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At first glance, you&#8217;ll notice that the module follows the same internal structure as the others: <strong>API</strong>, <strong>Application</strong>, <strong>Domain</strong>, and <strong>Infrastructure</strong>. This is intentional and consistent across all modules, reinforcing clear boundaries and ownership.</p><h3>API</h3><p>The <strong>API</strong> project is the module&#8217;s public entry point. It exposes endpoints (for example, <code>ListingsEndpoints.cs</code>) and defines how the outside world &#8212; including other modules &#8212; can interact with Listings. Nothing outside the module talks directly to its internals.</p><h3>Application</h3><p>The <strong>Application</strong> layer contains the use cases and orchestration logic. This is where the module&#8217;s behavior lives &#8212; not in controllers, not in infrastructure.</p><p>The important part here is the <strong>Features</strong> folder.</p><p>Each feature (like <code>CreateListing</code>, <code>GetListingById</code>, or <code>GetUserListings</code>) is implemented as a <strong>vertical slice</strong> &#8212; meaning everything related to that use case lives together. This follows <strong>Jimmy Bogard&#8217;s Vertical Slice Architecture</strong>.</p><p>Instead of grouping code by technical concerns (controllers, services, handlers), we group it by <strong>business intent</strong>.</p><p>Each feature typically contains:</p><ul><li><p>The request/command</p></li><li><p>The handler</p></li><li><p>Validation</p></li><li><p>Any feature-specific logic</p></li></ul><p>This makes features easy to understand, easy to modify, and easy to delete.</p><h3>Domain</h3><p>The <strong>Domain</strong> project contains the core business logic:</p><ul><li><p>Entities</p></li><li><p>Value Objects</p></li><li><p>Domain Events</p></li></ul><p>This layer is pure business logic. It has no dependencies on infrastructure, frameworks, or external concerns. It represents the heart of the Listings domain.</p><h3>Infrastructure</h3><p>The <strong>Infrastructure</strong> project handles everything related to persistence and external systems &#8212; for example, database access and ORM configuration. This is the only place where the module touches technical details like EF Core.</p><h3>Why this works so well</h3><p>This combination &#8212; <strong>Modular Monolith + Clean boundaries + Vertical Slice Architecture</strong> &#8212; gives you:</p><ul><li><p>Strong separation between business domains</p></li><li><p>Highly focused, readable features</p></li><li><p>Minimal coupling between modules</p></li><li><p>A structure that scales without becoming unmanageable</p></li></ul><p>Each module is cohesive. Each feature is explicit. And the entire system remains a <strong>single, simple deployable unit</strong>.</p><h2>Final Words</h2><p>I hope this post helps you appreciate the Modular Monolith approach &#8212; and gives you a clear idea of how to build one.</p><p>I strongly believe you should always start small and avoid overengineering. Every large company started small, and they only scaled when it became necessary. Don&#8217;t start with expensive decisions. Build iteratively.</p><p>Don&#8217;t begin with the idea of building the next Netflix. Start with a basic streaming app, then add one feature, then another &#8212; but keep it simple. Once you see real demand, <em>then</em> you can justify adding complexity. Not before.</p><p>This is exactly why Modular Monolith fits my mindset so well. It keeps you on the right track: a single-process application, built with clear module boundaries around real business capabilities. And if your product succeeds and you truly need to scale out, you can evolve the right modules into microservices with far less pain.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Maybe Microsoft Finally Got It Right with Microsoft Agent Framework]]></title><description><![CDATA[Let's talk about Microsoft's NEWEST agentic framework &#8212; Microsoft Agent Framework]]></description><link>https://systemshogun.com/p/maybe-microsoft-finally-got-it-right</link><guid isPermaLink="false">https://systemshogun.com/p/maybe-microsoft-finally-got-it-right</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sun, 21 Dec 2025 06:23:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/v92-htX8lBg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I mean&#8230; c&#8217;mon. Microsoft has shipped <em>a lot</em> of &#8220;next big thing&#8221; AI frameworks lately.<br>First it was Semantic Kernel. Then AutoGen showed up. Every few months, there&#8217;s a new framework, a new promise, and a new &#8220;this is the future&#8221; narrative.</p><p>And now we have <a href="https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview">Microsoft Agent Framework</a>, which, according to Microsoft is&#8230; the best agentic framework that combines the strengths of Semantic Kernel and AutoGen.</p><p>I&#8217;ve heard that before. But as techies and architects, we need to be aware of what&#8217;s coming so we can prepare and make informed decisions about solutions at work. </p><p>In my newest YouTube video, I show a quick demo where we use the Microsoft Agent Framework and the A2A protocol to enable communication between agents, using tools to persist state in Cosmos DB. </p><p>Who knows if this is really the path forward for Microsoft? At the very least, it&#8217;s fun&#8212;and since it&#8217;s Sunday, why not give it a try?</p><div id="youtube2-v92-htX8lBg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;v92-htX8lBg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/v92-htX8lBg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Stop Asking One LLM. Ask a Council Instead.]]></title><description><![CDATA[An overview of the LLM Council repo - a vibe coded project from Andrej Karpathy where you send prompts to a board of AIs]]></description><link>https://systemshogun.com/p/stop-asking-one-llm-ask-a-council</link><guid isPermaLink="false">https://systemshogun.com/p/stop-asking-one-llm-ask-a-council</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 06 Dec 2025 06:56:57 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5ee077e3-287f-417b-a30b-44cd88525ff2_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I stumbled upon a fascinating repo from Andrej Karpathy: <strong><a href="https://github.com/karpathy/llm-council">LLM Council</a></strong>.</p><p>It&#8217;s a lightweight local web app that looks like ChatGPT, but instead of relying on a single model, it lets multiple LLMs answer your question, critique each other, and then produce a final, consolidated result.</p><p>Here&#8217;s the core idea, quoting the repo:</p><blockquote><p>Instead of asking a question to your favorite LLM provider (OpenAI GPT-5.1, Google Gemini, Anthropic Claude, xAI Grok, etc.), you can group them into your &#8220;LLM Council&#8221;.</p></blockquote><p>Your query is sent to all of them through <a href="https://openrouter.ai/">OpenRouter</a>, they each give an answer, they review and rank each other&#8217;s work, and finally a Chairman LLM produces the final response.</p><p>And honestly &#8212; it&#8217;s brilliant.</p><h2><strong>Why This Is Cool</strong></h2><p>When you ask a single LLM a question, you&#8217;re trusting one model&#8217;s reasoning, training data, biases, and blind spots.</p><p>With <em>LLM Council</em>, a few things change:</p><ul><li><p>Multiple models answer the same question independently.</p></li><li><p>They then <strong>evaluate each other&#8217;s responses</strong>.</p></li><li><p>The system picks a <strong>&#8220;Chairman LLM&#8221; </strong>(you choose which) to read all answers and critiques, and then write a final, improved response.</p></li></ul><p>It&#8217;s like having a panel of experts debate the prompt before speaking to you.</p><h2><strong>Why This is Cool?</strong></h2><p>LLMs behave differently:</p><ul><li><p>Some are great with code (Claude).</p></li><li><p>Some are great coders and overall thinkers (GPT-5.1).</p></li><li><p>Some are creative and fast (Gemini).</p></li><li><p>Some are cheap and fast (Mixtral, Qwen, etc.).</p></li></ul><p>Instead of guessing which one is best for each prompt, LLM Council lets them <strong>collaborate</strong>.</p><p>As models become more specialized, this &#8220;debate &#8594; critique &#8594; consolidate&#8221; pattern becomes extremely powerful.</p><p>It&#8217;s basically a tiny research lab running on your laptop.</p><h2><strong>Trying It Out</strong></h2><p>The repo is simple to clone and run. Basically, you just follow the README and you&#8217;re done. You&#8217;ll need an <strong>OpenRouter API key</strong> &#8212; everything else works out of the box.</p><h2><strong>Demo</strong></h2><h3><strong>Running It on Windows</strong></h3><p>If you&#8217;re on Windows, there&#8217;s one extra step: the repo uses a <code>start.sh</code> shell script.<br>Windows doesn&#8217;t run <code>.sh</code> files natively, so you need a shell environment.</p><p>I used <strong><a href="https://git-scm.com/install/">Git Bash</a></strong> (included with Git for Windows) to run the script:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sRgU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sRgU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 424w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 848w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1272w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sRgU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png" width="578" height="367" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:367,&quot;width&quot;:578,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:25468,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/180861958?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sRgU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 424w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 848w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1272w, https://substackcdn.com/image/fetch/$s_!sRgU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f5e12e-62e4-4c0b-8f7f-5692d67aba6f_578x367.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Mac and Linux users don&#8217;t need this &#8212; both platforms support shell scripts out of the box.</p><p>Here&#8217;s a quick demo of how this works with a prompt &#8220;How to become a great solution architect? I want to be able to build systems like Netflix and Uber.&#8221;</p><div id="youtube2--72SM6Fe10w" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;-72SM6Fe10w&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/-72SM6Fe10w?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Upload Files to Your API from a Copilot Studio Agent]]></title><description><![CDATA[Learn how to upload files from your Copilot Studio agent to your own API]]></description><link>https://systemshogun.com/p/upload-files-to-your-api-from-a-copilot</link><guid isPermaLink="false">https://systemshogun.com/p/upload-files-to-your-api-from-a-copilot</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Tue, 25 Nov 2025 10:31:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q5eM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Copilot Studio has hundreds of built-in integrations, yet there is virtually no documentation on how to upload files to your own API. You can easily send files to SharePoint or Google Drive, but the moment you want to send one to your own backend, you&#8217;re on your own.</p><p>That&#8217;s why in today&#8217;s post, I&#8217;ll walk you through how I managed to make it work and, hopefully, help you apply it in your own projects.</p><h3>The API</h3><p>First, here&#8217;s how my API looks. It receives the file from Copilot Studio, performs basic validation, and uploads it to a storage account.</p><pre><code>    [HttpPost(&#8221;upload&#8221;)]
    public async Task&lt;IActionResult&gt; Upload([FromBody] UploadFileRequest request)
    {
        if (string.IsNullOrWhiteSpace(request.ContentBytes))
            return BadRequest(&#8221;contentBytes is missing&#8221;);

        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(&#8221;name is missing&#8221;);

        // Handle possible data:image/png;base64,...
        var base64 = request.ContentBytes.Contains(&#8217;,&#8217;)
            ? request.ContentBytes.Split(&#8217;,&#8217;).Last()
            : request.ContentBytes;

        byte[] fileBytes;

        try
        {
            fileBytes = Convert.FromBase64String(base64);
        }
        catch
        {
            return BadRequest(&#8221;Invalid base64 content&#8221;);
        }

        var container = _blobServiceClient.GetBlobContainerClient(&#8221;uploadedfiles&#8221;);
        await container.CreateIfNotExistsAsync();

        var blob = container.GetBlobClient(request.Name);

        using var stream = new MemoryStream(fileBytes);
        await blob.UploadAsync(stream, overwrite: true);

        return Ok(new
        {
            FileName = request.Name,
            Size = fileBytes.Length,
            Status = &#8220;Uploaded&#8221;
        });
    }
}</code></pre><p>Here&#8217;s my <strong>UploadFileRequest</strong><code> request </code>model. This is the format I am sending from Copilot Studio, so it needs strictly match it.</p><pre><code>public class UploadFileRequest
{
    public string ContentBytes { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
}</code></pre><h3>Copilot Studio Flow</h3><p>Next, I created a flow for the whole file upload process. The flow expects a file, then it runs an HTTP post against my API, attaching the file content and the file name.</p><p><strong>contentBytes</strong>: <code>triggerBody()?[&#8217;file&#8217;][&#8217;content&#8217;]</code></p><p>To add it, use the F(x) icon on the right.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5fY3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5fY3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 424w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 848w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1272w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5fY3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png" width="794" height="204" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:204,&quot;width&quot;:794,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20540,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5fY3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 424w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 848w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1272w, https://substackcdn.com/image/fetch/$s_!5fY3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7ca8ffd-f81c-4ae4-a9d6-2f9b1943ce43_794x204.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Then add it here and save it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CsLw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CsLw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 424w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 848w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1272w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CsLw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png" width="489" height="259" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:259,&quot;width&quot;:489,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5974,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CsLw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 424w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 848w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1272w, https://substackcdn.com/image/fetch/$s_!CsLw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd224cc23-1509-4e24-827c-27ef8e7d1c75_489x259.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Do the same for the name below.</p><p><strong>name</strong>: <code>triggerBody()?[&#8217;file&#8217;]?[&#8217;name&#8217;]</code></p><p>In the end, I added a condition that checks the response from the file upload. If it returns <strong>200</strong>, it uses the <strong>Respond to agent</strong> action to confirm the file was uploaded successfully. Otherwise, it uses the same action to indicate that the upload failed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q5eM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q5eM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 424w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 848w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1272w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q5eM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png" width="966" height="1238" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1238,&quot;width&quot;:966,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:143219,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q5eM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 424w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 848w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1272w, https://substackcdn.com/image/fetch/$s_!q5eM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb97dd34-ea93-4cb5-9c41-d8572bbbb5a0_966x1238.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Agent</h3><p>Now that we have the flow created, we need to create an agent. I named mine <strong>File Upload Agent</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TIjv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TIjv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 424w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 848w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1272w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TIjv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png" width="1312" height="365" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:365,&quot;width&quot;:1312,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:47296,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TIjv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 424w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 848w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1272w, https://substackcdn.com/image/fetch/$s_!TIjv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64974bf7-6581-4b6d-9a91-a7fbcec856ed_1312x365.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Topic</h3><p>The last step is to create a topic that gets triggered any time a person mentions that they want to upload a file.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aHN3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aHN3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 424w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 848w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1272w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aHN3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png" width="1363" height="373" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:373,&quot;width&quot;:1363,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:51999,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aHN3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 424w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 848w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1272w, https://substackcdn.com/image/fetch/$s_!aHN3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bb9deb1-0b9c-4306-9a4e-e1c684e84905_1363x373.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Name it <strong>File Upload</strong></p><p>Below is the topic setup. It defines what this topic is about &#8212; in our case, file uploads &#8212; and what will trigger it.<br>When the user asks for a file to be uploaded, the agent responds with a question prompting them to attach the file in the chat.<br>In the next step, we call the flow created earlier. As input, we set <strong>File content (Record)</strong> to <strong>First(System.Activity.Attachments)</strong>, which grabs the first attached file. If you want to process all files, simply remove <code>First</code>.<br>For the output, we assign the response to a variable called <strong>OutputMsg</strong> and display it inside a <strong>Message </strong>element.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QVWP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QVWP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 424w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 848w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1272w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QVWP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png" width="346" height="1061" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1061,&quot;width&quot;:346,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:70268,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/179894331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QVWP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 424w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 848w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1272w, https://substackcdn.com/image/fetch/$s_!QVWP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba9ae2c9-4d82-46c0-abd4-ec5cb9e9e4bd_346x1061.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And that&#8217;s it. If you follow the same approach, you&#8217;ll be able to upload files from Copilot Studio (directly in the agent chat) to your own API.</p><p>While researching how to do this, I couldn&#8217;t find a single resource or piece of documentation on it, so I hope this helps others as well.</p>]]></content:encoded></item><item><title><![CDATA[Azure Cosmos DB Partitioning Fundamentals]]></title><description><![CDATA[Learn the fundamentals of partitioning in Azure Cosmos DB (NoSQL API)]]></description><link>https://systemshogun.com/p/azure-cosmos-db-partitioning-fundamentals</link><guid isPermaLink="false">https://systemshogun.com/p/azure-cosmos-db-partitioning-fundamentals</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 03 Nov 2025 22:08:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hBs-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today&#8217;s post is a quick introduction to partitioning with <a href="https://azure.microsoft.com/en-us/products/cosmos-db">Azure Cosmos DB</a>.</p><p>Azure Cosmos DB is a fully managed Platform as a Service (PaaS) and serves as Microsoft&#8217;s flagship distributed database solution.</p><p>How you design the partitioning strategy for your Cosmos DB is undoubtedly one of the most critical architectural decisions you&#8217;ll make when building a solution on Microsoft Azure&#8217;s highly scalable, globally distributed NoSQL database.</p><h4><strong>Request Units (RUs)</strong></h4><p>Each operation you perform in Cosmos DB &#8212; whether it&#8217;s a <strong>read</strong>, <strong>write</strong>, <strong>update</strong>, or <strong>query</strong> &#8212; consumes a certain number of <strong><a href="https://learn.microsoft.com/en-us/azure/cosmos-db/request-units">Request Units (RUs)</a></strong>.</p><p>RUs represent a normalized measure of system resources such as <strong>CPU, memory, and IOPS (input/output operations per second)</strong> required to perform a given operation. This abstraction makes performance predictable &#8212; instead of worrying about the underlying infrastructure, you only manage RUs.</p><p>The amount of RUs consumed depends on several factors:</p><ul><li><p><strong>Item size</strong> &#8211; larger JSON documents consume more RUs.</p></li><li><p><strong>Operation type</strong> &#8211; writes and queries typically cost more than point reads.</p></li><li><p><strong>Query complexity</strong> &#8211; filters, cross-partition queries, and sorting increase RU consumption.</p></li><li><p><strong>Indexing</strong> &#8211; since Cosmos DB automatically indexes all properties by default, write operations cost more if you have large or deeply nested documents.</p></li></ul><p>You provision throughput in RUs per second (e.g., 400 RU/s), which defines how much performance capacity your container or database can handle. If your workload exceeds that capacity, Cosmos DB starts to throttle requests, returning a <strong>429 (Request Rate Too Large)</strong> response until throughput is available again.</p><p>You can monitor RU consumption using the Azure Portal, SDK, or query metrics to identify and optimize expensive operations.</p><h3><strong>Basic Database Structure</strong></h3><p>In the Azure Cosmos DB NoSQL API, the data model consists of three main components &#8212; <strong>database</strong>, <strong>container(s)</strong>, and <strong>item(s)</strong> &#8212; as illustrated in the diagram below.</p><ul><li><p>A <strong>database</strong> is the top-level logical namespace that groups one or more containers.</p></li><li><p>A <strong>container</strong> is the fundamental unit of scalability and throughput. It holds a collection of JSON documents (items) and defines the <strong>partition key</strong> used to distribute data across partitions.</p></li><li><p>An <strong>item</strong> is a single JSON document stored within a container. Each item must include an <code>id</code> property and a value for the partition key.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TtzI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TtzI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 424w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 848w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1272w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TtzI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png" width="455" height="553" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:553,&quot;width&quot;:455,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22701,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TtzI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 424w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 848w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1272w, https://substackcdn.com/image/fetch/$s_!TtzI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bfee501-c85f-4bc8-aafa-b09178dbddf7_455x553.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>What is Partitioning?</h4><p>Partitioning literally means <em>dividing into parts</em>. In Azure Cosmos DB, partitioning is a core mechanism that enables your application to scale efficiently and maintain consistent performance at any size.</p><p>Whenever you create a container, you must define a <strong>partition key</strong> &#8212; a property within your JSON documents that determines how data is distributed across partitions. The partition key choice directly affects scalability, query performance, and cost efficiency.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hBs-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hBs-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hBs-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png" width="1221" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1221,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:423598,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hBs-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!hBs-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a68ed52-a8fe-4f99-a490-c589c5e225b4_1221x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each partition key value creates a virtual bucket called a <strong>logical partition</strong>, which contains all items that share the same partition key value. Choosing the right partition key is critical to achieving balanced data distribution. Ideally, the partition key should contain a value that never changes and one that has <strong>high cardinality</strong> &#8212; meaning it should have many unique values to evenly distribute data and requests across multiple logical partitions.</p><p>Otherwise, you may end up with what&#8217;s known as a <strong>hot partition</strong> &#8212; a single logical partition that stores a disproportionately large share of your data or handles most of the requests. This can lead to uneven throughput consumption, throttling, and degraded performance as that single partition becomes a bottleneck for your workload.</p><p>A <strong>logical partition</strong> resides within a <strong>physical partition</strong>. Depending on your data volume and throughput, a container can span multiple physical partitions. Each physical partition hosts one or more logical partitions, and Azure Cosmos DB automatically manages this distribution behind the scenes to maintain performance and scalability.</p><p>Now, let&#8217;s dive a bit deeper into partitioning strategy and discuss how to choose an effective partition key. </p><h4>Partitioning Strategy</h4><p>Let&#8217;s imagine we&#8217;re building a simple e-commerce website, similar to Amazon, where we have <strong>products</strong>, <strong>categories</strong>, and <strong>reviews</strong>. When users visit the site, they see a list of categories, select one, and then browse the products that belong to it.</p><p>For the sake of simplicity, we won&#8217;t attempt to replicate Amazon&#8217;s full complexity here &#8212; instead, we&#8217;ll focus on a simplified example that helps illustrate the fundamentals of partitioning in Azure Cosmos DB.</p><p>So, based on the structure we just defined, we&#8217;ll have three containers &#8212; one for <strong>products</strong>, one for <strong>categories</strong>, and one for <strong>reviews</strong>.</p><p>Now comes the interesting (and often challenging) part: choosing the right partition key for each container. The partition key determines how data is distributed and accessed, so picking the right one is essential for achieving scalability, balanced throughput, and optimal query performance.</p><p><strong>Application accessing patterns</strong></p><p>Whenever you choose a partition key, your starting point should always be how your application accesses the data. The way users query and interact with your data model should guide your partitioning strategy.</p><p>For our fictitious e-commerce app, when a user visits the website, they first see a list of categories and select one. After choosing a category, they&#8217;re shown a list of products that belong to it. From there, they can click on a specific product to view its details, add it to the cart, or purchase it.</p><p>Now, based on this, let&#8217;s choose the right partition key for each container, starting with the simplest one - categories.</p><p><strong>categories</strong></p><p>This one is easy. Since we don&#8217;t have that many categories (tens and not thousands) we&#8217;ll use the <strong>id</strong> as a partition key for it. What this means is that for each category we&#8217;ll have a single logical partition.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qeiD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qeiD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 424w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 848w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1272w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qeiD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png" width="1162" height="808" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:808,&quot;width&quot;:1162,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:427859,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qeiD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 424w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 848w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1272w, https://substackcdn.com/image/fetch/$s_!qeiD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd0f678-fe1d-45e6-ab6c-4e9e818864c7_1162x808.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>products</strong></p><p>You might be tempted to use the product&#8217;s ID<em> </em>as the partition key here as well &#8212; it offers high cardinality and would evenly distribute data across partitions. While that&#8217;s not necessarily wrong, remember that our partitioning strategy is driven by how the application accesses data, not just by distribution.</p><p>In our e-commerce example, users typically browse products by category, not by individual product ID. That&#8217;s why a better choice for the Products container is <strong>CategoryID</strong> as the partition key &#8212; it aligns with the most common access pattern (fetching all products within a category) while still providing good distribution and query performance.</p><p>This approach keeps queries efficient, since the data you need (all products in a given category) is co-located within the same logical partition. As a result, Cosmos DB doesn&#8217;t have to perform a cross-partition query, which improves latency, throughput efficiency (RUs), and overall performance.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xnZj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xnZj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xnZj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png" width="1221" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1221,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:473799,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xnZj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 424w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 848w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1272w, https://substackcdn.com/image/fetch/$s_!xnZj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c34bd36-6310-4f73-8a7d-ef9698a90fc2_1221x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>reviews</strong></p><p>This one should be straightforward. In any e-commerce platform, <strong>reviews</strong> naturally belong to the <strong>product</strong> they describe &#8212; users leave feedback directly under the product page for others to see.</p><p>To maintain high cardinality and maximize query efficiency, we&#8217;ll use the <strong>ProductID</strong> as the partition key for the Reviews container. This way, all reviews for a given product are stored within the same logical partition, allowing the application to retrieve them with a single, efficient query &#8212; no cross-partition scans, no unnecessary RU consumption, and minimal latency.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PUUK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PUUK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 424w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 848w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1272w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PUUK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png" width="1265" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1265,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:476908,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/177918296?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PUUK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 424w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 848w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1272w, https://substackcdn.com/image/fetch/$s_!PUUK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d2327b7-e80a-49ba-8fd0-4ebbf4d44e9c_1265x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For example, whenever you&#8217;re fetching a product&#8217;s reviews, you&#8217;ll run a query like this:</p><pre><code>SELECT * 
FROM r 
WHERE r.ProductId = "123"</code></pre><p>This query retrieves all reviews for the product with ID <strong>123</strong>. Since all reviews for a given product reside in the same logical partition, Cosmos DB executes this as a single-partition query, ensuring low latency and efficient RU consumption.</p><h4>Thanks</h4><p>That&#8217;s a quick overview of the Cosmos DB partitioning fundamentals and the practical approach I personally use when designing data models. These are the core concepts that, once mastered, make it much easier to build scalable, high-performing solutions in Azure.</p><h4>YouTube</h4><p>Here&#8217;s a 10-minute video where I explain all this + a code demo.</p><div id="youtube2-TfR0I7QB4MY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;TfR0I7QB4MY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/TfR0I7QB4MY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>If you want to really go deep into this topic, here&#8217;s links that I recommend:</p><ul><li><p><a href="https://learn.microsoft.com/en-us/azure/cosmos-db/partitioning-overview">Partitioning and horizontal scaling - Azure Cosmos DB | Microsoft Learn</a></p></li><li><p><a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/model-partition-example">Model and Partition Data using a Real-World Example - Azure Cosmos DB for NoSQL | Microsoft Learn</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Private Container Communication in Azure Container Apps]]></title><description><![CDATA[Learn the basics of private containers communication with Azure Container Apps]]></description><link>https://systemshogun.com/p/private-container-communication-in</link><guid isPermaLink="false">https://systemshogun.com/p/private-container-communication-in</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Tue, 30 Sep 2025 07:10:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!qamL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today I am going to show you how you can deploy containers to Azure Container Apps (ACA) that can communicate with each other <em>privately </em>using <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep">Bicep</a>. </p><p>Most of the tutorials out there concentrate on building and deploying public containers that can be accessed from everyone over the internet. This approach, however, is not ideal and is rarely used in real production environments. </p><p>That&#8217;s why today I will show you how you can deploy two containers, one accessible from the internet (the web app UI) and another one that&#8217;s only accessible by the first container (the backend).</p><p>Keep in mind that this is not a full-blown tutorial. You should clone the repo and study it on your own. You can find the source code of my demo here: <a href="https://github.com/kaldren/acr-private-demo">kaldren/acr-private-demo</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qamL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qamL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 424w, https://substackcdn.com/image/fetch/$s_!qamL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 848w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1272w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qamL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png" width="370" height="599" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:599,&quot;width&quot;:370,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35951,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qamL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 424w, https://substackcdn.com/image/fetch/$s_!qamL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 848w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1272w, https://substackcdn.com/image/fetch/$s_!qamL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d6b052b-acc3-41c3-95d8-0e7f63a73140_370x599.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>What is Azure Container Apps</h3><p>Azure Container Apps is a serverless platform for deploying and running containers. It is built on top of Kubernetes, and it abstracts away most of its complexity for you. Basically, things like scaling, container orchestration, configuration and server updates are all done for you by Azure.</p><h3>Bicep</h3><p>For this demo I&#8217;ve decided to use Bicep to deploy my infrastructure. You can check the source code for a more detailed walkthrough.</p><h3>Container Communication in Azure Container Apps</h3><p>There are a few ways for containers to talk to each other in ACA.</p><ul><li><p>Default fully qualified domain name (FQDN)</p></li><li><p>A custom domain name</p></li><li><p>The container app name, for instance <code>http://&lt;APP_NAME&gt;</code> for internal requests</p></li><li><p>A Dapr URL</p></li></ul><p>Today I will show you how the third option above, connecting via the app name for internal requests. </p><p>Let&#8217;s take a look at the bicep files now.</p><h3>Repo Walkthrough</h3><h4>Azure Container Registry</h4><p>First, to be able to deploy containers in Azure via ACA you need to have <a href="https://azure.microsoft.com/en-us/products/container-registry">Azure Container Registry</a> (ACR). This service can build and store your container images. Once the image is in the ACR your Container App can pull from it and use the image to build and deploy a container instance.</p><pre><code>@description(&#8217;Name of the Container Registry&#8217;)
param containerRegistryName string

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; = {
  name: containerRegistryName
  location: resourceGroup().location
  identity: {
    type: &#8216;SystemAssigned&#8217;
  }
  sku: {
    name: &#8216;Basic&#8217;
  }
  properties: {
    adminUserEnabled: true
  }
}

output containerRegistry object = {
  name: containerRegistry.name
  endpoint: containerRegistry.properties.loginServer
}</code></pre><h4>Managed Identity</h4><p>Once we deploy the ACR we need to create another resource - a <a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview">Managed identity</a>. We will then assign the role <a href="https://learn.microsoft.com/en-us/azure/container-registry/container-registry-rbac-built-in-roles-overview?tabs=registries-configured-with-rbac-registry-abac-repository-permissions">AcrPull </a>to it for the ACR that we created above. This will allow it to pull container images from the registry. We will then assign this identity to our container apps which would allow them to pull the images from the registry themselves.</p><pre><code>param containerRegistryName string

resource managedIdentity &#8216;Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview&#8217; = {
  location: resourceGroup().location
  name: &#8216;mi-containerapp&#8217;
}

// Set ArcPull role assignment for the managed identity on the ACR
resource acrPull &#8216;Microsoft.Authorization/roleAssignments@2022-04-01&#8217; = {
  name: guid(managedIdentity.id, &#8216;AcrPull&#8217;)
  scope: containerRegistry
  properties: {
    roleDefinitionId: subscriptionResourceId(
      &#8216;Microsoft.Authorization/roleDefinitions&#8217;,
      &#8216;7f951dda-4ed3-4680-a7ca-43fe172d538d&#8217; // AcrPull role ID
    )
    principalId: managedIdentity.properties.principalId
    principalType: &#8216;ServicePrincipal&#8217;
  }
}

output managedIdentityId string = managedIdentity.id

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; existing = {
  name: containerRegistryName
}</code></pre><h4>Azure Container Apps</h4><p>We will deploy two containers - one for the frontend and one for the backend.</p><pre><code>param location string = resourceGroup().location
param containerAppEnvironmentName string
param containerAppName string
param containerRegistryName string
param imageName string
param imageTag string

resource containerAppEnvironment &#8216;Microsoft.App/managedEnvironments@2025-02-02-preview&#8217; = {
  name: containerAppEnvironmentName
  location: location
  properties: {}
}

resource containerApp &#8216;Microsoft.App/containerApps@2022-03-01&#8217; ={
  name: containerAppName
  location: location
  <strong>identity</strong>: {
    type: &#8216;SystemAssigned, UserAssigned&#8217;
    userAssignedIdentities: {
      &#8216;${managedIdentity.id}&#8217;: {}
    }
  }
  properties:{
    managedEnvironmentId: containerAppEnvironment.id
    configuration: {
      <strong>ingress</strong>: {
        targetPort: 9696
        external: true
      }
      <strong>registries</strong>: [
        {
          server: &#8216;${containerRegistry.name}.azurecr.io&#8217;
          identity: managedIdentity.id
        }
      ]
    }
    template: {
      containers: [
        {
          image: &#8216;${containerRegistry.name}.azurecr.io/${imageName}:${imageTag}&#8217;
          name: imageName
        }
      ]
    }
  }
}

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; existing = {
  name: containerRegistryName
}

resource managedIdentity &#8216;Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview&#8217; existing = {
  name: &#8216;mi-containerapp&#8217;
}</code></pre><p>The most important sections (in bold) to note are:</p><ul><li><p>identity section: Here we assign the managed identity created above to the app</p></li><li><p>registry section: We use the identity to authenticate with the ACR</p></li><li><p>ingress section: We allow incoming <strong>external</strong> requests (from the internet) because the web app needs to be accessible from everyone</p></li></ul><p>Here&#8217;s the deployed <strong>frontend</strong> container app.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S_Hl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S_Hl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 424w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 848w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1272w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png" width="1351" height="945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:945,&quot;width&quot;:1351,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:122210,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!S_Hl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 424w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 848w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1272w, https://substackcdn.com/image/fetch/$s_!S_Hl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F368d2b40-1c13-43c6-903a-50b9bedd3d0b_1351x945.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Now, let&#8217;s take a look at the <strong>backend</strong> container app.</p><pre><code>param location string = resourceGroup().location
param containerAppEnvironmentName string
param containerAppName string
param containerRegistryName string
param imageName string
param imageTag string

resource containerAppEnvironment &#8216;Microsoft.App/managedEnvironments@2025-02-02-preview&#8217; = {
  name: containerAppEnvironmentName
  location: location
  properties: {}
}

resource containerApp &#8216;Microsoft.App/containerApps@2022-03-01&#8217; ={
  name: containerAppName
  location: location
  identity: {
    type: &#8216;SystemAssigned, UserAssigned&#8217;
    userAssignedIdentities: {
      &#8216;${managedIdentity.id}&#8217;: {}
    }
  }
  properties:{
    managedEnvironmentId: containerAppEnvironment.id
    configuration: {
      ingress: {
        targetPort: 8686
        external: false
      }
      registries: [
        {
          server: &#8216;${containerRegistry.name}.azurecr.io&#8217;
          identity: managedIdentity.id
        }
      ]
    }
    template: {
      containers: [
        {
          image: &#8216;${containerRegistry.name}.azurecr.io/${imageName}:${imageTag}&#8217;
          name: imageName
        }
      ]
    }
  }
}

resource containerRegistry &#8216;Microsoft.ContainerRegistry/registries@2025-05-01-preview&#8217; existing = {
  name: containerRegistryName
}

resource managedIdentity &#8216;Microsoft.ManagedIdentity/userAssignedIdentities@2025-01-31-preview&#8217; existing = {
  name: &#8216;mi-containerapp&#8217;
}</code></pre><p>The only difference here is the <strong>external</strong> <strong>ingress</strong> is set to <strong>false</strong>. That&#8217;s because we don&#8217;t want this container to be accessible from the internet but only from the network inside the <a href="https://learn.microsoft.com/en-us/azure/container-apps/environment">Azure Container App Environment</a>.</p><p>Here&#8217;s how the <strong>backend</strong> container app looks like.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!And5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!And5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 424w, https://substackcdn.com/image/fetch/$s_!And5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 848w, https://substackcdn.com/image/fetch/$s_!And5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1272w, https://substackcdn.com/image/fetch/$s_!And5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!And5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png" width="1294" height="964" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:964,&quot;width&quot;:1294,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:123675,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!And5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 424w, https://substackcdn.com/image/fetch/$s_!And5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 848w, https://substackcdn.com/image/fetch/$s_!And5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1272w, https://substackcdn.com/image/fetch/$s_!And5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ea3e24-b4c5-49eb-9c3d-c14ce606ac11_1294x964.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h4>main.bicep</h4><p>Here we add all bicep files as modules. For example:</p><pre><code>module acr &#8216;modules/container-registry.bicep&#8217; = {
  name: &#8216;acr-${randomSuffix}&#8217;
  params: {
    containerRegistryName: containerRegistryName
  }
  dependsOn: [
    rg
  ]
  scope: resourceGroup(resourceGroupName)
}</code></pre><p>(Check the repo for the full file content)</p><h4>deploy.sh</h4><p>I&#8217;ve written a shell script that does the deployment for me. Here&#8217;s part of it.</p><pre><code># az login -- Might need to login first. Uncomment if needed.

az deployment sub create \
  --name rgDeployment \
  --location eastus \
  --parameters params/main.dev.bicepparam deployContainerApp=false

# # Login to ACR
ACR_LOGIN_SERVER=$(az acr show -n $ACR_NAME --query loginServer -o tsv)
az acr build --registry $ACR_NAME \
  --image $BACKEND_IMAGE:$BACKEND_TAG \
  --file ../backend/Dockerfile ../backend

ACR_LOGIN_SERVER=$(az acr show -n $ACR_NAME --query loginServer -o tsv)
az acr build --registry $ACR_NAME \
  --image $FRONTEND_IMAGE:$FRONTEND_TAG \
  --file ../frontend/Dockerfile ../frontend

az deployment sub create \
  --name rgDeployment \
  --location eastus \
  --parameters params/main.dev.bicepparam deployContainerApp=true</code></pre><p>What this does is it is first deploying the ACR and managed identity resources without the container apps (see the <strong>deployContainerApp</strong> is set to false). That&#8217;s because I want to build and deploy the images for the frontend and backend first, so they are available for when we start deploying the container apps. </p><h4>Dockerfile</h4><p>Nothing fancy here. We just set the version to <strong>Python 3.13</strong>, install <strong>requirements.txt</strong> and run the apps on ports 8686 and 9696.</p><pre><code>FROM python:3.13
WORKDIR /app
ENV HELLO_MESSAGE=&#8221;Hello from the backend!&#8221;
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8686
CMD [&#8221;uvicorn&#8221;, &#8220;main:app&#8221;, &#8220;--host&#8221;, &#8220;0.0.0.0&#8221;, &#8220;--port&#8221;, &#8220;8686&#8221;]</code></pre><pre><code>FROM python:3.13
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 9696
# Run streamlit app on container&#8217;s port 9696
CMD [&#8221;streamlit&#8221;, &#8220;run&#8221;, &#8220;main.py&#8221;, &#8220;--server.port=9696&#8221;, &#8220;--server.address=0.0.0.0&#8221;]</code></pre><h4>compose.yaml</h4><p>This one I used for local development and testing.</p><h4>Talking Containers</h4><p>Let&#8217;s take a quick look at the frontend code to see how, once we deploy the infrastructure, we are able to connect to the backend container via the private network and not the public internet.</p><pre><code>import streamlit as st
import requests

st.title(&#8221;Container Demo UI&#8221;)
st.text(&#8221;This text will change if the backend is called successfully.&#8221;)

API_URL = &#8220;http://<strong>app-backend-dev</strong>&#8221;

def call_hi_get():
    try:
        response = requests.get(f&#8221;{API_URL}/hi&#8221;)
        if response.status_code == 200:
            st.text(response.json().get(&#8221;message&#8221;, &#8220;No message in response&#8221;))
    except requests.RequestException as e:
        st.text(f&#8221;Error reaching backend: {e}&#8221;)
    
if st.button(&#8221;Call /hi endpoint!&#8221;):
    response = call_hi_get()</code></pre><p>Here we&#8217;re using the <strong>app name</strong>, which in this case is <strong>app-backend-dev. </strong>We&#8217;re not using any FQDNs or FQDNs with internal in their url (e.g. https://app-frontend-dev.<strong>internal</strong>.alabala-xxx.eastus.azurecontainerapps.io) - I&#8217;ve seen this approach presented by folks, but I really, really hate it, because it is ugly and it requires you to figure out the actual FQDN first. </p><p>This approach is much cleaner - it relies on the service discovery system of ACA, which is much cleaner solution. We don&#8217;t have to care what the FQDN is.</p><h4>Azure</h4><p>Here&#8217;s our infrastructure in Azure.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JRrR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JRrR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 424w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 848w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1272w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JRrR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png" width="254" height="242" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:242,&quot;width&quot;:254,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9823,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/174315096?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JRrR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 424w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 848w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1272w, https://substackcdn.com/image/fetch/$s_!JRrR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe906ac3-7bc7-43fa-8f5a-b3f1ce3f33b7_254x242.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you want to learn more, then clone the repo, take a look and try it on your own.</p><p>Thanks.</p>]]></content:encoded></item><item><title><![CDATA[Build Remote MCP Server with Azure Container Apps]]></title><description><![CDATA[Learn how to build a simple remote MCP server with Microsoft Azure Container Apps and test it with Claude Desktop]]></description><link>https://systemshogun.com/p/build-remote-mcp-server-with-azure</link><guid isPermaLink="false">https://systemshogun.com/p/build-remote-mcp-server-with-azure</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 15 Sep 2025 11:11:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!6jT6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we&#8217;re going to take a quick look at how we can build a simple <em>remote </em>MCP server using Python, deploy it to <a href="https://azure.microsoft.com/en-us/products/container-apps">Azure Container Apps</a> and integrate it with <a href="https://claude.ai/download">Claude Desktop</a>. All of the examples I&#8217;ve seen online are for <em>local </em>MCP server using stdio, which is fine for a demo, but in reality, you&#8217;re most likely going to use remote MCP servers. That&#8217;s why I decided to dig into it and write a quick blog post about it.</p><h3>What is MCP?</h3><p>MCP stands for <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol</a>, and it is an open-source standard for connecting your AI applications to external systems. </p><p>Those systems can live on your own machine, like your filesystem, or be deployed thousands of kilometers away in some distant server, like the <a href="https://learn.microsoft.com/en-us/azure/developer/azure-mcp-server/">Azure MCP server</a>.</p><p>In a nutshell, MCP allows you to easily integrate your AI application with other sources, like data stores, APIs, and etc.,</p><h3>How MCP works?</h3><p>MCP uses the client-server architecture that we are all familiar with. There are three main actors involved in the whole process:</p><ol><li><p><strong>MCP Host</strong></p><p><strong>An AI application, powered by language model that supports MCP.</strong> Examples of such applications are Claude Desktop, Claude Code, GitHub Copilot.</p><p>Keep in mind that for this to work <strong>the language model needs to support tool calling</strong> and not every model supports this. Also, I&#8217;m saying language model and not large language model (LLM), because nothing stops a small language model (SLM) from being able to use MCP, if the model supports it.</p></li><li><p><strong>MCP Client</strong></p><p>An <strong>MCP Host</strong> creates a client, which then connects to an <strong>MCP Server</strong>. Each MCP Server has its own dedicated client.</p></li><li><p><strong>MCP Server</strong></p><p>This is what provides the extended functionalities to your AI application. This is basically what we called &#8220;external system&#8221; earlier. <strong>Think of it as an API that your AI-powered application can call.</strong> And like we said, it can live on the same machine as your AI application or anywhere on the internet.</p></li></ol><p>The following is a basic diagram of the whole process.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6jT6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6jT6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 424w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 848w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1272w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6jT6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png" width="403" height="341" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e700dc39-3402-4b03-9f08-4dd765573136_403x341.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:341,&quot;width&quot;:403,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7525,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6jT6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 424w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 848w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1272w, https://substackcdn.com/image/fetch/$s_!6jT6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe700dc39-3402-4b03-9f08-4dd765573136_403x341.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One thing to note is the <strong>two-way connection</strong> between the <strong>MCP Client</strong> and the <strong>MCP Server</strong>. That&#8217;s because the protocol allows for both the client AND the server to send messages to each other.</p><h3>Features of MCP</h3><p>So, what does MCP gives us? There are a few options available.</p><ol><li><p><strong>Tools</strong></p><p>These are functions provided by the <strong>MCP server</strong> that an LLM can call. You can think of them like API endpoints if you want. These functions could be calling a database, another API or executing any kind of business logic or integration. Tools are without question the most powerful feature of MCP.</p></li><li><p><strong>Resources</strong></p><p>Read-only data sources that supply contextual information, such as file contents, database schemas, or API documentation.</p></li><li><p><strong>Prompts</strong></p><p>Pre-built templates for guiding the model in using specific tools and resources.</p></li></ol><p>These are all features that the <strong>MCP Server</strong> provides. There are also features that the MCP Client provides and that&#8217;s why the connection above is bidirectional. However, these are not that interesting for our understanding of MCP and for this demo, so if you want to learn more, <a href="https://modelcontextprotocol.io/docs/getting-started/intro">please read the docs</a>, which are really good.</p><p>Now, let&#8217;s build our remote MCP server.</p><p><strong>requirements.txt</strong></p><pre><code>fastmcp</code></pre><p>We&#8217;re going to be using <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a> to build our server.</p><p><strong>main.py</strong></p><pre><code>from fastmcp import FastMCP

mcp = FastMCP("Demo MCP")

@mcp.tool
def call_the_ceo() -&gt; str:
    return "Calling the CEO... DONE!"

@mcp.tool
def send_email(to: str, subject: str, body: str) -&gt; str:
    return f"Email sent to {to} with subject '{subject}' and body '{body}'"

@mcp.tool
def weather_info(location: str) -&gt; str:
    return f"The weather in {location} is sunny with a high of 999&#176;F."

if __name__ == "__main__":
    mcp.run()</code></pre><p>We defined 3 tools. One that calls the CEO, another one that sends email and third one for weather info.</p><p><strong>Dockerfile</strong></p><pre><code># Use official Python image
FROM python:3.12-slim

# Set working directory
WORKDIR /app

# Copy requirements and source code
COPY requirements.txt ./
COPY main.py ./

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Expose port
EXPOSE 8000

# Set entrypoint
CMD ["fastmcp", "run", "./main.py:mcp", "--transport", "http", "--host", "0.0.0.0", "--port", "8000"]</code></pre><p>Since we&#8217;re going to use containers, we need Dockerfile. We&#8217;re first going to run this locally using Docker Desktop and test it with a tool called <a href="https://modelcontextprotocol.io/legacy/tools/inspector">MCP Inspector</a>. Then we will push this to <a href="https://azure.microsoft.com/en-us/products/container-registry">Azure Container Registry</a> and then run it on Container Apps.</p><p>Make sure you install the packages inside virtual environment and then run it. </p><pre><code>uv venv
uv pip install -r .\requirements.txt
.venv\Scripts\activate
</code></pre><p>Now, let&#8217;s build our image.</p><pre><code>docker build -t mcp-server-demo .</code></pre><p>Run the image</p><pre><code>docker run --name mcp-server-demo --rm -p 8000:8000 `
   -e HOST=0.0.0.0 -e PORT=8000 `
   mcp-server-demo</code></pre><p>Now, let&#8217;s run the MCP Inspector. Inside the project folder, run the following.</p><pre><code>npx @modelcontextprotocol/inspector fastmcp run .\main.py</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hbRo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hbRo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 424w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 848w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1272w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hbRo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png" width="855" height="529" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:529,&quot;width&quot;:855,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31550,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hbRo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 424w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 848w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1272w, https://substackcdn.com/image/fetch/$s_!hbRo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bfe18af-92e0-4b13-8fc8-30325c55d672_855x529.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Click <strong>Connect</strong> and if all done correctly, you should see the following.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fago!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fago!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 424w, https://substackcdn.com/image/fetch/$s_!fago!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 848w, https://substackcdn.com/image/fetch/$s_!fago!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1272w, https://substackcdn.com/image/fetch/$s_!fago!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fago!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png" width="1456" height="759" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:759,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:73844,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fago!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 424w, https://substackcdn.com/image/fetch/$s_!fago!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 848w, https://substackcdn.com/image/fetch/$s_!fago!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1272w, https://substackcdn.com/image/fetch/$s_!fago!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9a097d1-2b7f-4613-b683-86f36af1d2e2_2490x1298.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Click on <strong>Tools</strong> and you should see all the tools that we have exposed. You can test each one of them if you want by providing the required parameters and clicking on <strong>Run Tool</strong>.</p><p>That&#8217;s great, we&#8217;ve tested the MCP server, and we saw it working <em>locally</em>. Now, let&#8217;s deploy this to Container Apps and use it <em>remotely</em>.</p><h3><strong>Deploy to Container Apps (Steps):</strong></h3><ol><li><p>Create resource group in Azure</p></li><li><p>Create Azure Container Registry</p></li><li><p>Log into your container registry using <a href="https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest">azure cli</a></p><pre><code>az acr login --name <strong>[YOUR AZURE CONTAINER REGISTRY NAME]</strong></code></pre></li><li><p>Tag your container image</p><pre><code>docker tag mcp-server-demo <strong>[YOUR CONTAINER REGISTRY NAME]</strong>.azurecr.io/mcp-server-demo:latest</code></pre></li><li><p>Push to Azure Container Registry</p><pre><code>docker push <strong>[YOUR CONTAINER REGISTRY NAME]</strong>.azurecr.io/mcp-server-demo:latest</code></pre></li><li><p>Create Container App and in the set-up steps add the image you just pushed. Also, don&#8217;t forget to enable external traffic (<strong>Ingress</strong> option).</p></li><li><p>Once deployed, copy your container app&#8217;s URL</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eZ85!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eZ85!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 424w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 848w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1272w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eZ85!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png" width="1456" height="367" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:367,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79452,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eZ85!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 424w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 848w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1272w, https://substackcdn.com/image/fetch/$s_!eZ85!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2e3ccfc-146e-43d4-b1a3-1dd2a41a153a_2112x532.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p></li><li><p>Open Claude Desktop (assuming you have it installed)</p></li><li><p>On the left screen at the bottom click on your user and then to <strong>Settings</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!txKY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!txKY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 424w, https://substackcdn.com/image/fetch/$s_!txKY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 848w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1272w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!txKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png" width="271" height="329" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:329,&quot;width&quot;:271,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15365,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!txKY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 424w, https://substackcdn.com/image/fetch/$s_!txKY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 848w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1272w, https://substackcdn.com/image/fetch/$s_!txKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F807c3e2b-64a8-453f-810e-e18d8b44a8e4_271x329.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p></li><li><p>Click on <strong>Developer </strong>and then on <strong>Edit Config</strong></p></li><li><p>Open <strong>claude_desktop_config.json </strong>if it didn&#8217;t open automatically</p></li><li><p>Add the following snipper and replace <strong>[YOUR CONTAINER APP URL] </strong>with your actual container app url you copied earlier.</p><pre><code>{
  "mcpServers": {
    "custom-mcp": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "[YOUR CONTAINER APP URL]"
      ]
    }
  }
}
</code></pre></li><li><p>Install custom-mcp package (the one we used above)</p><pre><code>npm i -g mcp-remote</code></pre></li><li><p>Open Claude Desktop</p></li></ol><p>Now, if you&#8217;ve followed along (and if I didn&#8217;t miss any step) you shouldn&#8217;t see any errors. Also, you should see the remote MCP server by clicking on the bar icon. Make sure it is enabled.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cJge!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cJge!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 424w, https://substackcdn.com/image/fetch/$s_!cJge!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 848w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1272w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cJge!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png" width="866" height="625" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:625,&quot;width&quot;:866,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55953,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cJge!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 424w, https://substackcdn.com/image/fetch/$s_!cJge!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 848w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1272w, https://substackcdn.com/image/fetch/$s_!cJge!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10d47bd8-072b-4db5-a0be-af2d7f0aff82_866x625.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Now, let&#8217;s test this with the following prompt.</p><pre><code>call the ceo</code></pre><p>If everything was done correctly, you should see something similar to the following.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XZqx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XZqx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 424w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 848w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1272w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XZqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png" width="523" height="397" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:397,&quot;width&quot;:523,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37847,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XZqx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 424w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 848w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1272w, https://substackcdn.com/image/fetch/$s_!XZqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa598aa8e-8a8b-4219-9756-4ffc94519ecb_523x397.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Choose either <strong>Allow always</strong> as we trust our own MCP server.</p><p>Here&#8217;s the result:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e4vp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e4vp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 424w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 848w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1272w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e4vp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png" width="745" height="236" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:236,&quot;width&quot;:745,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27646,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!e4vp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 424w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 848w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1272w, https://substackcdn.com/image/fetch/$s_!e4vp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1f8e74-3aa7-4326-9e09-241a7edbb98b_745x236.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>As you can see this actually worked. Now, let&#8217;s ask about the weather.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FAo_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FAo_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 424w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 848w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1272w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FAo_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png" width="731" height="284" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:284,&quot;width&quot;:731,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50237,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FAo_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 424w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 848w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1272w, https://substackcdn.com/image/fetch/$s_!FAo_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F172b8878-677f-43f6-8cda-e97fdbdeb7bf_731x284.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We can see that it says it&#8217;s 999F, which is exactly what we put in our code above. It works!</p><p>Now, let&#8217;s send an email, but let&#8217;s see how the LLM can provide a better user experience on its own, without us explicitly programming it to do so. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xeui!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xeui!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 424w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 848w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1272w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xeui!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png" width="714" height="249" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ea90a2e-6879-4491-af55-41950d54518a_714x249.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:249,&quot;width&quot;:714,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41308,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xeui!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 424w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 848w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1272w, https://substackcdn.com/image/fetch/$s_!Xeui!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea90a2e-6879-4491-af55-41950d54518a_714x249.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here, if I wanted to send email directly, I would&#8217;ve provided the parameters. But instead, I said that I want to send an email and the LLM knows what parameters the tool needs in order for the email to be sent - email address, subject and body. And it asks me for that. Smart, and cool!</p><p>Now, let&#8217;s send the email.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3WNg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3WNg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 424w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 848w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1272w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3WNg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png" width="733" height="248" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:248,&quot;width&quot;:733,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43520,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/173636230?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3WNg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 424w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 848w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1272w, https://substackcdn.com/image/fetch/$s_!3WNg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ddf32af-de6c-4ce6-9e1b-15d1e157d12b_733x248.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Honestly, I&#8217;m too tired to fix any grammar mistakes at this point and am not going to use AI to do that either. I hope you found this quick demo useful!</p><p>Until next time!</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Build MCP Server with Claude Desktop and Python]]></title><description><![CDATA[Try MCP by building a server and trying it as quickly as possible using Claude Desktop]]></description><link>https://systemshogun.com/p/build-mcp-server-with-claude-desktop</link><guid isPermaLink="false">https://systemshogun.com/p/build-mcp-server-with-claude-desktop</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Fri, 15 Aug 2025 06:02:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fd3774a7-50b6-40db-a7a3-ba006de58669_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>MCP (<a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol</a>) is gaining a lot of attention lately. It&#8217;s an open-source protocol that enables large language models (LLMs) to call external functions and enrich their context with data from sources like databases, APIs, and file systems.</p><p>Today, I&#8217;ll show you a quick way to experiment with it. For more details, check the official documentation.</p><h3>Prerequisites</h3><p>To follow along, make sure you have the following installed:</p><ul><li><p><a href="https://www.python.org/downloads/">Python</a></p></li><li><p><a href="https://claude.ai/download">Claude Desktop</a></p></li><li><p><a href="https://github.com/astral-sh/uv">uv</a></p></li></ul><h3>Step 1: Set up the project</h3><h4>Windows</h4><pre><code># Create a new directory for our project
uv init fakedb
cd fakedb

# Create virtual environment and activate it
uv venv
.venv\Scripts\activate

# Install dependencies
uv add mcp[cli]

# Create our server file
code fakedb.py</code></pre><h4>macOS</h4><pre><code># Create a new directory for our project
uv init fakedb
cd fakedb

# Create virtual environment and activate it
uv venv
source .venv/bin/activate

# Install dependencies
uv add "mcp[cli]"

# Create our server file
touch fakedb.py</code></pre><h3>Step 2: Build a simple MCP server</h3><p>Let&#8217;s add an endpoint to our MCP server that retrieves user information by user ID.</p><p><code>fakedb.py</code></p><p>This script creates a simple MCP server named <code>fakedb</code> with one tool, <code>get_user_info</code>. The tool takes a user ID, looks it up in a hardcoded dictionary (<code>fake_db</code>), and returns the matching user&#8217;s name and age. You could make this more interesting by integrating a real database and performing an actual query, but for simplicity, we&#8217;re keeping it in-memory here.</p><pre><code>from typing import Any
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("fakedb")

@mcp.tool()
def get_user_info(user_id: str) -&gt; dict[str, Any] | None:
    """Get user information from the database.

    Args:
        user_id: The ID of the user to retrieve.
    """

    # Here we're simulating a real database lookup 
    # (you can try with a real one to make it more interesting)
    fake_db = {
        "1234": {"name": "Alice", "age": 30},
        "5678": {"name": "Bob", "age": 25},
    }
    return fake_db.get(user_id)

if __name__ == "__main__":
    # Initialize and run the server
    mcp.run(transport='stdio')</code></pre><h3>Step 3: Connect to Claude Desktop</h3><h4>Windows</h4><p>Open:</p><pre><code>%APPDATA%\Claude\claude_desktop_config.json</code></pre><h4>macOS</h4><p>Open:</p><pre><code>~/Library/Application Support/Claude/claude_desktop_config.json</code></pre><p>Replace the file contents with:</p><pre><code>{
  "mcpServers": {
    "userDatabase": {
      "command": "uv",
      "args": [
        "--directory",
        "&lt;&lt;FULL PATH TO YOUR PROJECT DIRECTORY&gt;&gt;",
        "run",
        "fakedb.py"
      ]
    }
  }
}</code></pre><p>This configuration creates a new MCP server connection called <strong>userDatabase</strong>. The <code>command</code> and <code>args</code> simply run your MCP server.</p><p>Restart Claude Desktop, and you should see your new MCP server with one endpoint, called <strong>get_user_info</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tby5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tby5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 424w, https://substackcdn.com/image/fetch/$s_!tby5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 848w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1272w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tby5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png" width="1452" height="1182" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1182,&quot;width&quot;:1452,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132981,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170978183?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tby5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 424w, https://substackcdn.com/image/fetch/$s_!tby5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 848w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1272w, https://substackcdn.com/image/fetch/$s_!tby5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda328e5-4616-4793-92d7-9ee9c140f3db_1452x1182.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lc6T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lc6T!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 424w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 848w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1272w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lc6T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png" width="1456" height="618" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:618,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74534,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170978183?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lc6T!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 424w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 848w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1272w, https://substackcdn.com/image/fetch/$s_!lc6T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50d9b9a0-9941-44c5-bbf6-9f96a51882a7_1493x634.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Step 4: Test it in Claude</h3><p>Now, if you ask Claude for user information (for example, &#8220;<strong>Give me all you have about user with id 1234 in tabular format</strong>&#8221;), the LLM will:</p><ol><li><p>Detect the <code>get_user_info</code> tool.</p></li><li><p>Call it with the provided <code>user_id</code>.</p></li><li><p>Return the result in a neat, tabular format, exactly how we defined it.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hysk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hysk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 424w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 848w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1272w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hysk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png" width="1456" height="743" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:743,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105551,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170978183?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Hysk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 424w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 848w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1272w, https://substackcdn.com/image/fetch/$s_!Hysk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c67913-c1ef-4781-9f83-e5038ee58c05_1541x786.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can see from the image above the <strong>prompt</strong>, <strong>the tool</strong> that was used and <strong>the output</strong>.</p><p>So this is it! A quick and neat way to test what MCP is and how it works without writing lots of complex code.</p><p>Thanks for reading!</p>]]></content:encoded></item><item><title><![CDATA[Using PostgreSQL as a Vector Database for RAG]]></title><description><![CDATA[Did you know you can use PostgreSQL, the mighty relational database, as your vector store too?]]></description><link>https://systemshogun.com/p/using-postgresql-as-a-vector-database</link><guid isPermaLink="false">https://systemshogun.com/p/using-postgresql-as-a-vector-database</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 09 Aug 2025 05:08:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a4350915-d1bf-4e29-94b4-f390cad97386_1740x1218.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Did you know you can use <a href="https://www.postgresql.org/">PostgreSQL</a>, the mighty relational database, as your vector store too? That means you don't need to rely on two separate products if you want to implement <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">RAG </a>in a solution that already uses this open-source database.</p><p>Here&#8217;s a quick overview of how you can make it happen.</p><p>To keep things simple, I&#8217;ve used <a href="https://console.cloud.timescale.com/">TigerCloud</a> to spin up a PostgreSQL instance in the cloud.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rfL6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rfL6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 424w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 848w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1272w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rfL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png" width="1456" height="347" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:347,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:118527,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170185659?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rfL6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 424w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 848w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1272w, https://substackcdn.com/image/fetch/$s_!rfL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18769922-5cf2-40be-9069-17e2eb8431bd_2768x659.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><p>Now, back to code.</p><p><code>general.txt</code></p><p>For the purpose of this demo, I&#8217;ve used Python&#8217;s Q&amp;A section in their documentation that you can see here: <a href="https://docs.python.org/3/faq/general.html#id2">General Python FAQ &#8212; Python 3.13.5 documentation</a></p><p>You can either copy the text from that page, or click <a href="https://docs.python.org/3/archives/python-3.13-docs-text.zip">Download</a> to download the whole docs, then go to <code>faq </code>folder and take the <code>general.txt</code> from there and put it in your solution.</p><p><code>requirements.txt</code></p><p>Here are all the packages I needed:</p><pre><code>openai
pandas
numpy
psycopg2-binary
pgvector
dotenv</code></pre><p><code>.env</code></p><p>For the demo I had to get my OpenAI key as well as the connection string from TigerCloud:</p><pre><code>OPENAI_API_KEY=&lt;YOUR OPENAI API KEY HERE&gt;
TIMESCALE_CONNECTION_STRING=&lt;YOUR TIMESCALE CONNECTION STRING HERE&gt;</code></pre><p><code>utils.py</code></p><pre><code>import numpy as np
import openai
from pgvector.psycopg2 import register_vector

# Function to process input with retrieval of most similar documents from the database
def process_input_with_retrieval(user_input, conn):
    delimiter = "```"

    #Step 1: Get documents related to the user input from database
    related_docs = get_top3_similar_docs(get_embeddings(user_input), conn)

    # Step 2: Get completion from OpenAI API
    # Set system message to help set appropriate tone and context for model
    system_message = f"""
    You are a friendly chatbot. \
    You can answer questions about Python, the programming language. \
    You respond in a concise, technically credible tone. Always use ONLY the data from the context and DO NOT PROVIDE any additional information. If you can't find the answer in the context, say "I don't know". Even if you find something that you think is not correct, but if it is in the context - say it.
    """

    # Prepare messages to pass to model
    # We use a delimiter to help the model understand the where the user_input starts and ends
    messages = [
        {"role": "system", "content": system_message},
        {"role": "user", "content": f"{delimiter}{user_input}{delimiter}"},
        {"role": "assistant", "content": f"Relevant Python information: \n {related_docs[0][0]} \n {related_docs[1][0]} {related_docs[2][0]}"}   
    ]

    final_response = get_completion_from_messages(messages)
    return final_response

# Helper function: get text completion from OpenAI API
def get_completion_from_messages(messages, model="gpt-4o", temperature=0, max_tokens=1000):
    openai_client = openai.OpenAI()
    response = openai_client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=temperature, 
        max_tokens=max_tokens, 
    )
    return response.choices[0].message.content

# Helper function: Get top 3 most similar documents from the database
def get_top3_similar_docs(query_embedding, conn):
    embedding_array = np.array(query_embedding)
    # Register pgvector extension
    register_vector(conn)
    cur = conn.cursor()
    # Get the top 3 most similar documents using the KNN &lt;=&gt; operator
    cur.execute("SELECT content FROM embeddings ORDER BY embedding &lt;=&gt; %s LIMIT 3", (embedding_array,))
    top3_docs = cur.fetchall()
    return top3_docs

# Load text file content (duh!)
def load_text_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return file.read()

# Function to chunk text into smaller pieces
def chunk_text(text, num_chunks):
    """
    Split text into a specified number of chunks.
    
    Args:
        text (str): The text to be chunked
        num_chunks (int): Number of chunks to create
        
    Returns:
        list: List of text chunks
    """
    if num_chunks &lt;= 0:
        raise ValueError("Number of chunks must be greater than 0")
    
    if not text:
        return [""] * num_chunks
    
    text_length = len(text)
    chunk_size = text_length // num_chunks
    remainder = text_length % num_chunks
    
    chunks = []
    start = 0
    
    for i in range(num_chunks):
        # Add one extra character to the first 'remainder' chunks
        current_chunk_size = chunk_size + (1 if i &lt; remainder else 0)
        end = start + current_chunk_size
        chunks.append(text[start:end])
        start = end
    
    return chunks

# Function to get embeddings for a given text using OpenAI API
def get_embeddings(text):
    openai_client = openai.OpenAI()
    response = openai_client.embeddings.create(
        model="text-embedding-3-small",
        input = text.replace("\n"," ")
    )
    return response.data[0].embedding</code></pre><p><code>main.py</code></p><pre><code>from dotenv import load_dotenv, find_dotenv
import os
import openai
import pandas as pd
import numpy as np
import psycopg2
from utils import chunk_text, get_embeddings, load_text_file, process_input_with_retrieval
from psycopg2.extras import execute_values
from pgvector.psycopg2 import register_vector
_ = load_dotenv(find_dotenv()) 
openai.api_key  = os.environ['OPENAI_API_KEY'] 

new_list = []
text = load_text_file('general.txt')

# You can change the number of chunks here based on your needs and the size of the text
chunks = chunk_text(text, 5)

for i in range(len(chunks)):
    embedding = get_embeddings(chunks[i])
    new_list.append([chunks[i], embedding])

df = pd.DataFrame(new_list, columns=['content', 'embedding'])

print(f"Created DataFrame with {len(df)} rows")

connection_string  = os.environ['TIMESCALE_CONNECTION_STRING']

# Connect to PostgreSQL database in Timescale using connection string
conn = psycopg2.connect(connection_string)
cur = conn.cursor()

#install pgvector
cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
conn.commit()

#install pgvectorscale
cur.execute("CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE;")
conn.commit()

# Create table to store embeddings and metadata
table_create_command = """
-- Remove table embeddings if it exists
DROP TABLE IF EXISTS embeddings;
CREATE TABLE embeddings (
            id bigserial primary key, 
            content text,
            embedding vector(1536)
            );
            """

cur.execute(table_create_command)
cur.close()
conn.commit()


#Batch insert embeddings and metadata from dataframe into PostgreSQL database
register_vector(conn)
cur = conn.cursor()

# Prepare the list of tuples to insert
data_list = [(row['content'], np.array(row['embedding'])) for index, row in df.iterrows()]

# Use execute_values to perform batch insertion
execute_values(cur, "INSERT INTO embeddings (content, embedding) VALUES %s", data_list)

# Commit after we insert all embeddings
conn.commit()

# Create an index on the data for faster retrieval
cur.execute('CREATE INDEX embedding_idx ON embeddings USING diskann (embedding);')
conn.commit()

input = 'Is Python portable?'

response = process_input_with_retrieval(input, conn)
print(input)
print(response)</code></pre><p>Here&#8217;s the outcome</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z6KJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 424w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 848w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1272w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png" width="1456" height="78" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:78,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:19830,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/170185659?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 424w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 848w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1272w, https://substackcdn.com/image/fetch/$s_!Z6KJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b51147a-4ba7-414a-b5eb-82f07c98e16f_2105x113.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>You can play around with this by changing the input, the data, etc.</p><p>All in all, it works great. And the fact that you are using one of the most widely used relational databases as your vector store for RAG is super convenient.</p><p>Thanks:</p><ul><li><p><a href="https://www.tigerdata.com/blog/postgresql-as-a-vector-database-using-pgvector">PostgreSQL as a Vector Database: A Pgvector Tutorial | TigerData</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Multi-Agent App with Azure AI Foundry Agent Service]]></title><description><![CDATA[Build Multi-Agent app using Azure AI Foundry Agent Service]]></description><link>https://systemshogun.com/p/multi-agent-app-with-azure-ai-foundry</link><guid isPermaLink="false">https://systemshogun.com/p/multi-agent-app-with-azure-ai-foundry</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 04 Aug 2025 08:08:48 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b2ec290b-194b-49d2-9f9b-60568d2dfb2e_420x300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last Sunday, I decided to explore the new <strong><a href="https://learn.microsoft.com/en-us/azure/ai-foundry/agents/overview">Azure AI Foundry Agent Service</a></strong> and built a small multi-agent onboarding app powered behind the scenes by three coordinated agents.</p><p>This blog post is meant to give you a high-level overview of how I approached it, not a full step-by-step tutorial. </p><p>You can watch the video at the end to see the app in action.</p><h3><strong>The Onboarding App</strong></h3><p>I built a simple onboarding web app for, you guessed it, onboarding new employees in a company. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LlaZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LlaZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 424w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 848w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1272w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png" width="1318" height="788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:788,&quot;width&quot;:1318,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53800,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/169990941?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LlaZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 424w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 848w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1272w, https://substackcdn.com/image/fetch/$s_!LlaZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156dd94a-0b86-4335-80a8-8d09bff37607_1318x788.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The app takes three inputs: the employee&#8217;s full name, email, and department. Behind the scenes, it creates three agents:</p><ul><li><p><code>department_rag_agent</code> uses Azure AI Search to perform a vector-based RAG (see <a href="https://learn.microsoft.com/en-us/azure/search/retrieval-augmented-generation-overview?tabs=docs">RAG and generative AI - Azure AI Search</a>) search and find relevant onboarding information for the specified department (for example, Software Engineering).</p></li><li><p><code>discord_agent</code> is responsible for sending a welcome message on Discord, which includes the employee&#8217;s details and any department-specific information returned by the RAG agent.</p></li><li><p><code>onboarding_agent</code> acts as the orchestrator. It first invokes the RAG agent to gather relevant information, then passes the results to the Discord agent to send the final message.</p></li></ul><p>The entire process is coordinated automatically once the onboarding form is submitted.</p><p>Here&#8217;s the end result message in Discord:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sOlp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sOlp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 424w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 848w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1272w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sOlp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png" width="1456" height="1382" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1382,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:244619,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/169990941?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sOlp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 424w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 848w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1272w, https://substackcdn.com/image/fetch/$s_!sOlp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa30ca9ef-68ed-4be8-a544-279a3230feed_1574x1494.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Project Structure Overview</h3><p>This project showcases a multi-agent onboarding flow using Azure AI Foundry Agent Service. The main folders are:</p><ul><li><p><code>src/api/</code> &#8211; Contains the FastAPI backend with agent logic, tools, and utilities.</p></li><li><p><code>func-api/</code> &#8211; An Azure Function App that exposes an endpoint for sending Discord messages. This is needed because connected agents do not support local function calls (see <a href="https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/connected-agents?pivots=python">How to use connected agents - Azure AI Foundry | Microsoft Learn</a>).</p></li><li><p><code>web/</code> &#8211; A React.js frontend that serves as the UI for triggering the onboarding flow.</p></li></ul><h3>Environment Setup</h3><ul><li><p><strong>Azure Function App</strong> (for webhook to Discord)</p></li><li><p><strong>Azure AI Search</strong> (for RAG)</p></li><li><p><strong>Azure OpenAI</strong> (for embedding model)</p></li><li><p><strong>Azure AI Foundry</strong> project (for LLM)</p></li></ul><p>To run the app locally or deploy it yourself, you&#8217;ll need a <code>.env</code> file with the following variables:</p><pre><code># Azure AI Foundry
PROJECT_ENDPOINT=   # Your Azure AI Foundry project endpoint
MODEL_DEPLOYMENT_NAME=   # The model deployment name used for your agents (e.g., gpt-4o-mini)

# Azure AI Search
AZURE_SEARCH_ENDPOINT=   # Endpoint of your Azure AI Search resource
AZURE_SEARCH_API_KEY=    # API key for your Azure AI Search instance
AZURE_SEARCH_INDEX_NAME= # Name of the index used for department RAG search</code></pre><p>Also, you need to make sure you connect your Azure AI Search resource with your AI Foundry (see <a href="https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/connections-add?pivots=fdp-project">How to add a new connection in Azure AI Foundry portal - Azure AI Foundry | Microsoft Learn</a>)</p><h3><strong>Final Thoughts</strong></h3><p>This was a fun Sunday experiment that gave me a hands-on introduction to Azure AI Foundry Agent Service (seriously, Microsoft, please give it a shorter and cooler name). With multi-agent coordination, Azure AI Search, and Discord integration via Azure Functions and an OpenAPI spec, I built a simple but practical onboarding automation flow that helped me understand what this framework is currently capable of.</p><p>Keep in mind that this framework is still new and actively evolving, so things may change quickly.</p><p>Here&#8217;s a demo of the app as well as quick walkthrough of the solution</p><div id="youtube2-0YLFdzMoMCo" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;0YLFdzMoMCo&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/0YLFdzMoMCo?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h3>Resources</h3><ul><li><p><a href="https://github.com/kaldren/multiagent-foundry-ai-agents">Github Repo</a></p></li><li><p><a href="https://learn.microsoft.com/en-us/azure/ai-foundry/agents/overview">What is Azure AI Foundry Agent Service?</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Monitoring Multi-Agent Systems with Langfuse and CrewAI]]></title><description><![CDATA[How to Monitor CrewAI Multi-Agent Systems with Langfuse]]></description><link>https://systemshogun.com/p/monitoring-multi-agent-systems-with</link><guid isPermaLink="false">https://systemshogun.com/p/monitoring-multi-agent-systems-with</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Mon, 14 Jul 2025 06:13:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1c09eaea-5891-4558-9136-39ef22b8022d_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Multi-agent systems are becoming increasingly complex, making effective monitoring crucial for production deployments. In this post, I'll demonstrate how to implement comprehensive monitoring for your multi-agent applications using Langfuse, with CrewAI serving as our framework for building the demo system.</p><p>CrewAI is a popular open-source framework that simplifies the creation of collaborative AI agent systems. Combined with Langfuse's specialized monitoring capabilities, we can gain deep insights into agent interactions, performance, cost, and behavior patterns.</p><h3>Why Langfuse Over Traditional Monitoring Tools?</h3><p>You might wonder why we need a specialized tool like Langfuse when established monitoring solutions like Azure Monitor or AWS CloudWatch already exist. Here's the key difference:</p><p>Traditional monitoring tools excel at tracking infrastructure metrics, application performance, and system health. However, they weren't designed for the unique challenges of AI agent systems. Langfuse fills this gap by providing:</p><ul><li><p><strong>LLM-specific observability</strong>: Track token usage, model performance, and response quality across different language models</p></li><li><p><strong>Agent conversation flows</strong>: Visualize complex multi-agent interactions and decision chains</p></li><li><p><strong>Cost tracking</strong>: Monitor AI model costs in real-time across your entire agent ecosystem</p></li><li><p><strong>Prompt engineering insights</strong>: Analyze how different prompts affect agent behavior and outcomes</p></li><li><p><strong>Human feedback integration</strong>: Capture and analyze user feedback to improve agent performance</p></li></ul><h3>Langfuse Dashboard Overview</h3><p>Here's a demo dashboard showing what Langfuse monitoring looks like in action:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ctzM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ctzM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 424w, https://substackcdn.com/image/fetch/$s_!ctzM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 848w, https://substackcdn.com/image/fetch/$s_!ctzM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 1272w, https://substackcdn.com/image/fetch/$s_!ctzM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ctzM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png" width="1456" height="753" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:753,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:154587,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/168200334?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ctzM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 424w, https://substackcdn.com/image/fetch/$s_!ctzM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 848w, https://substackcdn.com/image/fetch/$s_!ctzM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 1272w, https://substackcdn.com/image/fetch/$s_!ctzM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d9c71af-34d9-463b-9613-f0a4771d6f1e_2465x1275.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As you can see, we have a comprehensive view of our multi-agent system's performance. The dashboard displays several key metrics:</p><ul><li><p><strong>38 total traces tracked</strong> across our agent interactions, giving us visibility into every conversation and task execution</p></li><li><p><strong>Model costs of $159,065</strong> with detailed breakdowns by model type (gpt-4-0613 and gpt-4o-mini), helping us understand the financial impact of our agent operations</p></li><li><p><strong>Trace activity over time</strong> showing peak usage patterns and system load distribution</p></li><li><p><strong>Model usage analytics</strong> with cost and token consumption trends, essential for optimizing our agent configurations</p></li></ul><p>The left sidebar shows different trace categories like "blog-post-agent," "Crew Created," "blog-post-title," and "Task Created," which correspond to different agents and processes in our CrewAI system. This granular tracking allows us to identify bottlenecks, optimize specific agent behaviors, and ensure our multi-agent workflow is performing efficiently.</p><p>Now let's dive into how to set up this monitoring for your own CrewAI agents.</p><h3>Setting Up Langfuse with CrewAI</h3><p>Let's walk through the implementation step by step. First, here are the required dependencies for our project:</p><ul><li><p>crewai</p></li><li><p>langfuse </p></li><li><p>openlit </p></li><li><p>crewai-tools</p></li></ul><h4>Environment Configuration</h4><p>Start by setting up your environment variables in a <code>.env</code> file:</p><ul><li><p>LANGFUSE_SECRET_KEY=your_langfuse_secret_key</p></li><li><p>LANGFUSE_PUBLIC_KEY=your_langfuse_public_key</p></li><li><p>LANGFUSE_HOST=http://localhost:3000</p></li><li><p>OPENAI_API_KEY=your_openai_api_key</p></li></ul><h4>Implementation Code</h4><p>Here's the complete implementation that demonstrates how to integrate Langfuse monitoring with CrewAI:</p><pre><code>from crewai import LLM, Agent, Task, Crew
from langfuse import Langfuse, observe
from langfuse.openai import openai
import dotenv
import os

# Load environment variables
dotenv.load_dotenv()

# Initialize Langfuse client
langfuse = Langfuse(
    secret_key=os.getenv("LANGFUSE_SECRET_KEY"),
    public_key=os.getenv("LANGFUSE_PUBLIC_KEY"),
    host=os.getenv("LANGFUSE_HOST")
)

openai_api_key = os.getenv("OPENAI_API_KEY")
client = openai.OpenAI(api_key=openai_api_key)

@observe(name="blog-agent", as_type="generation")
def generate_blog_post_title():
    # Configure LLM for CrewAI
    llm = LLM(model="gpt-4o-mini", api_key=openai_api_key)

    # Create the agent
    blog_agent = Agent(
        role="Blog Post Agent",
        goal="Generate a blog post title about AI",
        backstory="You are a blog post agent that generates blog posts about AI",
        llm=llm,
        verbose=True
    )
    
    # Define the task
    title_task = Task(
        description="Generate a blog post title about AI",
        expected_output="A blog post title about AI",
        agent=blog_agent
    )

    # Create and execute the crew
    crew = Crew(agents=[blog_agent], tasks=[title_task])
    response = crew.kickoff()

    # Additional Langfuse span for specific tracking
    with langfuse.start_as_current_span(name="blog-post-title"):
        return response.raw

# Execute the function
print(generate_blog_post_title())</code></pre><h4>Key Integration Points</h4><p>The magic happens in several places:</p><ul><li><p><strong>@observe decorator</strong>: This automatically tracks the entire function execution, capturing inputs, outputs, and performance metrics</p></li><li><p><strong>Langfuse client initialization</strong>: Connects to your Langfuse instance (running locally on port 3000 in this example)</p></li><li><p><strong>CrewAI execution</strong>: The standard CrewAI workflow runs normally while Langfuse captures all the underlying LLM calls</p></li><li><p><strong>Additional spans</strong>: The <code>start_as_current_span</code> context manager allows you to add custom tracking points</p></li></ul><p>When you run this code, Langfuse automatically captures every interaction between your agents, including token usage, response times, and the complete conversation flow that led to the final blog post title.</p><h4>Configuring Model Pricing for Cost Tracking</h4><p>One crucial step for accurate cost monitoring is configuring your models in the Langfuse platform. Without this configuration, Langfuse won't be able to calculate the actual costs of your agent interactions.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qY_R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qY_R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 424w, https://substackcdn.com/image/fetch/$s_!qY_R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 848w, https://substackcdn.com/image/fetch/$s_!qY_R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 1272w, https://substackcdn.com/image/fetch/$s_!qY_R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qY_R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png" width="1456" height="397" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:397,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36566,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/168200334?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qY_R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 424w, https://substackcdn.com/image/fetch/$s_!qY_R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 848w, https://substackcdn.com/image/fetch/$s_!qY_R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 1272w, https://substackcdn.com/image/fetch/$s_!qY_R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe4961b0-0e7c-4fb1-bb20-55dafb8c0f0d_1857x506.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As you can see in the screenshot above, you need to add your specific models to Langfuse with their pricing information. For our example using <code>gpt-4o-mini</code>, the configuration includes the following dummy configuration:</p><ul><li><p>Input tokens: $5.0000000 per 1M units</p></li><li><p>Output tokens: $5.0000000 per 1M units</p></li><li><p>Input cached tokens: $5.0000000 per 1M units</p></li><li><p>Output reasoning tokens: $5.0000000 per 1M units</p></li></ul><p>To set this up for your own project:</p><ol><li><p>Navigate to your Langfuse dashboard</p></li><li><p>Go to the Models section</p></li><li><p>Click "Add Model" or configure an existing one</p></li><li><p>Enter the correct pricing based on your model provider's current rates</p></li></ol><p><strong>Important</strong>: Make sure to use the actual pricing from your model provider. For <code>gpt-4o-mini</code>, you should verify the current OpenAI pricing rates, as they may differ from the example values shown, which are for illustrative purposes and are not real ($5.00 per 1M units). Always check OpenAI's official pricing page for the most up-to-date rates.</p><p>Once configured, Langfuse will automatically calculate costs for every agent interaction, giving you the detailed cost breakdowns we saw in the dashboard earlier.</p><h3>Conclusion</h3><p>I was researching open-source monitoring tools over the weekend and decided to give Langfuse a try. It seems like an established platform, well-liked by the community. I liked it too and maybe I will use it in some production project soon.</p><p>The combination of Langfuse and CrewAI provides excellent visibility into agent interactions, token usage, and costs&#8212;all essential for production multi-agent systems. As your systems grow, these observability features become invaluable for tracking performance and optimizing workflows.</p><p>This isn't an advertisement for either tool, just sharing what worked well for my own projects. There are other monitoring solutions out there that I will test and write about soon.</p><p>Ready to try it yourself? Set up your Langfuse instance, integrate it with your favorite agentic framework, and start monitoring your multi-agent systems today.</p><p><strong>Useful Resources:</strong></p><ul><li><p><a href="https://langfuse.com/docs">Langfuse Documentation</a></p></li><li><p><a href="https://docs.crewai.com">CrewAI Documentation</a></p></li></ul><p>Written by human and AI &#129302;&#128104;&#8205;&#128187;</p>]]></content:encoded></item><item><title><![CDATA[The Dark Side of Vibe Coding]]></title><description><![CDATA[Why I see vibe coding as potentially cancerous]]></description><link>https://systemshogun.com/p/the-dark-side-of-vibe-coding</link><guid isPermaLink="false">https://systemshogun.com/p/the-dark-side-of-vibe-coding</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sat, 05 Jul 2025 11:05:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4dxi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Unless you&#8217;ve been living under a rock for the past couple of years, you&#8217;ve probably noticed that AI dominates your feeds on LinkedIn, YouTube, and just about every other platform. Everything is about AI&#8212;everyone&#8217;s talking about it, building with it, or trying to monetize it.</p><p>As much as I love AI&#8212;and I do use it, build systems with it, and follow the space closely&#8212;there&#8217;s one trend I keep seeing across platforms like X, Reddit, and LinkedIn that really worries me. It&#8217;s what people call <strong>vibe coding</strong>.</p><h3>So, what is vibe coding?</h3><p>Vibe coding is a new approach to software development that leverages large language models (LLMs) to generate code based on natural language prompts&#8212;essentially letting the AI handle the implementation while the developer focuses on describing what they want in plain language instead of code.</p><p>In other words, it&#8217;s not about writing code in the traditional sense anymore. It&#8217;s about typing prompts into an AI tool, copying whatever comes out, and hoping it works&#8212;without fully understanding what&#8217;s being built under the hood.</p><p>Platforms like Lovable, Bolt.new, and Replit make it extremely easy to spin up an app quickly with AI and zero coding. While that&#8217;s great for rapid prototyping or learning, <strong>it encourages a mindset where understanding is not needed</strong> &#8212; as long as the app runs. This new idea of building an application by just typing to some magic black box (AI) what you want, and you magically get 'something' is extremely dangerous. </p><p>I love the quote, <em>&#8220;<strong>a picture is worth a thousand words</strong>,&#8221;</em> and nothing illustrates the danger better than a quick search on GitHub. If you type something like <code>OPENAI_API_KEY=</code> into the search bar, you'll instantly find thousands of leaked API keys. Why?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zgrv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zgrv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 424w, https://substackcdn.com/image/fetch/$s_!zgrv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 848w, https://substackcdn.com/image/fetch/$s_!zgrv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 1272w, https://substackcdn.com/image/fetch/$s_!zgrv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zgrv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png" width="712" height="260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:260,&quot;width&quot;:712,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:47217,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/167570666?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zgrv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 424w, https://substackcdn.com/image/fetch/$s_!zgrv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 848w, https://substackcdn.com/image/fetch/$s_!zgrv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 1272w, https://substackcdn.com/image/fetch/$s_!zgrv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc16617-caaa-4dd5-b0f9-c9ea35d85e3a_712x260.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Because many vibe coders&#8212;especially those who rely heavily on AI tools&#8212;either blindly copy-paste the output into their codebase or use tools that inject the code for them automatically, often including sensitive information like API keys, database URIs, and credentials. They deploy it and move on&#8212;without understanding the risks.</p><p>This is the dark side of vibe coding: no fundamentals, no awareness of security, no understanding of secret management&#8212;just code being deployed by people with no real understanding of coding or proper development practices.</p><p>These vibe coders aren't aware that secrets should be stored in environment variables and protected using secure services like Azure Key Vault, AWS Secrets Manager, or Vault. They don&#8217;t know about <code>.gitignore</code> or the consequences of exposing a key in a public repository.</p><p>Many of them started coding only after LLMs became widely available and skipped the foundational learning entirely. They rely solely on prompts, with little to no understanding of security, architecture, or even basic coding principles&#8212;which is extremely dangerous.</p><p>They&#8217;re just chasing a working demo with gradient backgrounds and emojis, not building a secure, reliable system.</p><p>I&#8217;m not against vibe coding&#8212;when it&#8217;s used for basic, boilerplate tasks you already understand but simply don&#8217;t want to spend hours doing. That&#8217;s totally fine.</p><p>But the idea of building entire systems or applications blindly, without understanding what&#8217;s actually happening under the hood, and expecting them to work long-term&#8212;that&#8217;s a recipe for disaster.</p><h3>Vibe coding is cancerous</h3><p>Vibe coding is harmless when confined to trivial boilerplate or an isolated sandbox.<br>The rot begins when entire features&#8212;or worse, full systems&#8212;reach production without anyone truly understanding the code.</p><p>If this starts spreading like cancer, the consequences will be severe. Entire teams will ship code they don&#8217;t understand, creating systems that are fragile, insecure, and impossible to maintain. Technical debt will explode. Debugging will become guesswork. Critical bugs and vulnerabilities will slip through because no one knows what the code is supposed to do in the first place.</p><p>Over time, we&#8217;ll have a generation of developers who can prompt but can&#8217;t code&#8212;who can build demos, but not systems.<br>And companies will be left with software that&#8217;s essentially a black box&#8212;one that breaks, exposes sensitive data, or collapses under real-world load with no one capable of fixing it.</p><h4>The developer risks</h4><p>First, the engineer who ships vibe-coded solutions often has no idea what&#8217;s inside the AI-generated black box. And when you don&#8217;t understand something, you can&#8217;t control it&#8212;just like mistaking an alligator for a puppy.</p><p>But the real danger is long-term: relying solely on vibe coding erodes your fundamentals&#8212;if you had any to begin with. You stop thinking in terms of design patterns, data structures, security, performance, and scalability. Over time, you become someone who can only prompt but not debug, only generate but not reason. You become completely dependent on these tools&#8212;and suddenly, you can't build anything on your own.</p><p>And when the AI fails&#8212;or the job market demands real engineering skills&#8212;what&#8217;s the need for you? You&#8217;re done.</p><h4>The business risk</h4><p>Unchecked vibe code in production can sink a company financially and reputationally unless a qualified engineer reviews every line. Imagine someone &#8220;prompt-engineering&#8221; a banking service and pressing Deploy. Here&#8217;s what can&#8212;and often does&#8212;go wrong:</p><ul><li><p><strong>Hard-coded secrets</strong> leak to GitHub, letting attackers drain accounts in minutes.</p></li><li><p><strong>Missing input validation</strong> enables SQL injection or path-traversal attacks that expose customer data.</p></li><li><p><strong>Broken authorization logic</strong> lets one user see or transfer another user&#8217;s funds.</p></li><li><p><strong>Concurrency bugs</strong> (e.g., unsafely updating balances) cause double withdrawals or negative totals.</p></li><li><p><strong>Unbounded retries / infinite loops</strong> spike cloud bills and DOS your own service.</p></li><li><p><strong>Silent error-handling</strong> swallows exceptions, leaving corrupted data with no audit trail.</p></li><li><p><strong>Regulatory breaches</strong>&#8212;like logging raw cardholder data&#8212;trigger multi-million-euro GDPR fines.</p></li><li><p><strong>Untested edge cases</strong> crash mobile apps, torpedoing user trust and app-store ratings.</p></li></ul><p>Let&#8217;s say your vibe-coded solution makes it to production. If you don&#8217;t understand how it works, then even when things start breaking, you might not realize that the feature is the root cause. It could take days&#8212;or worse, weeks&#8212;to connect the dots.</p><p>And here&#8217;s the nightmare scenario: how do you debug something you don&#8217;t even understand?<br>You&#8217;re staring at code that looks like magic, with no idea what&#8217;s normal and what&#8217;s broken.<br>Good luck fixing a production issue under pressure when you weren&#8217;t the one who actually written the code.</p><p>Keep vibe coding on a short leash; once it slips into critical paths, it mutates from a time-saver into an existential threat.</p><h3>Who profits from selling this illusion?</h3><p>This whole mindset has been fueled by the big tech companies, who are investing tens&#8212;and even hundreds&#8212;of billions into AI, while aggressively pushing the narrative that &#8220;everyone can be a developer now.&#8221; But that&#8217;s one of the biggest and costliest lies the tech world has ever seen.</p><p>They promote ideas like the &#8220;prompt engineer&#8221; or the &#8220;AI pair programmer&#8221; by building tools such as GitHub Copilot, Claude Code, and Cursor&#8212;platforms where you can let AI generate large chunks of code with little to no understanding of what it&#8217;s actually doing. For many, it feels like magic&#8212;a black box that just works.</p><p>But that same code might as well launch a nuclear missile, and they wouldn&#8217;t know the difference.<br>Of course, I&#8217;m exaggerating (I hope) to make a point&#8212;but the danger is real.</p><h3>Let&#8217;s be real now</h3><p>AI is here to stay. It&#8217;s a major breakthrough and absolutely boosts productivity&#8212;that's a fact. But what AI is not is a self-sufficient software engineer that can build and maintain entire systems on its own.</p><p>As much as companies pushing this narrative want us to believe otherwise&#8212;mostly to justify the billions they've poured into AI, boost their stock prices, and keep investors happy&#8212;the reality is very different. There&#8217;s no AI system you can just turn on and expect it to build the next Netflix or Amazon.</p><p>Yes, there are incredible tools that help you build and ship faster. That&#8217;s true. But that&#8217;s where it ends.</p><p>I especially love when companies claim that &#8220;30%,&#8221; &#8220;50%,&#8221; or even &#8220;60%&#8221; of their code is now written by AI. It sounds impressive&#8212;until you realize what it actually means. It doesn&#8217;t mean there&#8217;s some evil AI in the basement writing production-grade systems while engineers sip mojitos. It simply means that some portion of the code pushed to production contains AI-generated snippets&#8212;most of which are (hopefully) still reviewed, edited, and understood by real developers.</p><p><strong>Judgement Day</strong></p><p>My advice to you: never be that guy.<br>Thanks, AI, for painting this so beautifully on the first try.</p><p>Until next time&#8212;write code, learn code, not just prompts.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4dxi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4dxi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!4dxi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!4dxi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!4dxi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4dxi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2554156,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/167570666?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4dxi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!4dxi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!4dxi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!4dxi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2064cae-abfd-4fe3-8f04-17992ec04648_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Written by human and AI &#10084;&#65039;&#129302;</strong></p>]]></content:encoded></item><item><title><![CDATA[What Uber Taught Me About Design]]></title><description><![CDATA[Uber simplicity isn&#8217;t magic &#8212; it&#8217;s architecture and design done right]]></description><link>https://systemshogun.com/p/what-uber-taught-me-about-design</link><guid isPermaLink="false">https://systemshogun.com/p/what-uber-taught-me-about-design</guid><dc:creator><![CDATA[Kaloyan Drenski]]></dc:creator><pubDate>Sun, 15 Jun 2025 09:49:33 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FnnL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On a recent trip to Barcelona, I used <strong>Uber</strong> for the very first time and I was blown away. What struck me wasn&#8217;t just that I got from point A to point B &#8212; it was <em>how easy</em> it was. I tapped a few buttons, my ride arrived in minutes, payment was invisible, and I didn&#8217;t have to think. It just <em>worked</em>.</p><p>As a solution architect, this experience got me thinking. Simplicity for the user isn&#8217;t magic. It&#8217;s the result of conscious, often <em>painstaking</em> decisions made by designers, architects, and engineers. The complexity doesn't disappear &#8212; <em>it shifts</em>. I call this <strong>Burden Shifting Design</strong>: the practice of moving the hard parts away from the user experience and into the product&#8217;s architecture, processes, and operations.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FnnL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FnnL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 424w, https://substackcdn.com/image/fetch/$s_!FnnL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 848w, https://substackcdn.com/image/fetch/$s_!FnnL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 1272w, https://substackcdn.com/image/fetch/$s_!FnnL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FnnL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png" width="1456" height="528" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52f56881-9373-4d97-8423-f525b66b901e_2012x730.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:528,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:187409,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://systemshogun.com/i/165923520?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FnnL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 424w, https://substackcdn.com/image/fetch/$s_!FnnL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 848w, https://substackcdn.com/image/fetch/$s_!FnnL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 1272w, https://substackcdn.com/image/fetch/$s_!FnnL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52f56881-9373-4d97-8423-f525b66b901e_2012x730.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Moving the hard work from the user&#8217;s side to the product&#8217;s side</figcaption></figure></div><div><hr></div><h3>The Wrong Approach: Offloading Complexity to the User</h3><p>Too often, systems are designed for the convenience of the company or development team rather than the user. The result? Clunky interfaces, fragmented workflows, and experiences that force users to handle the complexity that should have been absorbed by the product:</p><ul><li><p>&#8220;Please select from these 12 confusing options.&#8221;</p></li><li><p>&#8220;You need to navigate through multiple forms to complete this simple task.&#8221;</p></li><li><p>&#8220;Tell us which department to contact because we can&#8217;t figure it out for you.&#8221;</p></li><li><p>&#8220;Enter these details again because our systems don&#8217;t talk to each other.&#8221;</p></li><li><p>&#8220;We require you to upload the same document twice in different places.&#8221;</p></li></ul><p>Technically, these products might work. But they fail the user because they place the burden in the wrong place. In time, products like these tend to lose the interest and loyalty of their users, or they get replaced by newer, smarter products that offer a better, simpler experience.</p><p>We&#8217;ve seen this happen again and again. <strong>BlackBerry</strong> and <strong>Nokia</strong> lost to the <strong>iPhone</strong> because <strong>Apple</strong> shifted the complexity inward, creating a seamless experience, while BlackBerry and Nokia clung to outdated, clunky interfaces.</p><p><strong>Blockbuster</strong> was replaced by <strong>Netflix</strong>, which removed the friction of store visits, late fees, and availability issues, shifting the complexity to digital licensing, streaming infrastructure, and personalization. </p><p><strong>Yahoo Search</strong> lost to <strong>Google</strong>, which delivered cleaner, faster, and more relevant results by shouldering the complexity of better algorithms and data handling behind the scenes.</p><div><hr></div><h3>The Right Approach: Burden Shifting Design</h3><p>Now think about Uber. For me as a user, booking a ride was totally effortless. I just tapped a few buttons, and everything was taken care of. The app matched me with a driver in real time, calculated the price dynamically, processed my payment securely without me lifting a finger, and provided live tracking with accurate ETAs. It felt seamless &#8212; no friction, no confusion, no extra effort on my part.</p><p>And what impressed me even more was how smoothly things worked even when something didn&#8217;t go as planned. One time, I booked a ride, but the driver was taking too long to arrive. I decided to cancel &#8212; and even that was simple. With a single tap, I cancelled the ride, and the app handled the rest &#8212; no awkward calls, no hassle, no stress.</p><p>Behind the scenes, here&#8217;s what Uber was doing for <em>me</em>:</p><ul><li><p>Matching me with a driver in real time</p></li><li><p>Calculating the price dynamically based on demand</p></li><li><p>Processing my payment securely</p></li><li><p>Tracking the driver live and giving me accurate ETAs</p></li><li><p>Handling cancellations, refunds, and adjustments smoothly</p></li><li><p>Ensuring compliance with local laws and regulations</p></li><li><p>Doing all this seamlessly while managing billions of transactions like mine every day</p></li></ul><p>The user enjoys simplicity because Uber takes on the complexity. This is the essence of <strong>Burden Shifting Design</strong> &#8212; <strong>moving the hard work from the user&#8217;s side to the product&#8217;s side.</strong></p><p>Next time we start designing a system, let&#8217;s think about Uber and companies like it. Let&#8217;s ask ourselves: <em>How can we take on the hard work, so our users don&#8217;t have to?</em> That&#8217;s where great products begin.</p>]]></content:encoded></item></channel></rss>