In the rapidly evolving world of automation, agentic workflows are becoming the backbone of modern business processes. From onboarding new customers to processing data pipelines, we rely on these automated systems to execute complex sequences of tasks. But the true power and reliability of any workflow lies in its foundation: the way individual tasks are defined.
At the core of this discussion is a fundamental choice: should a task be atomic or composite? The answer to this question dramatically impacts the scalability, reliability, and maintainability of your entire automation strategy.
Let's break down the difference and see why thinking "atomically" is the key to building robust systems.
An atomic task, or what we at action.do call an Atomic Action, is the smallest, indivisible unit of work within a larger process. Think of it as a single, self-contained building block designed to do one thing and do it well.
As our FAQ explains, an atomic action "either completes successfully or fails entirely, without partial states."
Key characteristics of an atomic action include:
Think of it like a LEGO brick. A single 2x4 brick is an atomic unit. It's simple, predictable, and you can use it to build anything from a simple wall to an elaborate spaceship.
At action.do, defining an atomic action is simple and clear. It’s business-as-code, turning a conceptual task into a concrete, API-callable function.
Here, send-welcome-email is a perfect atomic action. It takes clear inputs, performs one specific job, and reports a clear success or failure.
A composite task, in contrast, bundles multiple distinct operations into a single, monolithic block. For example, imagine an onboard-new-customer task that internally tries to:
On the surface, this seems convenient. One call does it all! But this approach is fraught with peril:
Building with atomic actions transforms your workflow automation. By breaking down the onboard-new-customer process, you would define four separate, atomic action.do actions:
Then, you use a workflow.do to orchestrate these building blocks in the desired sequence. This is the fundamental difference: Actions are the building blocks; workflows are the blueprints that connect them.
This approach unlocks powerful advantages:
Q: What is an 'atomic action' in the context of .do?
A: An atomic action is the smallest, indivisible unit of work in a workflow. It represents a single, well-defined task, like 'send an email' or 'create a user record', ensuring that it either completes successfully or fails entirely, without partial states.
Q: How is an action.do different from a full workflow.do?
A: An action.do represents a single task. A workflow.do is a collection of one or more actions orchestrated to achieve a larger business process. Actions are the building blocks; workflows are the blueprints that connect them.
Q: Can I reuse actions across different workflows?
A: Absolutely. Actions are designed to be modular and reusable. You can define an action once, like 'generate-report', and call it from any number of different workflows, promoting DRY (Don't Repeat Yourself) principles in your automations.
While bundling tasks might seem like a shortcut, it introduces fragility and complexity that undermines your long-term automation goals. For truly robust, scalable, and maintainable agentic workflows, the path is clear: decompose complex processes into their fundamental, atomic components.
By embracing atomic actions, you're not just defining tasks; you're creating a powerful library of reusable capabilities that will serve as the foundation for all your future automations.
Ready to turn complexity into simple, repeatable tasks? Discover how action.do provides the building blocks for your next agentic workflow.
import { action } from '@do-sdk/core';
export const sendWelcomeEmail = action({
name: 'send-welcome-email',
description: 'Sends a welcome email to a new user.',
inputs: {
to: { type: 'string', required: true },
name: { type: 'string', required: true }
},
handler: async ({ inputs, context }) => {
const { to, name } = inputs;
// Your email sending logic (e.g., using an API like SendGrid)
console.log(`Sending welcome email to ${name} at ${to}`);
// A successful execution returns a consistent output
return { success: true, messageId: 'xyz-123' };
},
});