<?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:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Khaled Md Tuhidul Hossain on Medium]]></title>
        <description><![CDATA[Stories by Khaled Md Tuhidul Hossain on Medium]]></description>
        <link>https://medium.com/@mth.tuhin?source=rss-938fca332512------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*b9fMKFX84zImxUYo2mnRTQ.png</url>
            <title>Stories by Khaled Md Tuhidul Hossain on Medium</title>
            <link>https://medium.com/@mth.tuhin?source=rss-938fca332512------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 09 Jun 2026 04:31:37 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@mth.tuhin/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Microsoft Agent Framework: The Successor to Semantic Kernel and AutoGen]]></title>
            <link>https://medium.com/@mth.tuhin/microsoft-agent-framework-the-successor-to-semantic-kernel-and-autogen-723f6fadae8e?source=rss-938fca332512------2</link>
            <guid isPermaLink="false">https://medium.com/p/723f6fadae8e</guid>
            <category><![CDATA[microsoft-agent-framework]]></category>
            <category><![CDATA[agent-development-kit]]></category>
            <category><![CDATA[agentic-workflow]]></category>
            <category><![CDATA[microsoft-autogen]]></category>
            <dc:creator><![CDATA[Khaled Md Tuhidul Hossain]]></dc:creator>
            <pubDate>Wed, 03 Jun 2026 05:24:26 GMT</pubDate>
            <atom:updated>2026-06-03T05:24:26.715Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/861/1*OGS9PS0D_-U2WNQsaSncCg.png" /></figure><p>I’ve written about both Semantic Kernel and AutoGen on this blog before, and the split was always a bit awkward. Semantic Kernel was the .NET-first, enterprise-leaning option with kernels, plugins, filters and telemetry. AutoGen was the Python-friendly playground for multi-agent conversations. Two teams at Microsoft, two SDKs, two mental models for what was basically the same job.</p><p>That split is done. The two teams have shipped one framework, the <a href="https://github.com/microsoft/agent-framework">Microsoft Agent Framework (MAF)</a>, and they’re calling it the direct successor to both. From the overview docs:</p><blockquote>Agent Framework combines AutoGen’s simple agent abstractions with Semantic Kernel’s enterprise features (session-based state management, type safety, middleware, telemetry) and adds graph-based workflows for explicit multi-agent orchestration.</blockquote><p>So: .NET and Python with the same API shape, a hosting story tied to Microsoft Foundry, model clients for most of the names you’d expect (Azure OpenAI, OpenAI, Anthropic, Ollama, GitHub Copilot, Google Gemini, ONNX, a few more), and a graph-based workflow engine that neither parent framework really had.</p><p>What follows is a tour of what MAF feels like once you start typing. Agents and workflows, the building blocks underneath them, the orchestration patterns, and the hosting story. Code in both Python and C#, because the framework treats them as peers and so should the post.</p><h3>The mental model</h3><p>Everything in MAF is either an agent or a workflow.</p><p>PrimitiveWhat it isAgentA single LLM-driven loop that can call tools and MCP servers. Use it when the task is open-ended or conversational.WorkflowA graph of executors (agents, functions, or custom nodes) connected by edges, with type-safe routing, checkpointing, and human-in-the-loop support. Use it when the process has well-defined steps.</p><p>The docs are also honest about when you should use neither:</p><blockquote>If you can write a function to handle the task, do that instead of using an AI agent.</blockquote><p>Which I appreciate. Half the agent posts I read in 2025 were people building agents for jobs a case statement could have done.</p><p>Underneath both pillars, the same five names keep coming up: ChatClient (the model abstraction), AgentSession (state), ContextProvider (memory and dynamic instructions), middleware (interception), and MCP clients (tool integration). Learn those and most of the framework stops being surprising.</p><h3>Installing</h3><p>Python:</p><p>bash</p><pre>pip install agent-framework</pre><p>.NET:</p><p>bash</p><pre>dotnet add package Microsoft.Agents.AI<br>dotnet add package Microsoft.Agents.AI.Foundry<br>dotnet add package Azure.AI.Projects<br>dotnet add package Azure.Identity</pre><p>The Python install is one meta-package that pulls in subpackages for each provider. On .NET you pick the integration packages you need.</p><h3>Hello agent</h3><p>Smallest possible agent in C#, against Azure OpenAI:</p><p>csharp</p><pre>using Azure.AI.OpenAI;<br>using Azure.Identity;<br>using Microsoft.Agents.AI;</pre><pre>var endpoint = Environment.GetEnvironmentVariable(&quot;AZURE_OPENAI_ENDPOINT&quot;)<br>    ?? throw new InvalidOperationException(&quot;AZURE_OPENAI_ENDPOINT is not set.&quot;);<br>var deploymentName = Environment.GetEnvironmentVariable(&quot;AZURE_OPENAI_DEPLOYMENT_NAME&quot;) ?? &quot;gpt-4o-mini&quot;;<br>AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())<br>    .GetChatClient(deploymentName)<br>    .AsAIAgent(<br>        instructions: &quot;You are good at telling jokes.&quot;,<br>        name: &quot;Joker&quot;);<br>Console.WriteLine(await agent.RunAsync(&quot;Tell me a joke about a pirate.&quot;));<br>await foreach (var update in agent.RunStreamingAsync(&quot;Tell me a joke about a pirate.&quot;))<br>{<br>    Console.WriteLine(update);<br>}</pre><p>Pick a client, call .AsAIAgent(...), get an AIAgent. Run it or stream it.</p><p>The Python version of the same shape, this time against a Foundry project:</p><p>python</p><pre>import asyncio<br>from agent_framework import Agent<br>from agent_framework.foundry import FoundryChatClient<br>from azure.identity import AzureCliCredential<br></pre><pre>async def main() -&gt; None:<br>    client = FoundryChatClient(<br>        project_endpoint=&quot;https://your-project.services.ai.azure.com&quot;,<br>        model=&quot;gpt-4o&quot;,<br>        credential=AzureCliCredential(),<br>    )<br>    agent = Agent(<br>        client=client,<br>        name=&quot;HelloAgent&quot;,<br>        instructions=&quot;You are a friendly assistant. Keep your answers brief.&quot;,<br>    )<br>    result = await agent.run(&quot;What is the capital of France?&quot;)<br>    print(f&quot;Agent: {result}&quot;)<br>    print(&quot;Agent (streaming): &quot;, end=&quot;&quot;, flush=True)<br>    async for chunk in agent.run(&quot;Tell me a one-sentence fun fact.&quot;, stream=True):<br>        if chunk.text:<br>            print(chunk.text, end=&quot;&quot;, flush=True)<br>    print()<br><br>if __name__ == &quot;__main__&quot;:<br>    asyncio.run(main())</pre><p>A couple of things you might miss on first read. stream=True is a flag on the same run call, not a separate API. And auth goes through azure-identity, so there&#39;s no API key sitting in the sample. Small choices, but the kind of small choice that tells you whether the people who built it have ever had to clean up after a hardcoded key getting committed.</p><h3>Tools</h3><p>Tools are how the agent does anything beyond producing tokens. In Python you decorate a function with @tool. In .NET you wrap a method with AIFunctionFactory.Create.</p><p>Python:</p><p>python</p><pre>from random import randint<br>from typing import Annotated<br>from agent_framework import Agent, tool<br>from agent_framework.foundry import FoundryChatClient<br>from azure.identity import AzureCliCredential<br>from pydantic import Field</pre><pre># NOTE: approval_mode=&quot;never_require&quot; is fine for samples.<br># Use &quot;always_require&quot; in production for human-in-the-loop confirmation.<br>@tool(approval_mode=&quot;never_require&quot;)<br>def get_weather(<br>    location: Annotated[str, Field(description=&quot;The location to get the weather for.&quot;)],<br>) -&gt; str:<br>    &quot;&quot;&quot;Get the weather for a given location.&quot;&quot;&quot;<br>    conditions = [&quot;sunny&quot;, &quot;cloudy&quot;, &quot;rainy&quot;, &quot;stormy&quot;]<br>    return f&quot;The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C.&quot;<br><br>agent = Agent(<br>    client=FoundryChatClient(credential=AzureCliCredential()),<br>    name=&quot;WeatherAgent&quot;,<br>    instructions=&quot;You are a helpful weather agent. Use the get_weather tool to answer questions.&quot;,<br>    tools=[get_weather],<br>)</pre><p>.NET:</p><p>csharp</p><pre>using System.ComponentModel;<br>using Azure.AI.OpenAI;<br>using Azure.Identity;<br>using Microsoft.Agents.AI;<br>using Microsoft.Extensions.AI;</pre><pre>[Description(&quot;Get the weather for a given location.&quot;)]<br>static string GetWeather([Description(&quot;The location to get the weather for.&quot;)] string location)<br>    =&gt; $&quot;The weather in {location} is cloudy with a high of 15°C.&quot;;<br>AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())<br>    .GetChatClient(deploymentName)<br>    .AsAIAgent(<br>        instructions: &quot;You are a helpful assistant&quot;,<br>        tools: [AIFunctionFactory.Create(GetWeather)]);<br>Console.WriteLine(await agent.RunAsync(&quot;What is the weather like in Amsterdam?&quot;));</pre><p>The approval_mode argument is one I wish more frameworks shipped with. It lets the framework pause a run, surface the tool call to a human, and resume once someone clicks approve. Not a bolt-on, just a flag.</p><p>Tools can also come from MCP servers. MAF ships an MCP client, so you can pull in remote tool catalogues without wrapping anything yourself. That matters more in 2026 than it would have a year ago, because the useful tool surfaces (databases, internal APIs, SaaS connectors) are showing up as MCP servers now instead of per-framework adapters.</p><h3>Sessions: state that carries across turns</h3><p>A single run call is stateless. To keep conversation history around, create an AgentSession:</p><p>csharp</p><pre>AgentSession session = await agent.CreateSessionAsync();</pre><pre>Console.WriteLine(await agent.RunAsync(&quot;Tell me a joke about a pirate.&quot;, session));<br>Console.WriteLine(await agent.RunAsync(<br>    &quot;Now add some emojis and tell it in the voice of a pirate&#39;s parrot.&quot;,<br>    session));</pre><p>Python is the same shape:</p><p>python</p><pre>session = agent.create_session()</pre><pre>result = await agent.run(&quot;My name is Alice and I love hiking.&quot;, session=session)<br>print(f&quot;Agent: {result}\n&quot;)<br>result = await agent.run(&quot;What do you remember about me?&quot;, session=session)<br>print(f&quot;Agent: {result}&quot;)</pre><p>The session holds messages, tool call history, and arbitrary state that providers can read and write. This is the piece SK had and AutoGen mostly punted on, and it’s what makes MAF feel like an SDK you’d ship rather than a demo you’d show.</p><h3>Context providers: memory you can actually reason about</h3><p>If you’ve built agents the hard way, you know what “memory” usually means: injecting user preferences, retrieved knowledge, or dynamic instructions into every call without losing your mind. MAF formalises this with context providers. They’re objects with before_run and after_run hooks that can read and mutate the prompt and the session state.</p><p>python</p><pre>from agent_framework import Agent, AgentSession, ContextProvider, SessionContext<br></pre><pre>class UserMemoryProvider(ContextProvider):<br>    &quot;&quot;&quot;A context provider that remembers user info in session state.&quot;&quot;&quot;<br>    DEFAULT_SOURCE_ID = &quot;user_memory&quot;<br>    def __init__(self):<br>        super().__init__(self.DEFAULT_SOURCE_ID)<br>    async def before_run(self, *, agent, session, context: SessionContext, state):<br>        user_name = state.get(&quot;user_name&quot;)<br>        if user_name:<br>            context.extend_instructions(<br>                self.source_id,<br>                f&quot;The user&#39;s name is {user_name}. Always address them by name.&quot;,<br>            )<br>        else:<br>            context.extend_instructions(<br>                self.source_id,<br>                &quot;You don&#39;t know the user&#39;s name yet. Ask for it politely.&quot;,<br>            )<br>    async def after_run(self, *, agent, session, context: SessionContext, state):<br>        for msg in context.input_messages:<br>            text = msg.text if hasattr(msg, &quot;text&quot;) else &quot;&quot;<br>            if isinstance(text, str) and &quot;my name is&quot; in text.lower():<br>                state[&quot;user_name&quot;] = text.lower().split(&quot;my name is&quot;)[-1].strip().split()[0].capitalize()</pre><p>Attach it to the agent and it runs on every call:</p><p>python</p><pre>agent = Agent(<br>    client=client,<br>    name=&quot;MemoryAgent&quot;,<br>    instructions=&quot;You are a friendly assistant.&quot;,<br>    context_providers=[UserMemoryProvider()],<br>)</pre><p>I like this design. Anything you’d otherwise stitch in by hand (RAG retrieval, a user-preferences cache, a safety filter) is a context provider. They compose cleanly because each one only writes to its own source_id slice of state, so two providers can&#39;t quietly stomp on each other&#39;s keys.</p><h3>Workflows: graphs of executors</h3><p>This is the part of MAF I was most curious about. AutoGen had conversations between agents. SK had plan-and-execute. Neither could really say “run this graph deterministically, with checkpoints, with a human approval node sitting on this edge.” Workflows are the answer to that gap.</p><p>A workflow is built from executors (anything that processes an input and produces an output) connected by edges. Simplest possible workflow in C#:</p><p>csharp</p><pre>using Microsoft.Agents.AI.Workflows;</pre><pre>Func&lt;string, string&gt; uppercaseFunc = s =&gt; s.ToUpperInvariant();<br>var uppercase = uppercaseFunc.BindAsExecutor(&quot;UppercaseExecutor&quot;);<br>var reverse = new ReverseTextExecutor();<br>WorkflowBuilder builder = new(uppercase);<br>builder.AddEdge(uppercase, reverse).WithOutputFrom(reverse);<br>var workflow = builder.Build();<br>await using Run run = await InProcessExecution.RunAsync(workflow, &quot;Hello, World!&quot;);<br>foreach (WorkflowEvent evt in run.NewEvents)<br>{<br>    if (evt is ExecutorCompletedEvent executorComplete)<br>    {<br>        Console.WriteLine($&quot;{executorComplete.ExecutorId}: {executorComplete.Data}&quot;);<br>    }<br>}<br>internal sealed class ReverseTextExecutor() : Executor&lt;string, string&gt;(&quot;ReverseTextExecutor&quot;)<br>{<br>    public override ValueTask&lt;string&gt; HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)<br>        =&gt; ValueTask.FromResult(string.Concat(message.Reverse()));<br>}</pre><p>In Python it’s Executor classes or @executor-decorated functions, wired with a WorkflowBuilder:</p><p>python</p><pre>from agent_framework import Executor, WorkflowBuilder, WorkflowContext, executor, handler<br>from typing_extensions import Never<br></pre><pre>class UpperCase(Executor):<br>    def __init__(self, id: str):<br>        super().__init__(id=id)<br>    @handler<br>    async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -&gt; None:<br>        await ctx.send_message(text.upper())<br><br>@executor(id=&quot;reverse_text&quot;)<br>async def reverse_text(text: str, ctx: WorkflowContext[Never, str]) -&gt; None:<br>    await ctx.yield_output(text[::-1])<br><br>upper = UpperCase(id=&quot;upper_case&quot;)<br>workflow = WorkflowBuilder(start_executor=upper).add_edge(upper, reverse_text).build()<br>events = await workflow.run(&quot;hello world&quot;)<br>print(events.get_outputs())  # [&#39;DLROW OLLEH&#39;]py</pre><p>The graph API gives you fan-out, fan-in, switch/case, loops, and superstep-based checkpointing. The framework can persist workflow state at each superstep boundary, so a crash mid-workflow doesn’t lose anything: restart the process and it picks up where it stopped. That’s the durability story SK never quite finished.</p><p>For lower-ceremony cases there’s a functional workflow API where the whole thing is one async function and the framework infers the graph:</p><p>python</p><pre>from agent_framework import Agent, workflow</pre><pre>writer = Agent(name=&quot;WriterAgent&quot;, instructions=&quot;Write a short poem (4 lines max) about the given topic.&quot;, client=client)<br>reviewer = Agent(name=&quot;ReviewerAgent&quot;, instructions=&quot;Review the given poem in one sentence. Is it good?&quot;, client=client)<br><br>@workflow<br>async def poem_workflow(topic: str) -&gt; str:<br>    poem = (await writer.run(f&quot;Write a poem about: {topic}&quot;)).text<br>    review = (await reviewer.run(f&quot;Review this poem: {poem}&quot;)).text<br>    return f&quot;Poem:\n{poem}\n\nReview: {review}&quot;<br><br>result = await poem_workflow.run(&quot;a cat learning to code&quot;)</pre><p>Agents inside workflows are just function calls. No special wrappers, no message bus to learn. The workflow engine still sees the structure because each await boundary becomes an executor.</p><h3>Orchestration patterns</h3><p>On top of the workflow graph, MAF ships pre-built orchestration builders for the patterns you actually want.</p><p>BuilderTopologyUse whenSequentialBuilderA then B then CA pipeline of agents that each see the conversation so far.ConcurrentBuilderFan-out, fan-inSeveral agents work on the same input in parallel; an aggregator combines the results.HandoffBuilderMeshOne agent decides which other agent should take over. The framework auto-registers handoff tools, so the LLM literally calls transfer_to_refund_agent(...).GroupChatBuilderManager plus participantsRound-table conversation with a selector that picks who speaks next.MagenticBuilderManager-led plan and executeThe Magentic pattern from the AutoGen research: a planning manager that drives a team of specialist agents to solve a harder task.</p><p>Sequential workflow in five lines:</p><p>python</p><pre>from agent_framework.orchestrations import SequentialBuilder</pre><pre>workflow = SequentialBuilder(participants=[writer, reviewer], output_from=&quot;all&quot;).build()<br>result = await workflow.run(&quot;Write a tagline for a budget-friendly eBike.&quot;)</pre><p>A handoff workflow looks like a triage agent with three specialists wired into a mesh. The triage agent doesn’t call the specialists directly. It calls a handoff tool that the framework auto-generated, and the workflow engine routes the conversation to the chosen specialist while preserving the history:</p><p>python</p><pre>from agent_framework.orchestrations import HandoffBuilder</pre><pre>triage, refund, order, returns = create_agents(client)<br>workflow = HandoffBuilder(<br>    coordinator=triage,<br>    participants=[refund, order, returns],<br>).build()</pre><p>Magentic is the one I keep coming back to. You hand it a couple of specialist agents (a researcher and a coder in the sample) and a question like <em>“estimate the energy efficiency of these ML models”</em>. A manager LLM plans the work, decides who runs each step, and reconciles the outputs. It’s the AutoGen Magentic pattern, but now there’s checkpointing and an event stream around it, so you can actually see what happened when a run goes sideways at 2am.</p><h3>Providers</h3><p>MAF doesn’t pick a model vendor for you. The current set, straight from the samples directory:</p><ul><li>Microsoft Foundry, the new home for Azure-hosted agents</li><li>Azure OpenAI, both chat completions and responses APIs</li><li>OpenAI direct</li><li>Anthropic</li><li>Ollama for local models</li><li>GitHub Copilot SDK</li><li>Amazon Bedrock</li><li>Google Gemini</li><li>ONNX for on-device inference</li><li>Copilot Studio</li><li>Custom, for when you want to bring your own client</li></ul><p>Switching providers is a constructor change. The AIAgent / Agent surface above doesn&#39;t move. That&#39;s the part I&#39;ve been waiting on since SK and AutoGen split the world in two.</p><h3>Middleware</h3><p>Cross-cutting concerns (logging, retry, redaction, guardrails) are middleware. The pattern is the next delegate you&#39;d expect from ASP.NET or Express. The samples include an auto_retry example that wraps an agent in exponential backoff without the agent code knowing anything about it. Middleware composes, and it works around a single agent or around a whole workflow node.</p><h3>Hosting</h3><p>A few of the hosting targets that ship out of the box:</p><ul><li>Foundry-hosted agents. Deploy an agent to Microsoft Foundry infrastructure with two extra lines of code. The agent runs on a managed runtime, you keep the same SDK shape.</li><li>Azure Functions. Agents as serverless endpoints.</li><li>Durable Task, Durable Agents, Durable Workflows. Long-running, checkpointed workflows on top of the Durable Task framework. Process crashes and machine reboots don’t kill the run.</li><li>A2A. Expose any MAF agent over the <a href="https://blog.tuhidulhossain.com/post/the-agent2agent-a2a-protocol-a-complete-guide-to-ai-agent-interoperability-20260419">A2A protocol</a> so other agents (built on anything) can talk to it. I’ve written about A2A v1.0 separately; the fact that MAF speaks it natively is a real signal that Microsoft expects you to interop, not silo.</li><li>Container. Plain Docker for everywhere else.</li></ul><h3>Observability</h3><p>OpenTelemetry is built in. Spans for agent runs, tool calls, workflow executors, and model client calls all land in whatever tracing pipeline you already have. I’ve spent more hours than I want to admit retrofitting traces into SK and AutoGen, so this one matters more to me than it probably should.</p><h3>Declarative agents and skills</h3><p>Two smaller pieces worth a mention.</p><p>Declarative agents let you define an agent in YAML instead of code. Instructions, model, tools, context providers, all in a versionable file. Useful when product or compliance owns the prompt and devs own the runtime.</p><p>Agent skills let you build domain-specific knowledge bases (from files, inline code, or class libraries) that agents can discover and use at runtime. It’s a structured replacement for the “stuff everything into the system prompt” pattern: only the relevant skill gets pulled in for a given query.</p><p>There’s also a DevUI for poking at agent state and workflows during development, and an AF Labs subdirectory for experimental work like agent benchmarking and RL.</p><h3>Where this leaves Semantic Kernel and AutoGen</h3><p>Both repos are still up. The migration guides on Microsoft Learn are detailed enough that I think the team genuinely wants people to move, not just gesture at it. Kernels become chat clients, plugins become tools, AutoGen team chats become orchestration builders. None of that translation is particularly hard.</p><p>What I actually care about, more than the consolidation, is what it lets you do once everything lives in one place. The same agent code goes from agent.RunAsync(&quot;hello&quot;) on a laptop to a Magentic team running for hours inside a Durable workflow on Foundry, with OpenTelemetry traces and an MCP tool catalogue feeding everyone. SK couldn&#39;t really do the multi-agent half. AutoGen couldn&#39;t really do the production half. MAF is the first time I&#39;ve looked at Microsoft&#39;s agent story and not had to mentally split it into two.</p><p>I’m going to build something real on it over the next month and write that up separately. There’ll be sharp edges (there always are), but the shape of the thing looks right.</p><p>Links</p><ul><li>Original Post: <a href="https://blog.tuhidulhossain.com/post/microsoft-agent-framework-the-successor-to-semantic-kernel-and-autogen-20260531">https://blog.tuhidulhossain.com/post/microsoft-agent-framework-the-successor-to-semantic-kernel-and-autogen-20260531</a></li><li>Overview: <a href="https://learn.microsoft.com/en-us/agent-framework/overview/?pivots=programming-language-csharp">learn.microsoft.com/en-us/agent-framework/overview</a></li><li>Repo: <a href="https://github.com/microsoft/agent-framework">github.com/microsoft/agent-framework</a></li><li>Python samples: <a href="https://github.com/microsoft/agent-framework/tree/main/python/samples">python/samples</a></li><li>.NET samples: <a href="https://github.com/microsoft/agent-framework/tree/main/dotnet/samples">dotnet/samples</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=723f6fadae8e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An Enterprise Git Branching Strategy: main, develop, prod, and Promoting Through Staging]]></title>
            <link>https://medium.com/@mth.tuhin/an-enterprise-github-branching-strategy-main-develop-prod-and-promoting-through-staging-75e70f69eebc?source=rss-938fca332512------2</link>
            <guid isPermaLink="false">https://medium.com/p/75e70f69eebc</guid>
            <category><![CDATA[git-branching-strategy]]></category>
            <category><![CDATA[git]]></category>
            <category><![CDATA[github]]></category>
            <dc:creator><![CDATA[Khaled Md Tuhidul Hossain]]></dc:creator>
            <pubDate>Thu, 21 May 2026 03:52:29 GMT</pubDate>
            <atom:updated>2026-05-21T04:15:14.091Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jWZR51RjWPeqOoK5.png" /></figure><p>Most teams I’ve worked with don’t fail at branching because they picked the wrong model. They fail because nobody wrote the rules down. A new engineer joins, opens a PR straight into main, ships a hotfix that quietly clobbers someone&#39;s half-merged release, and a week disappears trying to figure out what happened.</p><p>This is the write-up I wish I’d handed to every team I joined. It’s a four-branch model that’s held up for me in larger orgs: long-lived main, develop, and prod, with feature/* , fix/* &amp; hotfix/*for the actual work. Code promotes through a Staging pipeline (QC/Test, then PreProd) before anything touches Production, and every release loops back into main so the baseline doesn&#39;t quietly drift away from what&#39;s running.</p><h3>The branching model</h3><pre>         feature/*   fix/*              hotfix/*<br>               \      /                   /<br>                \    /                   /<br>   main  ─────► develop ─────────────► prod ─────► (back-merge into main)<br>   ▲               │                    │<br>   │               ▼                    ▼<br> baseline       Staging                Production<br>              (QC/Test → PreProd)</pre><p>Four long-lived branches, two short-lived families:</p><ul><li>main is the baseline. It always mirrors what&#39;s currently running in Production. Nobody commits to it directly.</li><li>develop is the integration branch. Every feature and fix lands here first. CI ships every push into Staging (QC/Test).</li><li>prod is the release branch. When a release is cut from develop, it merges (or fast-forwards) into prod. CI deploys every push to Production.</li><li>feature/* are short-lived branches for new work, cut from develop.</li><li>fix/* are short-lived branches for bugs, also cut from develop. Hotfixes are different and get their own section below.</li><li>hotfix/* are short‑lived branches for urgent production fixes.</li></ul><p>After each Production release, prod gets back-merged into main so main keeps reflecting what&#39;s actually live.</p><h3>Why three long-lived branches instead of one</h3><p>Pure trunk-based development is great if you have feature flags everywhere, fast CI, and a culture that’s comfortable shipping on green. Most enterprises don’t. They have compliance reviews, change advisory boards, customer maintenance windows, and a QA team that needs something stable to test against. Pretending otherwise just pushes the complexity into Friday afternoons.</p><p>Three long-lived branches give you three stable answers:</p><p>BranchWhat it representsWho deploys itmainThe last thing successfully released to ProductionNobody, automateddevelopWhat QA is currently testingCI on every pushprodWhat&#39;s currently running in ProductionCI on every push</p><p>That’s the whole payoff. When a PM asks “what’s in QA right now,” the answer is git log develop. When a support engineer asks &quot;what&#39;s actually running for customers,&quot; the answer is git log prod (or main, which mirrors it after every release).</p><h3>Branch naming standard, with ticket references</h3><p>Branch names aren’t decoration. They’re the link between a commit, a PR, a CI run, a deploy, and the ticket that justified the work in the first place. Keep them disciplined and your git log doubles as a searchable audit trail.</p><p>The standard:</p><pre>&lt;type&gt;/&lt;TICKET-ID&gt;-&lt;short-kebab-description&gt;</pre><p>Where:</p><ul><li>&lt;type&gt; is one of feature, fix, hotfix, chore, docs, refactor.</li><li>&lt;TICKET-ID&gt; is your issue tracker key. Jira (PROJ-1234), Linear (ENG-87), GitHub issues (gh-512). This part is required. No exceptions.</li><li>&lt;short-kebab-description&gt; is 3 to 6 words, lowercase, hyphenated, describing intent.</li></ul><p>Examples that pass review:</p><pre>feature/PROJ-1421-add-saml-login<br>feature/PROJ-1455-export-invoices-to-csv<br>fix/PROJ-1488-null-pointer-on-empty-cart<br>fix/gh-512-trim-whitespace-in-email-field<br>hotfix/PROJ-1502-payment-gateway-timeout<br>chore/PROJ-1499-bump-spring-boot-to-3-1</pre><p>Examples that should get bounced at PR time:</p><pre>feature/new-stuff                 no ticket, no scope<br>feature/khaled-experiment         owned by a person, not by a ticket<br>PROJ-1421                         missing the type prefix<br>feature/PROJ_1421_add_saml        underscores instead of hyphens</pre><h3>Why the ticket ID matters</h3><p>When the ticket ID is in the branch name, it shows up everywhere for free:</p><ul><li>PR titles auto-populate from templates and link back to the ticket.</li><li>Commit messages with PROJ-1421 in them get attached to the ticket automatically by Jira or Linear smart commits.</li><li>Release notes can be assembled by grouping commits on ticket prefix.</li><li>Anyone running git blame six months later can trace a line back to the reason it exists.</li></ul><p>A small enforcement helper for .github/workflows/branch-name.yml:</p><pre>name: Branch name check<br>on:<br>  pull_request:<br>    branches: [develop, prod]<br>jobs:<br>  check:<br>    runs-on: ubuntu-latest<br>    steps:<br>      - name: Validate branch name<br>        run: |<br>          BRANCH=&quot;${{ github.head_ref }}&quot;<br>          if [[ ! &quot;$BRANCH&quot; =~ ^(feature|fix|hotfix|chore|docs|refactor)/[A-Z]+-[0-9]+-[a-z0-9-]+$ ]] \<br>             &amp;&amp; [[ ! &quot;$BRANCH&quot; =~ ^(feature|fix|hotfix|chore|docs|refactor)/gh-[0-9]+-[a-z0-9-]+$ ]]; then<br>            echo &quot;Branch &#39;$BRANCH&#39; does not match required pattern:&quot;<br>            echo &quot;  &lt;type&gt;/&lt;TICKET-ID&gt;-&lt;short-description&gt;&quot;<br>            exit 1<br>          fi</pre><p>Pair it with a commit message check (Commitlint or a custom hook) that wants the same ticket ID inside the squashed commit, and the convention starts enforcing itself.</p><h3>How developers actually work day to day</h3><p>The flow is the same whether it’s a feature or a bug fix:</p><ol><li>Pull develop and branch off it.</li></ol><pre>   git checkout develop<br>   git pull --ff-only<br>   git checkout -b feature/PROJ-1421-add-saml-login</pre><p>2. Commit small, push often. Push early so CI runs against your branch and your team can see what you’re up to.</p><p>3. Open a PR into develop. The PR template should ask for.</p><ul><li>The ticket link (auto-filled from the branch name).</li><li>A short “why,” not just “what.”</li><li>Test evidence: screenshots, logs, or a link to the test run.</li><li>A rollback note. “If this breaks Production, what do we do?”</li></ul><p>4. Get a code review. At least one approving reviewer. All CI checks green: lint, unit, integration, security scan.</p><p>5. Squash-merge into develop. Squash, not merge-commit. The unit of history on develop is &quot;one ticket, one commit.&quot; Delete the branch on merge.</p><p>6. Watch Staging. Within a few minutes your change is live on QC/Test and QA picks it up against the ticket.</p><p>That’s the loop. Nobody ever commits directly to develop, prod, or main. GitHub branch protection rules enforce it: required reviews, required status checks, no direct pushes, no force-pushes.</p><h3>The Staging pipeline: QC/Test, then PreProd</h3><p>Splitting “staging” into two stages is the single change that makes this model work for enterprise use, and it’s the one I see most often skipped. The two stages do completely different jobs. Don’t conflate them.</p><h3>Stage 1: QC/Test</h3><ul><li>Trigger: every push to develop.</li><li>Data: synthetic or anonymized. Reset whenever. Cheap to break.</li><li>Audience: QA engineers, devs verifying their own work, product owners doing UAT against tickets.</li><li>Goal: does the change behave correctly? Does it pass functional acceptance?</li></ul><p>QC/Test is allowed to be unstable. That’s its job. It should fail loudly while there’s still time to fix things.</p><h3>Stage 2: PreProd</h3><ul><li>Trigger: promotion from QC/Test once a release candidate is identified (usually a tag on develop, e.g. rc-2023.08.15).</li><li>Data: sanitized, but production-shaped. Same database engine, same versions, similar scale.</li><li>Audience: SRE, performance testers, security review, the change advisory board.</li><li>Goal: does the change still behave correctly under conditions that actually look like Production? Migrations, integrations, load, secrets, networking.</li></ul><p>PreProd is the dress rehearsal. If it’s green, the Production deploy is mechanical. If you’re nervous on Production deploy day, your PreProd isn’t doing its job.</p><p>Promotion in CI:</p><pre># .github/workflows/promote-to-preprod.yml<br>on:<br>  workflow_dispatch:<br>    inputs:<br>      release_tag:<br>        description: &#39;Release candidate tag on develop&#39;<br>        required: true<br>jobs:<br>  promote:<br>    environment: preprod        # Requires reviewer approval<br>    runs-on: ubuntu-latest<br>    steps:<br>      - uses: actions/checkout@v3<br>        with:<br>          ref: ${{ inputs.release_tag }}<br>      - run: ./deploy.sh preprod</pre><p>GitHub Environments give you the approval gate for free. Configure the preprod environment with required reviewers and promotion is never accidental.</p><h3>Releasing to Production</h3><p>Once PreProd has signed off:</p><ol><li>Open a release PR from develop into prod. No new commits in this PR. It&#39;s just promoting what&#39;s already been tested.</li><li>Get release approval. Usually the release manager and one engineering lead.</li><li>Merge. CI deploys prod to Production automatically.</li><li>Tag the release on prod:</li></ol><pre>git checkout prod<br>git pull --ff-only<br>git tag -a v2023.08.15 -m &quot;Release 2023.08.15&quot;<br>git push origin v2023.08.15 </pre><p>5. Back-merge prod into main:</p><pre>git checkout main<br>git pull --ff-only<br>git merge --ff-only prod    # if main was clean; otherwise open a back-merge PR<br>git push origin main</pre><p>After step 5, main is identical to what&#39;s running in Production again. That property is what makes main useful as a baseline for hotfixes and for auditors.</p><h3>Hotfixes, the one exception</h3><p>Bugs found in Production can’t wait for the regular develop → prod cycle. The model handles this with a controlled exception:</p><ol><li>Branch from main (or equivalently prod), not from develop. You want to fix exactly what&#39;s live, without dragging in unreleased changes.</li></ol><pre>git checkout prod<br>git pull --ff-only<br>git checkout -b hotfix/PROJ-1502-payment-gateway-timeout</pre><p>2. Make the fix. Add a regression test. Open two PRs:</p><ul><li>One into prod, which deploys to Production after PreProd validation. PreProd can be fast-tracked, but it isn&#39;t optional.</li><li>One into develop, so the fix isn&#39;t lost in the next regular release.</li></ul><p>3. After the hotfix ships, back-merge prod into main as usual.</p><p>The “two PRs” rule looks like duplication. It’s actually the single biggest defense against a classic enterprise bug: a hotfix ships to Production, the next regular release accidentally reverts it, and the same bug reappears at the worst possible moment. Usually on a weekend.</p><h3>Branch protection: the rules CI enforces</h3><p>Configuration is half the strategy. On GitHub, set these for main, develop, and prod:</p><ul><li>Require a pull request before merging. No direct pushes.</li><li>Require approvals. At least 1 for develop, at least 2 for prod and main.</li><li>Require status checks to pass: branch name check, lint, unit, integration, security scan.</li><li>Require branches to be up to date before merging.</li><li>Require linear history on develop (squash merges only).</li><li>Restrict who can push to main and prod (release managers only, even though merges still go through PR).</li><li>No force pushes. No deletions.</li></ul><p>I know this list looks like bureaucracy. It isn’t. It’s what turns a Confluence document into an actual process. Without enforcement, the document is just a document, and people will route around it the first time it’s inconvenient.</p><h3>Common failure modes, and how this model handles them</h3><p><strong>“Someone merged a half-finished feature and now QA is blocked.”</strong> QC/Test is the right place for that to surface. Revert the PR on develop, redeploy, and you&#39;ve lost minutes instead of days.</p><p><strong>“A hotfix shipped to Production but the bug came back next sprint.”</strong> The hotfix wasn’t merged into develop. The two-PR rule prevents this. Branch protection enforcing it makes the rule real instead of aspirational.</p><p><strong>“main and Production drifted apart.&quot;</strong> The back-merge step got skipped somewhere. Add a scheduled CI job that fails if main is more than N commits behind prod. You want this loud and early, not discovered during the next hotfix.</p><p><strong>“Nobody knows what’s in this release.”</strong> Branch names weren’t referencing tickets, so the release notes can’t be auto-generated. The branch-name CI check fixes this at the source.</p><p><strong>“PreProd passed but Production broke.”</strong> Almost always means PreProd doesn’t actually resemble Production. Different data shape, missing integration, smaller scale, secrets stubbed out. Spend the money on PreProd parity. It’s the cheapest incident prevention I know.</p><h3>When this model isn’t the right fit</h3><p>Worth being honest about:</p><ul><li>Trunk-based shops with mature feature-flag infrastructure and continuous deployment will find this heavy. They’re right for their context.</li><li>Tiny teams (1 to 3 people) shipping a side project don’t need three long-lived branches. You’d just be paying overhead for fun.</li><li>Libraries and SDKs, where “Production” means “whatever your users have installed,” need a release-train model instead.</li></ul><p>This model earns its keep when you have multiple teams contributing to one codebase, a separate QA function, change-management requirements, and an SRE or platform team running Production. Outside of that, simpler is fine.</p><h3>Wrapping up</h3><p>The branches aren’t really the strategy. The discipline around them is. You can pick a different name for develop or prod and the model still works, but skip the back-merge into main for a couple of releases and the whole thing falls apart quietly.</p><p>To recap the loop: feature/* and fix/* come off develop and go back via PR. develop deploys automatically to QC/Test. Release candidates get promoted to PreProd behind an approval gate. Once PreProd is green, develop merges into prod, which deploys to Production. After every release, prod is back-merged into main so the baseline keeps mirroring what&#39;s live. Branch names always reference a ticket, so the trail from &quot;why did we ship this&quot; to &quot;what code changed&quot; is one git log away.</p><p>Write the rules down. Put them behind CI checks and branch protection. Then mostly forget about Git, and let everyone spend their energy on the actual product.</p><p><strong>Originally published on [Blog]</strong>(<a href="https://blog.tuhidulhossain.com/post/enterprise-github-branching-strategy-with-staging-and-production-20230815">https://blog.tuhidulhossain.com/post/enterprise-github-branching-strategy-with-staging-and-production-20230815</a>) on <strong>August 15, 2023.</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=75e70f69eebc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Night a Botnet Tried to Break ZephyPhone & Why You Never Knew]]></title>
            <link>https://medium.com/@mth.tuhin/the-night-a-botnet-tried-to-break-zephyphone-why-you-never-knew-a36463c2ecd8?source=rss-938fca332512------2</link>
            <guid isPermaLink="false">https://medium.com/p/a36463c2ecd8</guid>
            <category><![CDATA[telecommunication]]></category>
            <category><![CDATA[hacking]]></category>
            <category><![CDATA[zephyphone]]></category>
            <category><![CDATA[business]]></category>
            <dc:creator><![CDATA[Khaled Md Tuhidul Hossain]]></dc:creator>
            <pubDate>Sun, 03 May 2026 15:49:17 GMT</pubDate>
            <atom:updated>2026-05-03T15:50:17.558Z</atom:updated>
            <content:encoded><![CDATA[<p>3 AM. Phone buzzing. It’s monitoring. Of course it is.</p><p>Login attempts on <a href="https://zephyphone.com/">ZephyPhone</a> had gone from quiet to a few hundred a minute. All failing. All from different IPs all over the place. I sat up, grabbed the laptop, and honestly just watched the graph for a bit before I did anything useful.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/256/0*b91i0yUzM4L_UC7F" /></figure><p>By the time I’d made coffee we’d taken hundreds of thousands of bad requests in a day.</p><p>A botnet. Spread across a lot of countries, throwing leaked password dumps at us, hoping somebody had reused a password they shouldn’t have. Pretty standard stuff if you read security blogs. Less standard when it’s your platform on the receiving end.</p><p>Nobody using <a href="https://zephyphone.com/">ZephyPhone</a> noticed any of it.</p><p>I’m writing about this because founders go strangely quiet when it happens. We post when we ship things. We post when we raise. Somebody points a botnet at us and the timeline goes silent. I don’t really get it. So.</p><h3>What the attack actually was:</h3><p>Credential stuffing. The boring kind. A bot reads username/password pairs off some old breach list and throws them at your login until something opens. It’s not clever. It just doesn’t have to be.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D-Nk-lhanSuA2jnYrU2BqA.jpeg" /></figure><p>The annoying part was the shape. Lots of IPs, each one going slowly enough to look like a regular user. Added together they were doing real volume. Tuned, basically, to look invisible to the kind of per-IP limit most apps have.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LOINhptZxad-4jrs" /></figure><p>Ip Locations</p><p>It also came in two waves, which is the part that stuck with me. The first one ramped up fast, ran for about an hour, then went completely quiet. Long enough that I half-convinced myself that was it. Then the second wave hit, much bigger than the first, and just sat there for hours. Looking back, the first wave was almost certainly a probe — see what gets blocked, watch how the system reacts, then come back with the real thing. That’s changed how I’ll think about the early minutes of any future incident. Sometimes what you’re seeing isn’t the attack. It’s somebody knocking to find out where the doors are.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ePLCEQQMdZCpagQ3" /></figure><p>Total Actions Data ( Red: Total, Green : Allowed, Purple : Blocked)</p><p>The thing I re-learned: if you only have one layer and the attacker has more IPs than that layer cares about, you don’t really have a defense. You have a thing that makes you feel like you have one. I knew that. There’s still a difference between knowing it and watching it on a chart at 3 AM.</p><h3>What actually saved us:</h3><p>I’d put time into hardening the platform a few weeks earlier. I want to say that was foresight, but no, I’d just been reading too many post mortems from other companies and got nervous.</p><p>I’m not going to walk through what we did. Publishing the map of our defenses doesn’t help anyone except the next person who wants to poke at them. What I’ll say is the shape: several layers between the public internet and anything that touches a customer account, none of them sharing assumptions, monitoring sitting on top. Nothing fancy. Just done properly and done before we needed it.</p><p>The thing that worked wasn’t any single rule. It was that you’d have to beat several different things at once, and they don’t think alike. That’s the whole idea.</p><p>Most of the attack got handled before our code ever saw the request. App servers were cool. Database didn’t blink. Calls connected. Texts went out. New customers onboarded in the middle of it. The only ones having a stressful night were me and whatever was sending the alerts.</p><h3>A few things I keep thinking about:</h3><p>Rate limiting isn’t a thing, it’s a whole category. Per-IP, per-account, per-endpoint, at the edge or in the app — these solve different problems and I used to file them all under one mental folder. You often need a few of them going at the same time, and they shouldn’t look like each other.</p><p>Doing this work at the edge was cheaper than I expected. The bill is genuinely modest. The bigger saving is your app not having to politely say no to a million bad requests. Stuff that never gets to you costs you nothing.</p><p>The expensive part is choosing to do it. A week on security plumbing is a week I’m not shipping something a customer asked for. That trade hurts. I still think it was right. We’ll see how I feel after the next time.</p><p>I nearly didn’t post this. There’s a voice that says I’ll spook people, look weak, basically wave a flag at the next person looking for a target. I think that voice is wrong. Not totally sure. Talk to me in a month.</p><p>You don’t get the attack you planned for. I’d thought about a few categories. Hadn’t thought enough about this one. The defenses happened to be in the right place, which I’ll cop to being a bit lucky about, and now I trust them in a way I didn’t before. Watching something work is different from believing it works.</p><h3>If you’re a ZephyPhone customer:</h3><p>I want you to hear this from me, not from a help page.</p><p>We saw it. We stopped it. You didn’t have to think about it. That’s the whole deal.</p><p>You run your business. We keep the thing underneath running, including when somebody is actively trying to take it down. That’s why I started this. Communications that work — and that keep working on the bad days too.</p><p>We’ll keep spending on this. We’ll keep telling you when things happen. Trust shows up one ordinary day at a time, and I’m fine with ordinary.</p><p>Thanks for sticking with <a href="https://zephyphone.com/">ZephyPhone</a>.</p><p>P.S. If you’ve got questions, or you’re a founder dealing with your own version of a 3 AM alert, just message me. I read everything.</p><p>#CyberSecurity #SaaS #Startups #FounderJourney #Engineering #BuildInPublic #ZephyPhon</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a36463c2ecd8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Agent2Agent (A2A) Protocol: Learning v1.0 by Building a Small Agent]]></title>
            <link>https://medium.com/@mth.tuhin/the-agent2agent-a2a-protocol-learning-v1-0-by-building-a-small-agent-1216161bf223?source=rss-938fca332512------2</link>
            <guid isPermaLink="false">https://medium.com/p/1216161bf223</guid>
            <category><![CDATA[agentic-ai]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[a2a-protocol]]></category>
            <dc:creator><![CDATA[Khaled Md Tuhidul Hossain]]></dc:creator>
            <pubDate>Sun, 19 Apr 2026 13:47:16 GMT</pubDate>
            <atom:updated>2026-04-19T13:47:16.858Z</atom:updated>
            <content:encoded><![CDATA[<p>There’s a reason I held off on writing about A2A until now.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JxFLYt290jY7wpXO33xEoA.png" /></figure><p>I’d been watching the <a href="https://a2a-protocol.org/">Agent2Agent (A2A) Protocol</a> since it first showed up, originally out of Google and eventually handed off to the Linux Foundation. The idea was always right: a neutral wire protocol so agents built on different frameworks could talk to each other. But every time I sat down to try it, the spec was still moving. v0.1, v0.2, v0.3, breaking changes between minor releases. Agent Cards weren’t cryptographically signed, so discovering a new agent was basically a handshake on faith. One endpoint, one agent, which ruled out every SaaS shape I cared about. JSON-RPC was the only binding, which locked out teams standardized on gRPC. And the one that really kept me out: no version negotiation, so any upgrade meant a flag-day cutover across every agent in a deployment.</p><p>Promising protocol, but not one you could bet on yet. So I waited.</p><p>Then on March 12, 2026, v1.0 shipped. The four headline changes happen to be the four things that had been keeping me out:</p><ul><li>Signed Agent Cards — the /.well-known/agent-card.json document now carries a cryptographic signature, so you can verify that a card was actually issued by the domain claiming it. Without this, decentralized discovery was always going to be a security footnote.</li><li>Multi-tenancy — one endpoint can host many agents. SaaS providers can finally ship a per-tenant agent without running a fleet of processes behind a reverse proxy.</li><li>Multi-protocol bindings — same logical agent, exposed over JSON-RPC or gRPC. Pick whichever fits your stack.</li><li>Version negotiation — backward-compatible migration from v0.3 onward is now a spec-level guarantee. No more flag-day cutovers.</li></ul><p>The v1.0 announcement keeps using the word <em>maturity</em> rather than <em>reinvention</em>, and I think that’s the right word. This isn’t a redesign. It’s the point where I stopped flinching every time a new version dropped.</p><p>So I built a small agent on top of the official <a href="https://pypi.org/project/a2a-sdk/">a2a-sdk</a> to actually use the thing.</p><p>One caveat up front: the Python SDK is still on 0.3.x. It accepts the v1.0 card fields (my sample declares protocol_version=&quot;1.0&quot;), but the v1.0-specific features -- signed cards, the gRPC binding, version negotiation -- aren&#39;t wired up in the SDK yet. What works today is the core flow: discovery, message/send, streaming, cooperative cancellation, input-required multi-turn, and push-notification webhooks. That&#39;s most of what you need to write a working agent.</p><p>Repo: <a href="https://github.com/encryptedtouhid/a2a_protocol_sample">github.com/encryptedtouhid/a2a_protocol_sample</a> — a small A2A reference agent in Python, built on the official a2a-sdk.</p><p>This post is what I wish someone had handed me when I started.<br>So what is A2A, really?</p><p>The elevator pitch: A2A is to agent-to-agent communication what HTTP is to browser-to-server communication. A neutral wire protocol that two systems, built by teams who don’t know each other and don’t share a stack, can both agree to speak.</p><p>That’s the whole value proposition.</p><p>Underneath, it’s JSON-RPC 2.0 with a Server-Sent Events layer for streaming and optional webhooks for push notifications. Nothing exotic on the wire — you can pretty-print any message and read it with your eyes.</p><p>The five objects you need to get comfortable with:</p><ul><li>Agent Card — a JSON document at /.well-known/agent-card.json that says who this agent is, what it can do, how to talk to it, and how to authenticate. Think business card, but for robots.</li><li>Task — the unit of work. Has an id, a context, a status, a history of messages, and a list of artifacts it produced.</li><li>Message — one turn in the conversation. Role is either user or agent. Inside, it carries Parts.</li><li>Part — the atomic content container. One of: text, a file reference, or structured JSON data. That’s the entire content model.</li><li>Artifact — something the agent produced. Messages are ephemeral chat turns; artifacts are deliverables that stick around.</li></ul><p>The task state machine is small and pleasant to look at:</p><pre>submitted -&gt; working -&gt; completed<br>                     \-&gt; canceled / failed / rejected<br>                     \-&gt; input-required (resumable)<br>                     \-&gt; auth-required (resumable)</pre><p>Notice the two resumable states. That’s where A2A gets more interesting than a plain request/response protocol, and I’ll get to that.</p><h3>Only five RPC methods actually matter</h3><p>The spec has a handful of methods. If you squint, five of them are doing all the real work:</p><p>message/sendSend a message, get a task back. Plain request/response.<br>message/streamSame idea, but the response is an SSE stream of events.<br>tasks/getFetch a task by id. Optionally trim the history.<br>tasks/cancelCooperatively ask a task to stop.<br>tasks/pushNotificationConfig/*Register a webhook so the agent can call <em>you</em>.</p><p>The rest — tasks/resubscribe and the authenticated extended card -- is quality of life. Nice to have, not what makes A2A work.</p><h3>Inside the sample repo</h3><p>The actual layout is much smaller than you’d guess:</p><pre>src/a2a_sample/<br>  __init__.py    # re-exports, 3 lines<br>  auth.py        # bearer-token Starlette middleware, 31 lines<br>  executor.py    # AgentExecutor: routes the first word to a skill, 61 lines<br>  server.py      # agent card + A2AStarletteApplication wiring, 143 lines<br>  skills.py      # echo / summarize / count / form / debug, 118 lines<br><br>demos/<br>  run_server.py<br>  webhook_receiver.py<br>  _common.py<br>  01_discovery.py                # fetch the well-known card<br>  02_send_message.py             # plain message/send<br>  03_streaming.py                # message/stream over SSE<br>  04_multiturn_input_required.py # input-required, multi-turn<br>  05_cancel.py                   # tasks/cancel mid-stream<br>  06_push_notifications.py       # register a webhook and watch it fire<br>  07_extended_card.py            # authenticated extended card</pre><p>About 356 lines of application code across five files. Three dependencies:</p><pre>a2a-sdk[http-server]&gt;=0.3.26<br>uvicorn[standard]&gt;=0.30<br>httpx&gt;=0.28</pre><p>The SDK does most of the heavy lifting. Once you see what’s already in the box, the protocol gets a lot easier to reason about:</p><ul><li>Every Pydantic model — AgentCard, Task, Message, Part, Artifact, TaskState, TaskArtifactUpdateEvent, the lot.</li><li>The JSON-RPC 2.0 transport and request routing.</li><li>The SSE streaming event queue.</li><li>InMemoryTaskStore -- the default task store.</li><li>BasePushNotificationSender -- the webhook dispatcher.</li><li>TaskUpdater -- a facade that lets a skill say updater.complete() or updater.requires_input() without hand-building status events.</li><li>ClientFactory and AuthInterceptor on the client side.</li></ul><p>What’s left for you to write:</p><ul><li>An AgentCard that describes your agent.</li><li>An AgentExecutor that routes incoming messages to your logic.</li><li>Whatever skills your agent actually does.</li><li>Auth middleware, if you need any.</li></ul><p>That’s the entire pattern. The SDK handles protocol correctness; you handle agent behavior.</p><h3>The five skills</h3><p>The sample exposes four public skills and one auth-gated skill, and each one pokes a different corner of the protocol:</p><ul><li>echo — the happy path. Text in, text out. Nothing fancy.</li><li>summarize — returns a TextPart (the summary) plus a DataPart (word/character stats). Non-streaming, shows the structured content model.</li><li>count N — streams numbers 1 to N with a 300ms delay between each. This is where you watch streaming and cancellation work.</li><li>form — asks for a name, then an email, then returns a contact record. This is the input-required state in action.</li><li>debug — only visible on the authenticated extended card. Shows how auth-gated capability discovery works.</li></ul><p>The dispatcher in executor.py picks a skill based on the first word of the user&#39;s first message:</p><pre>initial_text = skills.initial_user_text(task, context.message).strip()<br>head = initial_text.split(None, 1)[0].lower() if initial_text else &quot;&quot;<br><br>try:<br>    if head == &quot;summarize&quot;:<br>        await skills.skill_summarize(initial_text, updater)<br>    elif head == &quot;count&quot;:<br>        await skills.skill_count(initial_text, updater)<br>    elif head == &quot;form&quot;:<br>        await skills.skill_form(updater, task, context.message)<br>    elif head == &quot;debug&quot;:<br>        await skills.skill_debug(updater)<br>    else:<br>        await skills.skill_echo(initial_text, updater)</pre><p>A str.split() and an if/elif. For a reference sample, that&#39;s the right amount of code.</p><h3>Discovery: the well-known agent card</h3><p>The first thing any A2A client does is ask the agent to introduce itself. A2A borrows RFC 8615’s well-known URI — you fetch a JSON document from a fixed path and read off everything you need. The SDK’s A2ACardResolver does the work:</p><p>python</p><pre>async def fetch_public_card(http: httpx.AsyncClient) -&gt; AgentCard:<br>    return await A2ACardResolver(http, base_url=BASE_URL).get_agent_card()</pre><p>Run demo 01 and you get back the agent’s skills, its preferred transport, its security schemes, whether it supports streaming, all of it. No docs to read, no Slack message to ask “hey what’s the API look like” — the agent tells you itself.</p><p>It’s a boring design choice, which is usually a good sign in protocol work.</p><h3>Sending a message</h3><p>Everything JSON-RPC goes through the SDK’s client, built from the agent card plus an auth interceptor:</p><p>python</p><pre>def build_client(http, card, *, streaming: bool = False) -&gt; Client:<br>    factory = ClientFactory(<br>        ClientConfig(<br>            httpx_client=http,<br>            streaming=streaming,<br>            supported_transports=[TransportProtocol.jsonrpc],<br>        )<br>    )<br>    return factory.create(<br>        card,<br>        interceptors=[AuthInterceptor(StaticCredentialService(BEARER_TOKEN))],<br>    )</pre><p>Sending a message is a one-liner on top:</p><pre>async for event in client.send_message(_user(&quot;echo hello A2A&quot;)):<br>    if isinstance(event, Message):<br>        continue<br>    task, update = event<br>    if update is None:<br>        print(f&quot;  task id={task.id} state={task.status.state.value}&quot;)</pre><p>The task comes back already in the completed state because echo is instant. For anything slower, you&#39;d want streaming.</p><h3>Where streaming starts to earn its keep</h3><p>Request/response is fine for echo. It’s not fine for an agent that spends fifteen seconds thinking and produces output incrementally. You don’t want to hold an HTTP connection open for fifteen seconds and dump everything at the end — the user would assume it crashed.</p><p>A2A’s answer is message/stream. Same message shape, but the response is an SSE stream of JSON-RPC envelopes. The client side dispatches on event type:</p><pre>async for event in client.send_message(msg):<br>    if isinstance(event, Message):<br>        continue<br>    task, update = event<br>    if isinstance(update, TaskArtifactUpdateEvent):<br>        chunk = update.artifact.parts[0].root<br>        text = getattr(chunk, &quot;text&quot;, &quot;&quot;)<br>        print(f&quot;[artifact] append={update.append} last={update.last_chunk} chunk={text!r}&quot;)<br>    elif isinstance(update, TaskStatusUpdateEvent):<br>        print(f&quot;[status] state={update.status.state.value} final={update.final}&quot;)</pre><p>The count skill on the server side emits each number as an appended chunk of the same artifact. append=True on chunks 2 through N, last_chunk=True on the last one:</p><pre>async def skill_count(text: str, updater: TaskUpdater) -&gt; None:<br>    target = max(1, min(int(match.group()) if match else 5, 100))<br>    artifact_id = f&quot;count-{updater.task_id}&quot;<br>    for i in range(1, target + 1):<br>        await updater.add_artifact(<br>            [Part(root=TextPart(text=f&quot;{i}\n&quot;))],<br>            artifact_id=artifact_id,<br>            name=&quot;count&quot;,<br>            append=i &gt; 1,<br>            last_chunk=i == target,<br>        )<br>        await asyncio.sleep(0.3)<br>    await updater.complete(</pre><p>That append=True, last_chunk=False pattern is how A2A says &quot;glue these together until you see last_chunk=True.&quot; Same mental model as HTTP chunked transfer, just lifted up to the agent&#39;s output instead of the HTTP body.</p><p>First time I ran this, I left it running and just watched the chunks arrive. There’s something satisfying about watching a protocol do its thing in real time.</p><h3>Cancellation done right: cooperative, not forced</h3><p>This one I actually have opinions about.</p><p>A2A cancellation is cooperative. When you call tasks/cancel, the server doesn&#39;t hard-kill a coroutine -- the SDK raises asyncio.CancelledError inside the running skill, and the executor is expected to emit a terminal canceled status and return. It sounds like more work than a hard kill, but it&#39;s the right call. Forced termination is how you end up with half-written rows, partial files, and state that corrupts the next run.</p><p>The executor handles it with a try/except around the dispatch:</p><p>python</p><pre>try:<br>    if head == &quot;count&quot;:<br>        await skills.skill_count(initial_text, updater)<br>    # ... other skills<br>except asyncio.CancelledError:<br>    await updater.update_status(TaskState.canceled, final=True)<br>    raise</pre><p>The cancel demo fires the cancel mid-stream:</p><pre>if isinstance(update, TaskArtifactUpdateEvent):<br>    text = update.artifact.parts[0].root.text.strip()<br>    print(f&quot;[chunk]   {text}&quot;)<br>    if not canceled and text == &quot;3&quot;:<br>        await client.cancel_task(TaskIdParams(id=task_id))<br>        canceled = True</pre><p>Run demo 05 and you see it: three chunks arrive, the cancel request goes out, one or two more chunks land in flight, then the stream closes with state=canceled final=True. No orphaned state.</p><h3>The multi-turn trick: input-required</h3><p>Okay, this is the feature I didn’t know I wanted until I used it.</p><p>In A2A, a task can pause and wait for more input without losing its place. The agent transitions to input-required, the client comes back with another message carrying the same task_id and context_id, and the task picks up where it left off. Same task id. Same artifacts. Same context. Just... paused.</p><p>The form skill demonstrates it. Three turns: ask for name, ask for email, complete with a contact record.</p><pre>task = await _send(client, _user(&quot;form&quot;))<br>print(f&quot;[turn 1] state={task.status.state.value}&quot;)  # input-required<br><br>task = await _send(client, _user(&quot;Ada Lovelace&quot;, task.id, task.context_id))<br>print(f&quot;[turn 2] state={task.status.state.value}&quot;)  # input-required<br><br>task = await _send(client, _user(&quot;ada@example.com&quot;, task.id, task.context_id))<br>print(f&quot;[turn 3] state={task.status.state.value}&quot;)  # completed</pre><p>Where the skill stashes the collected name between turns is the nice trick. It doesn’t need a database; it writes the data into the agent message’s metadata field on turn 2, then walks the task history on turn 3 to read it back:</p><pre>if prior_agent_turns == 1:<br>    await updater.requires_input(<br>        updater.new_agent_message(<br>            [Part(root=TextPart(text=FORM_QUESTIONS[1]))],<br>            metadata={&quot;name&quot;: incoming_text},<br>        ),<br>        final=True,<br>    )<br>    return<br><br># later, on turn 3:<br>for m in task.history or []:<br>    if m.role == Role.agent and m.metadata and &quot;name&quot; in m.metadata:<br>        name = str(m.metadata[&quot;name&quot;])<br>        break</pre><p>The form demo looks trivial, but the same state machine handles any “agent waits for something external” case — a human approval, an auth challenge (via the defined-but-not-demoed auth-required state), a slow third-party service. One mental model covers all of them.</p><p>This is the bit I keep coming back to when I think about why A2A exists. Anyone can design a request/response protocol. Designing one that pauses cleanly and resumes without losing its mind — that’s the hard part, and A2A got it right.</p><h3>Push notifications for the really long-running stuff</h3><p>SSE is wonderful when the client can stay connected. But sometimes the task runs for an hour and the client is a mobile app that gets backgrounded, or a serverless function that already returned, or just a process that might die and come back. For that, A2A lets you register a webhook:</p><p>python</p><pre>config = TaskPushNotificationConfig(<br>    task_id=task_id,<br>    push_notification_config=PushNotificationConfig(<br>        url=WEBHOOK_URL,<br>        token=SHARED_SECRET,<br>    ),<br>)<br>result = await client.set_task_callback(config)</pre><p>The SDK’s BasePushNotificationSender POSTs every event to your webhook with an X-A2A-Notification-Token header carrying your shared secret. You can register multiple webhooks per task, list them, delete them -- standard CRUD.</p><p>Server-side wiring is about four lines:</p><p>python</p><pre>push_store = InMemoryPushNotificationConfigStore()<br>push_sender = BasePushNotificationSender(<br>    httpx.AsyncClient(timeout=10.0),<br>    config_store=push_store,<br>)</pre><p>For demo 06 you have to start a little webhook receiver in a third terminal. Watching the events land in the receiver while the task is still running is when the async design actually clicks.</p><p>One thing to call out: v1.0 also defines JWT-signed push payloads via the a2a-sdk[encryption] extra, but the sample uses only the shared-secret token. Fine for a demo on localhost, not fine for production cross-org setups. Don&#39;t ship this exact pattern past your own infrastructure without upgrading the verification.</p><h3>Auth: a tiny Starlette middleware</h3><p>The sample accepts one bearer token, demo-secret-token, declared in the card&#39;s securitySchemes and required on every non-discovery path. The middleware fits in about a dozen lines:</p><p>python</p><pre>class BearerAuthMiddleware(BaseHTTPMiddleware):<br>    async def dispatch(self, request, call_next):<br>        if request.url.path in PUBLIC_PATHS:<br>            return await call_next(request)<br>        header = request.headers.get(&quot;authorization&quot;, &quot;&quot;)<br>        scheme, _, token = header.partition(&quot; &quot;)<br>        if scheme.lower() != &quot;bearer&quot; or token != BEARER_TOKEN:<br>            return JSONResponse(<br>                {&quot;error&quot;: &quot;invalid or missing bearer token&quot;},<br>                status_code=401,<br>            )<br>        return await call_next(request)</pre><p>Real production agents would wire in OAuth2, OIDC, or mTLS — the card supports declaring any of those. This is just the simplest thing that works so the rest of the demos have something to authenticate against.</p><h3>What this sample deliberately doesn’t do</h3><p>v1.0 shipped, but not all of it is in the SDK yet, and the sample doesn’t reach past what the SDK does. So here’s what’s missing:</p><ul><li>Signed Agent Cards — the v1.0 feature that makes cross-org discovery safe. Not in the SDK’s 0.3.x release yet; the sample’s card is unsigned.</li><li>gRPC binding — the SDK has a gRPC transport, but the sample only exposes JSON-RPC.</li><li>Version negotiation — the card hardcodes protocol_version=&quot;1.0&quot;; there&#39;s no client-side version probing.</li><li>auth-required state -- defined in the TaskState enum, but no skill currently triggers the flow. Good candidate for a follow-up demo.</li><li>FilePart — the Part type exists but nothing in the sample produces or consumes one.</li><li>OAuth2 / mTLS — only the HTTP bearer scheme is wired up.</li><li>Batch JSON-RPC — allowed by the spec, not exercised here.</li><li>Persistence — the default InMemoryTaskStore is used. A restart wipes the world.</li><li>JWT-signed push payloads — the SDK has an [encryption] extra for this; the sample uses shared-secret tokens.</li></ul><p>The core message flow is solid. The security and cross-stack features still need the ecosystem to catch up.</p><h3>Running it yourself</h3><p>Clone, set up, run the server in one terminal, fire demos from another:</p><pre>git clone https://github.com/encryptedtouhid/a2a_protocol_sample.git<br>cd a2a_protocol_sample<br>./run.sh setup<br><br># terminal 1<br>./run.sh server<br><br># terminal 2<br>./run.sh demo 01   # fetch the agent card<br>./run.sh demo 03   # watch the streaming counter<br>./run.sh demo 04   # play with input-required</pre><p>For push notifications (demo 06) you also need a webhook receiver in a third terminal:</p><pre>./run.sh webhook</pre><p>The run.sh script handles the venv and works on Linux, macOS, and Windows (Git Bash / WSL). ./run.sh list shows every demo. Go through them in order on the first pass; each one introduces one new concept and builds on the last.</p><h3>Why I care about this one</h3><p>We spent a decade learning that microservices only work when services agree on a wire protocol. Nobody ships a new microservice in 2026 and says “let’s invent our own transport.” Agents are walking the same path, just five years behind.</p><p>A2A is the lingua franca that lets a LangGraph orchestrator delegate to a Semantic Kernel agent, which hands off to a CrewAI team, without any of them importing each other’s SDKs. No adapters. No shared deployment. No “we need to agree on Python versions” meeting. Just JSON over HTTP with shapes everyone can parse.</p><p>And it pairs neatly with MCP:</p><ul><li>MCP — agent-to-tool. “Here’s a database, a filesystem, a GitHub API. Go use them.”</li><li>A2A — agent-to-agent. “Here’s another agent with its own brain. Delegate to it.”</li></ul><p>They’re both still young, and they’ll both change. The hard part of any protocol ecosystem is getting enough people to agree on one, and right now that’s A2A.</p><h3>If you want to go deeper</h3><ul><li>Clone the sample: <a href="https://github.com/encryptedtouhid/a2a_protocol_sample">github.com/encryptedtouhid/a2a_protocol_sample</a></li><li>Read the spec: <a href="https://a2a-protocol.org/">a2a-protocol.org</a></li><li>Browse the SDK: <a href="https://pypi.org/project/a2a-sdk/">a2a-sdk on PyPI</a></li><li>Run the demos in order. Don’t skip around on the first pass.</li><li>Once it makes sense, swap the echo skill for a real LLM call and watch the rest of the protocol keep working unchanged.</li></ul><p>Honestly, the fastest way to learn a protocol is to build something on top of it. The repo’s ~360 lines are a reasonable starting sample if you’d rather read first and build second.</p><p>Happy to hear what you think. If something in the code confuses you, open an issue — that probably means I need to either fix the code or write better comments, and either way I’d want to know.</p><p>Original Post : <a href="https://blog.tuhidulhossain.com/post/the-agent2agent-a2a-protocol-a-complete-guide-to-ai-agent-interoperability-20260419">https://blog.tuhidulhossain.com/post/the-agent2agent-a2a-protocol-a-complete-guide-to-ai-agent-interoperability-20260419</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1216161bf223" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CpuGuard.NET : CPU Usage Control for ASP.NET Core Applications]]></title>
            <link>https://medium.com/@mth.tuhin/cpuguard-net-cpu-usage-control-for-asp-net-core-applications-c0a618df1ef9?source=rss-938fca332512------2</link>
            <guid isPermaLink="false">https://medium.com/p/c0a618df1ef9</guid>
            <category><![CDATA[net-core-8]]></category>
            <category><![CDATA[net-core-3]]></category>
            <category><![CDATA[net-core-5]]></category>
            <category><![CDATA[cpu-limits]]></category>
            <dc:creator><![CDATA[Khaled Md Tuhidul Hossain]]></dc:creator>
            <pubDate>Sun, 16 Jun 2024 12:57:39 GMT</pubDate>
            <atom:updated>2024-06-16T14:17:51.723Z</atom:updated>
            <content:encoded><![CDATA[<p>Recently, I released an applications: YouTube to PowerPoint Converter. However, I soon realized that this applications were consuming all my resources during conversion processes. As I was running over 20+ personal applications on a small VM with minimal resources in aws.</p><p>It was then that I felt the need for a CPU limitation tool. Instead of relying on IIS and server utilities, I decided to create my own middleware.</p><p>Introducing CpuGuard.NET, a middleware I developed to effortlessly manage CPU usage for ASP. NET Core applications. With CpuGuard.NET, you can:</p><p>1️⃣ Limit CPU usage for the entire application.</p><p>2️⃣ Control CPU usage for specific requests.</p><p>( * Stay tuned for the upcoming feature to limit CPU usage for specific controllers and methods! )</p><p>This powerful tool ensures your applications run smoothly, even under heavy loads. It’s easy to integrate and helps you maintain optimal performance.</p><p>Check it out on NuGet: [CpuGuard.NET]</p><p>(https://github.com/encryptedtouhid/CpuGuard.NET)</p><h3>Installation</h3><p>Install the NuGet package:</p><pre>dotnet add package CpuGuard.NET</pre><h3>Usage</h3><p>Whole Application CPU Limit Example:</p><pre>    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)<br>    {<br>        app.UseRouting();<br><br>        // Use the whole application CPU limit middleware with 20% CPU limit and 1 second monitoring interval<br>        app.UseCpuLimitMiddleware(20.0, TimeSpan.FromMilliseconds(1000));<br><br>        app.UseEndpoints(endpoints =&gt;<br>        {<br>            endpoints.MapControllers();<br>        });<br>    }</pre><p>Specific Request CPU Limit Example:</p><pre>    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)<br>    {<br>        app.UseRouting();<br>        <br>        // Use the specific request CPU limit middleware with 15% CPU limit and 1 second monitoring interval<br>        app.UseCpuLimitRequestMiddleware(15.0, TimeSpan.FromMilliseconds(1000));<br><br>        app.UseEndpoints(endpoints =&gt;<br>        {<br>            endpoints.MapControllers();<br>        });<br>    }</pre><p>Any kind of suggestions to improve it is highly appreciated.</p><p>Source Blog: <a href="https://blog.tuhidulhossain.com/post/2024/06/16/cpuguard-net-cpu-usage-control-for-asp-net-core-applications">https://blog.tuhidulhossain.com/post/2024/06/16/cpuguard-net-cpu-usage-control-for-asp-net-core-applications</a></p><p>#ASPNetCore #Middleware #CPULimit #PerformanceOptimization #DotNetDevelopment #CpuGuardNET</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c0a618df1ef9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Install Vs Code Extension’s with bash Script]]></title>
            <link>https://medium.com/@mth.tuhin/install-vs-code-extensions-with-bash-script-a198540308c9?source=rss-938fca332512------2</link>
            <guid isPermaLink="false">https://medium.com/p/a198540308c9</guid>
            <category><![CDATA[vscode-extension]]></category>
            <category><![CDATA[bash]]></category>
            <category><![CDATA[vscode]]></category>
            <category><![CDATA[bash-script]]></category>
            <category><![CDATA[visual-studio-code]]></category>
            <dc:creator><![CDATA[Khaled Md Tuhidul Hossain]]></dc:creator>
            <pubDate>Sat, 29 Jul 2023 10:20:20 GMT</pubDate>
            <atom:updated>2023-07-31T13:20:01.521Z</atom:updated>
            <content:encoded><![CDATA[<p>Visual Studio Code, developed by Microsoft, stands out as a top-notch IDE for Node.js. This free and lightweight code editor boasts impressive capabilities.</p><p>The main factor driving my preference for VS Code is its exceptional support for debugging JavaScript and Node.js code. Additionally, the abundance of free extensions in the Visual Studio Marketplace allows for effortless customization.</p><p>With thousands of extensions available in the marketplace, it can be challenging to determine the ones that best suit your needs.<br>And these are the extensions I use regular basis:<br>1.<a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.npm-intellisense">npm Intellisense</a><br>2. <a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">ESLint</a><br>3.<a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier — Code formatter</a><br>4.<a href="https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets">JavaScript (ES6) code snippets</a><br>5.<a href="https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv">DotENV</a><br>6.<a href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments">Better Comments</a><br>7.<a href="https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml">YAML</a><br>8.<a href="https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker">Code Spell Checker</a><br>9.<a href="https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons">Vscode-icons</a><br>10.<a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLen</a>s</p><p>The process of re-configuring our development environment whenever we switch to a new device or environment can be quite exasperating and time-consuming. To save time and minimize effort, I devised a bash script that efficiently installs all the required VS Code extensions in one go.</p><pre>#!/bin/bash<br><br># Check if Visual Studio Code is installed<br>if ! [ -x &quot;$(command -v code)&quot; ]; then<br>  echo &quot;Visual Studio Code is not installed. Please install it first.&quot;<br>  exit 1<br>fi<br><br># Check if Node.js (npm) is installed<br>if ! [ -x &quot;$(command -v npm)&quot; ]; then<br>  echo &quot;Node.js (npm) is not installed. Please install it first.&quot;<br>  exit 1<br>fi<br><br># Replace &#39;EXTENSION_NAMES&#39; with the list of extension names separated by commas<br>EXTENSION_NAMES=&quot;christian-kohler.npm-intellisense,dbaeumer.vscode-eslint,esbenp.prettier-vscode,eamodio.gitlens,xabikos.JavaScriptSnippets,mikestead.dotenv,aaron-bond.better-comments,redhat.vscode-yaml,streetsidesoftware.code-spell-checker,vscode-icons-team.vscode-icons&quot;<br><br># Split the extension names into an array using comma as the delimiter<br>IFS=&#39;,&#39; read -ra EXTENSIONS &lt;&lt;&lt; &quot;$EXTENSION_NAMES&quot;<br><br># Loop through each extension name and install it<br>for EXTENSION_NAME in &quot;${EXTENSIONS[@]}&quot;; do<br>  # Trim leading and trailing whitespaces<br>  EXTENSION_NAME=$(echo &quot;$EXTENSION_NAME&quot; | sed -e &#39;s/^[[:space:]]*//&#39; -e &#39;s/[[:space:]]*$//&#39;)<br>  <br>  # Install the extension using the &#39;code&#39; command-line interface<br>  code --install-extension &quot;$EXTENSION_NAME&quot;<br>  <br>  echo &quot;The extension $EXTENSION_NAME has been installed successfully.&quot;<br>done</pre><p>Here is how to run the `.sh` file on Ubuntu (Linux), Windows, and macOS.</p><p>**1. Ubuntu (Linux):**</p><p>To run the `.sh` file on Ubuntu or any Linux distribution, you need to use the terminal. Here’s the step-by-step process:</p><p>Step 1: Open the terminal by pressing `Ctrl + Alt + T` or by searching for “Terminal” in the applications.</p><p>Step 2: Navigate to the directory where you saved the `vsExtensions.sh` file using the `cd` command. For example, if the file is on your desktop, you can use the following command:</p><pre>```bash<br>cd ~/Desktop<br>```</pre><p>Step 3: Make sure the script is executable by running the following command:</p><pre>```bash<br>chmod +x vsExtensions.sh<br>```</pre><p>Step 4: Now, you can execute the script using the `./` command:</p><pre>```bash<br> bash ./vsExtensions.sh<br>```</pre><p>The script will run, and if Visual Studio Code and Node.js (npm) are installed on your system, it will proceed to install the specified extensions.</p><p>**2. Windows:**</p><p>To run the `.sh` file on Windows, you can use one of the following methods:</p><p>**Git Bash**</p><p>You can use Git Bash, which is a Bash-compatible environment bundled with Git for Windows.</p><p>Step 1: Install Git for Windows (if you haven’t already) from the official website: <a href="https://gitforwindows.org/">https://gitforwindows.org/</a></p><p>Step 2: After installing Git for Windows, open “Git Bash” from the Start menu.</p><p>Step 3: Navigate to the directory where you saved the `vsExtensions.sh` file using the `cd` command. For example, if the file is on your desktop, you can use the following command:</p><pre>```bash<br>cd ~/Desktop<br>```</pre><p>Step 4: Make sure the script is executable by running the following command:</p><pre>```bash<br>chmod +x vsExtensions.sh<br>```</pre><p>Step 5: Now, you can execute the script using the `bash` command:</p><pre>```bash<br>bash vsExtensions.sh<br>```</pre><p>**3. macOS (Mac):**</p><p>To run the `.sh` file on macOS (Mac), you can use the built-in terminal, which provides a Bash shell.</p><p>Step 1: Open the Terminal application from the “Utilities” folder under “Applications” or use Spotlight to search for “Terminal.”</p><p>Step 2: Navigate to the directory where you saved the `vsExtensions.sh` file using the `cd` command. For example, if the file is on your desktop, you can use the following command:</p><pre>```bash<br>cd ~/Desktop<br>```</pre><p>Step 3: Make sure the script is executable by running the following command:</p><pre>```bash<br>chmod +x vsExtensions.sh<br>```</pre><p>Step 4: Now, you can execute the script using `./`:</p><pre>```bash<br>./vsExtensions.sh<br>```</pre><p>The script will run, and if Visual Studio Code and Node.js (npm) are installed on your system, it will proceed to install the specified extensions.</p><p>Now Enjoy :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a198540308c9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Your Own Model and Train : Custom Vision AI — Part 1]]></title>
            <link>https://medium.com/@mth.tuhin/building-your-own-model-and-train-custom-vision-ai-part-1-6367cc2f8d55?source=rss-938fca332512------2</link>
            <guid isPermaLink="false">https://medium.com/p/6367cc2f8d55</guid>
            <category><![CDATA[model-training]]></category>
            <category><![CDATA[azure-cognitive-service]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[custom-vision]]></category>
            <dc:creator><![CDATA[Khaled Md Tuhidul Hossain]]></dc:creator>
            <pubDate>Tue, 07 Mar 2023 17:47:36 GMT</pubDate>
            <atom:updated>2023-03-07T17:48:13.625Z</atom:updated>
            <content:encoded><![CDATA[<h3>Building Your Own Model and Train : Custom Vision AI — Part 1</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MB2S-kBnWHXj3e3Kw_PzHg.jpeg" /></figure><p>Anyone can create and train their own unique image recognition models using <a href="https://www.customvision.ai/">CustomVision.ai</a>, a Microsoft cloud-based service. Users of this service can design a model specifically for their requirements, whether they be for object recognition, image classification, or even facial recognition. We’ll go over how to create your own model and train it in <a href="https://www.customvision.ai/">CustomVision.ai</a> in this article.</p><p><strong>Step 1: Register on CustomVision.ai</strong></p><p>The first step to building your own model is to sign up for CustomVision.ai. This is a free tool provided by Microsoft that allows you to create custom image classifiers. Once you’ve signed up, you can access the CustomVision.ai portal and start building your own model.</p><p><strong>Step 2: Create a new project</strong></p><p>The next step after logging in to <a href="https://www.customvision.ai/">CustomVision.ai</a> is to start a new project. The “New Project” button on the home page can be used to accomplish this. Prior to selecting the classification method you want to use, you must first give your project a name and a description. For instance, you might decide to categorize images based on whether or not they feature dogs or cats.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/547/1*zdW5gVVM3W8-uJfF1YqKSg.png" /></figure><p><strong>Step 3: Upload and Add Tag in images</strong></p><p>The following step is to upload your images after creating your project. You can do this by selecting the desired images by clicking the “Add Images” button. CustomVision.ai allows you to upload multiple images at once, and it will automatically label them based on the file names.</p><p>Selecting the images and selecting the “Add Tag” button will allow you to make more precise labels. After giving the tag a name, it will be applied to all of the chosen pictures. I’ve created three type of Tags named “<strong>Cats</strong>”, “<strong>Dogs</strong>” &amp; “<strong>Monkeys</strong>”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1XPkuoQlisQcP32JidIPhQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F3F8Z0PSHvk1D21CjS_pyw.png" /></figure><p><strong>Step 4: Train your model</strong></p><p>The next step is to train your model after you’ve uploaded and labeled your images. To do this, select the training parameters by clicking the “Train” button. Automatic data splitting into training and validation sets is performed by CustomVision.ai before your model training process begins.</p><p>Depending on how complex your model is and how large your dataset is, the training process may take some time. The training status will be shown on the page by CustomVision.ai.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c0fGf59O3bKZ5n4jGpHC8Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yQqOuPS6NmYC8Jb99yExAA.png" /></figure><p><strong>Step 5: Testing your model</strong></p><p>The performance of your model can be tested after it has been trained. A testing interface is offered by <a href="https://www.customvision.ai/">CustomVision.ai</a> where users can upload new images and check to see if the model correctly recognizes the objects or subjects in them. If the model is not performing as well as you would like, you can keep improving it by adding more images and tags or by changing the training parameters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3jswbFw7HuVs_2MbltFOcg.png" /></figure><p>In conclusion, <a href="https://www.customvision.ai/">CustomVision.ai</a> offers a simple platform for creating and refining unique image recognition models. You can build your own model and train it to recognize the things you need by following the procedures described in this article. The potential for image recognition and classification with this potent tool is limitless, and it creates avenues for numerous applications in numerous fields.</p><p>Original Post Link : <a href="https://www.linkedin.com/pulse/building-your-own-model-train-custom-vision-ai-part-khaled">https://www.linkedin.com/pulse/building-your-own-model-train-custom-vision-ai-part-khaled</a></p><p>Follow for more <a href="https://www.customvision.ai/">CustomVision.ai</a> related content. ❤️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6367cc2f8d55" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>