In the ever-evolving landscape of automation, the goal is always the same: to make processes more efficient, reliable, and scalable. Yet, as our automations grow in complexity, they often become brittle, monolithic, and difficult to manage. A small change in one part can cause a cascade of failures elsewhere. How do we build robust systems that can adapt and scale without crumbling?
The answer lies in going smaller. Welcome to the world of atomic actions—the fundamental building blocks for modern agentic workflows. With action.do, we're championing a new paradigm: breaking down complex processes into their smallest, indivisible units to build automation that is simple, powerful, and built to last.
Think of building a complex structure with LEGOs. You don’t start with a fully formed wall; you start with a single, simple brick. An atomic action is that brick for your automation.
In the context of action.do, an atomic action is the smallest, indivisible unit of work in a workflow. It represents a single, well-defined task, like 'send a welcome email', 'create a user record in a CRM', or 'generate a monthly report'.
The "atomic" part is crucial. It means the action either completes successfully or it fails entirely. There are no partial states. You don't have a user who was halfway created or an email that was partially sent. This all-or-nothing principle is the bedrock of building reliable systems.
Instead of abstract concepts, let's look at how you define an action with action.do. This is business-as-code in its purest form: defining a business process in a clear, version-controlled, and reusable format.
Here’s an example of an action.do that sends a welcome email, written in TypeScript:
Let's break this down:
An atomic action is powerful on its own, but its true potential is unlocked when you combine it with others. This is the difference between an action.do and a workflow.do.
By creating a library of modular, reusable actions, you enable a powerful new way of working:
This approach is the future of workflow automation, especially in the context of agentic workflows. When an AI agent needs to accomplish a task, it doesn't need to learn how to interact with ten different APIs. It just needs a library of well-described actions—tools in its toolbox—that it can call to get the job done. Your send-welcome-email action becomes a reliable tool the agent can use anytime it needs to welcome a new user.
The shift to atomic actions isn't just a technical improvement; it's a strategic one. It's about moving away from brittle, hard-to-maintain scripts and towards a future of robust, scalable, and intelligent task automation.
Ready to build better?
Embrace the building block of automation. Start turning your complex processes into simple, powerful, and repeatable tasks with action.do.
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.
Q: What kind of logic can I put inside an action's handler?
A: The handler can contain any Node.js/TypeScript logic. This includes making API calls to third-party services, performing data transformations, interacting with databases, or executing any custom business logic required to complete the task.
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 SendGrid, Looop, etc.) would go here
console.log(`Sending welcome email to ${name} at ${to}`);
// Return a success status and any relevant output
return { success: true, messageId: 'xyz-123' };
},
});