{
    "version": "https://jsonfeed.org/version/1",
    "title": "LittleHorse Blog",
    "home_page_url": "https://littlehorse.io/blog",
    "description": "LittleHorse Blog",
    "items": [
        {
            "id": "https://littlehorse.io/blog/orchestrating-modular-monoliths",
            "content_html": "<p>In the era of microservices, the word \"monolith\" has often been treated as something to decommission. However, as many engineering teams have discovered, decomissioning long existing services can be a non-starter.  In addition, prematurely splitting a monolithic application into many or dozens of distributed services can lead to an operational nightmare. Instead, we suggest the <strong>Modular Monolith</strong>.</p>\n<p>But how do you give a modular monolith the resilience, observability, and workflow modularity of a distributed system? The answer lies in orchestration.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"defining-the-monolith\">Defining the Monolith<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#defining-the-monolith\" class=\"hash-link\" aria-label=\"Direct link to Defining the Monolith\" title=\"Direct link to Defining the Monolith\" translate=\"no\">​</a></h2>\n<p>A <strong>monolith</strong> is simply a software application where all logical components- interface, business rules, and data access—are combined into a single deployable.</p>\n<p>A <strong>modular monolith</strong> takes this a step further. It is still a single deployable unit, but its internal architecture is strictly divided into distinct, loosely coupled modules (often based on Domain-Driven Design). Each module holds its own logic and data, communicating with other modules through well-defined, in-process interfaces rather than tangled function calls.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-orchestrate-a-monolith\">Why Orchestrate a Monolith?<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#why-orchestrate-a-monolith\" class=\"hash-link\" aria-label=\"Direct link to Why Orchestrate a Monolith?\" title=\"Direct link to Why Orchestrate a Monolith?\" translate=\"no\">​</a></h2>\n<p>You might associate orchestration exclusively with microservices. However, the same principals can be applied to monoliths, often with many of the same benifits.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-are-the-benefits\">What are the Benefits?<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#what-are-the-benefits\" class=\"hash-link\" aria-label=\"Direct link to What are the Benefits?\" title=\"Direct link to What are the Benefits?\" translate=\"no\">​</a></h3>\n<ul>\n<li class=\"\"><strong>Visibility and Observability</strong>: By using an orchestration engine to manage cross-module workflows, you gain real-time visibility into the state of long-running business processes. You can see exactly which module is currently executing a task and where bottlenecks lie.</li>\n<li class=\"\"><strong>Resilience and Retries</strong>: Orchestration engines naturally handle retries, timeouts, and compensation logic (rollbacks). If a module fails due to a transient error, the orchestrator can reliably retry the task without requiring you to write complex polling logic or background jobs.</li>\n<li class=\"\"><strong>A Stepping Stone to Microservices</strong>: If you orchestrate your monolith's modules today, extracting a module into a microservice tomorrow becomes trivial. The orchestrator already manages the communication; you just change where the worker lives.</li>\n<li class=\"\"><strong>Smaller intergration points</strong>: As you start to decompose a monolith in place, you can then expose small functional parts to others.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"how-to-decompose-a-monolith\">How to Decompose a Monolith<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#how-to-decompose-a-monolith\" class=\"hash-link\" aria-label=\"Direct link to How to Decompose a Monolith\" title=\"Direct link to How to Decompose a Monolith\" translate=\"no\">​</a></h2>\n<p>Decomposing a traditional \"big ball of hair\" monolith into a modular one requires discipline:</p>\n<ol>\n<li class=\"\"><strong>Identify Bounded Contexts</strong>: Group related functionality into logical domains (e.g., Inventory, Billing, User Management).</li>\n<li class=\"\"><strong>Expose Tasks</strong>: Each module will need an input and output that can be held in documentation or service catalog.  LittleHorse automatically documents the data contracts as taskDefs are registered.</li>\n<li class=\"\"><strong>Introduce Orchestration</strong>: For workflows that span multiple modules, introduce an orchestration layer to coordinate the steps, replacing deeply nested, synchronous method calls.</li>\n</ol>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"treating-a-monolith-like-a-microservice-dos-and-donts\">Treating a Monolith Like a Microservice: Dos and Don'ts<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#treating-a-monolith-like-a-microservice-dos-and-donts\" class=\"hash-link\" aria-label=\"Direct link to Treating a Monolith Like a Microservice: Dos and Don'ts\" title=\"Direct link to Treating a Monolith Like a Microservice: Dos and Don'ts\" translate=\"no\">​</a></h2>\n<p>When building a modular monolith, borrowing concepts from microservices can be highly effective, provided you avoid the pitfalls.</p>\n<ul>\n<li class=\"\"><strong>Do</strong> enforce strict boundaries. Use your language's module system (e.g., Java Modules, C# Projects) to prevent unauthorized cross-module dependencies.</li>\n<li class=\"\"><strong>Do</strong> design for failure. Assume a module <em>could</em> fail and build resilience into the workflow.</li>\n<li class=\"\"><strong>Don't</strong> introduce network boundaries prematurely. Keep module communication in-process to enjoy the low latency and simplicity of a single deployment.</li>\n<li class=\"\"><strong>Don't</strong> share a unified database transaction across modules. If a workflow spans modules, use eventual consistency and orchestration rather than a giant two-phase commit.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"example-use-cases-and-benefits\">Example Use Cases and Benefits<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#example-use-cases-and-benefits\" class=\"hash-link\" aria-label=\"Direct link to Example Use Cases and Benefits\" title=\"Direct link to Example Use Cases and Benefits\" translate=\"no\">​</a></h2>\n<p>Imagine an <strong>E-commerce Checkout</strong> process. It requires:</p>\n<ol>\n<li class=\"\">Reserving inventory.</li>\n<li class=\"\">Processing payment.</li>\n<li class=\"\">Sending a confirmation email.</li>\n<li class=\"\">Shipping the items</li>\n</ol>\n<p>In a traditional monolith, this might be a single, synchronous HTTP request.  If reserving the inventory or shipping the item, times out, fails or takes days there is no graceful failure condition without a complex system of job queus or external systems.</p>\n<p>In an <strong>orchestrated modular monolith</strong>, these four steps are distinct tasks coordinated by an orchestrator. If the inventory module succeeds but the payment module encounters an error, the orchestrator pauses the workflow and retries the payment module later, or fails another way succesfully. The user gets a responsive experience, and your system remains highly reliable without the operational overhead of managing three separate microservices.  In addition external systems can add additional context to this entire process by using <a href=\"https://littlehorse.io/blog/callbacks\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">External Events</a></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-quiet-power-of-littlehorseio\">The Quiet Power of LittleHorse.io<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#the-quiet-power-of-littlehorseio\" class=\"hash-link\" aria-label=\"Direct link to The Quiet Power of LittleHorse.io\" title=\"Direct link to The Quiet Power of LittleHorse.io\" translate=\"no\">​</a></h2>\n<p>As you start treating your monolith's modules as independent actors, you need an orchestration engine that is fast, reliable, and unobtrusive. This is where <strong>LittleHorse.io</strong> shines as an ideal bridge.</p>\n<p>LittleHorse was built to handle high-throughput, mission-critical workflows. In the context of a modular monolith, you can easily treat each internal module as a LittleHorse \"Task Worker.\"</p>\n<p>Because LittleHorse is highly performant and language-agnostic, integrating it doesn't add the heavy overhead usually associated with external orchestration. It quietly routes tasks between your monolith's modules, managing state, retries, and error handling seamlessly.</p>\n<p>More importantly, LittleHorse makes integrating your monolith into a broader microservice environment effortless. Because LittleHorse doesn't care where a Task Worker lives, a single workflow can seamlessly coordinate a task running inside your dependable monolith alongside a task running in a newly deployed microservice. It provides the perfect, low-friction integration point for hybrid architectures, quietly handling the complex state management behind the scenes.  This also allows you to branch of features and functionality, avoiding the complex and lengthy development times associated with monoliths.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"summary\">Summary<a href=\"https://littlehorse.io/blog/orchestrating-modular-monoliths#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\" translate=\"no\">​</a></h2>\n<p>The choice isn't just between a tangled monolith and a sprawling microservice architecture. By adopting a modular monolith and orchestrating its workflows, you gain the operational simplicity of a single deployment alongside the resilience, visibility, and scalability of distributed systems. With tools like LittleHorse smoothly bridging the gap, you can incrementally modernize your architecture at your own pace, focusing on business value rather than infrastructure complexity.</p>",
            "url": "https://littlehorse.io/blog/orchestrating-modular-monoliths",
            "title": "Orchestrating Modular Monoliths: Benefits, Strategies, and How LittleHorse Can Help",
            "summary": "In the era of microservices, the word \"monolith\" has often been treated as something to decommission. However, as many engineering teams have discovered, decomissioning long existing services can be a non-starter.  In addition, prematurely splitting a monolithic application into many or dozens of distributed services can lead to an operational nightmare. Instead, we suggest the Modular Monolith.",
            "date_modified": "2026-04-17T00:00:00.000Z",
            "author": {
                "name": "Mitchell Henderson"
            },
            "tags": [
                "architecture",
                "monolith",
                "Microservices",
                "orchestration",
                "littlehorse"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/have-your-claw",
            "content_html": "<p>Among leaders and executives, there has been much recent buzz about OpenClaw in the context of enterprise automation. While Claws do give us a glimpse of what Agents can do, there are fundamental limitations to the Claw architecture when used as process orchestrators. Thankfully, there is a safer, cheaper, and more predictable way to automate processes with Agents. (And no, LittleHorse is not a competitor with Claws...as we will see in this post, LittleHorse focuses on enterprise process orchestration while Claws fit best as powerful personal assistants).</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>In this post, we will:</p><ol>\n<li class=\"\">Understand exactly how Agents and Claws work (it's not scary, I promise!).</li>\n<li class=\"\">Examine why Claws make for great assistants but are not built for automation of enterprise processes.</li>\n<li class=\"\">Discuss how <a class=\"\" href=\"https://littlehorse.io/blog/business-as-code\">Business-as-Code</a> presents a safer, cheaper, and more predictable way to orchestrate critical business flows using Agents.</li>\n</ol><p>While I often write specifically for engineers, this post focuses more on business-level challenges and should be relevant to both engineers and executives alike.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"understanding-claws\">Understanding Claws<a href=\"https://littlehorse.io/blog/have-your-claw#understanding-claws\" class=\"hash-link\" aria-label=\"Direct link to Understanding Claws\" title=\"Direct link to Understanding Claws\" translate=\"no\">​</a></h2>\n<p>To understand how the Claw architecture works at a high level, we will take a scenic (but still pleasant and relaxing!) route:</p>\n<ol>\n<li class=\"\">What even is an Agent? (How does it work?)</li>\n<li class=\"\">How do Claws Work?</li>\n<li class=\"\">What's the deal with NemoClaw?</li>\n</ol>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>If you've been hands-on with agents for a while and understand the internals of a Claw, you can <a href=\"https://littlehorse.io/blog/have-your-claw#where-do-claws-fit\" class=\"\">skip ahead</a>. The following section assumes no technical background.</p><p>I'll write a technical deep dive on gradient descent, LLM's, and agent internals once I finish the <a class=\"\" href=\"https://littlehorse.io/blog/decision-workers\">Agentic Patterns</a> series.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agent-basics\">Agent Basics<a href=\"https://littlehorse.io/blog/have-your-claw#agent-basics\" class=\"hash-link\" aria-label=\"Direct link to Agent Basics\" title=\"Direct link to Agent Basics\" translate=\"no\">​</a></h3>\n<p>At the risk of oversimplifying one of the most impactful inventions of the 21st century, an agent is simply a computer program that calls an LLM repeatedly in a fancy loop.</p>\n<p>The program (sometimes called an \"Agent Harness\") first defines a set of tools that the LLM can use. For example, tools might allow the LLM to perform such actions as \"search the web,\" \"run a bash (terminal) command,\" or \"read the contents of this file on my laptop.\" Once the tools are defined, the program then:</p>\n<ol>\n<li class=\"\">Passes the user's input (prompt) into an LLM and asks the LLM what tool to call.</li>\n<li class=\"\">Reads the output from the LLM, and takes the action specified by the LLM.</li>\n<li class=\"\">Pastes the result of taking the action back into the LLM.</li>\n<li class=\"\">Repeats steps <code>1</code> through <code>3</code> until the LLM says \"I'm done!\"</li>\n</ol>\n<p>The key insight is that step <code>2</code> gives the LLM access to interact with the outside world through tools. This is the source of many security vulnerabilities: the most infamous one is \"prompt injection,\" in which a malicious prompt tricks the LLM into calling a tool with parameters that cause a data leak or which cause inappropriate actions to take place.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>The <a href=\"https://arxiv.org/abs/2302.04761\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Toolformer Paper</a> from 2023 is a good read to deep dive into exactly what's going on in step <code>2</code>!</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-claw-insight\">The Claw Insight<a href=\"https://littlehorse.io/blog/have-your-claw#the-claw-insight\" class=\"hash-link\" aria-label=\"Direct link to The Claw Insight\" title=\"Direct link to The Claw Insight\" translate=\"no\">​</a></h3>\n<p>A Claw is a thin (but extremely powerful) layer on top of an Agent that, in most cases, runs locally on your very own laptop. The Claw architecture utilizes three key insights (or are they tricks?):</p>\n<ol>\n<li class=\"\">The tools provided to the agent in a Claw allow it to do anything it wants on the computer, even accessing and using a browser through the Browser MCP. This unleashes the Agent into the world, allowing it to do pretty much anything a human can do with a computer.</li>\n<li class=\"\">The agent inside the Claw can \"remember\" things by writing them down to local files on the computer it runs on. This allows the Claw to create tasks for itself to do in the future.</li>\n<li class=\"\">The Claw harness runs the agent on an interval to process old tasks, such as periodically monitoring the stock market, looking for new GitHub issues to fix, etc.</li>\n</ol>\n<p>Lastly, a tiny amount of plumbing code allows users to prompt their agent through chat interfaces like WhatsApp, turning a Claw into a full-blown digital assistant. In fact, the Claw architecture is so elegant and simple that it can be implemented in just 500 lines of code (see: <a href=\"https://github.com/qwibitai/nanoclaw\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">The NanoClaw Project</a>) on top of a coding agent such as OpenCode.</p>\n<p>As we'll see shortly, the danger with the Claw architecture is how free-wheeling it is. Processes aren't coded anywhere: the behavior of the Claw is entirely up to the <em>probabilistic</em> (which means definitely eventually wrong) LLM and the non-binding instructions (or pleas?) that you give the Claw.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-about-nemoclaw\">What About NemoClaw?<a href=\"https://littlehorse.io/blog/have-your-claw#what-about-nemoclaw\" class=\"hash-link\" aria-label=\"Direct link to What About NemoClaw?\" title=\"Direct link to What About NemoClaw?\" translate=\"no\">​</a></h3>\n<p>NVidia recently released NemoClaw, which builds security on top of OpenClaw. NemoClaw is NVidia's enterprise stack for running OpenClaw, allowing you to install and run a Claw in an isolated sandbox (you can think of this as a virtual machine). NemoClaw also has the handy ability to run on cloud hardware, solving the problem wherein closing your laptop stops your agent from making progress.</p>\n<p>While NemoClaw does allow you to mitigate a lot of the security implications of running Claws in the enterprise, the project still does not turn the agent into a reliable, auditable, and efficient orchestrator. As we'll see in the next section, NemoClaw is a useful assistant but is not a panacea for enterprise automation.</p>\n<p>In short, NemoClaw removes some of the security considerations that OpenClaw has <em>as a personal assistant</em> but does not transform the Claw architecture into an enterprise process orchestrator.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"where-do-claws-fit\">Where Do Claws Fit?<a href=\"https://littlehorse.io/blog/have-your-claw#where-do-claws-fit\" class=\"hash-link\" aria-label=\"Direct link to Where Do Claws Fit?\" title=\"Direct link to Where Do Claws Fit?\" translate=\"no\">​</a></h2>\n<p>The Claw architecture is effective and elegantly simple: coding agent harnesses such as Claude Code and OpenCode can use a computer in ways similar to a human, enabling the Claw to accept instructions in natural language.</p>\n<p>That simplicity comes at a cost in governance, predictability, and token spend. For mission-critical business flows, we want the agent to <em>participate in</em> the process, not <em>invent</em> the process.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"effective-assistants\">Effective Assistants<a href=\"https://littlehorse.io/blog/have-your-claw#effective-assistants\" class=\"hash-link\" aria-label=\"Direct link to Effective Assistants\" title=\"Direct link to Effective Assistants\" translate=\"no\">​</a></h3>\n<p>OpenClaw (the most famous Claw!) cut its teeth with personal assistant tasks such as:</p>\n<ul>\n<li class=\"\">Check the git history from the last week and suggest documentation updates.</li>\n<li class=\"\">Summarize the sales pipeline before a staff meeting.</li>\n<li class=\"\">Look at my upcoming meetings tomorrow and prepare a report for each one, including who I'm meeting with (check LinkedIn) and any past interactions I've had with them (including email).</li>\n</ul>\n<p>For such tasks, the Claw architecture is highly effective and elegantly simple: coding agent harnesses such as Claude Code and OpenCode can perform these tasks simply by using bash commands and the Browser (through MCP). Handy interfaces (such as WhatsApp or Signal chats) make interacting with the Claw truly feel like a live personal assistant.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"claws-are-irresponsible-process-orchestrators\">Claws Are Irresponsible Process Orchestrators<a href=\"https://littlehorse.io/blog/have-your-claw#claws-are-irresponsible-process-orchestrators\" class=\"hash-link\" aria-label=\"Direct link to Claws Are Irresponsible Process Orchestrators\" title=\"Direct link to Claws Are Irresponsible Process Orchestrators\" translate=\"no\">​</a></h3>\n<p>The Claw-style loop which wakes an agent on a schedule and lets it <em>do something</em> on a computer is great for building an assistant that can solve open-ended personal automation tasks. However, this architecture is definitively unfit for enterprise process orchestration for critical processes such as:</p>\n<ul>\n<li class=\"\">Approving a loan.</li>\n<li class=\"\">Issuing a refund.</li>\n<li class=\"\">Modifying a customer's subscription.</li>\n<li class=\"\">Triggering a compliance review.</li>\n<li class=\"\">Updating a payment method.</li>\n</ul>\n<p>First, a system built on top of natural language instructions parsed by a non-deterministic computer program will never hold up under real-world edge cases such as prompt injection attacks, much less a regulatory audit. Details such as authorization, specific business logic, and when to involve human judgment should be codified rather than left to a probabilistic LLM.</p>\n<p>Secondly, the Claw architecture is extraordinarily expensive. Token costs can easily exceed hundreds of dollars per day per person, wiping away any efficiency gains. This is because lack of structure forces the agent to spend tokens figuring out what needs to be done, injecting creativity in places where it is potentially unwanted (and weighing down the wallet).</p>\n<p>Lastly, in addition to the challenges of using a Claw to automate back-office processes, OpenClaw's <a href=\"https://docs.openclaw.ai/gateway/security\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">own security docs</a> caution against deploying a Claw in a multi-tenant environment. Rather, users are advised to deploy a single Claw per human user of them in a true assistant-style deployment.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"predictably-intelligent-automation\">Predictably Intelligent Automation<a href=\"https://littlehorse.io/blog/have-your-claw#predictably-intelligent-automation\" class=\"hash-link\" aria-label=\"Direct link to Predictably Intelligent Automation\" title=\"Direct link to Predictably Intelligent Automation\" translate=\"no\">​</a></h2>\n<p>Claws spend tokens to figure out what to do. While this is great for open-ended, assistant-style tasks, this architecture is not suitable for business-critical workflow automation. In such cases with well-defined, sensitive processes, Claws are both expensive and error-prone due to the fact that processes are not codified but rather inferred in real-time by the LLM.</p>\n<p>If Claws aren't the answer, how can we take advantage of the transformational power of Agents? Simply put, codify the parts of your process that you know must happen and use agents to route individual cases within the larger process. That's what I've been writing about in the Agentic Patterns series, and that is exactly what <a class=\"\" href=\"https://littlehorse.io/blog/business-as-code\">Business-as-Code</a> excels at.</p>\n<p>For business process automation, Business-as-Code gives you a safer and more predictable way to deploy Agents without blowing your budget.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"business-as-code\">Business-as-Code<a href=\"https://littlehorse.io/blog/have-your-claw#business-as-code\" class=\"hash-link\" aria-label=\"Direct link to Business-as-Code\" title=\"Direct link to Business-as-Code\" translate=\"no\">​</a></h3>\n<p>The core premise of Business-as-Code is that you (or Claude) write high-level code which specifies what is supposed to happen (and when) in your business process. These actions take place across myriad systems: microservices, external SaaS platforms, agents, and legacy systems such as mainframes.</p>\n<p>Crucially, as much as possible of the process is codified in deterministic code: Agents are only responsible for making routing decisions within a larger process, such as classifying a refund request as valid or fraudulent in the context of a user's past interactions. The Agents do not have the capability to reinvent the order return process: that was already codified, reviewed, and accepeted in deterministic Java / Python / Go / C# code which matches your business process and is visualized in the LittleHorse Dashboard (as below):</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"A visualization of the above WfSpec\" src=\"https://littlehorse.io/assets/images/2026-01-16-wfspec-a785811d6d2ac4fbfe2cad2ece96cb00.png\" width=\"1843\" height=\"551\" class=\"img_ev3q\"></p>\n<p>When you use a Claw to automate enterprise processes, you'll repeatedly pay for tokens used to figure out steps that should have been deterministically codified to save costs and reduce errors. Business-as-Code lets you have your Agent and eat it too: you can reap the benefits of agentic intelligence, save money, and build processes that never break down.</p>\n<p>In short, if you haven't codified your process with Business-as-Code, the Agent will invent one for you.</p>",
            "url": "https://littlehorse.io/blog/have-your-claw",
            "title": "Executive POV: Have Your Claw and Eat It Too?",
            "summary": "Among leaders and executives, there has been much recent buzz about OpenClaw in the context of enterprise automation. While Claws do give us a glimpse of what Agents can do, there are fundamental limitations to the Claw architecture when used as process orchestrators. Thankfully, there is a safer, cheaper, and more predictable way to automate processes with Agents. (And no, LittleHorse is not a competitor with Claws...as we will see in this post, LittleHorse focuses on enterprise process orchestration while Claws fit best as powerful personal assistants).",
            "date_modified": "2026-04-07T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "AI, LLM's, and Agents",
                "analysis"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/decision-workers",
            "content_html": "<p>How can we mitigate issues like prompt injection, governance, and plain old integration challenges when using agents for automation? We've helped our customers overcome these problems with the <em>Decision Workers</em> pattern, which separates the concerns of intelligence and orchestration. In this pattern, LittleHorse <code>WfSpec</code>s achieve reliable and secure orchestration while Decision Workers (Agents with read-only tool access) provide the intelligence.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the second part in a four-part blog series about building agentic workflows with LittleHorse. This blog series will help you automate internal and customer-facing business processes with guardrails and security.</p><ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/agents-need-workflow\">Safe Agentic Decisions</a></li>\n<li class=\"\"><strong>[This Post]</strong> Decision Workers</li>\n<li class=\"\">[Coming Soon] Verified Agents</li>\n<li class=\"\">[Coming Soon] Workflow Dispatch Agents</li>\n</ol></div></div>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Example Decision Worker Workflow\" src=\"https://littlehorse.io/assets/images/decision-worker-wfspec-4754e9ee5b18636f281d143eb9a9c41e.svg\" width=\"1282\" height=\"477\" class=\"img_ev3q\"></p>\n<p>In this pattern, a LittleHorse workflow (<code>WfSpec</code>) consults an agent to extract important information and make a business decision about which path to take in the process. The agent does not orchestrate the process itself but rather serves as a router which <em>decides</em> what path to take. Along the way, deterministic guardrails enforce security and catch erroneous decisions that agents make, and the orchestrator (here, a <code>WfSpec</code>) shepherds the process to completion while collecting an audit trail of everything that happened.</p>\n<p>The key to making the agent inside the Decision Worker effective yet safe is to give it tools that have <em>read-only access</em> to sensitive systems. The agent is free to fetch as much context as it needs in order to make a decision; however, it does not directly take action itself but rather outputs a structured decision which is then acted upon by the <code>WfSpec</code>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"case-study-ticket-triage\">Case Study: Ticket Triage<a href=\"https://littlehorse.io/blog/decision-workers#case-study-ticket-triage\" class=\"hash-link\" aria-label=\"Direct link to Case Study: Ticket Triage\" title=\"Direct link to Case Study: Ticket Triage\" translate=\"no\">​</a></h2>\n<p>Let's analyze two architectures for a system that automatically triages incoming support requests over email. We'll look at the system with two architectures:</p>\n<ol>\n<li class=\"\">Giving the agent full responsibility for orchestrating the process.</li>\n<li class=\"\">Using Decision Workers to limit the responsibility of the agent.</li>\n</ol>\n<p>Our system will use an agent to read an email, fetch context using tools (eg. find info about the order, find info about the user, etc), and then decide how to handle the ticket:</p>\n<ol>\n<li class=\"\">Escalate to a human.</li>\n<li class=\"\">Send info about the order to the user.</li>\n<li class=\"\">Cancel the order and issue a refund.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"naive-agent-driven-orchestration\">Naive Agent-Driven Orchestration<a href=\"https://littlehorse.io/blog/decision-workers#naive-agent-driven-orchestration\" class=\"hash-link\" aria-label=\"Direct link to Naive Agent-Driven Orchestration\" title=\"Direct link to Naive Agent-Driven Orchestration\" translate=\"no\">​</a></h3>\n<p>A naive approach would be to give an agent multiple tools: some tools to fetch context (such as looking up information about the order and the user), and some tools to take action by hitting other API's. It would be dangerous to give the agent a tool such as:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">processRefundAndCancelOrder(String orderId, BigDecimal refundAmount);</span><br></span></code></pre></div></div>\n<p>The services and API's involved in processing refunds are decidedly not multi-tenant, so this approach requires the agent to correctly propagate tenancy context. This causes the agent to become vulnerable to prompt injection attacks: a malicious email could manipulate the model into issuing an unauthorized action (such as canceling someone else's order).</p>\n<p>Our problem is that the agent's decision space is not properly constrained: we're giving the agent the ability to decide to send a refund to the wrong person. In a multi-tenant system, that means the model must correctly preserve tenant context on every tool call. A malicious email can exploit that requirement through prompt injection, causing the system to cancel the wrong order or issue the wrong refund.</p>\n<p>Agents perform far more reliably when you give them <em>read</em> access to tools, and then parse and act upon a structured output decision. But how can we effectively orchestrate this?</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"decision-workers\">Decision Workers<a href=\"https://littlehorse.io/blog/decision-workers#decision-workers\" class=\"hash-link\" aria-label=\"Direct link to Decision Workers\" title=\"Direct link to Decision Workers\" translate=\"no\">​</a></h3>\n<p>As mentioned earlier, a Decision Worker is a pattern in which you wrap an agent inside a <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/tasks\">LittleHorse Task Worker</a>. The agent has read-only tool access to sensitive systems, and outputs a decision which is acted upon by the <code>WfSpec</code>. As we'll see later, this pattern vastly mitigates the exposure to prompt injection attacks (or even simple hallucinations).</p>\n<p>Here's a LittleHorse Task Worker which implements that:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">enum</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">SupportTicketResponse</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">   </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">ESCALATE_TO_TEAM</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">   </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">SEND_ORDER_INFO</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">   </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">CANCEL_ORDER</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@LHTaskMethod</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"triage-support-ticket\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">SupportTicketResponse</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">triageTicket</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">String</span><span class=\"token plain\"> emailContent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">   </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Not shown here for simplicity: use the LLM in a loop to gather more context</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">   </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// like a proper agent does.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">   </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">//</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">   </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// The agent is ONLY allowed to READ, not write.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">String</span><span class=\"token plain\"> response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> anthropic</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">messages</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">create</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">MessageCreateParams</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">builder</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">model</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"claude-sonnet-4-20250514\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">maxTokens</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">50</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">system</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"You are a support ticket classifier. Respond with exactly one of: \"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"ESCALATE_TO_TEAM, SEND_ORDER_INFO, CANCEL_ORDER. \"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"No other text.\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addUserMessage</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">emailContent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">build</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">content</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">trim</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">SupportTicketResponse</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">valueOf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Exception</span><span class=\"token plain\"> exn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">SupportTicketResponse</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">ESCALATE_TO_TEAM</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>Agents can take their time. Best to leave at least 60 seconds task timeout.</p></div></div>\n<p>Below is a <code>WfSpec</code> which places guardrails around the above Decision Worker, handling all orchestration and enforcing proper security contexts.</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@LHWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"handle-support-ticket\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">supportTicketFlow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WorkflowThread</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> emailBody </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"email-body\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">required</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> status </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"status\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">searchable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> userId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"user-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">searchable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> orderId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"order-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">searchable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"TRIAGING\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Fail and escalate to humans if the agent cannot extract a valid order-id which</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// belongs to the user</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"extract-order-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> emailBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doIf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"validate-order-and-user\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">not</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assignUserTask</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"review-support-ticket\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">null</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"support-team\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">withNotes</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"couldn't extract valid orderId\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Decision Agent here!!!</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> decision </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"decision\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    decision</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"triage-support-ticket\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> emailBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doIf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">decision</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">isEqualTo</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"CANCEL_ORDER\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> then </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"REFUNDING\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Avoid prompt injection: we know the orderId is tied to the user. This is</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// controlled by the workflow, not the agent.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        then</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"process-refund\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        then</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"cancel-order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        then</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"send-email\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Refund Issued\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"We've issued a refund for your recent order.\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doElseIf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">decision</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">isEqualTo</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"SEND_ORDER_INFO\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> then </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"RESPONDING\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        then</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"send-order-info\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doElseIf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">decision</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">isEqualTo</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"ESCALATE_TO_TEAM\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> then </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"ESCALATED\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        then</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assignUserTask</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"review-support-ticket\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">null</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"support-team\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">withNotes</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"DONE\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"an-agentic-harness\">An Agentic Harness<a href=\"https://littlehorse.io/blog/decision-workers#an-agentic-harness\" class=\"hash-link\" aria-label=\"Direct link to An Agentic Harness\" title=\"Direct link to An Agentic Harness\" translate=\"no\">​</a></h2>\n<p>A <code>WfSpec</code> is the perfect way to capture the power of an agent while reducing the amount of work it must do. It encodes processes which don't change (such as payments, onboarding, orders, etc) while freeing up agents to make decisions. Most importantly, using <code>WfSpec</code>s to harness your agents allows you to <a class=\"\" href=\"https://littlehorse.io/blog/agents-need-workflow#agents-are-not-orchestrators\">deterministically</a> control what your agent does and doesn't touch.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"least-privilege\">Least Privilege<a href=\"https://littlehorse.io/blog/decision-workers#least-privilege\" class=\"hash-link\" aria-label=\"Direct link to Least Privilege\" title=\"Direct link to Least Privilege\" translate=\"no\">​</a></h3>\n<p>With the Decision Worker pattern, your agent only has to be able to <em>find information</em> rather than take (potentially destructive) actions. For example, our email triage worker might be allowed to look up any information it wants about the user, past orders, etc, but it's not able to (nor does it need to) call any privileged API's with write access.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"avoiding-prompt-injection\">Avoiding Prompt Injection<a href=\"https://littlehorse.io/blog/decision-workers#avoiding-prompt-injection\" class=\"hash-link\" aria-label=\"Direct link to Avoiding Prompt Injection\" title=\"Direct link to Avoiding Prompt Injection\" translate=\"no\">​</a></h3>\n<p>Crucially, the Decision Worker pattern makes it much harder to trick agents into spilling secrets. This is because the agent itself does not have the tools needed to do so: we can write non-agentic (deterministic) code to explicitly validate that the order we are acting on belongs to the user who sent the initial email. Even with malicious emails, the <code>WfSpec</code> deterministically enforces security constraints and takes away the agent's ability to make errors (for example, canceling the wrong user's order).</p>\n<p>If you simply pass over the orchestration to the agent itself, you get no such defense.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"governance-and-durability\">Governance and Durability<a href=\"https://littlehorse.io/blog/decision-workers#governance-and-durability\" class=\"hash-link\" aria-label=\"Direct link to Governance and Durability\" title=\"Direct link to Governance and Durability\" translate=\"no\">​</a></h3>\n<p>Lastly, all of the traditional benefits of LittleHorse still apply here. You can see the execution history of all support tickets. LittleHorse still provides all of the durability and orchestration benefits between steps that it always does:</p>\n<ul>\n<li class=\"\">Audit trails of every step executed in every process, most importantly what decisions were made by the agents and when.</li>\n<li class=\"\">Useful debugging logs for any stuck or failed processes.</li>\n<li class=\"\">Ability to easily escalate to human intervention with <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/user-tasks\">UserTasks</a>.</li>\n</ul>\n<p>Most importantly, you can clearly see what decisions were made by which agent, and when.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"wrapping-up\">Wrapping Up<a href=\"https://littlehorse.io/blog/decision-workers#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\" translate=\"no\">​</a></h2>\n<p>Decision Workers are a pragmatic way to put agents into production: let the agent do what it is good at (interpreting messy human input and choosing intent), while the <code>WfSpec</code> does what deterministic software is good at (validation, side effects, retries, and escalation).</p>\n<p>In our ticket-triage example, this separation gives you a safer architecture. The agent never gets direct authority to perform privileged write actions. Instead, the <code>WfSpec</code> validates identity and order ownership, then executes the approved path with a full audit trail.</p>\n<p>Stay tuned for the next post about using agents to verify other agents!</p>",
            "url": "https://littlehorse.io/blog/decision-workers",
            "title": "Agentic Patterns II: Decision Workers",
            "summary": "How the Decision Workers pattern separates AI judgment from deterministic workflow orchestration.",
            "date_modified": "2026-03-07T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Agentic Patterns",
                "AI, LLM's, and Agents",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/agents-need-workflow",
            "content_html": "<p>Just how much decision power should you give your agents in a business process? On one extreme, AI hype-artists claim that agents should stitch together all API's and \"figure out your business process for you\" without any orchestrator. On the other extreme, detractors claim that agents should not be allowed to make decisions without human oversight. In reality, by giving agents proper guardrails you can maximize user experience intelligence while minimizing mistakes (and even save tokens while you're at it!).</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the first part in a four-part blog series about building agentic workflows with LittleHorse. This blog series will help you automate internal and customer-facing business processes with guardrails and security.</p><ol>\n<li class=\"\"><strong>[This Post]</strong> Safe Decision-Making</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/decision-workers\">Decision Workers</a></li>\n<li class=\"\">[Coming Soon] Verified Agents</li>\n<li class=\"\">[Coming Soon] Workflow Dispatch Agents</li>\n</ol></div></div>\n<p><a class=\"\" href=\"https://littlehorse.io/blog/agents-need-microservices\">Last week's blog</a> focused on the software development lifecycle: LittleHorse provides a governance harness for AI-written microservices to allow engineering teams to tackle larger problems with more success. In contrast, this new blog series takes a different angle: how should you architect <em>end-user systems</em> that involve agents at runtime (not development timme)? How can we build systems that maximize intelligence while reducing surface area for mistakes made by agents and minimizing token costs?</p>\n<div class=\"theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z\"></path></svg></span>NOTE</div><div class=\"admonitionContent_BuS1\"><p>While good and useful software can be produced by LLM's, I don't believe in wasting people's time by asking them to read AI slop. This blog was written entirely without the aid of LLM's.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agents-probably-intelligent\">Agents: <em>Probably</em> Intelligent<a href=\"https://littlehorse.io/blog/agents-need-workflow#agents-probably-intelligent\" class=\"hash-link\" aria-label=\"Direct link to agents-probably-intelligent\" title=\"Direct link to agents-probably-intelligent\" translate=\"no\">​</a></h2>\n<p>Software systems exist to serve people, free us from repetitive toil, and minimize human error. I do not enjoy writing simple CRUD endpoints on a REST API, nor do I enjoy pulling pieces of info out of an unstructured email and copying them into our CRM. Before the invention of LLM's, there was little hope of building systems which could automate these processes, but now we can!</p>\n<p>We've built quite a few agentic workflows at LittleHorse, and today I wanted to share some thoughts about how we have maximized the intelligence of these systems while minimizing error.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>I'm a big believer that automation will result in more jobs long term. Typewriters and automated elevators put an end to the jobs of scribes and elevator operators, but today we have far fewer unemployed people than we did before those two inventions.</p><p>The same will happen for copy-n-paste monkeys.</p></div></div>\n<p>This blog series will start by examining differences between agents and classical software, then we'll analyze which use-cases are best for which pattern. Finally, we'll take a look at how to marry agents together with classical software to safely handle the most complex use-cases.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agents-are-probabilistic-and-flexible\">Agents Are Probabilistic and Flexible<a href=\"https://littlehorse.io/blog/agents-need-workflow#agents-are-probabilistic-and-flexible\" class=\"hash-link\" aria-label=\"Direct link to Agents Are Probabilistic and Flexible\" title=\"Direct link to Agents Are Probabilistic and Flexible\" translate=\"no\">​</a></h3>\n<p>Agents are probabilistic, whereas classical software is deterministic. At the core of an agentic application is a Large Language Model, which is simply an autoregressive neural network trained to predict the next token (roughly, token == word).</p>\n<p><em>When agents do things, there is no guarantee that they will get the right answer. It's the same as asking a human to do something: sometimes they do the right thing, sometimes they make mistakes.</em></p>\n<p>In contrast, the software systems we've relied on for the past 40 years always produce the same results when run with the same input. When they make a mistake, the mistakes are predictable and can be fixed (we call them \"bugs\" <!-- -->😉<!-- -->).</p>\n<p>Imperative classical software produces correct answers for predictable inputs. However, classical software systems struggle to handle dynamic inputs. For example, it's really hard to write a (non-AI) program which reads an email and decides whether to issue a refund, re-send a package, escalate to customer support, or simply respond with order information.</p>\n<p>However, that is now possible today with agentic applications. LLM's excel at extracting structured data from natural prose and making decisions based on that data.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agents-are-not-orchestrators\">Agents Are Not Orchestrators<a href=\"https://littlehorse.io/blog/agents-need-workflow#agents-are-not-orchestrators\" class=\"hash-link\" aria-label=\"Direct link to Agents Are Not Orchestrators\" title=\"Direct link to Agents Are Not Orchestrators\" translate=\"no\">​</a></h3>\n<p>Due to their probabilistic flexibility, agents are <em>not</em> suitable to orchestrate business processes where you already know what steps must happen.</p>\n<p>The process for transferring money from one account to another at a bank is well-known and unchanging. Sure, an agent could probably figure out calling the tools in the right order 99.9% of the time, but the 0.1% of the time when it forgets to check for balances or to handle import taxes are devastating.</p>\n<p>Additionally, agents are typically just long-running software programs which are exceptionally susceptible to crashes and failures (due to their long-running nature and the amount of network calls they make). If the agent crashes during the middle of an important process, you're left with the question of how to identify and recover stuck processes.</p>\n<p>This multi-step orchestration where you <em>absolutely cannot</em> tolerate failure is what classic software is for. As I've <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices\">written before</a>, it's a very difficult problem but is <a class=\"\" href=\"https://littlehorse.io/blog/microservices-and-workflow\">much simpler</a> when you use an orchestrator like LittleHorse. In fact, whether you write the logic yourself or use agents to write your workflows and verify the result, LittleHorse <a class=\"\" href=\"https://littlehorse.io/blog/agents-need-microservices\">makes it easier</a> to achieve and verify correctness.</p>\n<p>As an added benefit, it is far cheaper to decide your critical business process once than to pay Anthropic or OpenAI for tokens to make the same decision over and over again. Trust me, those bills can add up real quick.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"unifying-agents-and-workflows\">Unifying Agents and Workflows<a href=\"https://littlehorse.io/blog/agents-need-workflow#unifying-agents-and-workflows\" class=\"hash-link\" aria-label=\"Direct link to Unifying Agents and Workflows\" title=\"Direct link to Unifying Agents and Workflows\" translate=\"no\">​</a></h2>\n<p>Agents and workflows are not substitutes but rather complements: LittleHorse provides a harness to integrate and govern agents safely. Many of our customers and open-source users have used LittleHorse with agents in creative ways; I plan to write about three of the most prominent ones in this blog series:</p>\n<ul>\n<li class=\"\"><strong>Decision Workers</strong>: a LittleHorse Task Worker uses an AI Agent to decide which path of a <code>WfSpec</code> to take, reserving decisions for AI and actions for the workflow orchestrator.</li>\n<li class=\"\"><strong>Verified Agents</strong>: LittleHorse asks two separate agents with different models to make a decision, and escalates to human oversight in the case of disagreement.</li>\n<li class=\"\"><strong>Agentic Workflow Dispatcher</strong>: users expose workflows as tools to an agent who triages incoming requests and delegates work to LittleHorse.</li>\n</ul>\n<p>In all three cases, Agents provide intelligence while LittleHorse shepherds processes to completion across multiple steps. The risk of \"rogue agents\" is mitigated because the agent itself doesn't need to be able to take much action: the agent simply decides what action to take, and LittleHorse can then take the action with rigid guardrails. Finally, these patterns allow our users to limit the size of the task given to the agents to be as small as possible.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"wrapping-up\">Wrapping Up<a href=\"https://littlehorse.io/blog/agents-need-workflow#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\" translate=\"no\">​</a></h2>\n<p>This is an exciting time for builders everywhere. At small startups and large enterprises alike, agents have given builders the tools to builid much more powerful apps than ever before.</p>\n<p>LittleHorse already powers many agentic applications by providing a harness for these distributed processes. However, I'm even more excited about our upcoming Agent Builder products in our Saddle Command Center which will make it even safer and easier to build agents. Stay tuned!</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>[If This Blog Were Written By an LLM]</div><div class=\"admonitionContent_BuS1\"><p>Agents are not substitues for workflow <!-- -->🤷‍♂️<!-- -->. They're complements <!-- -->🔥<!-- -->. It's a paradigm shift, you're absolutely right! <!-- -->🚀<!-- --> <!-- -->💪</p></div></div>",
            "url": "https://littlehorse.io/blog/agents-need-workflow",
            "title": "Agentic Patterns I: Safe Decision-Making",
            "summary": "A practical framework for giving AI agents decision power while keeping workflows safe, auditable, and cost-efficient.",
            "date_modified": "2026-03-02T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Agentic Patterns",
                "AI, LLM's, and Agents",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/agents-need-microservices",
            "content_html": "<p>As I <a class=\"\" href=\"https://littlehorse.io/blog/business-as-code\">wrote last month</a>, LittleHorse helps ensure alignment between your business goals and your software, acting as a Command Center for Business-as-Code. AI Agents are starting to unlock the production of vastly greater amounts of code than ever before, making this alignment and governance even more crucial.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"AI-Generated and Human-Generated Code\" src=\"https://littlehorse.io/assets/images/ai-writing-code-6209fc6df9ec5023543338d0ca47a0f0.png\" width=\"799\" height=\"534\" class=\"img_ev3q\"></p>\n<div class=\"theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z\"></path></svg></span>NOTE</div><div class=\"admonitionContent_BuS1\"><p>While good and useful software can be produced by LLM's, I don't believe in wasting people's time by asking them to read AI slop. This blog was written entirely without the aid of LLM's.</p></div></div>\n<p>In December 2021, I set out with my father to build the best harness for microservices that the world had ever seen. Agentic AI, and especially vibe-coding, was a monkey wrench that none of us could have predicted in 2021. The question every architect must face now is, \"how will my architecture change because of agentic development?\" It turns out that we're barreling straight for a world that will be saturated with microservices in need of a <em>Command Center.</em></p>\n<p>After much study (at the terminal, in the field, reading papers), I believe that:</p>\n<ol>\n<li class=\"\">AI-assisted coding will result in far more software being written: things that used to not make sense to automate are suddenly in-play.</li>\n<li class=\"\">This explosion of additional automation will naturally produce many small pieces of independently-deployable software which accomplish small tasks: <em>microservices</em>.</li>\n<li class=\"\">The primary focus of software architects and engineers will shift to harnessing microservices and understanding business requirements. We'll have to understand, verify, and maintain much larger systems than before.</li>\n</ol>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>A microservice is a small piece of software that is developed and deployed in-house to provide a small set of functionality. Microservices allow different pieces of software to be <a class=\"\" href=\"https://littlehorse.io/blog/promise-of-microservices\">deployed independently</a>, but they come at the cost of <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices\">distributed complexity</a>.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"increasing-automation\">Increasing Automation<a href=\"https://littlehorse.io/blog/agents-need-microservices#increasing-automation\" class=\"hash-link\" aria-label=\"Direct link to Increasing Automation\" title=\"Direct link to Increasing Automation\" translate=\"no\">​</a></h2>\n<p>In economics, <a href=\"https://en.wikipedia.org/wiki/Jevons_paradox\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Jevon's Paradox</a> posits that, when a market becomes able to use a resource more efficiently, the market ends up using more of that resource. This is because greater efficiency makes certain transactions, which were previously unprofitable, profitable.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"hobbyist-projects\">Hobbyist Projects<a href=\"https://littlehorse.io/blog/agents-need-microservices#hobbyist-projects\" class=\"hash-link\" aria-label=\"Direct link to Hobbyist Projects\" title=\"Direct link to Hobbyist Projects\" translate=\"no\">​</a></h3>\n<p>In the context of agentic coding and software, we will see vastly more code being produced by even more engineers. Commit data from GitHub corroborates this increase in total software written, and we've even seen an uptick in software job postings over the last three months since Opus 4.5 (and now 4.6) was released. (For you Codex fans out there, I'm not trying to argue which model is better. Just don't use GPT 3.5 please.)</p>\n<p>The first consequence of the cost of writing code* reaching zero is summarized by Jevon's Paradox: many things which were too costly to automate are now in-play. On the \"hobbyist\" side of things, I've seen business analysts vibe-coding data portals and dashboards, non-technical founders take over the maintenance of their website, and a litany of vibe-coded personal productivity apps (just open Twitter and you'll see dozens).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"enterprise-automation\">Enterprise Automation<a href=\"https://littlehorse.io/blog/agents-need-microservices#enterprise-automation\" class=\"hash-link\" aria-label=\"Direct link to Enterprise Automation\" title=\"Direct link to Enterprise Automation\" translate=\"no\">​</a></h3>\n<p>However, the productivity gains from agentic coding are not limited to just vibe-coded toys. Many enterprise projects which were previously too ambitious are now in play. In the field, we've seen:</p>\n<ul>\n<li class=\"\">A bank has achieved a 10x speedup in migration from COBOL to Java (yay Java!).</li>\n<li class=\"\">A logistics company built an in-house fleet management system using LittleHorse, saving money by canceling several SaaS and iPaaS subscriptions, and enjoying a more custom-built solution.</li>\n<li class=\"\">HR and Accounting firms used LittleHorse and agentic coding to build custom integrations between back-office SaaS apps.</li>\n</ul>\n<p>Without microservices and AI, none of these wins would have been possible. This trend will only accelerate.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>Writing code and building digital solutions to business problems are two distinct problems.</p></div></div>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"LittleHorse Architecture\" src=\"https://littlehorse.io/assets/images/lh-at-the-center-4f03d717b4102bd3e44b2ca7a967762f.svg\" width=\"1456\" height=\"1160\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"harnesssing-agent-driven-code\">Harnesssing Agent-Driven Code<a href=\"https://littlehorse.io/blog/agents-need-microservices#harnesssing-agent-driven-code\" class=\"hash-link\" aria-label=\"Direct link to Harnesssing Agent-Driven Code\" title=\"Direct link to Harnesssing Agent-Driven Code\" translate=\"no\">​</a></h2>\n<p>In a world where Claude writes vast quantities of code at a rate far faster than we can review it, how will we avoid the so-called \"AI Slopocalypse?\" We will create harnesses to give our enthusiastic robotic friends the best chances for success:</p>\n<ul>\n<li class=\"\">Break up your system into smaller components that agents can easily reason about, work on independently, and deploy independently (<em>cough</em> microservices <em>cough</em>).</li>\n<li class=\"\">Use a harness that allows you to easily compose the code produced by your agents and integrate with the legacy stack.</li>\n<li class=\"\">Most crucially, be able to audit and govern the Frankensystem that your agents build for you with the Saddle Command Center for microservices.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agents-need-microservices\">Agents Need Microservices<a href=\"https://littlehorse.io/blog/agents-need-microservices#agents-need-microservices\" class=\"hash-link\" aria-label=\"Direct link to Agents Need Microservices\" title=\"Direct link to Agents Need Microservices\" translate=\"no\">​</a></h3>\n<p>Even though LLM context windows continue to grow, agents—just like the smartest humans—have a higher chance of success when given a smaller problem to work on. The chances of success skyrocket when you give the agent the ability to verify that it arrived at the correct answer in a loop. I've noticed that our good friend Claude can be quite persistent when you give him Opus 4.6 and a way to check his answer.</p>\n<p>Also, the speed at which agents can deliver software scales linearly with the number of tasks that can be worked on in parallel. By using microservices, a single large problem (\"write a custom ERP system for me\") can be broken into manageable and parallelizable tasks, enabling great speedup.</p>\n<p>Lastly, breaking problems down into smaller chunks such as microservices, or better yet Task Workers in LittleHorse, makes it easier to define verification steps for each task given to agents.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"microservices-need-a-harness\">Microservices Need a Harness<a href=\"https://littlehorse.io/blog/agents-need-microservices#microservices-need-a-harness\" class=\"hash-link\" aria-label=\"Direct link to Microservices Need a Harness\" title=\"Direct link to Microservices Need a Harness\" translate=\"no\">​</a></h3>\n<p>As I wrote <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices\">two years ago</a>, microservices can quickly devolve into an unsupervised and chaotic mess. While it's easier to reason about a single microservice than about an entire monolith, teams are still left with the struggle of piecing the microservices together into a cohesive system.</p>\n<p>LittleHorse <a class=\"\" href=\"https://littlehorse.io/blog/microservices-and-workflow\">solves this problem</a> by providing a Command Center to compose, connect, and govern your microservice flows. It gives you:</p>\n<ul>\n<li class=\"\">A catalog of each task or microservice, including API contracts (inputs and outputs).</li>\n<li class=\"\">Pre-built durability primitives, such as retries and failure handlers.</li>\n<li class=\"\">A safe, type-checked, and auditable way to compose multiple microservices into working flows.</li>\n</ul>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>For more info about LittleHorse and microservices, check out my <a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">Integration Patterns series</a>.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"auditing--compliance\">Auditing &amp; Compliance<a href=\"https://littlehorse.io/blog/agents-need-microservices#auditing--compliance\" class=\"hash-link\" aria-label=\"Direct link to Auditing &amp; Compliance\" title=\"Direct link to Auditing &amp; Compliance\" translate=\"no\">​</a></h3>\n<p>We'll soon see agents producing code at a rate that far surpasses human review capacity. When this happens, how will we understand our system? More importantly, how will we pass security audits and prevent disastrous business process bugs? Some say \"just ask Claude to write documentation.\" However, docs can drift, and LLM's can still make mistakes.</p>\n<p>Using a microservice harness like LittleHorse provides a really elegant solution: <strong>the dashboard allows you to deterministically visualize the end-to-end flow of your system simply by projecting the code written by your agents.</strong></p>\n<p>Even better, every interaction is logged, traced, and indexed in LittleHorse so that you can easily chase down bugs and security incidents, and allow your system to self-heal. Claude is already quite capable at debugging LittleHorse workflows using <code>lhctl</code> and this capability will only get better.</p>\n<p>While it might be hard to understand a production-ready order-processing microservices architecture, it's simple to reason about your vibe-coded business logic when it's displayed on the LittleHorse dashhboard as follows:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"LittleHorse Dashboard\" src=\"https://littlehorse.io/assets/images/2026-01-16-wfspec-a785811d6d2ac4fbfe2cad2ece96cb00.png\" width=\"1843\" height=\"551\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"conclusion\">Conclusion<a href=\"https://littlehorse.io/blog/agents-need-microservices#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>2026 is quite an amazing time to be alive. I would be lying if I told you that I had vibe-coding and AI Agents in mind when I founded LittleHorse in 2021; it just so happens that a microservice harness is even more crucial in 2026 than it was four years ago.</p>\n<p>This post has been very forward-looking, but as we near the release of the Saddle Console and version <code>1.0</code> of the <a href=\"https://github.com/littlehorse-enterprises/littlehorse\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">LittleHorse Server</a>, I'd like to take a minute to look back. Working on LittleHorse has been a blessing in so many ways, but nothing has been better than getting to know my team members, advisors, and other folks I've met along the way.</p>\n<p>I'm deeply proud of my team. They've taught me so much about life, leadership, software, project management, business, and so many other things, and many of them have become my close friends. As we've had our first few big customer success stories with LittleHorse, I'm filled with pride for what we've built, and I'm excited to see what we will do this upcoming year.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"to-the-worried-engineer\">To the Worried Engineer<a href=\"https://littlehorse.io/blog/agents-need-microservices#to-the-worried-engineer\" class=\"hash-link\" aria-label=\"Direct link to To the Worried Engineer\" title=\"Direct link to To the Worried Engineer\" translate=\"no\">​</a></h3>\n<p>As with every seismic shift, there will be winners and losers. The winners of the AI era will be people who can think at a high level of abstraction with <em>agency</em> (pun very much intended). Unfortunately, though, folks who made their career off of solving small, well-written JIRA tickets will need to adapt to working at a much higher level of abstraction. They'll need to begin engaging business stakeholders directly, they'll need to understand the business value of what they're building, and they'll need to become accustomed to managing much larger systems.</p>\n<p>In short, I think the era of making 6 figures as a \"CRUD Monkey\" is over.</p>\n<p>For those of us who can adapt to the new world—by thinking backwards from business problems while still understanding fundamental architecture, by letting go of the need to micromanage syntax, by inventing better solutions rather than copy-n-pasting from StackOverflow—our window of opportunity is just opening. We now have an army of little personal assistants that can do the un-differientiated stuff for us. Now, software engineers can finally be true \"engineers.\" We are blessed to live in these times.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"further-reading\">Further Reading<a href=\"https://littlehorse.io/blog/agents-need-microservices#further-reading\" class=\"hash-link\" aria-label=\"Direct link to Further Reading\" title=\"Direct link to Further Reading\" translate=\"no\">​</a></h3>\n<p>Three papers in particular have been quite formative to writing this essay:</p>\n<ol>\n<li class=\"\">Anthropic's Claude Code succeeded in <a href=\"https://www.anthropic.com/engineering/building-c-compiler\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">writing a compiler in 2 weeks</a> without human intervention.</li>\n<li class=\"\">OpenAI's Codex Team <a href=\"https://openai.com/index/harness-engineering/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">blogged about <em>Harness Engineering</em></a>.</li>\n<li class=\"\">Chris Lattner, the creator of LLVM, <a href=\"https://www.modular.com/blog/the-claude-c-compiler-what-it-reveals-about-the-future-of-software\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">wrote about</a> Anthropic's C Compiler experiment.</li>\n</ol>",
            "url": "https://littlehorse.io/blog/agents-need-microservices",
            "title": "Orchestrating Agent-Built Code",
            "summary": "Why AI-generated software increases the need for governed microservice composition and durable orchestration.",
            "date_modified": "2026-02-20T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Business-as-Code",
                "AI, LLM's, and Agents",
                "Microservices"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/business-as-code",
            "content_html": "<p>The Infrastructure Software world was revolutionized by <em>Infrastructure-as-Code</em>, which provides observability, governance, and agility to infra teams. Analogous problems in application development are soon to be solved by a new paradigm: <em>Business-as-Code</em>.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the first part in a three-part series about what makes LittleHorse unique: we are the only platform that allows you write business-as-code for both fast and slow processes, unifying workflows and events.</p><ol>\n<li class=\"\"><strong>[This Post]</strong> Business-as-Code</li>\n<li class=\"\"><em>[Coming Soon]</em> Fast and Slow Processes</li>\n<li class=\"\"><em>[Coming Soon]</em> Workflows and Events</li>\n</ol></div></div>\n<p>Business-as-Code allows platform teams to enjoy:</p>\n<ul>\n<li class=\"\"><strong>Governance</strong> and audit trails of who made what change and when.</li>\n<li class=\"\"><strong>Agility</strong> to quickly deploy new infrastructure in compliance with central policies and without fear of breaking systems.</li>\n<li class=\"\"><strong>Observability</strong> and a single place to check what infrastructure and security policies are deployed</li>\n</ul>\n<p>While Infrastructure-as-Code has successfully brought governance, predictability, and agility to infrastructure and security, the development of business processes running on distributed applications remains unsolved and is ripe for a new paradigm: <em>Business-as-Code.</em></p>\n<p>Just as infrastructure teams once struggled with manual, error-prone deployments and inconsistent environments before IaC, application development teams today face analogous challenges when composing business processes across distributed systems. The same chaos that IaC solved for infrastructure—scattered configurations, lack of visibility, and brittle manual processes—now plagues how we build and maintain business logic in microservice architectures.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-cost-of-complexity\">The Cost of Complexity<a href=\"https://littlehorse.io/blog/business-as-code#the-cost-of-complexity\" class=\"hash-link\" aria-label=\"Direct link to The Cost of Complexity\" title=\"Direct link to The Cost of Complexity\" translate=\"no\">​</a></h2>\n<p>As I <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices\">previously wrote</a>, software engineers at the modern enterprise must build applications to support business processes in a highly distributed technical environment. This makes it difficult to deliver stable, working applications and causes significant technical complexity, resulting in a divide between the code engineering teams write versus what the business needs.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"distributed-apps-are-hard\">Distributed Apps are Hard<a href=\"https://littlehorse.io/blog/business-as-code#distributed-apps-are-hard\" class=\"hash-link\" aria-label=\"Direct link to Distributed Apps are Hard\" title=\"Direct link to Distributed Apps are Hard\" translate=\"no\">​</a></h3>\n<p>In order to make distributed processes reliable, engineers need to spend time on non-differentiated yet frustrating tasks such as:</p>\n<ul>\n<li class=\"\">Instrumenting every hop between systems with tracing for debugging and auditing purposes.</li>\n<li class=\"\">Adding <a class=\"\" href=\"https://littlehorse.io/blog/retries-and-dlq\">retries and dead-letter queues</a> for every link between systems.</li>\n<li class=\"\">Waiting for asynchronous <a class=\"\" href=\"https://littlehorse.io/blog/callbacks\">callbacks</a> from third-party systems.</li>\n<li class=\"\">Using patterns such as <a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">SAGA</a> and <a class=\"\" href=\"https://littlehorse.io/blog/transactional-outbox\">Transactional Outbox</a> to ensure data consistency.</li>\n</ul>\n<p>These integration challenges burn through IT budget; first, by forcing CTO's to buy expensive observability and APM platforms, and secondly by forcing engineers to spend time on reliability rather than new features. This unfortunate reality often stands in the way of true digital transformation.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"tech-and-business-alignment\">Tech and Business Alignment<a href=\"https://littlehorse.io/blog/business-as-code#tech-and-business-alignment\" class=\"hash-link\" aria-label=\"Direct link to Tech and Business Alignment\" title=\"Direct link to Tech and Business Alignment\" translate=\"no\">​</a></h3>\n<p>Beyond the technical challenges posed by building applications in distributed environments—and perhaps because of the distributed nature—, fragmented software architecture leads to a gradual disconnect between what your business needs and what your software does.</p>\n<p>In a classic microservice architecture, different teams are in charge of certain microservice <em>bounded contexts,</em> such as user management, order processing, inventory, or loyalty. Many organizations that we have worked with struggle to understand the interactions between these services. For example, one customer says:</p>\n<blockquote>\n<p>We have many different domains, and each team puts out an API that is called by other teams, but we have no way to understand the dependencies between all of those API's. And no one in the company understands our business processes end-to-end.</p>\n</blockquote>\n<p>Besides simply technical challenges, this fragmentation increases the likelihood that software is developed which doesn't actually drive business value because of a lack of clear understanding between product and engineering. This \"spaghetti architecture\" also prevents opportunities for code reuse and standardization.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"business-as-code-with-littlehorse\">Business-as-Code with LittleHorse<a href=\"https://littlehorse.io/blog/business-as-code#business-as-code-with-littlehorse\" class=\"hash-link\" aria-label=\"Direct link to Business-as-Code with LittleHorse\" title=\"Direct link to Business-as-Code with LittleHorse\" translate=\"no\">​</a></h2>\n<p>Just as Infrastructure-as-Code tools such as Terraform de-fragment infrastructure and make systems more reliable, Business-as-Code delivered through the LittleHorse Kernel allows engineers to have a single source of truth for how their microservices interact.</p>\n<p>The central concept of the LittleHorse Kernel is a <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/workflows\">Workflow Specification</a>, known in code as a <code>WfSpec</code>. A <code>WfSpec</code> is a <em>computational graph</em> defining which computational steps are to be executed, and when.</p>\n<p>Here's what a complete order processing workflow looks like in Business-as-Code. This single WfSpec handles the entire business process, from price calculation to credit checks to shipping and it's all in one place, readable by any Java developer. The following code snippet is from <a href=\"https://github.com/littlehorse-enterprises/littlehorse-demo\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">one of our examples</a>, showing the definition of a <code>WfSpec</code> that runs the process of handling an order:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@LHWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">value </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"process-order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> defaultTaskRetries </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"3\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">wfLogic</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WorkflowThread</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Declare a bunch of variables</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        email </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"email\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">asPublic</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">required</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">searchable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        item </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"item\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">asPublic</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">required</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        orderPrice </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareDouble</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"order-price\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">asPublic</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        status </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"order-status\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">searchable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        creditScore </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareDouble</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"credit-score\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"PROCESSING\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Calculate Price</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        orderPrice</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"fetch-price\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> item</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// If too big, bring in human approval</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doIf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">orderPrice</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">isGreaterThan</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10_000.0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">this</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">::</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">creditCheck</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"SHIPPING\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"ship-item\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> item</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">waitForEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"item-arrived\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">registeredAs</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Boolean</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">class</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"DONE\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"send-email\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Your Order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Your order was completed\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// You can use lambdas or you can use function pointers to fill out if bodies.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">private</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">creditCheck</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WorkflowThread</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        creditScore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"fetch-credit-score\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doIf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">creditScore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">isLessThan</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">500.0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> ifBody </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">UserTaskOutput</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> ifBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assignUserTask</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"verify-credit-check\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">null</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"billing-admins\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            ifBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doIf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">ifBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">condition</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Comparator</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">EQUALS</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">jsonPath</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"$.isApproved\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> subIfBody </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"REJECTED\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                subIfBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"send-email\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Your Order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Your order was rejected due to insufficient credit\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                subIfBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fail</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"insufficient-credit\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"User does not have sufficient credit\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>This is the entire business process. No hunting through Slack channels to figure out which service calls which. No wondering if the retry logic is in the queue config or the service code. It's all right here, and it compiles into a visual graph that product managers can actually understand.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"a-wfspec-is-code\">A <code>WfSpec</code> is Code<a href=\"https://littlehorse.io/blog/business-as-code#a-wfspec-is-code\" class=\"hash-link\" aria-label=\"Direct link to a-wfspec-is-code\" title=\"Direct link to a-wfspec-is-code\" translate=\"no\">​</a></h3>\n<p>First, you'll notice that the <code>WfSpec</code> is written in Java (as of this publishing, we also have clients in Python, Go, and C# and are evaluating demand for more languages). Although the code uses a DSL provided by the <code>WorkflowThread</code> object, it is compiled and run like any other Java application, and the DSL is readable by any Java programmer.</p>\n<p>Secondly, if you look at the DSL you'll see all of the features of a normal programming language. Variables, conditional branching, function calls (<code>TaskRun</code> execution via <code>wf.execute</code>), and more. This DSL code gets compiled down to a metadata object (a <code>WfSpec</code> protobuf, which you can see via <code>lhctl get wfSpec &lt;name&gt;</code>). The actual <code>WfSpec</code> is a computational graph containing <code>Node</code>s and <code>Edge</code>s between the <code>Node</code>s. Each <code>Node</code> is a computational step to be scheduled by the LittleHorse Server; the <code>Edge</code>s determine the control flow. This is much like the abstract syntax tree in normal programming languages.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"a-wfspec-is-business\">A <code>WfSpec</code> is Business<a href=\"https://littlehorse.io/blog/business-as-code#a-wfspec-is-business\" class=\"hash-link\" aria-label=\"Direct link to a-wfspec-is-business\" title=\"Direct link to a-wfspec-is-business\" translate=\"no\">​</a></h3>\n<p>Although the <code>WfSpec</code> is code (which comes with a great many set of benefits, such as testing, version control, etc), it is a much higher layer of abstraction than vanilla Java code. The <code>WfSpec</code> shown above spans the entire business process of handling orders. It deals with many different Bounded Contexts and microservices:</p>\n<ul>\n<li class=\"\">Shipping (via the <code>ship-item</code> task).</li>\n<li class=\"\">Notifications (via the <code>send-email</code> task).</li>\n<li class=\"\">External systems (via the <code>fetch-credit-score</code> task).</li>\n<li class=\"\">Inventory (via the <code>fetch-price</code> task).</li>\n<li class=\"\">Internal company admins (via the <code>verify-credit-check</code> <code>UserTaskRun</code>).</li>\n</ul>\n<p>The <code>WfSpec</code> code <em>compiles into</em> a business process, which can be visualized on the LittleHorse Dashboard:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"A visualization of the above WfSpec\" src=\"https://littlehorse.io/assets/images/2026-01-16-wfspec-a785811d6d2ac4fbfe2cad2ece96cb00.png\" width=\"1843\" height=\"551\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"life-with-business-as-code\">Life with Business-as-Code<a href=\"https://littlehorse.io/blog/business-as-code#life-with-business-as-code\" class=\"hash-link\" aria-label=\"Direct link to Life with Business-as-Code\" title=\"Direct link to Life with Business-as-Code\" translate=\"no\">​</a></h2>\n<p>The open-source LittleHorse Kernel gives engineering teams the ability to write end-to-end business processes in code, while abstracting away the non-differentiated tasks such as error handling, retries, and transactional consistency. This yields several benefits to organizations:</p>\n<ol>\n<li class=\"\">Engineering teams no longer lose time and sprints on frustrating tasks related to reliability, debugging, and fault tolerance. This allows engineering teams to work on interesting projects that deliver business value.</li>\n<li class=\"\">The result of work done by engineering teams mirrors the business process more closely, ensuring less \"wasted\" work and higher alignment between product and engineering.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"get-started\">Get Started!<a href=\"https://littlehorse.io/blog/business-as-code#get-started\" class=\"hash-link\" aria-label=\"Direct link to Get Started!\" title=\"Direct link to Get Started!\" translate=\"no\">​</a></h3>\n<p>I am a developer at heart, which is why I wrote the LittleHorse Kernel and open-sourced it. We want software engineers to be able to use familiar and comfortable primitives to operate at a higher level of business value.</p>\n<p>To get started:</p>\n<ul>\n<li class=\"\">Check out our code <a href=\"https://github.com/littlehorse-enterprises\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on GitHub</a> and leave us a star!</li>\n<li class=\"\">Give our <a class=\"\" href=\"https://littlehorse.io/docs/getting-started\">Quickstarts</a> a try!</li>\n<li class=\"\">Join us <a href=\"https://launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on Slack</a> for free support.</li>\n</ul>",
            "url": "https://littlehorse.io/blog/business-as-code",
            "title": "Business-as-Code",
            "summary": "Why Business-as-Code gives engineering teams a governed, observable way to define business processes in code.",
            "date_modified": "2026-01-16T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Business-as-Code",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/a2a-protocol",
            "content_html": "<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-is-a2a\">What is A2A?<a href=\"https://littlehorse.io/blog/a2a-protocol#what-is-a2a\" class=\"hash-link\" aria-label=\"Direct link to What is A2A?\" title=\"Direct link to What is A2A?\" translate=\"no\">​</a></h3>\n<p>As the sophistication of AI systems grows, so has the ability to automate tasks. However, scaling multi-agent systems has been a major bottleneck for many engineers. Frameworks like LangGraph seek to solve this problem, but the framework is not very scalable and the api's don't always fit the needs of developers, can become outdated, and may introduce breaking changes...hence the need for custom agents, and modularity. A2A is an effort by Google to standardize inter-agent communication and allow developers to integrate the capabilities of various frameworks.</p>\n<p>The probabalistic origins of machine learning and AI models cause a significant amount of non-determinism in the outputs, on the contrary to traditional algorithms that provide a deterministic output (i.e. given the same input, it always produces the same output). Because of this, we can't always predict the output of a machine learning algorithm. So maybe it is obvious that in order to allow these systems to share information, the outputs must be wrapped in some metadata to provide some scaffolding for a message format, even though the messages themselves are always unique.</p>\n<p><em>A2A is a protocol to allow AI agents to communicate autonomously amongst themselves in a standardized way. It was introduced by Google in April 2025 and donated to the Linux Foundation for more widespread adoption.</em></p>\n<p><strong>Example Query:</strong> <em>What is an AI agent?</em></p>\n<p><strong>Answer 1:</strong></p>\n<p><em>An AI agent is a software system that can perceive its environment, make decisions, and take actions to achieve specific goals with some degree of autonomy. Unlike traditional software that simply follows pre-programmed instructions, AI agents can adapt their behavior based on what they observe and learn.</em></p>\n<p><strong>Answer 2:</strong></p>\n<p><em>An AI agent is a software system that can perceive its environment, make decisions, and take actions to achieve specific goals with some degree of autonomy. Think of it as a digital entity that can operate independently rather than just responding to direct commands.</em></p>\n<p>We can see from this example that for two identical queries, the output can vary but still be useful for describing an AI agent. As the input gets more complex, the more these outputs can diverge.</p>\n<p><strong>Note:</strong> See also MCP, a protocol introduced by Anthropic to provide a standard of communication between AI models and tools, data, prompts, and other resources. While MCP allows the AI \"agent\" external functionality like querying a database, searching the internet, or executing a workflow, A2A allows multiple specialized AI agents to communicate with each other and complete complex tasks by utilizing their variety of skills.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-agent\">The Agent<a href=\"https://littlehorse.io/blog/a2a-protocol#the-agent\" class=\"hash-link\" aria-label=\"Direct link to The Agent\" title=\"Direct link to The Agent\" translate=\"no\">​</a></h3>\n<p>So what is an Agent? While the A2A protocol seems like it has been standardized, the definition of \"agent\" certainly has not. Langchain and Microsoft define an <a href=\"https://langchain-ai.github.io/langgraph/agents/overview/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">agent</a> as \"an LLM + tools + a prompt\". Google has more broadly stated that an AI agent is a software system that uses AI (not just LLMs) to proactively pursue goals and complete tasks. This allows us to expand the scope of agents to include more specialized machine learning algorithms, although the implementation of Google's agent development kit (<a href=\"https://google.github.io/adk-docs/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">ADK</a>) indicates that they really fall into the first camp. For now, I will focus on the \"LLM + tools + prompt\" definition, but stay tuned for more exploration into nuanced algorithms in the future.</p>\n<p>Let's take a look at our first \"agent\"...</p>\n<p><strong>Prerequisites</strong>:</p>\n<ul>\n<li class=\"\">Pyton 3.11+</li>\n<li class=\"\"><a href=\"https://aistudio.google.com/apikey\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Gemini API Key</a></li>\n</ul>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> os</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> dotenv </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> load_dotenv</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">load_dotenv</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<p>If you would like to follow along, and don't want to copy and paste code, you can clone this github <a href=\"https://github.com/joleson15/a2a.git\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">repo</a> and interact with the jupyter notebook version of this blog!</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> google</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">adk</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">agents </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> Agent</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> google</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">adk</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">tools </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> google_search</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">my_first_agent </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> Agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    model</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"gemini-2.5-flash\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"My_first_agent\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"A simple agent that can call a google search\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    instruction</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"You are a helpful google search agent. Conduct a search when you determine it is necessary to do so.\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    tools</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\">google_search</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<p>As you can see, there is not much effort to declare your first agent. Google's ADK abstracts away a lot of the headache, it's details are out of scope for this work, but will be covered in a future blog post. Each of the supplied parameters are self-descriptive:</p>\n<ul>\n<li class=\"\">Model: the large language model used as the intelligence engine for your agent</li>\n<li class=\"\">Name: a descriptive name, can only contain letters, numbers and underscores</li>\n<li class=\"\">Description: a helpful description can go a long way, especially once multiple agents are involved in a complex process</li>\n<li class=\"\">Instruction: an optional system prompt that gets passed in as context to the LLM that contains any additional information or instructions</li>\n<li class=\"\">Tools: a list of tools an agent is capable of using. In this case we have only outfitted our agent with the ability to conduct a google search</li>\n</ul>\n<p>Now we have a primitive agent. So how do we handle messages?</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agent-executor\">Agent Executor<a href=\"https://littlehorse.io/blog/a2a-protocol#agent-executor\" class=\"hash-link\" aria-label=\"Direct link to Agent Executor\" title=\"Direct link to Agent Executor\" translate=\"no\">​</a></h3>\n<p>The Agent Executor interface is contained in the a2a library, and provides a wrapper for the Agent to handle requests. The interface contains two main methods: <code>execute()</code>, which handles the main execution logic of the Agent's runtime and <code>cancel()</code>, which can be called during the execution of a long-running task.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">AgentExecutor</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">ABC</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">\"\"\"Agent Executor interface.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"display:inline-block;color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">    Implementations of this interface contain the core logic of the agent,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">    executing tasks based on requests and publishing updates to an event queue.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">    \"\"\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token decorator annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@abstractmethod</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> context</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> RequestContext</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> event_queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> EventQueue</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token boolean\">None</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">\"\"\"Execute the agent's logic for a given request context.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"display:inline-block;color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        The agent should read necessary information from the `context` and</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        publish `Task` or `Message` events, or `TaskStatusUpdateEvent` /</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        `TaskArtifactUpdateEvent` to the `event_queue`. This method should</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        return once the agent's execution for this request is complete or</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        yields control (e.g., enters an input-required state).</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"display:inline-block;color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        Args:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">            context: The request context containing the message, task ID, etc.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">            event_queue: The queue to publish events to.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        \"\"\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token decorator annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@abstractmethod</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cancel</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> context</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> RequestContext</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> event_queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> EventQueue</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token boolean\">None</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">\"\"\"Request the agent to cancel an ongoing task.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"display:inline-block;color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        The agent should attempt to stop the task identified by the task_id</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        in the context and publish a `TaskStatusUpdateEvent` with state</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        `TaskState.canceled` to the `event_queue`.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"display:inline-block;color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        Args:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">            context: The request context containing the task ID to cancel.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">            event_queue: The queue to publish the cancellation status update to.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token triple-quoted-string string\" style=\"color:rgb(206, 145, 120)\">        \"\"\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span></code></pre></div></div>\n<p>For now, we will focus only on event execution and leave cancellation to a future blog. Google provides an implementation of <code>execute()</code> for a generic ADK agent in their <a href=\"https://github.com/a2aproject\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">A2A Samples</a> repo that I will borrow for this example.</p>\n<p>The <code>execute()</code> method concurrently processes requests by keeping track of them with an <code>EventQueue</code>. Requests are presented in the form of a <code>RequestContext</code>, which contains the content of the request, along with other metadata. That is enough info for now, we will get into the weeds in future work.</p>\n<p>A critical component to the successful operation of an Agent is the <code>session</code>. A session is a stateful container that allows agents to interact asynchronously. It manages the ongoing interaction between the agents and/or users, context such as memory and state, and a request/response loop to handle communication and execution of actions.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">agent_execution </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> AgentExecutor</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> RequestContext</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">events </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> EventQueue</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">tasks </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> TaskUpdater</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">utils </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> new_task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> new_agent_text_message</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">types </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> TaskState</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> TextPart</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> Part</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> google</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">adk</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">runners </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> Runner</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> google</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">adk</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">artifacts </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> InMemoryArtifactService</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> google</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">adk</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">sessions </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> InMemorySessionService</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> google</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">adk</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">memory </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> InMemoryMemoryService</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> google</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">genai </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> types</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">MyAgentExecutor</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">AgentExecutor</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">__init__</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> Agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> status_message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">str</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Executing task...\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> artifact_name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">str</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"response\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">agent </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> agent</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">status_message </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> status_message</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">artifact_name </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> artifact_name</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">runner </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> Runner</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            app_name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            agent</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            artifact_service</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">InMemoryArtifactService</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            session_service</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">InMemorySessionService</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            memory_service</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">InMemoryMemoryService</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> context</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> RequestContext</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> event_queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> EventQueue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token boolean\">None</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> context</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">get_user_input</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        task </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> context</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">current_task </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">or</span><span class=\"token plain\"> new_task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">context</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> event_queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">enqueue_event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        updater </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> TaskUpdater</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">event_queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">contextId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># Update status with custom message</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> updater</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">update_status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                TaskState</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">working</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                new_agent_text_message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">status_message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">contextId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># Process with ADK agent</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            session </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">runner</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">session_service</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">create_session</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                app_name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                user_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"a2a_user\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                state</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                session_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">contextId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            content </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> types</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Content</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                role</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                parts</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\">types</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">from_text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">text</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            response_text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> event </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">runner</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">run_async</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                user_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"a2a_user\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                session_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">session</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                new_message</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">content</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">is_final_response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">and</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">content </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">and</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">content</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">parts</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> part </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">content</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">parts</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">hasattr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">and</span><span class=\"token plain\"> part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                            response_text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+=</span><span class=\"token plain\"> part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'\\n'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">elif</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">hasattr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'function_call'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                            </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># Log or handle function calls if needed</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                            </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">pass</span><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># Function calls are handled internally by ADK</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># Add response as artifact with custom name</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> updater</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">add_artifact</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\">Part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">root</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">TextPart</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">text</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">response_text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">artifact_name</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> updater</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">complete</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">except</span><span class=\"token plain\"> Exception </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">as</span><span class=\"token plain\"> e</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> updater</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">update_status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                TaskState</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">failed</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                new_agent_text_message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">f\"Error: </span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string-interpolation interpolation\">e</span><span class=\"token string-interpolation interpolation conversion-option punctuation\" style=\"color:rgb(212, 212, 212)\">!s</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">contextId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> task</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                final</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token boolean\">True</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cancel</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">self</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> context</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> event_queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">pass</span><br></span></code></pre></div></div>\n<p>Now that we can run an agent, how do we (and more importantly other agents, since this is a blog about A2A after all...) know what its capabilities are?</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agent-card\">Agent Card<a href=\"https://littlehorse.io/blog/a2a-protocol#agent-card\" class=\"hash-link\" aria-label=\"Direct link to Agent Card\" title=\"Direct link to Agent Card\" translate=\"no\">​</a></h3>\n<p>The agent card is a public-facing JSON schema that exposes information and metadata to clients (users and other agents). It is like a user profile, but for AI agents. To define an agent card properly, you need:</p>\n<ul>\n<li class=\"\"><code>name</code></li>\n<li class=\"\"><code>description</code>: description of the agent, its skills, and other useful information</li>\n<li class=\"\"><code>version</code></li>\n<li class=\"\"><code>url</code>: the web endpoint where we can find our agent</li>\n<li class=\"\"><code>capabilities</code>: supported A2A features like streaming or push notifications</li>\n<li class=\"\"><code>skills</code>: A pillar of A2A, the skills discovered in the <code>AgentCard</code> come in a list of <code>AgentSkill</code> objects.</li>\n</ul>\n<p>For public agents, the A2A project reccomends that the agent card be discoverable at a well-known uri. The standard discovery path is: <code>https://{server-url}/.well-known/agent.json</code> This is the discovery method we will use for now, but for more information, see the <a href=\"https://a2aproject.github.io/A2A/latest/topics/agent-discovery/#the-role-of-the-agent-card\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">A2A discovery</a> page.</p>\n<p>Lets give our agent an <code>AgentCard</code>:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">types </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> AgentCard</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> AgentCapabilities</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">agent_card </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> AgentCard</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">my_first_agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">my_first_agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">description</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    url</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"http://localhost:9999/\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    version</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'1.0'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    capabilities</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">AgentCapabilities</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        streaming</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token boolean\">True</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    defaultInputModes</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text/plain\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    defaultOutputModes</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text/plain\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    skills</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"agent-skills\">Agent Skills<a href=\"https://littlehorse.io/blog/a2a-protocol#agent-skills\" class=\"hash-link\" aria-label=\"Direct link to Agent Skills\" title=\"Direct link to Agent Skills\" translate=\"no\">​</a></h3>\n<p>Agent skills describe specific capabilities the agent has, like searching the web, querying a database, executing an algorithmm...etc. Clients can find out what skills an agent has from the <code>AgentCard</code>. It's kind of like the agentic version of a resume. Skills have some attributes to define:</p>\n<ul>\n<li class=\"\"><code>id</code>: a unique id</li>\n<li class=\"\"><code>name</code></li>\n<li class=\"\"><code>description</code>: more detailed information about the skill's functionality</li>\n<li class=\"\"><code>tags</code>: keywords</li>\n<li class=\"\"><code>examples</code>: example usage of the skill</li>\n<li class=\"\"><code>inputModes</code> and <code>outputModes</code>: supported modes for input and output, like text or json</li>\n</ul>\n<p>Let's go back and define the Google search <code>AgentSkill</code> for our agent:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">types </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> AgentSkill</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">web_search_skill </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> AgentSkill</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'google_search'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Google Search'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Searches the web using the google_search tool'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'web search'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'google'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'search'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'look up'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">agent_card</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">skills</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">append</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">web_search_skill</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<p>Now our agent has advertized the ability to search the web on its agent card. We will take a deep dive into <code>AgentSkill</code>s, <code>AgentCapabilities</code>, and <code>AgentCard</code>s another time, but now lets zoom out a little bit. We have given lots of detail about our agent. Where it lives, what it can do, examples for how to use it... So how do we run it and start testing this stuff out?</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"starting-the-server\">Starting the Server<a href=\"https://littlehorse.io/blog/a2a-protocol#starting-the-server\" class=\"hash-link\" aria-label=\"Direct link to Starting the Server\" title=\"Direct link to Starting the Server\" translate=\"no\">​</a></h3>\n<p>A2A follows a client-server architecture, where the client--a user-facing application or agent--initiates a request to other agents acting as servers that handle those requests, similarly to a traditional web browser or API sending requests to a remote web server. They send structured metadata and information over HTTP using <a href=\"https://www.jsonrpc.org/specification\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">JSON-RPC 2.0</a> as the format.</p>\n<p>With all of the agent functionality defined thus far, let's deploy an <code>agent</code> as a <code>server</code>. The A2A library provides an out-of-the-box app using the <a href=\"https://www.starlette.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Starlette</a> framework to implement the server endpoints and route JSON-RPC requests. This is perfect for our use-case.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">request_handlers </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> DefaultRequestHandler</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">tasks </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> InMemoryTaskStore</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">apps </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> A2AStarletteApplication</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">create_server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    request_handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> DefaultRequestHandler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        agent_executor</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">MyAgentExecutor</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">my_first_agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        task_store</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">InMemoryTaskStore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> A2AStarletteApplication</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        agent_card</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">agent_card</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        http_handler</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">request_handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<p>The server application takes a <code>DefaultRequestHandler</code> as an argument, which is the engine for all A2A protocol methods, the <code>AgentExecutor</code>, the <code>TaskStore</code>, the <code>QueueManager</code>, and all other JSON-RPC related logic. Lets spin up the server.</p>\n<p>Since we are using jupyter, we need some fancy event handling so as to not have the server process cause the cell to run forever. We need to run it in a background process using a thread.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> uvicorn</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> asyncio</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> nest_asyncio</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> threading</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> time</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">nest_asyncio</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">apply</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">run_agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    loop </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> asyncio</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">new_event_loop</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    asyncio</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">set_event_loop</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">loop</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    server </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> create_server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    config </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> uvicorn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Config</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        app</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">build</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        host</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"127.0.0.1\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        port</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">9999</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        log_level</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"error\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    uvicorn_server </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> uvicorn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">config</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    loop</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">run_until_complete</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">uvicorn_server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">serve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">thread </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> threading</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Thread</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">target</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">run_agent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> daemon</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">thread</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">start</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">time</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">sleep</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">print</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Server is running at http://127.0.0.1:9999\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<p><strong>Quick note: for now, if we need to stop the server, we have to restart the jupyter kernel.</strong></p>\n<p>Great news! We now have our very first agent up and running! So now what? We want to be able to interact with our AI agent and benefit from all of the cool stuff it can do. As I mentioned, A2A communication occurs over HTTP, so lets send a simple http request to our agent. We can start by fetching the <code>AgentCard</code> to see what our agent can do.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"fetching-the-agentcard\">Fetching the <code>AgentCard</code><a href=\"https://littlehorse.io/blog/a2a-protocol#fetching-the-agentcard\" class=\"hash-link\" aria-label=\"Direct link to fetching-the-agentcard\" title=\"Direct link to fetching-the-agentcard\" translate=\"no\">​</a></h3>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> httpx</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> json</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">with</span><span class=\"token plain\"> httpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">AsyncClient</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">as</span><span class=\"token plain\"> http_client</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> http_client</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"http://localhost:9999/.well-known/agent.json\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    agent_card_json </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">print</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">dumps</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">agent_card_json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> indent</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">#json.dumps is completely optional but makes the output more readable</span><br></span></code></pre></div></div>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"capabilities\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token property\">\"streaming\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"defaultInputModes\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text/plain\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"defaultOutputModes\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text/plain\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"description\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"A simple agent that can call a google search\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"name\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"My_first_agent\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"protocolVersion\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"0.2.5\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"skills\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token property\">\"description\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Searches the web using the google_search tool\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token property\">\"id\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"google_search\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token property\">\"name\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Google Search\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token property\">\"tags\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"web search\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"google\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"search\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"look up\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"url\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"http://localhost:9999/\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"version\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"1.0\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>Amazing! Just as we defined it earlier, the <code>AgentCard</code> is showing all of the information about our agent. To interact with our A2A server properly, we need an A2A client.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">client </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> A2AClient</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">my_first_agent_card </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> AgentCard</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">**</span><span class=\"token plain\">agent_card_json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">a2a_client </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> A2AClient</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    httpx_client</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">http_client</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    agent_card</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">my_first_agent_card</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    url</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"http://localhost:9999/\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><br></span></code></pre></div></div>\n<p><code>agent_card</code> and <code>url</code> parameters are not both required, but one of them is neeeded to fetch the url of the agent's RPC endpoint. With our client, we can send RPC messages to the A2A server (our agent) by sending an HTTP POST request to the RPC endpoint (the URL). So how do we structure this message and what is it going to look like?</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"json-rpc\">JSON RPC<a href=\"https://littlehorse.io/blog/a2a-protocol#json-rpc\" class=\"hash-link\" aria-label=\"Direct link to JSON RPC\" title=\"Direct link to JSON RPC\" translate=\"no\">​</a></h3>\n<p>Without diving too deep into the weeds of JSON-RPC 2.0, we can analyze the layout of a simple A2A message, encapsulated in a <code>JSONRPCRequest</code> object:</p>\n<ul>\n<li class=\"\"><code>jsonrpc</code>: must be <code>2.0</code></li>\n<li class=\"\"><code>method</code>: <code>\"message/send\"</code>, <code>\"tasks/get\"</code> ...</li>\n<li class=\"\"><code>params</code>: typically an <code>object</code> containing the parameters used for invocation</li>\n<li class=\"\"><code>id</code>: unique identifier, not always required if the request does not expect a response.</li>\n</ul>\n<p>More info <a href=\"https://a2aproject.github.io/A2A/v0.2.5/specification/#611-json-rpc-structures\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>. We will use the <code>message/send</code> method, and pass in our first message:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">rpc_request </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'message'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'role'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'parts'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'kind'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'What are some trending topics in AI right now?'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'messageId'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> uuid</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">uuid4</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">hex</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>The Python SDK for A2A provides lots of useful helper methods that help properly structure our input. Here, we'll create a <code>SendMessageRequest</code> and pass in the <code>rpc_request</code> as the <code>params</code> to our <code>JSONRPCRequest</code> object using the <code>MessageSendParams</code> method:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">request </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> SendMessageRequest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">str</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">uuid</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">uuid4</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        params</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">MessageSendParams</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">**</span><span class=\"token plain\">rpc_request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"optional-run-the-below-cell-to-view-the-whole-rpc-request\">Optional: run the below cell to view the whole RPC Request<a href=\"https://littlehorse.io/blog/a2a-protocol#optional-run-the-below-cell-to-view-the-whole-rpc-request\" class=\"hash-link\" aria-label=\"Direct link to Optional: run the below cell to view the whole RPC Request\" title=\"Direct link to Optional: run the below cell to view the whole RPC Request\" translate=\"no\">​</a></h4>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> uuid</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">types </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> SendMessageRequest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> MessageSendParams</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">rpc_request </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'message'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'role'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'parts'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'kind'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'What are some trending topics in AI right now?'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'messageId'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> uuid</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">uuid4</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">hex</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">request </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> SendMessageRequest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">str</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">uuid</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">uuid4</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    params</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">MessageSendParams</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">**</span><span class=\"token plain\">rpc_request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">print</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">id='70c6ac85-0e2e-44d9-8183-a71ffaa846b9' jsonrpc='2.0' method='message/send' params=MessageSendParams(configuration=None, message=Message(contextId=None, extensions=None, kind='message', messageId='de7bfa7380ed4c909ad2ad0b22d888f3', metadata=None, parts=[Part(root=TextPart(kind='text', metadata=None, text='What are some trending topics in AI right now?'))], referenceTaskIds=None, role=&lt;Role.user: 'user'&gt;, taskId=None), metadata=None)</span><br></span></code></pre></div></div>\n<p>Tying everything together, we can send a properly formatted JSON RPC request to our server:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> uuid</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">types </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> SendMessageRequest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> MessageSendParams</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> a2a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">client </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> A2AClient</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">timeout_config </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> httpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Timeout</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            timeout</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">120.0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            connect</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10.0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            read</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">120.0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            write</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10.0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            pool</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">5.0</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">with</span><span class=\"token plain\"> httpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">AsyncClient</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">timeout</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">timeout_config</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">as</span><span class=\"token plain\"> http_client</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> http_client</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"http://localhost:9999/.well-known/agent.json\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    agent_card_json </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    my_first_agent_card </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> AgentCard</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">**</span><span class=\"token plain\">agent_card_json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    a2a_client </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> A2AClient</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        httpx_client</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">http_client</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        agent_card</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">my_first_agent_card</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    message </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"What are some trending topics in AI right now?\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    rpc_request </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'message'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'role'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'parts'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'kind'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'messageId'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> uuid</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">uuid4</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">hex</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    request </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> SendMessageRequest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">str</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">uuid</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">uuid4</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        params</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\">MessageSendParams</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">**</span><span class=\"token plain\">rpc_request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> a2a_client</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">send_message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<p>Just for fun, lets print out the response. We should expect to get back a response from our server in the form of a <a href=\"https://a2aproject.github.io/A2A/latest/specification/#6112-jsonrpcresponse-object\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">JSONRPCResponse</a> object.</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">print</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">root=SendMessageSuccessResponse(id='9d9dd71f-f5cf-4005-942a-5ecbdd474dbc', jsonrpc='2.0', result=Task(artifacts=[Artifact(artifactId='2c1511f7-200c-470b-8059-f9ca3819c348', description=None, extensions=None, metadata=None, name='response', parts=[Part(root=TextPart(kind='text', metadata=None, text='Several key themes and advancements are dominating the field of Artificial Intelligence (AI) in 2025, reflecting its rapid evolution and increasing integration across various sectors.\\n\\nSome of the most prominent trending topics in AI right now include:\\n\\n*   **Generative AI Evolution and Integration** Generative AI continues to be a major force, moving beyond basic content creation to more advanced and integrated applications. This includes the development of more sophisticated generative AI models, often referred to as Generative AI 2.0, that are becoming autonomous creators. There\\'s a strong focus on integrating generative AI into everyday applications, making it an \"obligatory copilot\" for skilled workers across various industries. Multimodal AI, which can process and generate content from various inputs like text, images, and video, is also seeing significant advancements.\\n*   **Agentic AI** A significant trend is the rise of \"Agentic AI,\" where AI systems can act autonomously to achieve specific goals, make decisions, and take actions independently without constant human oversight. These AI agents are envisioned as the \"apps of the AI era,\" handling tasks on users\\' behalf and transforming business processes. Examples include AIs that can surf the web, order groceries, predict sales, and manage inventory.\\n*   **AI in the Workplace and Productivity** AI\\'s adoption in the workplace is accelerating, with a focus on enhancing productivity, automating repetitive and mundane tasks, and transforming work dynamics. AI is increasingly used for tasks like sifting through emails, taking notes, generating content, and streamlining job processes. While concerns about job displacement exist, AI is also seen as augmenting human roles and creating new job opportunities.\\n*   **Specialized AI Applications** AI is being deeply embedded into specific industries, driving innovation and efficiency:\\n    *   **Healthcare:** AI is accelerating scientific research and improving healthcare outcomes, with applications in diagnosing patients and identifying health problems.\\n    *   **Finance:** AI is revolutionizing the finance landscape through autonomous finance, including customer service chatbots, automated forecasting, and AI-powered fraud detection.\\n    *   **Supply Chain:** AI is enhancing efficiency in supply chain management through optimized inventory and logistics, risk prediction, and sustainability drives.\\n    *   **Education:** AI is being explored for personalized learning experiences, although its implementation can be controversial.\\n*   **Ethical AI and Regulation** As AI becomes more powerful and pervasive, there is increased scrutiny on AI ethics, responsible AI, and the development of broader AI regulations. This includes concerns about privacy, data breaches, and ensuring fundamental rights are respected, as seen with initiatives like the European Union\\'s AI Act.\\n*   **Technical Advancements** Underlying these applications are continuous technical improvements. This includes more reasonable reasoning models, advancements in smaller language models (SLMs) that offer efficiency benefits, and the emerging field of \"Embodied AI,\" which aims to bring multimodal AI abilities into the physical world through robotics. There\\'s also a focus on improving the cost-efficiency of AI inference and exploring new model architectures beyond traditional transformers.\\n'))])], contextId='508efe3a-41ca-4e0b-9478-ea82dbce2ea6', history=[Message(contextId='508efe3a-41ca-4e0b-9478-ea82dbce2ea6', extensions=None, kind='message', messageId='7f67307cfede45438e5ed867c38bd9bb', metadata=None, parts=[Part(root=TextPart(kind='text', metadata=None, text='What are some trending topics in AI right now?'))], referenceTaskIds=None, role=&lt;Role.user: 'user'&gt;, taskId='fb816920-46e3-4bdf-8889-c7fdab377e7a'), Message(contextId='508efe3a-41ca-4e0b-9478-ea82dbce2ea6', extensions=None, kind='message', messageId='f2f05466-2943-4671-bd83-fe41eb2193d2', metadata=None, parts=[Part(root=TextPart(kind='text', metadata=None, text='Executing task...'))], referenceTaskIds=None, role=&lt;Role.agent: 'agent'&gt;, taskId='fb816920-46e3-4bdf-8889-c7fdab377e7a')], id='fb816920-46e3-4bdf-8889-c7fdab377e7a', kind='task', metadata=None, status=TaskStatus(message=None, state=&lt;TaskState.completed: 'completed'&gt;, timestamp='2025-07-17T00:37:42.277421+00:00')))</span><br></span></code></pre></div></div>\n<p>This is a bit tricky to parse through, since RPC calls come with a lot of attached metadata. Let's print just the json:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">response_dict </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">model_dump</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">mode</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> exclude_none</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">print</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">dumps</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">response_dict</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> indent</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"id\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"9d9dd71f-f5cf-4005-942a-5ecbdd474dbc\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"jsonrpc\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"2.0\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token property\">\"result\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token property\">\"artifacts\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"artifactId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"2c1511f7-200c-470b-8059-f9ca3819c348\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"name\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"response\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"parts\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token property\">\"kind\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token property\">\"text\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Several key themes and advancements are dominating the field of Artificial Intelligence (AI) in 2025, reflecting its rapid evolution and increasing integration across various sectors.\\n\\nSome of the most prominent trending topics in AI right now include:\\n\\n*   **Generative AI Evolution and Integration** Generative AI continues to be a major force, moving beyond basic content creation to more advanced and integrated applications. This includes the development of more sophisticated generative AI models, often referred to as Generative AI 2.0, that are becoming autonomous creators. There's a strong focus on integrating generative AI into everyday applications, making it an \\\"obligatory copilot\\\" for skilled workers across various industries. Multimodal AI, which can process and generate content from various inputs like text, images, and video, is also seeing significant advancements.\\n*   **Agentic AI** A significant trend is the rise of \\\"Agentic AI,\\\" where AI systems can act autonomously to achieve specific goals, make decisions, and take actions independently without constant human oversight. These AI agents are envisioned as the \\\"apps of the AI era,\\\" handling tasks on users' behalf and transforming business processes. Examples include AIs that can surf the web, order groceries, predict sales, and manage inventory.\\n*   **AI in the Workplace and Productivity** AI's adoption in the workplace is accelerating, with a focus on enhancing productivity, automating repetitive and mundane tasks, and transforming work dynamics. AI is increasingly used for tasks like sifting through emails, taking notes, generating content, and streamlining job processes. While concerns about job displacement exist, AI is also seen as augmenting human roles and creating new job opportunities.\\n*   **Specialized AI Applications** AI is being deeply embedded into specific industries, driving innovation and efficiency:\\n    *   **Healthcare:** AI is accelerating scientific research and improving healthcare outcomes, with applications in diagnosing patients and identifying health problems.\\n    *   **Finance:** AI is revolutionizing the finance landscape through autonomous finance, including customer service chatbots, automated forecasting, and AI-powered fraud detection.\\n    *   **Supply Chain:** AI is enhancing efficiency in supply chain management through optimized inventory and logistics, risk prediction, and sustainability drives.\\n    *   **Education:** AI is being explored for personalized learning experiences, although its implementation can be controversial.\\n*   **Ethical AI and Regulation** As AI becomes more powerful and pervasive, there is increased scrutiny on AI ethics, responsible AI, and the development of broader AI regulations. This includes concerns about privacy, data breaches, and ensuring fundamental rights are respected, as seen with initiatives like the European Union's AI Act.\\n*   **Technical Advancements** Underlying these applications are continuous technical improvements. This includes more reasonable reasoning models, advancements in smaller language models (SLMs) that offer efficiency benefits, and the emerging field of \\\"Embodied AI,\\\" which aims to bring multimodal AI abilities into the physical world through robotics. There's also a focus on improving the cost-efficiency of AI inference and exploring new model architectures beyond traditional transformers.\\n\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token property\">\"contextId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"508efe3a-41ca-4e0b-9478-ea82dbce2ea6\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token property\">\"history\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"contextId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"508efe3a-41ca-4e0b-9478-ea82dbce2ea6\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"kind\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"message\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"messageId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"7f67307cfede45438e5ed867c38bd9bb\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"parts\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token property\">\"kind\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token property\">\"text\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"What are some trending topics in AI right now?\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"role\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"user\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"taskId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"fb816920-46e3-4bdf-8889-c7fdab377e7a\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"contextId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"508efe3a-41ca-4e0b-9478-ea82dbce2ea6\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"kind\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"message\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"messageId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"f2f05466-2943-4671-bd83-fe41eb2193d2\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"parts\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token property\">\"kind\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"text\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token property\">\"text\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Executing task...\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"role\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"agent\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token property\">\"taskId\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"fb816920-46e3-4bdf-8889-c7fdab377e7a\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token property\">\"id\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"fb816920-46e3-4bdf-8889-c7fdab377e7a\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token property\">\"kind\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"task\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token property\">\"status\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token property\">\"state\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"completed\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token property\">\"timestamp\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"2025-07-17T00:37:42.277421+00:00\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>If we want to extract just the text, which is what we will pass in as context to future interactions with our agent, we can do that as well:</p>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'result'</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> response_dict </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">and</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'artifacts'</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> response_dict</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'result'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    artifacts </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> response_dict</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'result'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'artifacts'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> artifact </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> artifacts</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'parts'</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> artifact</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> part </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> artifact</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'parts'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token plain\"> part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">print</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">part</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<p>Extracted text:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    Several key themes and advancements are dominating the field of Artificial Intelligence (AI) in 2025, reflecting its rapid evolution and increasing integration across various sectors.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    Some of the most prominent trending topics in AI right now include:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    *   **Generative AI Evolution and Integration** Generative AI continues to be a major force, moving beyond basic content creation to more advanced and integrated applications. This includes the development of more sophisticated generative AI models, often referred to as Generative AI 2.0, that are becoming autonomous creators. There's a strong focus on integrating generative AI into everyday applications, making it an \"obligatory copilot\" for skilled workers across various industries. Multimodal AI, which can process and generate content from various inputs like text, images, and video, is also seeing significant advancements.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    *   **Agentic AI** A significant trend is the rise of \"Agentic AI,\" where AI systems can act autonomously to achieve specific goals, make decisions, and take actions independently without constant human oversight. These AI agents are envisioned as the \"apps of the AI era,\" handling tasks on users' behalf and transforming business processes. Examples include AIs that can surf the web, order groceries, predict sales, and manage inventory.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    *   **AI in the Workplace and Productivity** AI's adoption in the workplace is accelerating, with a focus on enhancing productivity, automating repetitive and mundane tasks, and transforming work dynamics. AI is increasingly used for tasks like sifting through emails, taking notes, generating content, and streamlining job processes. While concerns about job displacement exist, AI is also seen as augmenting human roles and creating new job opportunities.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    *   **Specialized AI Applications** AI is being deeply embedded into specific industries, driving innovation and efficiency:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        *   **Healthcare:** AI is accelerating scientific research and improving healthcare outcomes, with applications in diagnosing patients and identifying health problems.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        *   **Finance:** AI is revolutionizing the finance landscape through autonomous finance, including customer service chatbots, automated forecasting, and AI-powered fraud detection.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        *   **Supply Chain:** AI is enhancing efficiency in supply chain management through optimized inventory and logistics, risk prediction, and sustainability drives.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        *   **Education:** AI is being explored for personalized learning experiences, although its implementation can be controversial.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    *   **Ethical AI and Regulation** As AI becomes more powerful and pervasive, there is increased scrutiny on AI ethics, responsible AI, and the development of broader AI regulations. This includes concerns about privacy, data breaches, and ensuring fundamental rights are respected, as seen with initiatives like the European Union's AI Act.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    *   **Technical Advancements** Underlying these applications are continuous technical improvements. This includes more reasonable reasoning models, advancements in smaller language models (SLMs) that offer efficiency benefits, and the emerging field of \"Embodied AI,\" which aims to bring multimodal AI abilities into the physical world through robotics. There's also a focus on improving the cost-efficiency of AI inference and exploring new model architectures beyond traditional transformers.</span><br></span></code></pre></div></div>\n<p>...and that's it for now, we have covered a lot of ground, yet only brushed the surface of A2A and the power it truly holds.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"summary-what-have-we-learned\">Summary: What have we learned?<a href=\"https://littlehorse.io/blog/a2a-protocol#summary-what-have-we-learned\" class=\"hash-link\" aria-label=\"Direct link to Summary: What have we learned?\" title=\"Direct link to Summary: What have we learned?\" translate=\"no\">​</a></h4>\n<ul>\n<li class=\"\">Created a basic agent using Google's ADK</li>\n<li class=\"\">Covered the basics of A2A protocol</li>\n<li class=\"\">Launched a server to host our remote agent</li>\n<li class=\"\">RPC 2.0 Calls to interact with our agent</li>\n</ul>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"whats-next\">Whats Next?<a href=\"https://littlehorse.io/blog/a2a-protocol#whats-next\" class=\"hash-link\" aria-label=\"Direct link to Whats Next?\" title=\"Direct link to Whats Next?\" translate=\"no\">​</a></h4>\n<p>In the coming weeks, we will be releasing a series of blog posts covering the nuances of A2A in depth:</p>\n<ul>\n<li class=\"\">RPC Deep Dive</li>\n<li class=\"\">The \"Agent\"</li>\n<li class=\"\"><code>AgentCard</code>, <code>AgentSkills</code>, <code>AgentExecutor</code></li>\n<li class=\"\">Tools</li>\n<li class=\"\">Event Handling (tasks, messages...)</li>\n<li class=\"\">A2A with Model Context Protocol (MCP)</li>\n<li class=\"\">Debugging A2A + Tips and Tricks</li>\n<li class=\"\">Custom Agents</li>\n<li class=\"\">Authentication and Authorization</li>\n</ul>\n<p>We will also be building complex multi-agent workflows and learning how to compose them with the power of <a href=\"https://littlehorse.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Littlehorse.io</a>! Stay tuned!</p>",
            "url": "https://littlehorse.io/blog/a2a-protocol",
            "title": "Agent 2 Agent Protocol (A2A)",
            "summary": "Exploring Google's A2A protocol for standardized inter-agent communication and multi-agent systems",
            "date_modified": "2025-07-18T00:00:00.000Z",
            "author": {
                "name": "Jackson Oleson"
            },
            "tags": [
                "AI, LLM's, and Agents",
                "Protocols"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/callbacks",
            "content_html": "<p>Sometimes, business processes take time, and you need to wait for something to happen. It can be tricky to build backend software that reliably handles such cases; thankfully, workflow engines like LittleHorse make it a bit easier.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the fifth and final part in a five-part blog series on useful Integration Patterns. This blog series will help you build real-time, responsive applications and microservices that produce predictable results and prevent the Grumpy Customer Problem.</p><ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">Saga Transactions</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/transactional-outbox\">The Transactional Outbox Pattern</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/queuing\">Queuing and Backpressure</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/retries-and-dlq\">Retries and Dead-Letter Queues</a></li>\n<li class=\"\"><strong>[This Post]</strong> Callbacks and External Events</li>\n</ol></div></div>\n<p>Digital applications must support business processes, and not all business processes execute instantaneously. Sometimes, a process must wait for something to happen: an item needs to get back in stock, a delivery must be completed, or a customer must sign a document. In all three of these cases, the software systems you write need to wait for something <em>out of their control</em> to happen, and then resume taking action once it does happen.</p>\n<p>For the rest of this blog, we will use the term <em>External Event</em> to refer to such instants in which our overall process needs to wait for something external to happen.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"case-study-signing-a-document\">Case Study: Signing a Document<a href=\"https://littlehorse.io/blog/callbacks#case-study-signing-a-document\" class=\"hash-link\" aria-label=\"Direct link to Case Study: Signing a Document\" title=\"Direct link to Case Study: Signing a Document\" translate=\"no\">​</a></h2>\n<p>Let's consider a process in which a fictitious real-estate company sends a closing document to a lawyer, waits for execution, and upon completion then updates the status of a listing from \"in escrow\" to \"sold.\"</p>\n<p>The process might look like the following:</p>\n<figure style=\"max-width:100%;display:flex;justify-content:center;align-items:center;flex-direction:column;gap:0.5rem;margin-top:0.75rem;margin-bottom:0.75rem\"><img src=\"https://littlehorse.io/img/2025-01-14-signing-process.png\" alt=\"A business process diagram in which we first send a docusign, wait for completion, and then update the status of a listing to SOLD\" title=\"External Event Case Study\" style=\"border-radius:1rem;width:fit-content\"><figcaption style=\"text-align:center;font-style:italic;color:var(--ifm-font-color-base)\">External Event Case Study</figcaption></figure>\n<p>In order to make it a little bit more tricky, let's add one wrinkle: if the document is not signed within one week, we \"cancel\" the offer and roll back the flow.</p>\n<p>Let's take a look at how this process would be implemented without a composer and with LittleHorse. In both cases, the process will be kicked off by a <code>POST</code> call to the <code>/sign-document</code> endpoint on a web server.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>We will assume that, once the document is signed, we receive a webhook including some information about <em>which</em> document was signed.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"in-the-wild-west\">In the Wild West<a href=\"https://littlehorse.io/blog/callbacks#in-the-wild-west\" class=\"hash-link\" aria-label=\"Direct link to In the Wild West\" title=\"Direct link to In the Wild West\" translate=\"no\">​</a></h3>\n<p>In the \"wild west\" without a composer, we don't have a \"leader\" in the room that is in charge of composing the process from start to finish. Practically, we as software engineers won't write a single piece of code that represents the whole process.</p>\n<p>Let's start with the <code>/sign-document</code> endpoint (our entrypoint). This endpoint needs to do the following:</p>\n<ul>\n<li class=\"\">Send the document to be signed using some external API (eg. DocuSign).</li>\n<li class=\"\">Note <em>somewhere</em> that we need to cancel the transaction in seven days time.</li>\n</ul>\n<p>The first part is easy—just make an RPC call inside your HTTP handler. For the second part, we have two options: use a delayed queue, or write to a table and use a cronjob.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"using-delayed-queues\">Using Delayed Queues<a href=\"https://littlehorse.io/blog/callbacks#using-delayed-queues\" class=\"hash-link\" aria-label=\"Direct link to Using Delayed Queues\" title=\"Direct link to Using Delayed Queues\" translate=\"no\">​</a></h4>\n<p>We briefly discussed delayed queues in <a class=\"\" href=\"https://littlehorse.io/blog/retries-and-dlq#with-normal-queues\">Part IV</a>. Some queueing systems, such as AWS SQS and Rabbit MQ, allow you to write messages that are only delivered after some delay.</p>\n<p>Our entrypoint HTTP handler would put the message in the delay queue and send the document to be signed. The messages in the queue would be consumed and processed one week later by (yet another) system which consumes the queue, checks to see whether or not the document was signed, and then optionally cancels the listing (if the document wasn't signed).</p>\n<p>We would need one final HTTP handler, which is responsible for listening for completed documents and marking the listing as \"sold\" when the document is signed.</p>\n<p>Pseudocode for our HTTP handler for <code>POST /sign-document</code> might look like the following:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@PostMapping</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"/sign-document\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">signDocument</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@RequestMapping</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"document\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">DocumentDetails</span><span class=\"token plain\"> document</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">updateListingStatus</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">document</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">getListingId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"SIGNING_DOCUMENT\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">String</span><span class=\"token plain\"> queueName </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"check-expired-docusign\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">scheduleMessage</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">queueName</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> document</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">getListingId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Duration</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">ofDays</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">7</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"></path></svg></span>warning</div><div class=\"admonitionContent_BuS1\"><p>The above code is not robust to a sudden crash while the HTTP Handler is handling the request, which could lead to duplicated or dropped processing.</p></div></div>\n<p>The callback handler (which listens for completed documents) would update the status of the listing to \"SOLD\" when handling the document. When consuming the message one week later, our consumer would be responsible for checking to make sure that the listing was not yet in the \"SOLD\" status before taking compensatory actions.</p>\n<p>Note that the responsibility for overall business flow is spread across three components:</p>\n<ol>\n<li class=\"\">The entrypoint HTTP handler (<code>POST /send-document</code>)</li>\n<li class=\"\">The callback handler</li>\n<li class=\"\">The consumer.</li>\n</ol>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"a-note-on-cronjobs\">A Note on CronJobs<a href=\"https://littlehorse.io/blog/callbacks#a-note-on-cronjobs\" class=\"hash-link\" aria-label=\"Direct link to A Note on CronJobs\" title=\"Direct link to A Note on CronJobs\" translate=\"no\">​</a></h4>\n<p>If your message queue doesn't have the ability to schedule delayed writes, you aren't fully out of luck. You can spawn a process which periodically checks for listings that have been in the \"signing document\" status for over a week. This would likely necessitate a modification to the <code>/sign-document</code> endpoint so that you can keep track of when the document was initially signed, which is still more work.</p>\n<p>Furthermore, running cronjobs can be challenging from a Day 2 Operations perspective, and we still have the problem of our business logic being spread across three disparate components (which are most likely deployed separately).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"with-workflow\">With Workflow<a href=\"https://littlehorse.io/blog/callbacks#with-workflow\" class=\"hash-link\" aria-label=\"Direct link to With Workflow\" title=\"Direct link to With Workflow\" translate=\"no\">​</a></h3>\n<p>Using a workflow engine to handle your callback-driven workflows simplifies two key aspects of your architecture:</p>\n<ol>\n<li class=\"\">The deployment model—no need to manage cronjobs or delayed message queues.</li>\n<li class=\"\">The mental model—the entire end-to-end control flow lives in your <code>WfSpec</code>, which sits right in front of you.</li>\n</ol>\n<p>Additionally, running your processes in a workflow engine gives you all of the observability and reliability features that we have discussed in the first four episodes.</p>\n<p>An executable <code>WfSpec</code> for this use-case might look as follows:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> listingId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"listing-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> customerId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"customer-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"send-docusign\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> listingId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> customerId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// wait for the callback</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">long</span><span class=\"token plain\"> sevenDays </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">60</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">60</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">24</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">7</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">NodeOutput</span><span class=\"token plain\"> eventResult </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">waitForEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"document-signed\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">timeoutSeconds</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">sevenDays</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Fail if it doesn't return in a week</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">handleException</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">eventResult</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"TIMEOUT\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"cancel-listing\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> listingId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fail</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"signing-timeout\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Closing documents were not signed.\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"complete-listing\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    listingId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use the output from the external event in the workflow</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">format</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Closing documents signed by {0}.\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> eventResult</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">jsonPath</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"$.signer\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"analysis\">Analysis<a href=\"https://littlehorse.io/blog/callbacks#analysis\" class=\"hash-link\" aria-label=\"Direct link to Analysis\" title=\"Direct link to Analysis\" translate=\"no\">​</a></h2>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>The key point to note here is that, with LittleHorse you get to write a single piece of code (your <code>WfSpec</code>) that controls the entire process end-to-end.</p></div></div>\n<p>Our document signing system will necessarily have the following components:</p>\n<ol>\n<li class=\"\">The initial REST service must have the <code>POST /sign-document</code> endpoint.</li>\n<li class=\"\">There must be some system (Task Worker, Cron Job Poller, or Queue Consumer) that handles expired documents.</li>\n<li class=\"\">There must also be an implementation of the callback endpoint.</li>\n</ol>\n<p>This is the unfortunate reality of event-driven systems: business flows are spread across multiple components. However, using LittleHorse to compose these processes reduces the complexity of the deployment model and your mental model of how the system works.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>When business flows are spread across many components, that could be multiple components within the same deployable (a modular monolith), or it could mean multiple separately-deployed microservices. Either way, the problem of wrangling end-to-end flow remains.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"wrapping-up\">Wrapping Up<a href=\"https://littlehorse.io/blog/callbacks#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\" translate=\"no\">​</a></h2>\n<p>Congrats on making it through the Integration Patterns series! From the Saga Pattern to queues, retries, and the Outbox Pattern, we've gone through some of the more commonly-used tools and patterns in the belt of software engineers who build reliable and responsive systems.</p>\n<p>All of these tools are useful; unfortunately, they are also tricky to get right. Thankfully, LittleHorse makes it easy for you to implement these patterns, taking advantage of the reliability and responsiveness they provide while keeping your architecture simple.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"get-in-touch\">Get In Touch!<a href=\"https://littlehorse.io/blog/callbacks#get-in-touch\" class=\"hash-link\" aria-label=\"Direct link to Get In Touch!\" title=\"Direct link to Get In Touch!\" translate=\"no\">​</a></h3>\n<p>Whether or not you end up using LittleHorse for your microservices, I truly hope this series of blogs was useful to you. And if you do want to get involved with LittleHorse:</p>\n<ul>\n<li class=\"\">Try out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\">Quickstarts</a></li>\n<li class=\"\">Join us <a href=\"https://www.launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on Slack</a></li>\n<li class=\"\">Give us a star <a href=\"https://github.com/littlehorse-enterprises/littlehorse\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on GitHub</a>!</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"whats-next\">What's Next<a href=\"https://littlehorse.io/blog/callbacks#whats-next\" class=\"hash-link\" aria-label=\"Direct link to What's Next\" title=\"Direct link to What's Next\" translate=\"no\">​</a></h3>\n<p>Over the next few weeks, we will announce three big new releases (two open-source, one product). I also plan to write about the connection between Streams/Tables/Workflows, and how Workflow is similar to (but cooler than!) its cousin Durable Execution.</p>",
            "url": "https://littlehorse.io/blog/callbacks",
            "title": "Integration Patterns V: Callbacks and External Events",
            "summary": "How to handle callbacks and external events reliably in long-running distributed workflows.",
            "date_modified": "2025-01-14T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Integration Patterns",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/retries-and-dlq",
            "content_html": "<p>Retries and Dead-Letter Queues allow you to make multi-step processes reliable in the face of failure in order to avoid the Grumpy Customer Problem, but implementing them properly is quite a toil. Thankfully, LittleHorse helps keep your engineers happy by simplifying this process.</p>\n<!-- -->\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the fourth part in a five-part blog series on useful Integration Patterns. This blog series will help you build real-time, responsive applications and microservices that produce predictable results and prevent the Grumpy Customer Problem.</p><ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">Saga Transactions</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/transactional-outbox\">The Transactional Outbox Pattern</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/queuing\">Queuing and Backpressure</a></li>\n<li class=\"\"><strong>[This Post]</strong> Retries and Dead-Letter Queues</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/callbacks\">Callbacks and External Events</a></li>\n</ol></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"review-processing-application\">Review-Processing Application<a href=\"https://littlehorse.io/blog/retries-and-dlq#review-processing-application\" class=\"hash-link\" aria-label=\"Direct link to Review-Processing Application\" title=\"Direct link to Review-Processing Application\" translate=\"no\">​</a></h2>\n<p>In the <a class=\"\" href=\"https://littlehorse.io/blog/queuing\">previous post</a>, we discussed an application which accepts reviews of a product, and first uses a third-party AI service to detect offensive content before finalizing and accepting the review. The slow response times (and flakiness) of the AI service make asynchronous processing a natural fit for this use-case: once the review is saved into a database or queue, you can immediately respond to the client and analyze the review using the AI service later.</p>\n<p>We saw that we can successfully make our API responsive and performant for our users either by using a traditional message queue or event log (such as AWS SQS or Apache Kafka) or by using a composer. However, using a message queue has significant advantages in terms of debugging, monitoring, and long-term maintainability.</p>\n<p>We haven't yet addressed what happens when the flakey external service fails. In order to avoid the Grumpy Customer problem, we must periodically retry the request (and subsequent processing) until the service responds successfully.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"with-normal-queues\">With Normal Queues<a href=\"https://littlehorse.io/blog/retries-and-dlq#with-normal-queues\" class=\"hash-link\" aria-label=\"Direct link to With Normal Queues\" title=\"Direct link to With Normal Queues\" translate=\"no\">​</a></h2>\n<p>When we discussed the classic message queue architecture in the last post, we left off with a consumer that polled records from a queue, then made the external API call and either rejected or approved the review. In pseudo-java, naive code might look like:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Review</span><span class=\"token plain\"> reviewToProcess </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">iterAllEvents</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ReviewAnalysisResponse</span><span class=\"token plain\"> reviewAnalysis </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> thirdPartyService</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">analyzeReview</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">reviewAnalysis</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">containsOffensiveContent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">rejectReview</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">reviewAnalysis</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">else</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">acceptReview</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">reviewAnalysis</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">acknowledge</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">reviewToProcess</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>But what about when the call to <code>analyzeReview()</code> fails (because the service is flakey)? There are a lot of questions we need to consider.</p>\n<p>Do we let the exception bubble up, crash our pod, restart it, and then poll the message again (since we never called <code>acknowledge()</code>)? This might work if outages are very short and intermittent, but not for sustained downtime of the queue service. Additionally, what if there's a \"poison pill\" message that, for whichever reason, cannot be successfully processed (eg. a deserialization error)?</p>\n<p>Furthermore, if we start spamming the system with retried requests, won't we end up making a bad problem worse? Lastly, when do we \"give up\" for an individual message, and what happens when we do?</p>\n<p>In order to harden our app, we will do two things:</p>\n<ol>\n<li class=\"\">Introduce retries with exponential backoff for failed messages.</li>\n<li class=\"\">Send reviews to a dead-letter queue when they are fully exhausted.</li>\n</ol>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>Exponential backoff retries is a strategy where the delay between retry attempts increases exponentially, often with random jitter, to reduce contention and prevent synchronized retries. Read more <a href=\"https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/retry-backoff.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"retries-with-backoff\">Retries with Backoff<a href=\"https://littlehorse.io/blog/retries-and-dlq#retries-with-backoff\" class=\"hash-link\" aria-label=\"Direct link to Retries with Backoff\" title=\"Direct link to Retries with Backoff\" translate=\"no\">​</a></h3>\n<p>A wise man once said, \"if at first you don't succeed, try, try again!\" Processing messages from a queue is no different. However, if we naively retry every message immediately upon failure, we risk exacerbating the unavailability of our flakey service by overloading it with many retry requests at once. Exponential Backoff solves this problem by increasing the delay between retry attempts exponentially. This reduces the risk of accidentally DDOS'ing the flakey service. By adding a delay between attempts, we can safely wait until the flakey service comes back to life, and then resume processing.</p>\n<p>The first step towards implementing retries with exponential backoff is catching failures and enqueuing them with appropriate delay. Once we catch a failure, our consumer process needs to:</p>\n<!-- -->\n<ol>\n<li class=\"\">Calculate the amount of time to wait for the next retry.</li>\n<li class=\"\">Schedule a message to be delivered at that time.</li>\n</ol>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>Some queues have native support for retries; however, implementing exponential backoff is still <a href=\"https://www.brianstorti.com/rabbitmq-exponential-backoff/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">quite a challenge</a>.</p></div></div>\n<p>In order to calculate the amount of time to wait for the next retry, we follow the Exponential Backoff formula:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">time = base * exponent^n</span><br></span></code></pre></div></div>\n<p>Here, the <code>n</code> represents the attempt number. Therefore, for a given <code>Review</code>, we need to know the last time that we attempted to process it, which requires adding some metadata to the message. This is not the end of the world, but this piece of extra complexity adds one more opening for bugs to appear.</p>\n<p>Once we have calculated the next appropriate delivery time for the retried message, we must schedule it for delivery. Some queues, such as AWS SQS, support this natively with \"delay queues.\" However, other systems such as Apache Kafka and AWS Kinesis do not support delayed messages. For these cases, we would have to persist the messages ourselves and use a cronjob to re-publish the messages to the queue at a later date.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"introducing-the-dlq\">Introducing the DLQ<a href=\"https://littlehorse.io/blog/retries-and-dlq#introducing-the-dlq\" class=\"hash-link\" aria-label=\"Direct link to Introducing the DLQ\" title=\"Direct link to Introducing the DLQ\" translate=\"no\">​</a></h3>\n<p>Even with retries and exponential backoff, there might be some <code>Review</code>s that we quite simply cannot process (or the third-party service might be in especially dire straits one day). For these messages, we will use the <em>Dead-Letter Queue</em> Pattern.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>A Dead Letter Queue (DLQ) is a secondary queue used to store messages that cannot be processed successfully after a predefined number of retry attempts or due to specific errors. It helps isolate problematic messages for later inspection and debugging without disrupting the processing of valid messages.</p></div></div>\n<p>Once we have failed a certain number of times to process a <code>Review</code>, we can publish a message to a <code>failed-reviews</code> queue (or topic). DLQ's can be used purely for debugging and manual intervention purposes; however, applications can also consume from a DLQ and take automated actions on the messages in the queue.</p>\n<p>For example, our review-processing application might have an additional consumer which notifies the customer (or the on-call team, or both) that the review wasn't processed.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"retrying-with-a-composer\">Retrying With a Composer<a href=\"https://littlehorse.io/blog/retries-and-dlq#retrying-with-a-composer\" class=\"hash-link\" aria-label=\"Direct link to Retrying With a Composer\" title=\"Direct link to Retrying With a Composer\" translate=\"no\">​</a></h2>\n<p>In the previous section, we saw how retries with exponential backoff allow us to we can more reliably process our reviews in the face of a flakey third-party service. However, there was a lot of manual toil and additional infrastructure to set up (potentially involving a database and cronjob depending on whether our queue service supports delayed messages).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"adding-retries\">Adding Retries<a href=\"https://littlehorse.io/blog/retries-and-dlq#adding-retries\" class=\"hash-link\" aria-label=\"Direct link to Adding Retries\" title=\"Direct link to Adding Retries\" translate=\"no\">​</a></h3>\n<p>As you might have guessed, this is much easier to accomplish with a durable workflow composer. As we discussed in the last post, we can model the <em>entire</em> end-to-end process (analyze the review, and either save or reject it) in a single <code>WfSpec</code>, as shown below:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">reviewProcessorWf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WorkflowThread</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> userId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"user-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">searchable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> review </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareStr</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"review\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WfRunVariable</span><span class=\"token plain\"> approved </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">declareBool</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"approved\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Call the flakey API with exponential backoff.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> reviewResult </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"analyze-review\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> review</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">withRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">withExponentialBackoff</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ExponentialBackoffRetryPolicy</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">newBuilder</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setBaseIntervalMs</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">5000</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setMultiplier</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2.0F</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">build</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    approved</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">assign</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">reviewResult</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">jsonPath</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"$.okay\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">doIfElse</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">approved</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">isEqualTo</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"notify-customer-approved\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"notify-customer-rejected\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>With just this block, we defined our retry policy and don't need to worry about calculating the delay, keeping track of how many times a message has been retried, or persisting attempts:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> reviewResult </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"analyze-review\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> review</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">withRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">withExponentialBackoff</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ExponentialBackoffRetryPolicy</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">newBuilder</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setBaseIntervalMs</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">5000</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setMultiplier</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2.0F</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">build</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"processing-the-dlq\">Processing the DLQ<a href=\"https://littlehorse.io/blog/retries-and-dlq#processing-the-dlq\" class=\"hash-link\" aria-label=\"Direct link to Processing the DLQ\" title=\"Direct link to Processing the DLQ\" translate=\"no\">​</a></h3>\n<p>Since the workflow composer automatically persists the state of all workflows for debugging purposes, you don't even need a DLQ! You can quite simply search for <code>WfRun</code>'s with the <code>ERROR</code> status in the dashboard.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>If you want to manually save a failed <code>WfRun</code> after the outage is resolved, you can do so with the <code>lhctl rescue</code> command.</p></div></div>\n<p>In case we must process failed reviews (for example, notify the customer to say that the review was unable to be processed), we can do so using LittleHorse's <em>Failure Handling</em> feature, as follows:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">handleError</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">reviewResult</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"notify-customer-review-failed\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fail</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"review-not-processed\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// fail the workflow</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"wrapping-up\">Wrapping Up<a href=\"https://littlehorse.io/blog/retries-and-dlq#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\" translate=\"no\">​</a></h2>\n<p>Retries and DLQ's are almost always necessary if you require reliable message processing. While they can be tedious to implement, LittleHorse allows you to enjoy their benefits without the frustrating (and error-prone) toil.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"get-involved\">Get Involved!<a href=\"https://littlehorse.io/blog/retries-and-dlq#get-involved\" class=\"hash-link\" aria-label=\"Direct link to Get Involved!\" title=\"Direct link to Get Involved!\" translate=\"no\">​</a></h3>\n<p>Stay tuned for the next post, which will cover how you can write an application that must wait for something external to happen! In the meantime:</p>\n<ul>\n<li class=\"\">Try out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\">Quickstarts</a></li>\n<li class=\"\">Join us <a href=\"https://www.launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on Slack</a></li>\n<li class=\"\">Give us a star <a href=\"https://github.com/littlehorse-enterprises/littlehorse\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on GitHub</a>!</li>\n</ul>",
            "url": "https://littlehorse.io/blog/retries-and-dlq",
            "title": "Integration Patterns IV: Retries and Dead-Letter Queues",
            "summary": "How retries, idempotency, and dead-letter queues improve reliability in distributed applications.",
            "date_modified": "2024-12-13T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Integration Patterns",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/queuing",
            "content_html": "<p>When integrating API's, we sometimes have to tie together steps that can take a long time or might not always be available. If we force the callers of our API's to wait for completion, we find ourselves with some grumpy customers. So what can we do about this?</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the third part in a five-part blog series on useful Integration Patterns. This blog series will help you build real-time, responsive applications and microservices that produce predictable results and prevent the Grumpy Customer Problem.</p><ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">Saga Transactions</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/transactional-outbox\">The Transactional Outbox Pattern</a></li>\n<li class=\"\"><strong>[This Post]</strong> Queuing and Backpressure</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/retries-and-dlq\">Retries and Dead-Letter Queues</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/callbacks\">Callbacks and External Events</a></li>\n</ol></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-queue\">Why Queue?<a href=\"https://littlehorse.io/blog/queuing#why-queue\" class=\"hash-link\" aria-label=\"Direct link to Why Queue?\" title=\"Direct link to Why Queue?\" translate=\"no\">​</a></h2>\n<p>In software architecture, simple is almost always better. With fewer moving parts, there are less chances for failure, less things to debug, and fewer pieces of infrastructure. So when and why would you introduce queues to your architecture?</p>\n<p>Queues are useful when building services that need to accept a request from a client and then execute some processing which has any of the following characteristics:</p>\n<ol>\n<li class=\"\">Slow to execute.</li>\n<li class=\"\">Flakey, not always-available, or in need of retries.</li>\n<li class=\"\">Have a rate limit or cannot gracefully handle spikey workloads (backpressure).</li>\n<li class=\"\">Have multiple steps that need to all complete before the processing is finalized.</li>\n</ol>\n<p>Crucially, if your service enqueues requests, you need to make sure that the caller of your API doesn't need to wait for their entire request to be processed: a simple promise that it will get done should be sufficient. As we will see with a practical example, this is feasible in many business cases.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"example-reviews-application\">Example: Reviews Application<a href=\"https://littlehorse.io/blog/queuing#example-reviews-application\" class=\"hash-link\" aria-label=\"Direct link to Example: Reviews Application\" title=\"Direct link to Example: Reviews Application\" translate=\"no\">​</a></h3>\n<p>Consider a product reviews widget on an e-commerce site. In this application, users can submit reviews of a product. However, before a review can be approved to be displayed, it must first be checked for offensive content by a third-party AI service. Sometimes, this third-party service often has response times of over 10 seconds, and sometimes even goes down and is fully unavailable.</p>\n<p>A naive web app endpoint to handle this use-case might be:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@PostMapping</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"/review\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token generics punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token generics class-name\" style=\"color:rgb(78, 201, 176)\">String</span><span class=\"token generics punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">postReview</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@RequestBody</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">PostReviewRequest</span><span class=\"token plain\"> request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Call the third-party AI service, which takes a long time and is flakey</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ReviewAnalysisResponse</span><span class=\"token plain\"> reviewAnalysis </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> thirdPartyService</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">analyzeReview</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">OffensiveReviewException</span><span class=\"token plain\"> exn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Exception</span><span class=\"token plain\"> exn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">500</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// If we got here, the review is valid</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    reviewService</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">save</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">HttpStatus</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">CREATED</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>As promised ( <!-- -->😉<!-- --> ), this endpoint implementation has a sub-optimal user experience. Many times, when the flakey third-party AI service is unavailable, users will simply be unable to post reviews. Even when it is up, users will see the spinning waiting wheel for multiple seconds.</p>\n<p>The solution? Enqueue the request for processing later by some external system, and then respond immediately to the client's request. That can be done in two ways:</p>\n<ol>\n<li class=\"\"><strong>Traditional Queuing:</strong> simply put a record on some queue, streaming system, or event bus (such as Apache Pulsar, Apache Kafka, or AWS SQS).</li>\n<li class=\"\"><strong>Workflow Execution:</strong> tell a workflow composition engine like LittleHorse to start executing a process!</li>\n</ol>\n<p>Once the request is enqueued, there will be a system polling the queue to call the third-party analytics API and then either reject or approve the review. This system will be responsible for throttling requests according to the API's service limits, retrying failed messages, and waiting for the API to come back online in the case of an intermittent outage.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"composers-vs-plain-old-queues\">Composers vs. Plain Old Queues<a href=\"https://littlehorse.io/blog/queuing#composers-vs-plain-old-queues\" class=\"hash-link\" aria-label=\"Direct link to Composers vs. Plain Old Queues\" title=\"Direct link to Composers vs. Plain Old Queues\" translate=\"no\">​</a></h2>\n<p>Workflow engines <a class=\"\" href=\"https://littlehorse.io/blog/basics-of-workflow\">internally use message queues</a> on their own! So what's the difference from the user perspective?</p>\n<p>You can think of a workflow engine as a <em>super-smart</em> message queue, with certain clear advantages over message queues including advanced monitoring and better support for multi-step processes.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>The next post in this series will take a deep-dive into retries, idempotency, and failure handling, which is another area in which workflow engines shine above and beyond Plain Old Queues.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"monitoring-and-debugging\">Monitoring and Debugging<a href=\"https://littlehorse.io/blog/queuing#monitoring-and-debugging\" class=\"hash-link\" aria-label=\"Direct link to Monitoring and Debugging\" title=\"Direct link to Monitoring and Debugging\" translate=\"no\">​</a></h3>\n<p>Workflow engines provide more insight and oversight into your processes than do message queues. In our reviews application, if an angry user (<code>anakin@jeditemple.com</code>) calls customer support to complain that his review hadn't been processed in over two days, it would be tricky to find the <em>exact</em> cause with a pure message queue.</p>\n<p>However, with LittleHorse, you just search for the <code>WfRun</code> where <code>user-id == anakin@jeditemple.com</code>:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Dashboard Workflow Search\" src=\"https://littlehorse.io/assets/images/2024-10-28-workflow-search-6abd5f2dbb3914b3230502f28afab1f2.png\" width=\"496\" height=\"294\" class=\"img_ev3q\"></p>\n<p>and then look on the dashboard to see what went wrong:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Dashboard Task Error Message\" src=\"https://littlehorse.io/assets/images/2024-10-28-task-debugging-dc5d52151429b8428cfa326e57cc53d5.png\" width=\"895\" height=\"686\" class=\"img_ev3q\"></p>\n<p>We are also working on <em>workflow metrics</em> that will allow you to use LittleHorse to answer questions such as:</p>\n<ul>\n<li class=\"\">How long does the <code>process-review</code> workflow take on average?</li>\n<li class=\"\">How long does each <code>analyze-review</code> task attempt take on average, and what percentage of calls fail (i.e. how responsive is the API)?</li>\n<li class=\"\">What percentage of reviews are approved versus rejected?</li>\n</ul>\n<p>These will likely not be available until March 2025; however, we have nearly finalized the designs for them and have scheduled the implementation to start in January.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"multi-step-processes\">Multi-Step Processes<a href=\"https://littlehorse.io/blog/queuing#multi-step-processes\" class=\"hash-link\" aria-label=\"Direct link to Multi-Step Processes\" title=\"Direct link to Multi-Step Processes\" translate=\"no\">​</a></h3>\n<p>So far, the use-case we've discussed involves only two \"steps\" to be executed:</p>\n<ol>\n<li class=\"\">Analyze the product review.</li>\n<li class=\"\">Post the review to the site.</li>\n</ol>\n<p>You could arguably execute both steps at once: the only problem we are trying to solve is that we have a flakey API and we don't want our customers to have to wait for it. In theory, the same consumer which calls the external API could also update the visibility of the review to <code>APPROVED</code>.</p>\n<p>But what if the business requirements change, and we need to do some post-processing, such as notify a separately-managed (and also flakey) analytics service of what happened? That would require adding another queue:</p>\n<ol>\n<li class=\"\">Edit our original consumer to publish to a new queue.</li>\n<li class=\"\">Write a <em>new</em> consumer that subscribes to the second queue and notifies the flakey analytics service.</li>\n<li class=\"\">Instrument monitoring for the new queue infrastructure.</li>\n</ol>\n<p>This gets especially tricky when we want to handle intermittent availability from the analytics service: we'll have to copy the same boilerplate to handle retries and dead-letter-queues (more on that in the next post).</p>\n<p>However, with the workflow-driven approach, all you need to do is add a single line to your <code>WfSpec</code>:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"notify-analytics-service\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> review</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> approvalStatus</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"wrapping-up\">Wrapping Up<a href=\"https://littlehorse.io/blog/queuing#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\" translate=\"no\">​</a></h2>\n<p>Queueing is a great tool to improve the client experience of your API's when you can respond to your callers before all of your processing has been done. Workflow engines like LittleHorse can actually be thought of as a <em>super-smart queueing system</em>, which provides all of the advantages of queueing plus better observability and support for multi-step processes.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"get-involved\">Get Involved!<a href=\"https://littlehorse.io/blog/queuing#get-involved\" class=\"hash-link\" aria-label=\"Direct link to Get Involved!\" title=\"Direct link to Get Involved!\" translate=\"no\">​</a></h3>\n<p>Stay tuned for the next post, which will cover retries and dead-letter queues! In the meantime:</p>\n<ul>\n<li class=\"\">Try out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\">Quickstarts</a></li>\n<li class=\"\">Join us <a href=\"https://www.launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on Slack</a></li>\n<li class=\"\">Give us a star <a href=\"https://github.com/littlehorse-enterprises/littlehorse\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on GitHub</a>!</li>\n</ul>",
            "url": "https://littlehorse.io/blog/queuing",
            "title": "Integration Patterns III: Queueing",
            "summary": "How queues help build responsive systems and where workflow orchestration provides stronger guarantees.",
            "date_modified": "2024-10-28T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Integration Patterns",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/transactional-outbox",
            "content_html": "<p>Like the <a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">Saga Pattern</a>, the Transactional Outbox pattern is tool for defending against data loss in your applications. In this blog we cover how it works and how to do it <em>easier</em> using LittleHorse.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the first part in a five-part blog series on useful Integration Patterns. This blog series will help you build real-time, responsive applications and microservices that produce predictable results and prevent the Grumpy Customer Problem.</p><ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">Saga Transactions</a></li>\n<li class=\"\"><strong>[This Post]</strong> The Transactional Outbox Pattern</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/queuing\">Queuing</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/retries-and-dlq\">Retries and Dead-Letter Queues</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/callbacks\">Callbacks and External Events</a></li>\n</ol></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-transactional-outbox-pattern\">The Transactional Outbox Pattern<a href=\"https://littlehorse.io/blog/transactional-outbox#the-transactional-outbox-pattern\" class=\"hash-link\" aria-label=\"Direct link to The Transactional Outbox Pattern\" title=\"Direct link to The Transactional Outbox Pattern\" translate=\"no\">​</a></h2>\n<p>At the technical level, the <a href=\"https://microservices.io/patterns/data/transactional-outbox.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Transactional Outbox Pattern</a> allows you to atomically:</p>\n<ol>\n<li class=\"\">Update a database, and</li>\n<li class=\"\">Publish a record to a streaming log or message queue (such as Apache Kafka).</li>\n</ol>\n<p>The <a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern\">Saga Pattern</a> allows you to make a multi-step business process atomic. However, you can think of the Transactional Outbox pattern as a way to ensure that a process doesn't get dropped halfway through.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>The Transactional Outbox Pattern is often useful <em>within</em> a Saga transaction.</p><p>However, as we'll see later on in this article, LittleHorse removes the need to worry about such difficult technical details.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"case-study-customer-sign-up\">Case Study: Customer Sign-Up<a href=\"https://littlehorse.io/blog/transactional-outbox#case-study-customer-sign-up\" class=\"hash-link\" aria-label=\"Direct link to Case Study: Customer Sign-Up\" title=\"Direct link to Case Study: Customer Sign-Up\" translate=\"no\">​</a></h2>\n<p>As an example, let's consider the following Spring Boot REST endpoint (<code>POST /user</code>), which must:</p>\n<ol>\n<li class=\"\">Create a customer account in a database.</li>\n<li class=\"\">Send a message on a queue which results in a series of account setup actions, including a welcome email being sent to the customer.</li>\n</ol>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@PostMapping</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"/user\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token generics punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token generics class-name\" style=\"color:rgb(78, 201, 176)\">String</span><span class=\"token generics punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">createUser</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@RequestBody</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">CreateUserRequest</span><span class=\"token plain\"> request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    database</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">createUser</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    queue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">publishUserCreatedEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">HttpStatus</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">CREATED</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>A few things can go wrong here which would cause the user to be created in the database but the customer never gets a welcome email, and the account setup fails. First, the queue could be inaccessible (this <em>could</em> be saved at the application layer with an exception handler).</p>\n<p>However, one failure mode which <em>cannot</em> be caught at the application layer is if the Spring Boot app crashes during the process of publishing the record to the queue (on or just before the <code>queue.publishUserCreatedEvent()</code> line).</p>\n<p>This would definitely cause another case of the Grumpy Customer Problem!</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"using-a-transactional-outbox\">Using a Transactional Outbox<a href=\"https://littlehorse.io/blog/transactional-outbox#using-a-transactional-outbox\" class=\"hash-link\" aria-label=\"Direct link to Using a Transactional Outbox\" title=\"Direct link to Using a Transactional Outbox\" translate=\"no\">​</a></h3>\n<p>The core idea of a Transactional Outbox is to make use of transactions within a single database to atomically:</p>\n<ol>\n<li class=\"\">Make the database update.</li>\n<li class=\"\">Write the desired queue event to an <em>Outbox Table.</em></li>\n</ol>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Transactional Outbox Architecture\" src=\"https://littlehorse.io/assets/images/2024-09-30user-workflow-outbox-717d91ae3fad27e1d67957580625ab19.png\" width=\"3631\" height=\"1623\" class=\"img_ev3q\"></p>\n<p>Since items <code>1</code> and <code>2</code> happen within a single database, it's trivial to wrap them in a transaction. After the queue event is written to the Outbox Table, a separate process eventually reads the new records in the Outbox Table and pushes them to a queue.</p>\n<p>We would rewrite our Spring Boot endpoint to only write a transaction to the database. The SQL for that transaction would look something like:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">BEGIN TRANSACTION;</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">INSERT INTO user VALUES ...;</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">INSERT INTO outbox VALUES ...; # Insert the record for the queue</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">COMMIT;</span><br></span></code></pre></div></div>\n<p>The Spring Boot application should have another thread which reads records from the <code>outbox</code> table, publishes them to the queue or streaming system, and updates the record in the database as \"read\".</p>\n<div class=\"theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"></path></svg></span>warning</div><div class=\"admonitionContent_BuS1\"><p>The topic of Exactly-Once Semantics is complex; we do not have time in this post to discuss the implications of EOS and a Transactional Outbox.</p><p>As a hint, you can achieve EOS if you transactionally store the last-written offset inside your message broker. There are many \"gotchas\" to this depending on your message broker; for example, in Apache Kafka you must use <code>read_committed</code> consumers.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"using-littlehorse\">Using LittleHorse<a href=\"https://littlehorse.io/blog/transactional-outbox#using-littlehorse\" class=\"hash-link\" aria-label=\"Direct link to Using LittleHorse\" title=\"Direct link to Using LittleHorse\" translate=\"no\">​</a></h3>\n<p>The Outbox Pattern is necessary to persist outgoing records in the case that we suffer a crash between writing to the database and writing to the record queue. However, what if we could \"delegate\" persistence and reliability to some other system?</p>\n<p>Enter LittleHorse! What if we had a <code>WfSpec</code> that defined our process, as follows:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">wfLogic</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WorkflowThread</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> userRequest </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"create-user-request\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">JSON_OBJ</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">required</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"create-user\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> userRequst</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"send-welcome-email\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> user</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>Now, all our REST endpoint has to do is run the worklfow:</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@PostMapping</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"/user\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token generics punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token generics class-name\" style=\"color:rgb(78, 201, 176)\">String</span><span class=\"token generics punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">createUser</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token annotation punctuation\" style=\"color:rgb(212, 212, 212)\">@RequestBody</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">CreateUserRequest</span><span class=\"token plain\"> request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Just run the workflow</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    littlehorseClient</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">runWf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">RunWfRequest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">newBuilder</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setWfSpecName</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"user-workflow\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">putVariables</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"create-user-request\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">LHLibUtil</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">objToVarVal</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">request</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">build</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ResponseEntity</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">HttpStatus</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">CREATED</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Transactional Outbox Architecture with LittleHorse\" src=\"https://littlehorse.io/assets/images/2024-09-30-user-workflow-lh-e7a8ae4a642a0a8d2c4ab2635ce7b445.png\" width=\"1970\" height=\"1485\" class=\"img_ev3q\"></p>\n<p>No outbox table needed! If creating the user in the database fails, or if sending the welcome email fails, LittleHorse will patiently retry (according to your retry backoff policy) the <code>TaskRun</code>s until they succeed. In the event that you exhaust your retries, you still haven't lost data:</p>\n<ul>\n<li class=\"\">You can easily search for failed workflows.</li>\n<li class=\"\">You can restart failed workflows with the <code>rpc RescueThreadRun</code> once the database incident is resolved.</li>\n</ul>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>You can add retries using the <code>Workflow</code> object:</p><div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Workflow</span><span class=\"token plain\"> wf </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Workflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">newWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"user-workflow\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">this</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">::</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">wfLogic</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setDefaultTaskRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setDefaultTaskExponentialBackoffPolicy</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ExponentialBackoffRetryPolicy</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">newBuilder</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setBaseIntervalMs</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1000</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setMultiplier</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">3</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">build</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">registerWfSpec</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">littlehorseClient</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"wrapping-up\">Wrapping Up<a href=\"https://littlehorse.io/blog/transactional-outbox#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\" translate=\"no\">​</a></h2>\n<p>The Transactional Outbox Pattern is a useful and often necessary tool for building reliable integrations between systems. However, it takes time, infrastructure, and deep understanding of distributed systems to get it right. So why spend time solving problems that don't differentiate your business?</p>\n<p>Thankfully, LittleHorse offers a workaround to the original problem, removing the need to engage with the complexities of Transactional Outboxes.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"additional-use-cases\">Additional Use Cases<a href=\"https://littlehorse.io/blog/transactional-outbox#additional-use-cases\" class=\"hash-link\" aria-label=\"Direct link to Additional Use Cases\" title=\"Direct link to Additional Use Cases\" translate=\"no\">​</a></h3>\n<p>The Transactional Outbox pattern is useful anytime you need to update information in a database <em>and also</em> publish a record to a streaming log or a message queue.</p>\n<p>For example:</p>\n<ul>\n<li class=\"\"><strong>User registration:</strong> Save a new user's profile and push a message to a queue in order to trigger a verification email.</li>\n<li class=\"\"><strong>Appointment scheduling:</strong> Save appointment details and notify users via SMS or email.</li>\n<li class=\"\"><strong>Saga Transactions:</strong> Within a Saga transaction (such as the order processing scenario discussed in the <a class=\"\" href=\"https://littlehorse.io/blog/saga-pattern#case-study-order-processing\">last post</a>), a service may need to atomically update its database and push a record to a queue.</li>\n<li class=\"\"><strong>Inventory management:</strong> Update stock levels and push updates to warehouses or suppliers.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"alternative-log-first-architecture\">Alternative: Log-First Architecture<a href=\"https://littlehorse.io/blog/transactional-outbox#alternative-log-first-architecture\" class=\"hash-link\" aria-label=\"Direct link to Alternative: Log-First Architecture\" title=\"Direct link to Alternative: Log-First Architecture\" translate=\"no\">​</a></h3>\n<p>Another solution to this specific problem would be to have the request handler (our Spring Boot endpoint) publish directly to an event log like Apache Kafka. Then, there would be two consumer groups for that topic:</p>\n<ol>\n<li class=\"\">A consumer group which creates the <code>user</code> record in the database.</li>\n<li class=\"\">A consumer group which sends the welcome email.</li>\n</ol>\n<p>The REST endpoint would return <code>201</code> as soon as the record was acknowledged by the streaming platform.</p>\n<p>If you squint hard enough, you can see that this is very similar to what happens with LittleHorse; however, using this pattern, you are responsible for wiring together a complex topology of topics and queues, which is much harder than using a workflow!</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"get-involved\">Get Involved!<a href=\"https://littlehorse.io/blog/transactional-outbox#get-involved\" class=\"hash-link\" aria-label=\"Direct link to Get Involved!\" title=\"Direct link to Get Involved!\" translate=\"no\">​</a></h3>\n<p>Stay tuned for the next post on Queues and Backpressure! In the meantime:</p>\n<ul>\n<li class=\"\">Try out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\">Quickstarts</a></li>\n<li class=\"\">Join us <a href=\"https://www.launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on Slack</a></li>\n<li class=\"\">Give us a star <a href=\"https://github.com/littlehorse-enterprises/littlehorse\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on GitHub</a>!</li>\n</ul>",
            "url": "https://littlehorse.io/blog/transactional-outbox",
            "title": "Integration Patterns II: Transactional Outbox",
            "summary": "How the transactional outbox pattern prevents lost events and keeps distributed data changes consistent.",
            "date_modified": "2024-09-30T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Integration Patterns",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/saga-pattern",
            "content_html": "<p>The Saga pattern allows you to defend against data loss, dropped orders, and confused (or grumpy) customers. While useful, the Saga pattern is tricky to get right without a composer.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the first part in a five-part blog series on useful Integration Patterns. This blog series will help you build real-time, responsive applications and microservices that produce predictable results and prevent the Grumpy Customer Problem.</p><ol>\n<li class=\"\"><strong>[This Post]</strong> Saga Transactions</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/transactional-outbox\">The Transactional Outbox Pattern</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/queuing\">Queuing</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/retries-and-dlq\">Retries and Dead-Letter Queues</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/callbacks\">Callbacks and External Events</a></li>\n</ol></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-saga-pattern\">The Saga Pattern<a href=\"https://littlehorse.io/blog/saga-pattern#the-saga-pattern\" class=\"hash-link\" aria-label=\"Direct link to The Saga Pattern\" title=\"Direct link to The Saga Pattern\" translate=\"no\">​</a></h2>\n<p>At a technical level, the <a href=\"https://microservices.io/patterns/data/saga.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Saga Pattern</a> allows you to perform distributed transactions across multiple disparate systems without 2-phase commit.</p>\n<p>In plain English, it is a tool in the belt of a software engineer to prevent half-fulfilled bank transfers, hanging orders, or other failures which would result in a Grumpy Customer.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>The \"Saga\" pattern gets its name from literature and film, wherein a \"saga\" is a series of chronologically-ordered related works. For example, the \"Star Wars Saga.\"</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"use-cases\">Use Cases<a href=\"https://littlehorse.io/blog/saga-pattern#use-cases\" class=\"hash-link\" aria-label=\"Direct link to Use Cases\" title=\"Direct link to Use Cases\" translate=\"no\">​</a></h3>\n<p>Business processes often need to perform actions in two separate systems either all at once or not at all. For example, you may need to charge a customer's credit card, reserve inventory, and ship an item to the customer all at once or not at all. If the payment went through but shipping failed, we would see the Grumpy Customer Problem yet again.</p>\n<p>The Saga pattern is appropriate when:</p>\n<ul>\n<li class=\"\">A business process must take action across multiple separate systems (legacy monoliths, microservices, external API's, etc),</li>\n<li class=\"\">Each of those actions can be undone via a \"compensation task\", and</li>\n<li class=\"\">All actions must logically happen together or not at all.</li>\n</ul>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>It's also worth noting that a different flavor of the Saga pattern can also be used in <em>long-running</em> business processes. In a past job, for example, I worked on a project that implemented the Saga pattern to handle the scheduling of home inspections. In this case, the task of finding an inspector to show up at the home and confirming a time with the homeowner needed to be performed atomically.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"implementation\">Implementation<a href=\"https://littlehorse.io/blog/saga-pattern#implementation\" class=\"hash-link\" aria-label=\"Direct link to Implementation\" title=\"Direct link to Implementation\" translate=\"no\">​</a></h3>\n<p>While Saga is very hard to implement, it's simple to describe:</p>\n<ul>\n<li class=\"\">Try to perform the actions across the multiple systems.</li>\n<li class=\"\">If one of the actions fails, then run a <em>compensation</em> for all previously-executed tasks.</li>\n</ul>\n<p>The <em>compensation</em> is simply an action that \"undoes\" the previous action. For example, the compensation for a payment task might be to issue a refund.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"case-study-order-processing\">Case Study: Order Processing<a href=\"https://littlehorse.io/blog/saga-pattern#case-study-order-processing\" class=\"hash-link\" aria-label=\"Direct link to Case Study: Order Processing\" title=\"Direct link to Case Study: Order Processing\" translate=\"no\">​</a></h2>\n<p>Let's take a look at a familiar use-case: an order processing workflow involving the <code>inventory</code> service, and the <code>payments</code> service. (The <code>orders</code> service is involved implicitly.) As they would in a real world scenario, all of our services live on separate physical systems and have their own databases.</p>\n<p>In this business process, we first reserve inventory for the ordered item. Next, we charge the customer's credit card.</p>\n<p>If charging the credit card fails, then we have a problem: we've reserved inventory but not sold it.</p>\n<p>Our services need the following functionality. In SOA, these would be endpoints; in LittleHorse, they would be <code>TaskDef</code>s:</p>\n<ul>\n<li class=\"\"><code>create-order</code>: creates an order in the <code>PENDING</code> status.</li>\n<li class=\"\"><code>reserve-inventory</code>: marks an item as no longer available for sale.</li>\n<li class=\"\"><code>charge-payment</code>: charges the customer.</li>\n<li class=\"\"><code>release-inventory</code>: marks an item as available for sale again.</li>\n<li class=\"\"><code>cancel-order</code>: marks an order as <code>CANCELED</code>.</li>\n<li class=\"\"><code>complete-order</code>: marks an order as <code>COMPLETED</code>.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"using-message-queues\">Using Message Queues<a href=\"https://littlehorse.io/blog/saga-pattern#using-message-queues\" class=\"hash-link\" aria-label=\"Direct link to Using Message Queues\" title=\"Direct link to Using Message Queues\" translate=\"no\">​</a></h3>\n<p>Using message queues, the happy path looks like the following:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Architecture diagram\" src=\"https://littlehorse.io/assets/images/2024-09-24-choreography-simple-830e1fcf682eb3cde8b40210f92b3dd2.png\" width=\"544\" height=\"493\" class=\"img_ev3q\"></p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>The above image assumes the <em>choreography</em> pattern, in contrast to the <em>composer</em> pattern. The composer pattern is a ton of work and involves writing something that very much resembles LittleHorse!</p></div></div>\n<ol>\n<li class=\"\">Orders service calls <code>createOrder()</code>.</li>\n<li class=\"\">Orders service publishes to the <code>reserve-inventory</code> queue.</li>\n<li class=\"\">Inventory service reads the message and calls <code>reserveInventory()</code>.</li>\n<li class=\"\">Inventory service publishes to the <code>charge-payment</code> queue.</li>\n<li class=\"\">Payment service charges the credit card.</li>\n<li class=\"\">Payment service publishes to the <code>complete-order</code> queue.</li>\n<li class=\"\">Orders service consumes the record and calls <code>completeOrder()</code>.</li>\n</ol>\n<p>In just the happy path, we have strong coupling already between our services in three places, and we have three message queues to manage.</p>\n<p>But now we need to release the inventory and cancel the order when the payment doesn't go through. So the flow looks like this:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Architecture Diagram\" src=\"https://littlehorse.io/assets/images/2024-09-24-choreography-saga-b400a44518ce7213d491df50bad0bb72.png\" width=\"712\" height=\"493\" class=\"img_ev3q\"></p>\n<ol>\n<li class=\"\">Orders service calls <code>createOrder()</code>.</li>\n<li class=\"\">Orders service publishes to the <code>reserve-inventory</code> queue.</li>\n<li class=\"\">Inventory service reads the message and calls <code>reserveInventory()</code>.</li>\n<li class=\"\">Inventory service publishes to the <code>charge-payment</code> queue.</li>\n<li class=\"\">Payment service charges the credit card <em>unsuccessfully</em>.</li>\n<li class=\"\">Payment service publishes to the <code>release-inventory</code> queue.</li>\n<li class=\"\">Inventory service reads the record and calls <code>releaseInventory()</code>.</li>\n<li class=\"\">Inventory service publishes to the <code>cancel-order</code> queue.</li>\n<li class=\"\">Orders service consumes the record and calls <code>cancelOrder()</code>.</li>\n</ol>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>We still haven't even considered the case when the <code>reserve-inventory</code> step fails and we need to catch that exception and handle the order. For the sake of brevity, we will leave that out.</p></div></div>\n<p>Now, we have <em>five</em> different message queues that we have to wrangle with. We can also see that the overall business flow has started to leak across all of our different services.</p>\n<div class=\"theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z\"></path></svg></span>danger</div><div class=\"admonitionContent_BuS1\"><p>One thing we are ignoring in this blog post is <em>reliability</em>: to make this setup production-ready, we would also have to ensure that updates to the internal databases of the services are atomic along with pushing messages to the message queue. We will cover that in next week's post (along with how LittleHorse takes care of that for you).</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"using-littlehorse\">Using LittleHorse<a href=\"https://littlehorse.io/blog/saga-pattern#using-littlehorse\" class=\"hash-link\" aria-label=\"Direct link to Using LittleHorse\" title=\"Direct link to Using LittleHorse\" translate=\"no\">​</a></h3>\n<p>Using LittleHorse, in java, this whole workflow could look like the following. This is <em>real code</em> that does indeed compile and replaces the need for all of the complex queueing logic we had above.</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">sagaExample</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WorkflowThread</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> item </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"item\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STR</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> customer </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"customer\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STR</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> price </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"price\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">DOUBLE</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> orderId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"order-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STR</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"create-order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Saga Here! (We skipped this part in the previous section due to</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// complexity, but LittleHorse makes it simple enough.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">NodeOutput</span><span class=\"token plain\"> inventoryResult </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"reserve-inventory\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> item</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">handleException</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">inventoryResult</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"out-of-stock\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"cancel-order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fail</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"out-of-stock\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Item was out of stock. Order canceled\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">NodeOutput</span><span class=\"token plain\"> paymentResult </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"charge-payment\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> customer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> price</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Saga here again!!</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">handleException</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">paymentResult</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"credit-card-declined\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> handler </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"release-inventory\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> item</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"cancel-order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        handler</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fail</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"credit-card-declined\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Credit card was declined. Order canceled!\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"complete-order\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>Instead of managing five message queues and five strongly-coupled integration points between microservices, all we need to do is register the workflow, define <em>truly</em> modular tasks, and let LittleHorse take care of the rest.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"wrapping-up\">Wrapping Up<a href=\"https://littlehorse.io/blog/saga-pattern#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\" translate=\"no\">​</a></h2>\n<p>The Saga Pattern is one of five tools we will cover in this series on avoiding the Grumpy Customer Problem. It's simple to understand but <em>painfully complex</em> to implement. Fortunately, LittleHorse makes it easier!</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>A careful reader, or anyone who <a href=\"https://www.linkedin.com/feed/update/urn:li:activity:7244572885179121664/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">reads my rants on LinkedIn</a>, might note that in order to make the order processing workflow truly reliable, we would also need to do something like the Outbox pattern or Event Sourcing.</p><p>That is true, and we'll cover it in the next post (and you'll see how LittleHorse does that for you automatically!).</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"get-involved\">Get Involved!<a href=\"https://littlehorse.io/blog/saga-pattern#get-involved\" class=\"hash-link\" aria-label=\"Direct link to Get Involved!\" title=\"Direct link to Get Involved!\" translate=\"no\">​</a></h3>\n<p>Stay tuned for the next post on the Transactional Outbox Pattern! In the meantime:</p>\n<ul>\n<li class=\"\">Try out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\">Quickstarts</a></li>\n<li class=\"\">Join us <a href=\"https://www.launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on Slack</a></li>\n<li class=\"\">Give us a star <a href=\"https://github.com/littlehorse-enterprises/littlehorse\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on GitHub</a>!</li>\n</ul>",
            "url": "https://littlehorse.io/blog/saga-pattern",
            "title": "Integration Patterns I: Saga Transactions",
            "summary": "How the Saga pattern works, where it breaks down, and how orchestration simplifies distributed transactions.",
            "date_modified": "2024-09-24T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Integration Patterns",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/basics-of-workflow",
            "content_html": "<p>LittleHorse Enterprises is a workflow engine company. But what is a workflow engine?</p>\n<p>It is a system that allows you to reliably execute a series of steps while being robust to technical failures (network outages, crashes) and business process failures. A step in a workflow can be calling a piece of code on a server, reaching out to an external API, waiting for a callback from a person or external system, or more.</p>\n<p>A core challenge when automating a business process is <strong>Failure and Exception Handling:</strong> figuring out what to do when something doesn't happen, happens with an unexpected outcome, or plain simply fails. This is often difficult to reason about, leaving your applications vulnerable to uncaught exceptions, incomplete business workflows, or data loss.</p>\n<p>A workflow engine standardizes how to throw an exception, where the exception is logged, and the logic around when/how to retry. This gives you peace of mind that once a workflow run is started, it will reliably complete.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"workflow-architecture\">Workflow Architecture<a href=\"https://littlehorse.io/blog/basics-of-workflow#workflow-architecture\" class=\"hash-link\" aria-label=\"Direct link to Workflow Architecture\" title=\"Direct link to Workflow Architecture\" translate=\"no\">​</a></h2>\n<p>Any <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts\">workflow-driven application</a> has three components:</p>\n<ol>\n<li class=\"\">A really awesome workflow engine like LittleHorse.</li>\n<li class=\"\">A <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/workflows\">Workflow Specification</a>, which defines the series of steps in your application.</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/tasks\">Task Workers</a>, which are computer programs that execute work when the LittleHorse Server tells it to.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"workflow-specifications\">Workflow Specifications<a href=\"https://littlehorse.io/blog/basics-of-workflow#workflow-specifications\" class=\"hash-link\" aria-label=\"Direct link to Workflow Specifications\" title=\"Direct link to Workflow Specifications\" translate=\"no\">​</a></h3>\n<p>A Workflow Specification (or <code>WfSpec</code> in LittleHorse) is the configuration, or metadata object, that tells the engine what Tasks to run,\nwhat order to run the tasks, <strong>how to handle exceptions or failures,</strong> what variables are to be passed from task to task, and what inputs and outputs are required to run the workflow.</p>\n<p>In LittleHorse the <code>WfSpec</code> is submitted to and held by the LittleHorse server. Users of LittleHorse can define a <code>WfSpec</code> in vanilla code (Java/Go/Python) using the LittleHorse SDK. The SDK will compile your vanilla code into a <code>WfSpec</code> that the LittleHorse Server understands and keeps inside its data store.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>To learn how to write a <code>WfSpec</code> in LittleHorse, check out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/wfspec-development\"><code>WfSpec</code> Development docs</a>.</p></div></div>\n<p>In the background LittleHorse server takes the submitted spec from the SDK, and compiles a protobuf object that is submitted to the LittleHorse server.</p>\n<p>For example, the following code in Java defines a two-step workflow in which we look up the price of an item, charge a customer's credit card, and then ship an item.</p>\n<div class=\"language-java codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-java codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">ECommerceWorkflow</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">void</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">checkoutWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">WorkflowThread</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Create some Workflow Variables</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> customerId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"customer-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">VariableType</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STR</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">searchable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">required</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> itemId </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"item-id\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">VariableType</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STR</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">required</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> price </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">addVariable</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"price\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">VariableType</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">INT</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Fetch Price and save it into a variable</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> priceOutput </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"calculate-price\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> itemId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">mutate</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">price</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">VariableMutationType</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">ASSIGN</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> priceOutput</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Charge credit card (passing in the output from previous task)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"charge-credit-card\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> customerId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> price</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Ship item</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        wf</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">execute</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"ship-item\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> customerId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> itemId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>Just by using LittleHorse to define the above workflow, you get reliability, observability, retries, and governance out of the box!</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"tasks-and-task-workers\">Tasks and Task Workers<a href=\"https://littlehorse.io/blog/basics-of-workflow#tasks-and-task-workers\" class=\"hash-link\" aria-label=\"Direct link to Tasks and Task Workers\" title=\"Direct link to Tasks and Task Workers\" translate=\"no\">​</a></h3>\n<p>Tasks are the unit of work that can be executed a workflow engine. It's best to think in examples:</p>\n<ul>\n<li class=\"\">Change lower case letters to upper case letters.</li>\n<li class=\"\">Call an API with an input variable and pass along the output.</li>\n<li class=\"\">Fetch data from a database.</li>\n<li class=\"\">Convert a message from HL7 version 2.5 to HL7 version 3.</li>\n</ul>\n<p>Task workers are programs that use the LittleHorse SDK, connect to LittleHorse, and execute tasks when the workflow says it's time to do so.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>To learn how to write a Task Worker, check out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/task-worker-development\">Task Worker Development Guide</a>.</p></div></div>\n<p>You can also use <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/external-events\">External Events</a> or <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/user-tasks\">User Tasks</a> to wait for input from a human user or an external system (like a callback or webhook).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"workflow-clients\">Workflow Clients<a href=\"https://littlehorse.io/blog/basics-of-workflow#workflow-clients\" class=\"hash-link\" aria-label=\"Direct link to Workflow Clients\" title=\"Direct link to Workflow Clients\" translate=\"no\">​</a></h3>\n<p>Lastly you need to tell LittleHorse when to run a workflow. You can do it with our CLI (<code>lhctl</code>) but in production you'll need to use the LittleHorse SDK to kick off a workflow. You can do this with our page on <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/grpc/running-workflows\">Running Workflows using grpc</a></p>\n<p>You'll also need to tell LittleHorse about External Events that happen. You can also do this using <code>lhctl</code> or <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/grpc/posting-external-events\">with our SDK's</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"littlehorse-use-cases\">LittleHorse Use-Cases<a href=\"https://littlehorse.io/blog/basics-of-workflow#littlehorse-use-cases\" class=\"hash-link\" aria-label=\"Direct link to LittleHorse Use-Cases\" title=\"Direct link to LittleHorse Use-Cases\" translate=\"no\">​</a></h2>\n<p>There are many different types of workflow engines, each of which supports different use-cases. For example:</p>\n<ul>\n<li class=\"\"><strong>Batch ETL and Cronjob</strong> workflows are automated by systems like Apache Airflow and Dagster.</li>\n<li class=\"\"><strong>Infrastructure Provisioning and Configuration</strong> workflows can be automated by Ansible, Argo, and Jenkins.</li>\n<li class=\"\"><strong>IT Integration and BPM</strong> workflows may be automated by systems like Camunda and jBPM.</li>\n</ul>\n<p>However, <strong>LittleHorse allows you to compose business processes across your software systems.</strong> Some use-cases are included below.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"microservices\">Microservices<a href=\"https://littlehorse.io/blog/basics-of-workflow#microservices\" class=\"hash-link\" aria-label=\"Direct link to Microservices\" title=\"Direct link to Microservices\" translate=\"no\">​</a></h3>\n<p>All microservice-based applications are inherently distributed systems with the goal of supporting some business process (because no one writes microservices for the sake of writing code, right?). While often necessary, microservices <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices\">present many challenges</a> to developers due to their distributed nature.</p>\n<p>Our founder Colt McNealy wrote a <a class=\"\" href=\"https://littlehorse.io/blog/microservices-and-workflow\">detailed blog</a> about how a workflow engine's reliabile state management and oversight can mitigate some of the problems inherent in microservices. Check it out!</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"human-in-the-loop\">Human-in-the-Loop<a href=\"https://littlehorse.io/blog/basics-of-workflow#human-in-the-loop\" class=\"hash-link\" aria-label=\"Direct link to Human-in-the-Loop\" title=\"Direct link to Human-in-the-Loop\" translate=\"no\">​</a></h3>\n<p>Workflows often need to get input from humans:</p>\n<ul>\n<li class=\"\">Approval flows.</li>\n<li class=\"\">Waiting for information from customers.</li>\n<li class=\"\">Handling exceptional scenarios.</li>\n</ul>\n<p>That's hard to coordinate without a workflow engine. You'd have to build your own state management system that correlates tasks to workflows. LittleHorse <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/user-tasks\">User Tasks</a> make this much easier.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"rag-and-ai\">RAG and AI<a href=\"https://littlehorse.io/blog/basics-of-workflow#rag-and-ai\" class=\"hash-link\" aria-label=\"Direct link to RAG and AI\" title=\"Direct link to RAG and AI\" translate=\"no\">​</a></h3>\n<p>AI is only useful when you call it at the right time, with the right inputs, and do something with the outputs. That's a workflow. And all sorts of things can go wrong when using LLM's, which is why you need to have a robust workflow engine to provide oversight and exception handling.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"legacy-system-modernization\">Legacy System Modernization<a href=\"https://littlehorse.io/blog/basics-of-workflow#legacy-system-modernization\" class=\"hash-link\" aria-label=\"Direct link to Legacy System Modernization\" title=\"Direct link to Legacy System Modernization\" translate=\"no\">​</a></h3>\n<p>Whether you are integrating legacy systems that you inherited from the past, or integrating multiple tech stacks accrued through M&amp;A, your customers expect a real-time experience that seamlessly spans all of your systems. Workflow engines are useful for reliably composing actions and moving data across multiple different systems.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"api-gateway\">API gateway<a href=\"https://littlehorse.io/blog/basics-of-workflow#api-gateway\" class=\"hash-link\" aria-label=\"Direct link to API gateway\" title=\"Direct link to API gateway\" translate=\"no\">​</a></h3>\n<p>If we look at the properties of an API gateway and how they are used, a workflow engine makes sense.</p>\n<p>The usage of an API gateway is to have a single layer that abstracts further endpoints.\nIn practice this most often means calling the same API gateway multiple times, receiving the requested data, and doing some date manipulation or calculations at the application layer.\nA workflow engine performs all of the most common actions, and includes things like centralized security, possible data obscurity, failure handling, observability and allows for operators to scale compute.\nAll while still maintaining a central plane that can be shared across an entire orginization.\nAdditionally a workflow engine still allows for the standard CRUD(Create, Read, Update, Delete) operations that an API gateway provides.</p>",
            "url": "https://littlehorse.io/blog/basics-of-workflow",
            "title": "The Basics of Workflow",
            "summary": "A practical introduction to workflow engines, workflow specifications, and durable execution with LittleHorse.",
            "date_modified": "2024-09-04T00:00:00.000Z",
            "author": {
                "name": "Mitchell Henderson"
            },
            "tags": [
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/microservices-and-workflow",
            "content_html": "<p>While they are often necessary, microservices are a headache. Fortunately, the right workflow engine (such as LittleHorse) can drastically reduce the difficulty of managing microservices.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the third and final part of a 3-part blog series:</p><ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/promise-of-microservices\">The Promise of Microservices</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices\">The Challenge with Microservices</a></li>\n<li class=\"\"><strong>[This Post]</strong> Workflow and Microservices: A Match Made in Heaven</li>\n</ol></div></div>\n<p>If you're just joining for the third blog post, we have so far established that microservices are an effective tool for allowing your engineering team to grow beyond just a handful of people working on an enterprise application. However, microservice systems are by nature <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices#microservices-are-leaderless\"><strong>Leaderless</strong></a> and <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices#microservices-are-distributed\"><strong>Distributed</strong></a>, which yields challenges in:</p>\n<ul>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices#observability\"><strong>Observability</strong></a>,</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices#reliability-and-correctness\"><strong>Reliability</strong></a>, and</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices#microservice-coupling\"><strong>Complexity Management</strong></a>.</li>\n</ul>\n<p>Those challenges inspired me to create <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts\">LittleHorse</a> in the fall of 2021. LittleHorse provides primitives and guardrails out of the box which make it easier to wrangle with distributed systems and coordinate processes/transactions across multiple microservices.</p>\n<p>In this post, we'll discuss:</p>\n<ol>\n<li class=\"\">What <em>workflow</em> means.</li>\n<li class=\"\">How LittleHorse's workflow composition capabilities make it easier for you to reliably compose complex business processes.</li>\n</ol>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>Want to give LittleHorse a try? Get in touch with us!</p><ul>\n<li class=\"\">Join the <a href=\"https://www.launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><strong>LittleHorse Slack Community</strong></a> for the latest news and help from community experts.</li>\n<li class=\"\">Check out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\"><strong>Getting Started</strong></a> page.</li>\n<li class=\"\"><a href=\"https://docs.google.com/forms/d/e/1FAIpQLScXVvTYy4LQnYoFoRKRQ7ppuxe0KgncsDukvm96qKN0pU5TnQ/viewform\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><strong>Say hello</strong></a> if you'd like to get in touch with someone from the LittleHorse Enterprises team.</li>\n</ul></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-is-a-workflow\">What is a Workflow?<a href=\"https://littlehorse.io/blog/microservices-and-workflow#what-is-a-workflow\" class=\"hash-link\" aria-label=\"Direct link to What is a Workflow?\" title=\"Direct link to What is a Workflow?\" translate=\"no\">​</a></h2>\n<p>A workflow is a blueprint that defines a series of tasks to be performed (perhaps conditioned on certain inputs or external events) in order to achieve a business outcome.</p>\n<p>If you recall the e-commerce example from the <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices#the-nature-of-microservices\">previous blog post</a>, you can think of the abstract checkout process as a workflow. This example is interesting because it demonstrates multiple characteristics of common business processes that make microservice development hard.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"E-Commerce Checkout Process Diagram\" src=\"https://littlehorse.io/assets/images/2024-08-27-complex-checkout-915172cb1dfafc0a3e9a7cc0b042ac3a.png\" width=\"1472\" height=\"406\" class=\"img_ev3q\"></p>\n<p>First, a workflow can be <em>mission critical</em>. A customer would be very unhappy if the vendor charged their credit card but failed to ship their order. In technical terms, this means that the state of a workflow needs to be consistent and durable, which is hard to achieve in a distributed system.</p>\n<p>Next, a workflow can have exceptional cases. Our e-commerce flow has special logic to handle cases when the customer's credit card was invalid or when the ordered item was out of stock.</p>\n<p>Finally, a workflow can be <em>asynchronous</em>, meaning that it requires waiting for input from the external world in order to complete. For example, our e-commerce workflow sometimes must wait for a customer to update their credit card information before completing.</p>\n<p>The mission-critical nature of workflows, combined with asynchronous events and exceptional cases, places a premium on <em>consistency.</em> The results of workflows must be predictable for customers and easy to reason about for business managers and software engineers.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>A technical or business process does not need to satisfy all three characteristics to be a \"workflow.\" In fact, simple processes with just one or two linear steps can benefit from a workflow engine.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"workflow-engines\">Workflow Engines<a href=\"https://littlehorse.io/blog/microservices-and-workflow#workflow-engines\" class=\"hash-link\" aria-label=\"Direct link to Workflow Engines\" title=\"Direct link to Workflow Engines\" translate=\"no\">​</a></h3>\n<p>A workflow engine is a software system that makes sure the trains run on time in your processes. To use a workflow engine, you must:</p>\n<ol>\n<li class=\"\"><strong>Define your Tasks</strong>, which are units of work that can be executed in a workflow, and write <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/tasks\">Task Workers</a> which implement small functions or methods in code to execute those tasks.</li>\n<li class=\"\"><strong>Register a Workflow Specification</strong> (we call it a <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/workflows\"><code>WfSpec</code> in LittleHorse</a>) which specifies what tasks to execute and when.</li>\n<li class=\"\"><strong>Run your workflow</strong> so that the workflow engine can compose the process to completion.</li>\n</ol>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"LittleHorse Architecture\" src=\"https://littlehorse.io/assets/images/2024-08-28-lh-application-8847f083fce679fe41abec88d4375125.png\" width=\"1846\" height=\"1019\" class=\"img_ev3q\"></p>\n<p><a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/task-worker-development\">Task Workers</a> are where a workflow can interface with the outside world. Since a Task in a workflow results in the LittleHorse SDK calling a programming function/method of your choosing, Task Workers allow LittleHorse to integrate with any system. Task Workers can make database queries, call external API's, provision infrastructure on AWS, send push notifications to customer mobile apps, perform calculations, call an LLM API, and more.</p>\n<p>In LittleHorse, the <code>WfSpec</code> is <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/wfspec-development\">defined in code</a> in a language of your choice. Because LittleHorse was written with developers in mind, our DSL's have all of the primitives that you'd expect in a programming language: variables, control flow, exception handling, child threads, interrupts, and awaiting for external events. This allows workflows to be:</p>\n<ul>\n<li class=\"\">Easy to reason about.</li>\n<li class=\"\">Tracked in version control.</li>\n<li class=\"\">Familiar and easy to learn.</li>\n</ul>\n<p>Once you tell LittleHorse to <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/grpc/running-workflows\">run an instance of your <code>WfSpec</code></a>, LittleHorse will oversee the entire process until it completes. Failed tasks will be retried, every step will be journaled, and the state of your processes will be safely and durably persisted while waiting for external triggers.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-workflow\">Why Workflow?<a href=\"https://littlehorse.io/blog/microservices-and-workflow#why-workflow\" class=\"hash-link\" aria-label=\"Direct link to Why Workflow?\" title=\"Direct link to Why Workflow?\" translate=\"no\">​</a></h2>\n<p>Microservice applications that are designed as distributed workflows without a workflow engine (like a chain of dominoes falling) present operational challenges because there is no \"leader\" providing oversight over the microservice processes. Thankfully, a developer-focused and horizontally-scalable workflow engine like LittleHorse can fill the \"leader\" role, thus providing oversight and reliability, and taming the complexity of your business processes.</p>\n<p>Additionally, using a workflow engine allows you to develop a set of <em>reusable</em> and <em>modular</em> tasks which can be easily dropped into any business workflow with a common API. Rather than accumulating tech debt, workflow engines allow you to accumulate a set of useful lego bricks.</p>\n<p>In most existing organizations there's a long list of API calls required to simply <em>run</em> a workflow. Training engineers to use all of the new APIs while securely distributing access and permissions causes confusion and slow development cycles. Workflow engines provide a single API and single system that allows anyone to securely manage, run, and operate complex workflows.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"mission-critical-oversight\">Mission Critical Oversight<a href=\"https://littlehorse.io/blog/microservices-and-workflow#mission-critical-oversight\" class=\"hash-link\" aria-label=\"Direct link to Mission Critical Oversight\" title=\"Direct link to Mission Critical Oversight\" translate=\"no\">​</a></h3>\n<p>Mission critical business workflows leave no room for technical failures and outages. However, as we discussed <a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices#reliability-and-correctness\">last week</a>, the distributed nature of microservices means that technical failures are not likely but rather certain. LittleHorse provides retries and durable execution capabilities out of the box, removing the need to create complex infrastructure for cross-service transactions (such as dead-letter queues, Outbox tables, and the SAGA pattern).</p>\n<p>Additionally, mission-critical processes must be <em>audited</em> and <em>observed</em> in a secure manner with proper access controls. LittleHorse supports this?every step in a workflow is journaled, auditable, and searchable in our dashboard. When humans execute <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/user-tasks\">User Tasks</a>, you can view an audit trail of when and to whom it was assigned and executed; you can see when each <code>TaskRun</code> started, completed, and failed (and with what inputs). Our <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/principals-and-tenants\">ACL's and Multi-Tenancy</a> capabilities (and \"Masked Data\") ensure that the data remains accessible only to those who must see it.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"simple-asynchronous-processing\">Simple Asynchronous Processing<a href=\"https://littlehorse.io/blog/microservices-and-workflow#simple-asynchronous-processing\" class=\"hash-link\" aria-label=\"Direct link to Simple Asynchronous Processing\" title=\"Direct link to Simple Asynchronous Processing\" translate=\"no\">​</a></h3>\n<p>For microservice developers, handling asynchronous business processes is challenging because it forces you to persist state, correlate events, and wire together callbacks into a non-linear flow. Developers often need to create database tables for ongoing transactions and maintain complex flow diagrams showing how different services integrate with business events.</p>\n<p>However, LittleHorse provides two primitives to simplify this process:</p>\n<ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/external-events\"><strong>External Events</strong></a> allow workflows to block until something happens in the outside world, and then resume processing immediately thereafter.</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/user-tasks\"><strong>User Tasks</strong></a> are like External Events but they model getting input from humans. User Tasks support reminders, assignment, groups, and users.</li>\n</ol>\n<p>Together, User Tasks and External Events allow developers to transform complex asynchronous flows (such as our e-commerce example when we wait for a customer to provide a new credit card) into a more manageable linear flow.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"exception-handling\">Exception Handling<a href=\"https://littlehorse.io/blog/microservices-and-workflow#exception-handling\" class=\"hash-link\" aria-label=\"Direct link to Exception Handling\" title=\"Direct link to Exception Handling\" translate=\"no\">​</a></h3>\n<p>Finally, just as processes can fail at the technical level, they can also fail at the business level. As per our ongoing e-commerce example, cards can run out of funds, items go out of stock, customers can cancel orders while they are being processed.</p>\n<p>Handling any given exceptional case in a business workflow might involve actions in several different microservices. Without a workflow engine, therefore, each exceptional case results in more and more complex interdependencies in your microservices, creating the notoriously feared \"Distributed Monolith.\"</p>\n<p>In contrast, with LittleHorse as your workflow composer, the dependencies between microservices are mitigated and workflow concepts such as <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/exception-handling\">Failure Handling</a> allow you to easily define rollbacks, SAGA patterns, and edge cases without introducing further accidental complexity into your microservices. This allows startups and enterprises alike to implement robust, enterprise-grade business applications without accumulating costly technical debt.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"conclusion\">Conclusion<a href=\"https://littlehorse.io/blog/microservices-and-workflow#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>For a variety of reasons, startups and enterprises alike may need to work with microservices despite the challenges they bring. Thankfully, workflow engines like LittleHorse can mitigate those problems by providing oversight into your entire process.</p>\n<p>At the LittleHorse Council, we are very excited about the upcoming 1.0 release. Over the next few weeks, we will:</p>\n<ul>\n<li class=\"\">Complete additional load tests, chaos tests, and benchmarks in preparation for 1.0.</li>\n<li class=\"\">Blog about how you can write an e-commerce workflow in LittleHorse with Python.</li>\n<li class=\"\">Do final testing before we release!</li>\n</ul>\n<p>And if you enjoyed this post, give us a star <a href=\"https://github.com/littlehorse-enterprises/littlehorse\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">on GitHub</a> and try out <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\">our quickstarts</a> to get going with LittleHorse in under 5 minutes.</p>",
            "url": "https://littlehorse.io/blog/microservices-and-workflow",
            "title": "Microservices and Workflow: A Match Made in Heaven",
            "summary": "How workflow engines like LittleHorse reduce the complexity of orchestrating microservices.",
            "date_modified": "2024-09-02T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Microservices and Workflow",
                "Microservices",
                "Orchestration"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/challenge-of-microservices",
            "content_html": "<p>Microservices are often necessary, but unfortunately they bring with them some baggage. </p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the second part of a 3-part blog series:</p><ol>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/promise-of-microservices\">The Promise of Microservices</a></li>\n<li class=\"\"><strong>[This Post]</strong> The Challenge with Microservices</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/microservices-and-workflow\">Workflow and Microservices: A Match Made in Heaven</a></li>\n</ol></div></div>\n<p>Last week, I <a class=\"\" href=\"https://littlehorse.io/blog/promise-of-microservices\">blogged</a> about the problems that microservices solve, and why they are not only beneficial but necessary in some cases (a good bellwether is the size of your engineering team: beyond 1 or 2 dozen engineers, you will probably start to feel some problems that can be solved with microservices).</p>\n<p>When done correctly, microservices remove several bottlenecks to scaling your business. However, even well-architected microservices bring significant <em>accidental complexity</em>.</p>\n<p>In particular, microservices are:</p>\n<ol>\n<li class=\"\">Harder to <strong>observe</strong> and debug.</li>\n<li class=\"\">Harder to make <strong>reliable</strong> in the case of infrastructure or sofware failures.</li>\n<li class=\"\">More complex to <strong>maintain</strong> and evolve with changing business practices.</li>\n</ol>\n<p>In this article we will explore how the above problems arise from two key facts:</p>\n<ul>\n<li class=\"\">Microservices are <strong>distributed</strong>.</li>\n<li class=\"\">Microservices are <strong>choreographed without a leader</strong>.</li>\n</ul>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>Microservices bring with them additional challenges around operationalization and deployment. However, those challenges are out-of-scope for this blog post as we instead choose to focus on the challenges faced by <em>application development teams</em> rather than operations teams.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-nature-of-microservices\">The Nature of Microservices<a href=\"https://littlehorse.io/blog/challenge-of-microservices#the-nature-of-microservices\" class=\"hash-link\" aria-label=\"Direct link to The Nature of Microservices\" title=\"Direct link to The Nature of Microservices\" translate=\"no\">​</a></h2>\n<p>As I described in <a class=\"\" href=\"https://littlehorse.io/blog/promise-of-microservices\">last week's blog</a>:</p>\n<blockquote>\n<p>The term \"microservices\" refers to a software architecture wherein an enterprise application comprises a collection of small, loosely coupled, and independently deployable services (these small services are called \"microservices\" in contrast to larger monoliths). Each microservice focuses on a specific business capability and communicates with other services over a network, typically through API's, streaming platforms, or message queues.</p>\n</blockquote>\n<p>Crucially, a single microservice implements technical logic for a specific domain, or bounded context, within the larger company. In contrast, a comprehensive business process requires interacting with technology and people across <em>many</em> business domains. The classic example of microservices architecture, e-commerce checkout, involves at least <em>shipping</em>, <em>billing</em>, <em>notifications</em>, <em>inventory</em>, and <em>orders</em>.</p>\n<p>In the rest of this blog post we will examine microservices through the the lense of e-commerce checkout flow. To start with a simple use-case, the logical flow we will consider is:</p>\n<ol>\n<li class=\"\">When an order is placed, we create a record in a database in the <code>orders</code> service.</li>\n<li class=\"\">We then reserve inventory (and ensure that the item is in stock) in the <code>inventory</code> service.</li>\n<li class=\"\">We charge the customer using the <code>payments</code> service.</li>\n<li class=\"\">Next, we ship the item using the <code>shipping</code> service.</li>\n<li class=\"\">Finally, the <code>notifications</code> service notifies the customer that the parcel is on its way.</li>\n</ol>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Simple e-commerce workflow\" src=\"https://littlehorse.io/assets/images/2024-08-27-simple-checkout-301d7115bb5b77f34c84342998c2921d.png\" width=\"1472\" height=\"141\" class=\"img_ev3q\"></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"microservices-are-distributed\">Microservices are Distributed<a href=\"https://littlehorse.io/blog/challenge-of-microservices#microservices-are-distributed\" class=\"hash-link\" aria-label=\"Direct link to Microservices are Distributed\" title=\"Direct link to Microservices are Distributed\" translate=\"no\">​</a></h3>\n<p>Recall that each service (in the workflow diagram above, each box) is its own deployable artifact. That means that the happy-path business process described above will involve five different software systems from start-to-finish.</p>\n<p>In the above workflow diagram, each arrow can be accurately interpreted in two ways:</p>\n<ol>\n<li class=\"\">The logical flow of the business process.</li>\n<li class=\"\">The physical flow of information between microservices, either through network RPC calls or through a message broker like Apache Kafka.</li>\n</ol>\n<p>Guess what! This means we have a distributed system by definition. As Splunk <a href=\"https://www.splunk.com/en_us/blog/learn/distributed-systems.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">writes in a blog post</a>:</p>\n<blockquote>\n<p>A distributed system is simply any environment where multiple computers or devices are working on a variety of tasks and components, all spread across a network.</p>\n</blockquote>\n<p>You need to look no further than the <a href=\"https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Fallacies of Distributed Computing</a> (written by Sun Microsystems Fellow L. Peter Deutsch in 1994) to see that this means that microservices are no easy task.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"microservices-are-leaderless\">Microservices are Leaderless<a href=\"https://littlehorse.io/blog/challenge-of-microservices#microservices-are-leaderless\" class=\"hash-link\" aria-label=\"Direct link to Microservices are Leaderless\" title=\"Direct link to Microservices are Leaderless\" translate=\"no\">​</a></h3>\n<p>As we've seen already, any microservice-based application is a distributed system. Some distributed systems have the concept of a <em>leader</em>, which is a special node in the system that has special responsibilities.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>Apache Kafka is my favorite distributed system. In Apache Kafka, the <em>Controller</em> is a special Kafka server that is responsible for deciding which partition replicas are hosted on (and led by) which brokers. If the broker who was in charge of a partition goes down, then the Controller chooses a new broker from the ISR to take its place.</p><p>Therefore, the <em>Controller</em> in Apache Kafka can be thought of as a <em>leader</em>.</p></div></div>\n<p>While systems like Apache Kafka have clear leaders (for example, the <em>Controller</em> may re-assign partition leadership if the cluster becomes too imbalanced), in a microservice-based system there is no central leader to ensure that the chips fall correctly. This is by necessity, because the separation of development concerns and lifecycles across microservices means that microservices cannot and do not have leaders.</p>\n<p>You can think of our e-commerce microservice flow as a line of dominoes falling. Once the process starts, no one entity is responsible for ensuring its completion. The business workflow moves from <code>orders</code> to <code>inventory</code> to <code>payments</code> and so on. If <code>payments</code> fails for some reason (perhaps a network outage makes the Stripe API unavailable), then it's quite possible that the <code>shipping</code> service never finds out about the workflow.</p>\n<p>However, in real life such outcomes are not acceptable. This means that every single player in the system must:</p>\n<ol>\n<li class=\"\">Have built-in reliability mechanisms.</li>\n<li class=\"\">Understand the preceding and subsequent steps of the business process to route traffic.</li>\n</ol>\n<p>Implementing the above slows down development, more tightly couples one services to another, increases dependencies, and makes your microservice architecture much more heavyweight.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-challenges\">The Challenges<a href=\"https://littlehorse.io/blog/challenge-of-microservices#the-challenges\" class=\"hash-link\" aria-label=\"Direct link to The Challenges\" title=\"Direct link to The Challenges\" translate=\"no\">​</a></h2>\n<p>So far, we have established that there are many players involved in a business process, yet there's no one composer involved in ensuring that an ordered item is delievered to the the correct address. This yields three problems:</p>\n<ol>\n<li class=\"\"><strong>Reliability</strong> in the face of infrastructure failures.</li>\n<li class=\"\"><strong>Observability</strong> to enable system optimization and debugging.</li>\n<li class=\"\"><strong>Coupling</strong> of microservices to each other makes it hard to modify the system in response to new business requirements.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"reliability-and-correctness\">Reliability and Correctness<a href=\"https://littlehorse.io/blog/challenge-of-microservices#reliability-and-correctness\" class=\"hash-link\" aria-label=\"Direct link to Reliability and Correctness\" title=\"Direct link to Reliability and Correctness\" translate=\"no\">​</a></h3>\n<p>Processing orders is a mission-critical use-case. This means that orders should always complete and never be dropped (for example, we should not charge the customer's credit card and not ship the product to them).</p>\n<p>However, asynchronous processing such as that which I outlined above is prone to failures. For example, if you chain microservices together with direct RPC calls, a single network partition can cause an order to get stuck. Even with a reliable message broker such as Apache Kafka or AWS SQS sitting between your microservices, a write to the message broker could fail <em>after</em> the payment went through, still resulting in a stuck order.</p>\n<p>Just as communication <em>between</em> microservices can fail, the actions performed _by_each microservice can also fail. In many cases actions performed by a microservice depend upon failure-prone external systems and API's. If the Stripe API is down, or if the credit card is invalid, we can't just stop processing the order there! We must notify the customer of what went wrong and also release the inventory that we reserved.</p>\n<p>This means that microservice developers spend countless hours building out infrastructure to support:</p>\n<ul>\n<li class=\"\">Retries</li>\n<li class=\"\">Dead-Letter Queues</li>\n<li class=\"\">Rate-limiting</li>\n<li class=\"\">Timeouts</li>\n<li class=\"\">Transactional Outbox pattern</li>\n<li class=\"\">SAGA Pattern</li>\n</ul>\n<p>Back to the domino analogy, if one domino misses the next, the entire chain just stops.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"observability\">Observability<a href=\"https://littlehorse.io/blog/challenge-of-microservices#observability\" class=\"hash-link\" aria-label=\"Direct link to Observability\" title=\"Direct link to Observability\" translate=\"no\">​</a></h3>\n<p>The second problem with microservices is that once a process instance has started (i.e. the dominoes are falling), it is very difficult to observe what happens between steps 2 through 10. This means that multi-step processes with performance issues are hard to optimize, as there are many microservices which could be the bottleneck and it's hard to know which. Even worse, when a customer complains about a \"stuck order,\" it is difficult to find the point of failure.</p>\n<p>As a result, microservice engineers spend time and money:</p>\n<ul>\n<li class=\"\">Slogging through logs on DataDog</li>\n<li class=\"\">Implementing complex distributed tracing such as Zipkin, Jaeger, or Kiali</li>\n<li class=\"\">Saving the state of each process instance (in our case, the <code>order</code>) in a DB just for visibility purposes at every step</li>\n<li class=\"\">Coordinating with other teams to manually understand and debug workflows.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"microservice-coupling\">Microservice Coupling<a href=\"https://littlehorse.io/blog/challenge-of-microservices#microservice-coupling\" class=\"hash-link\" aria-label=\"Direct link to Microservice Coupling\" title=\"Direct link to Microservice Coupling\" translate=\"no\">​</a></h3>\n<p>Lastly, because microservices are leaderless, each player in the end-to-end process must have hard-coded integrations with the preceding and subsequent steps. This results in:</p>\n<ul>\n<li class=\"\"><strong>Process coupling</strong>, wherein changing a business process results in significant code updates to rewire the message queues or RPC calls between two steps.</li>\n<li class=\"\"><strong>Schema coupling</strong>, wherein different microservices have strong dependencies on each others' schemas.</li>\n</ul>\n<p>Microservices come with the promise of loose coupling; however, the unfortunate reality is that this is often not the case. As a result, teams often do have to coordinate with each other during deployments.</p>\n<p>To see an example of the complexity introduced by coupling of microservices, let's consider what happens to our e-commerce checkout workflow when we add a few edge cases to make it more realistic:</p>\n<ol>\n<li class=\"\">If the credit card is invalid, we request the customer to provide a new one, wait for two days, and either complete or cancel the order.</li>\n<li class=\"\">If the item is out of stock, we notify the customer who elects either to wait or cancel the order.</li>\n</ol>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Complex Checkout Architecture\" src=\"https://littlehorse.io/assets/images/2024-08-27-complex-checkout-915172cb1dfafc0a3e9a7cc0b042ac3a.png\" width=\"1472\" height=\"406\" class=\"img_ev3q\"></p>\n<p>In the above diagram, each arrow represents the flow of the business process <em>and</em> information. Each microservice must have custom logic which sends information to the right place. In essence, while we <em>intended</em> to have modular microservices that understand only their own Bounded Context, what we have is tightly-coupled systems which must understand pretty much the entire business workflow.</p>\n<p>Therefore, when business requirements change, unrelated microservices end up having to change their internal implementation as well.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"looking-forward\">Looking Forward<a href=\"https://littlehorse.io/blog/challenge-of-microservices#looking-forward\" class=\"hash-link\" aria-label=\"Direct link to Looking Forward\" title=\"Direct link to Looking Forward\" translate=\"no\">​</a></h2>\n<p>Microservices have clear and proven benefits, and are often not just advantageous but <em>necessary</em> in some cases. However, as we discussed today, those benefits do not come without a cost. Because microservices are inherently distributed systems, challenges such as reliability, observability, and coordination are exacerbated.</p>\n<p>Without spoiling the punchline of the next blog post, these challenges are why I started LittleHorse almost three years ago. Stay tuned for a description of how a <em>workflow composer</em> can alleviate a good portion of the headaches that come along with microservices.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"business-analytics\">Business Analytics<a href=\"https://littlehorse.io/blog/challenge-of-microservices#business-analytics\" class=\"hash-link\" aria-label=\"Direct link to Business Analytics\" title=\"Direct link to Business Analytics\" translate=\"no\">​</a></h3>\n<p>Astute readers may notice that when discussing the e-commerce checkout example, we didn't discuss the problem of <em>analytics.</em> We focused exclusively on online transaction processing, or ensuring that the orders are properly fulfilled and processed. However, no attention was paid to business analytics to optimize future sales!</p>\n<p>This area is yet another challenge. The LittleHorse Council is working on a major feature (an output Kafka Topic with records for anytime something <em>interesting</em> happens inside a <code>WfRun</code>) for the LittleHorse Server that will address this. Don't worry, we'll blog about it soon <!-- -->😉<!-- -->.</p>",
            "url": "https://littlehorse.io/blog/challenge-of-microservices",
            "title": "The Challenge of Microservices",
            "summary": "The operational and architectural challenges that emerge as microservice systems grow in size and complexity.",
            "date_modified": "2024-08-27T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Microservices and Workflow",
                "Microservices"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/promise-of-microservices",
            "content_html": "<p>If microservices add so much complexity, why bother with the hassle? </p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>This is the first part of a 3-part blog series:</p><ol>\n<li class=\"\"><strong>[This Post]</strong> The Promise of Microservices</li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/challenge-of-microservices\">The Challenge with Microservices</a></li>\n<li class=\"\"><a class=\"\" href=\"https://littlehorse.io/blog/microservices-and-workflow\">Workflow and Microservices: A Match Made in Heaven</a></li>\n</ol></div></div>\n<p>We've all <em>heard of</em> microservices, but unless you've read copious amounts of Sam Newman and Adam Bellemare's writings, you might be wondering whether, when, and why you should adopt them. In this blog post, we will examine the halcyon land promised by microservices.</p>\n<p>Microservices have been <a href=\"https://www.simform.com/blog/microservices-examples/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">deployed widely</a> across many large enterprises, most notably Netflix, Uber, Shopify, PayPal, and others. As we will discover throughout this blog series, a microservice architecture is mandatory once you reach a certain size of company, and it's probably overkill for a 12-person startup. The gray area inbetween is the interesting part!</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-are-microservices\">What are Microservices?<a href=\"https://littlehorse.io/blog/promise-of-microservices#what-are-microservices\" class=\"hash-link\" aria-label=\"Direct link to What are Microservices?\" title=\"Direct link to What are Microservices?\" translate=\"no\">​</a></h2>\n<p>The term \"microservices\" refers to a software architecture wherein an enterprise application comprises a collection of small, loosely coupled, and independently deployable services (these small services are called \"microservices\" in contrast to larger monoliths). Each microservice focuses on a specific business capability and communicates with other services over a network, typically through API's, streaming platforms, or message queues.</p>\n<p>In practice, this means that a user interaction with an application (such as placing an order) might trigger actions that occur in <em>many</em> small, independently-deployed software systems, such as:</p>\n<ul>\n<li class=\"\">A Notification service</li>\n<li class=\"\">An Inventory Management service</li>\n<li class=\"\">A Payments service</li>\n<li class=\"\">An Order History service</li>\n</ul>\n<p>From the user (client) perspective, one request is made (generally through a Load Balancer, API Gateway, or Ingress Controller) but that request may ping-pong between multiple back-end services and may also result in future actions being scheduled asynchronously:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Microservices Architecture\" src=\"https://littlehorse.io/assets/images/2024-08-22-microservices-arch-2389e95f878235ea53f0c0afa2eea967.png\" width=\"861\" height=\"962\" class=\"img_ev3q\"></p>\n<p>In contrast to microservices, a <em>monolithic</em> architecture would serve the entire \"place order\" request on a single deployable artifact:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Monolithic Architecture\" src=\"https://littlehorse.io/assets/images/2024-08-22-monolith-arch-894351f807fd64060d9d05380fc799cf.png\" width=\"250\" height=\"499\" class=\"img_ev3q\"></p>\n<p>In Domain Driven Design, accidental complexity refers to the unintentional complexity that you introduced to your architecture (deployments, service interactions, third-party dependencies, etc.). Rule #1 of maintaining software systems is to avoid introducing accidental complexity as much as possible.</p>\n<p>Simply by looking at the visuals above, microservices add a significant dose of accidental complexity to your architecture (more on this in next week's post!). Given that, what benefits would make up for the extra complexity introduced by microservices?</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-now\">Why Now?<a href=\"https://littlehorse.io/blog/promise-of-microservices#why-now\" class=\"hash-link\" aria-label=\"Direct link to Why Now?\" title=\"Direct link to Why Now?\" translate=\"no\">​</a></h2>\n<p>I would be first to admit that microservices bring with them a series of headaches around cost, observability, maintenance, and ease of evolution (otherwise, I would not have founded LittleHorse Enterprises!). However, microservice architecture plays a vital role in addressing two critical trends reshaping the software development landscape today:</p>\n<ul>\n<li class=\"\">Increased digitization of companies in all business sectors (accelerated by the rise of AI).</li>\n<li class=\"\">Elasticity of cloud computing.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"increased-digitization\">Increased Digitization<a href=\"https://littlehorse.io/blog/promise-of-microservices#increased-digitization\" class=\"hash-link\" aria-label=\"Direct link to Increased Digitization\" title=\"Direct link to Increased Digitization\" translate=\"no\">​</a></h3>\n<p>The level of digitization expected of businesses in order to compete in the modern market has drastically increased: IT teams must build software that interfaces with an ever-expanding list of external API's, legacy systems, user interfaces, internal tools, and SaaS providers.</p>\n<p>For example: in the early 2000's, it was perfectly acceptable (even <em>expected</em>) for a passenger to book airline tickets over the telephone or through a travel agency. However, such an experience would be unheard of today and would immediately hobble an airline who provided such poor digital services.</p>\n<p>In addition to using automation to provide better customer services, companies are generating, processing, and analyzing massive amounts of data. For example, grocery stores with razor-thin margins analyze seasonal consumption patterns in order to optimize inventory and prevent costly food waste.</p>\n<p>These trends have coincided with (or <em>caused</em>, I would argue) a proliferation in the number of 1) software developers, and 2) software tools and API's found within companies in all industries, leading to two new problems:</p>\n<ol>\n<li class=\"\">Allowing large teams of software developers to productively work on an enterprise application in parallel (without stepping on each others' toes).</li>\n<li class=\"\">Ensuring that business requirements are effectively communicated to the entire (larger) software engineering team.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"cloud-elasticity\">Cloud Elasticity<a href=\"https://littlehorse.io/blog/promise-of-microservices#cloud-elasticity\" class=\"hash-link\" aria-label=\"Direct link to Cloud Elasticity\" title=\"Direct link to Cloud Elasticity\" translate=\"no\">​</a></h3>\n<p>As the importance and quantity of digital software systems exploded over the last two decades, so has the availability of nearly-infinite compute power delivered through cloud infrastructure providers such as AWS.</p>\n<p>The promise of <em>elasticity</em>, or the ability to quickly spin compute resources up or down according to load and only pay for what you use, is unique to the cloud: for on-prem datacenters, spinning up new compute means buying new machines from Sun Microsystems (hopefully not Microsoft!), and scaling down compute means trying to sell them off on the secondary market. (Ask my father about how that went for a lot of people in 2001.)</p>\n<p>Beyond scaling up and down, elasticity enables different deployment patterns that did not exist before. Whereas pre-cloud enterprises had dedicated and centralized data-center teams who were in charge of running applications, the accessibility of cloud computing gave rise to the DevOps movement. This has empowered smaller teams of software developers to take on the task of transferring software from \"it works on my laptop!\" to \"it's now deployed in production!\"</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-microservices\">Why Microservices?<a href=\"https://littlehorse.io/blog/promise-of-microservices#why-microservices\" class=\"hash-link\" aria-label=\"Direct link to Why Microservices?\" title=\"Direct link to Why Microservices?\" translate=\"no\">​</a></h2>\n<p>Despite the extra complexity it brings, the microservice architecture can more than pay for itself by ensuring organizational alignment and allowing enterprise architectures to take full advantage of the cloud's elasticity.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"organizational-alignment\">Organizational Alignment<a href=\"https://littlehorse.io/blog/promise-of-microservices#organizational-alignment\" class=\"hash-link\" aria-label=\"Direct link to Organizational Alignment\" title=\"Direct link to Organizational Alignment\" translate=\"no\">​</a></h3>\n<p>As discussed earlier, the business problems that software engineering organizations must solve today dwarf those that were solved in the 1990's, and so do the software engineering teams that tackle those problems.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>I am not belittling the engineers of the 90's; the problems they solved were arguably <em>much harder</em> than the problems we face today, and there were fewer engineers to face those problems. However, it is a fact that users expect more digital-native experiences today than they did twenty years ago.</p></div></div>\n<p>By breaking applications into smaller services, we can accomplish several important things:</p>\n<ul>\n<li class=\"\">Break up our software engineering team into smaller teams which are each responsible for individual microservices.</li>\n<li class=\"\">Allow different components of a system to be developed with separate tech stacks and released independently.</li>\n</ul>\n<p>Engineering teams of over a few dozen engineers working on the same deployable piece of software is a recipe for inefficiency. Merge conflicts, arguments over tech stack, slow \"release trains,\" and excessive intra-team coordination are just a few problems that arise. However, by breaking your application into smaller microservices, you can also break up your engineering organization into smaller, more efficient teams each in charge of a small number (prefably one!) of microservices.</p>\n<p>As an added benefit, properly-designed microservice architectures can follow the principles of Domain Driven Design. Ideally, a single microservice corresponds to a <em>Bounded Context</em> inside the business. This enables a small piece of the technical platform (a microservice) to be managed by a small team of software engineers, who collaborate closely with subject-matter experts and business stakeholders within a very specific domain of the business. Such close collaboration can foster better alignment between business goals and the software produced by engineering teams.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"moving-faster\">Moving Faster<a href=\"https://littlehorse.io/blog/promise-of-microservices#moving-faster\" class=\"hash-link\" aria-label=\"Direct link to Moving Faster\" title=\"Direct link to Moving Faster\" translate=\"no\">​</a></h3>\n<p>Microservices can allow developers to move faster by enabling continuous delivery and independent deployment of services. In a monolithic architecture, releasing a new feature or fixing a bug typically requires redeploying the entire application. Since microservices allow smaller pieces of your application to be deployed independently, engineering teams can iterate faster and deliver incremental value to business stakeholders.</p>\n<p>These positive effects are amplified by the advent of cloud computing. Since deploying a new application no longer requires buying a physical machine and plugging it into your datacenter but rather just applying a new <code>Deployment</code> and <code>Service</code> on a Kubernetes cluster, it is now truly feasible for small teams of software engineers to own their application stack from laptop-to-production (obviously, within the guardrails set by the central platform team). Furthermore, cloud computing is a pay-as-you-go (and often even pay-for-what-you-use) expense rather than an up-front cost. Therefore, the dollar cost of infrastructure required to support microservices is much lower today than it would have been before the advent of cloud computing and kubernetes.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"conclusion\">Conclusion<a href=\"https://littlehorse.io/blog/promise-of-microservices#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>The microservice architecture is not just a Twitter-driven buzzword but rather a way of designing system that has several real advantages. For most organizations with over two dozen software engineers, building applications with microservices is not an option but rather a <em>necessity</em>. However, those advantages come with a cost.</p>\n<p>We will discuss those challenges in next week's blog post...in the meantime, though, join our <a href=\"https://www.launchpass.com/littlehorsecommunity/free\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Community Slack</a> to get the latest updates!</p>",
            "url": "https://littlehorse.io/blog/promise-of-microservices",
            "title": "The Promise of Microservices",
            "summary": "Why teams adopt microservices, where they work well, and what tradeoffs come with the architecture.",
            "date_modified": "2024-08-22T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Microservices and Workflow",
                "Microservices"
            ]
        },
        {
            "id": "https://littlehorse.io/blog/helm-and-k8s-operators",
            "content_html": "<p>About <a href=\"https://helm.sh/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Helm</a> vs Kubernetes Operators.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"helm\">Helm<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#helm\" class=\"hash-link\" aria-label=\"Direct link to Helm\" title=\"Direct link to Helm\" translate=\"no\">​</a></h2>\n<p>Helm is like <code>brew</code> or <code>npm</code> for Kubernetes. There are repositories containing charts, and each chart allows you to install an application into your K8s cluster.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"how-it-works\">How it Works<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#how-it-works\" class=\"hash-link\" aria-label=\"Direct link to How it Works\" title=\"Direct link to How it Works\" translate=\"no\">​</a></h3>\n<p>Under the hood, Helm works by filling out some templated Kubernetes yaml files with user-provided values, drastically reducing boilerplate and allowing you to deploy a reasonably complex application without the user of the helm chart having to understand too much about how to manage such an application.</p>\n<p>In addition to that template-engine functionality, Helm also manages versions of your application. You can use Helm to release a new version of your app (for example, updating the docker image tag) and then quickly roll back to a previous version if you discover a bug. This is great for teams deploying stateless applications such as microservices or <a class=\"\" href=\"https://littlehorse.io/docs/server/concepts/tasks\">LittleHorse Task Workers</a>.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-good\">The Good<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#the-good\" class=\"hash-link\" aria-label=\"Direct link to The Good\" title=\"Direct link to The Good\" translate=\"no\">​</a></h3>\n<p>First, it is quite simple to write a Helm chart. This means that most DevOps teams can quickly write a helm chart that can be used by microservice teams across their organization.</p>\n<p>Secondly, Helm is a client library (well, it has been since the removal of Tiller...but that's another blog post). Therefore, you don't need to run any privileged pods inside the K8s cluster; all you need is a CI server with permission to create the necessary K8s resources.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"limitations\">Limitations<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#limitations\" class=\"hash-link\" aria-label=\"Direct link to Limitations\" title=\"Direct link to Limitations\" translate=\"no\">​</a></h3>\n<p>Unfortunately, Helm doesn't do much beyond initial installation and upgrades. Monitoring, self-healing, autoscaling, certificate rotation, and management of non-Kubernetes resources (eg. Kafka Topics, LittleHorse Task Definitions, AWS LoadBalancers, etc) are some exercises left to the reader, to name just a few.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"operators\">Operators<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#operators\" class=\"hash-link\" aria-label=\"Direct link to Operators\" title=\"Direct link to Operators\" translate=\"no\">​</a></h2>\n<p><a href=\"https://operatorframework.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Kubernetes Operators</a> are a pattern introduced by Red Hat that intends to capture the knowledge of an expert Site Reliability Engineer (or, more punnily, a software operator) into a program that manages (or operates) a complex application.</p>\n<p>To accomplish this, a Kubernetes Operator <em>extends</em> the Kubernetes API to introduce a new resource type that is custom-made for your application. The Operator works in tandem with Kubernetes itself to manage applications of a specific type.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"how-they-work\">How they Work<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#how-they-work\" class=\"hash-link\" aria-label=\"Direct link to How they Work\" title=\"Direct link to How they Work\" translate=\"no\">​</a></h3>\n<p>A Kubernetes Operator has two components:</p>\n<ol>\n<li class=\"\">A <code>CustomResourceDefinition</code>, which defines the extension to the Kubernetes API (including relevant configurations for your application type).</li>\n<li class=\"\">A Controller, which watches any resources from your Custom Resource Definition and \"reconciles\" them.</li>\n</ol>\n<p>The <code>CustomResourceDefinition</code> can be over-simplified as an Open API (not Open AI) specification for how your custom resource will look. For example, in LittleHorse Platform, the simplest version of a <code>LHCluster</code> resource (which creates a, you guessed it, LittleHorse Cluster) is:</p>\n<div class=\"language-yaml codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-yaml codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> littlehorse.io/v1</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token key atrule\">kind</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> LHCluster</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token key atrule\">metadata</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token key atrule\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> hello</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token plain\">littlehorse</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token key atrule\">namespace</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> lh</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token key atrule\">spec</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token key atrule\">server</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token key atrule\">version</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"0.2.0\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token key atrule\">listeners</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token plain\"> </span><span class=\"token key atrule\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> internal</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token plain\">k8s</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token key atrule\">type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> internal</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token key atrule\">port</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2023</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token key atrule\">replicas</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">3</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token key atrule\">storage</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token key atrule\">volumeSize</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"10G\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token key atrule\">kafka</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token key atrule\">strimziClusterRef</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token key atrule\">clusterName</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> my</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token plain\">strimzi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token plain\">kafka</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token plain\">cluster</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token key atrule\">listenerPort</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">9093</span><br></span></code></pre></div></div>\n<p>The <code>CustomResourceDefinition</code> allows you to <code>kubectl apply -f &lt;that file up there&gt;</code>, and then you can <code>kubectl get lhclusters</code>:</p>\n<p>Now how does the LittleHorse cluster get created, configured, managed, and monitored? That's where the Controller comes into play. In the Operator pattern, a Controller is a process (normally, it runs as a <code>Pod</code> in a cluster) that watches for all events related to a <code>CustomResourceDefinition</code> and manipulates the external world to match what the Custom Resources specify.</p>\n<p>Generally, that means creating a bunch of Kubernetes <code>Service</code>s, <code>Deployment</code>s, etc. to spin up an instance of an application. For example, the <a href=\"https://strimzi.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Strimzi</a> Kafka Operator watches <code>Kafka</code> resources and deploys an actual Kafka cluster.</p>\n<p>However, a Controller can also manage non-kuberentes resources. For example, many <code>Ingress</code> controllers provision or configure physical load balancers outside of the Kubernetes cluster. As another great example, the <a href=\"https://strimzi.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Strimzi</a> Kafka Topic Operator watches for <code>KafkaTopic</code> resources and creates (you guessed it) Kafka Topics using the Kafa Admin API.</p>\n<p>We at LittleHorse plan to add similar CRD's that are specific to LittleHorse...stay tuned to learn about the <code>LHTaskDef</code> and <code>LHPrincipal</code> CRD's <!-- -->😉<!-- -->.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-good-1\">The Good<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#the-good-1\" class=\"hash-link\" aria-label=\"Direct link to The Good\" title=\"Direct link to The Good\" translate=\"no\">​</a></h3>\n<p>Kubernetes Operators are beautiful. Since they were developed by Red Hat, they (along with <a href=\"https://strimzi.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Strimzi</a>) are the biggest reason why Red Hat is in my top-three favorite software companies of all time.</p>\n<p>A well-written operator can make it a breeze to manage even the most daunting applications. Since the Controller is code written in a general-purpose language (normally Go or Java), an Operator can do just about anything that can be automated by an SRE. This includes:</p>\n<ul>\n<li class=\"\">Autoscaling and alerting based on metrics</li>\n<li class=\"\">Self-healing and mitigation in the face of hardware faults or degradations</li>\n<li class=\"\">Certificate rotation</li>\n<li class=\"\">Metadata management in your application (for example, creating Kafka Users)</li>\n<li class=\"\">Intelligent rolling restarts that preserve high availability</li>\n<li class=\"\">Provisioning infrastructure <em>outside of</em> Kubernetes, for example <a href=\"https://crossplane.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">CrossPlane</a>.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-ugly\">The Ugly<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#the-ugly\" class=\"hash-link\" aria-label=\"Direct link to The Ugly\" title=\"Direct link to The Ugly\" translate=\"no\">​</a></h3>\n<p>The biggest downside to Operators in Kubernetes is that writing a Controller is <em>hard</em>. Additionally, it requires running a <code>Pod</code> with special privileges that allow the <code>Pod</code> to create other K8s resources. Because of this, writing an Operator for something like standardizing your team's blueprints for deploying a microservice just doesn't make sense.</p>\n<p>Future blogs will dive into some of the challenges that we had to overcome with LittleHorse Platform, and how we minimized the permissions that our Operator needs to provide a self-driving LittleHorse experience to our customers.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"helm-or-operators\">Helm or Operators?<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#helm-or-operators\" class=\"hash-link\" aria-label=\"Direct link to Helm or Operators?\" title=\"Direct link to Helm or Operators?\" translate=\"no\">​</a></h2>\n<p>Well, I'm a software engineer, so I'm going to say \"it depends.\" However, Kafka legend Gwen Shapira said in a fantastic <a href=\"https://open.spotify.com/episode/0BYwF3e8y5OzrPt0xYMyqb?si=0c7d44154b434d0e\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">podcast</a> that some \"it depends\" are more helpful than others. So in an effort to fall in the \"more helpful\" side:</p>\n<ul>\n<li class=\"\">If you want a framework for deploying simple stateless applications while minimizing boilerplate (i.e. allowing different teams to deploy microservices), then you probably want Helm.</li>\n<li class=\"\">If your application doesn't require much hand-holding after initial configuration on Kubernetes, Helm might do.</li>\n<li class=\"\">If you want to provide a Kubernetes-native way to manage non-kubernetes infrastructure, you need an Operator.</li>\n<li class=\"\">If you want to provide a self-driving experience for consumers of a highly complex application such as Kafka, ElasticSearch, or LittleHorse, you need an Operator.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"littlehorse-platform\">LittleHorse Platform<a href=\"https://littlehorse.io/blog/helm-and-k8s-operators#littlehorse-platform\" class=\"hash-link\" aria-label=\"Direct link to LittleHorse Platform\" title=\"Direct link to LittleHorse Platform\" translate=\"no\">​</a></h3>\n<p>LittleHorse Platform is an enterprise-ready distribution of LittleHorse that runs in your own Kubernetes environment. We believe that Helm is fantastic for deploying many stateless applications, and even some stateful applications. However, Helm wouldn't let us go far enough towards providing our customers with a fully self-driving LittleHorse experience. As such, we chose to put in the extra work and build a full Kubernetes Operator. Stay tuned for an extensive list of current and upcoming LittleHorse Platform features, all powered by the <a href=\"https://javaoperatorsdk.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Java Operator SDK</a>.</p>\n<p>To inquire about LittleHorse Platform, contact <code>sales@littlehorse.io</code>. To get started with LittleHorse Community (free for production use under the SSPL), check out our <a class=\"\" href=\"https://littlehorse.io/docs/server/developer-guide/install\">Installation Docs</a>.</p>",
            "url": "https://littlehorse.io/blog/helm-and-k8s-operators",
            "title": "Helm and Kubernetes Operators",
            "summary": "To Helm or to Operator?",
            "date_modified": "2023-09-01T00:00:00.000Z",
            "author": {
                "name": "Colt McNealy"
            },
            "tags": [
                "Kubernetes"
            ]
        }
    ]
}