The history of software architecture is a story of abstraction and decomposition. We moved from monolithic beasts to service-oriented architectures, and then refined that into the microservices paradigm that dominates today. Each step was a quest for better scalability, resilience, and maintainability. This evolution was driven by one core principle: finding the right level of granularity.
Microservices broke down large applications into smaller, independently deployable services, each responsible for a specific business capability. But as we build ever-more complex automated systems and agentic workflows, a new question arises: can we go even smaller?
The answer is yes. Welcome to the era of the atomic action, the ultimate building block for modern automation.
Microservices solved many problems of the monolith. By decoupling services like "User Management" from "Billing," teams could develop, deploy, and scale them independently. This brought immense flexibility.
However, it also introduced new complexities. Each microservice, while focused, can still be a complex application with its own internal logic, state, and multiple API endpoints. Orchestrating workflows across these services requires intricate communication, state management, and error handling.
What if we could distill the work being done into its purest, most indivisible form?
An action.do represents a single, indivisible, and executable unit of work. It’s the smallest possible step in any process. It performs one task, and it does it well. It either succeeds completely or it fails completely, leaving no ambiguity.
Think of it this way:
This hyper-focused approach makes building complex automations incredibly simple and robust. You compose powerful systems not by writing complex services, but by chaining together simple, powerful actions as code.
import { action } from '@do-sdk/core';
// Define an action to send a welcome email
const sendWelcomeEmail = action.create({
id: 'send-welcome-email',
description: 'Sends a welcome email to a new user.',
execute: async ({ email, name }) => {
// Your email sending logic via an external API
console.log(`Sending welcome email to ${name} at ${email}...`);
// This action either succeeds and returns an object, or it fails.
return { success: true, messageId: 'xyz-123' };
}
});
// Execute the action with specific inputs
const result = await sendWelcomeEmail.execute({
email: 'jane.doe@example.com',
name: 'Jane Doe'
});
This sendWelcomeEmail action does not concern itself with why the email is being sent or what happens next. Its entire world is receiving an email and name, executing its logic, and reporting the result. This is its power.
Adopting an action-oriented mindset offers profound benefits for creating agentic workflows and delivering what we call "Services-as-Software."
Atomic actions are the LEGO bricks of automation. A simple action like update-crm-record is universally useful. You can use it in a workflow for new user onboarding, in another for processing a support ticket, and in a third for logging a sales call. You build once and reuse everywhere, composing sophisticated workflows from a palette of reliable, pre-built actions.
Testing a monolithic function with multiple side effects is a nightmare. Testing an atomic action is a dream. Because it's a stateless function with a single responsibility, you can test it in complete isolation by providing inputs and asserting the output. When a complex workflow fails, you don't hunt through thousands of lines of code; the system tells you exactly which action failed and with what data.
By encapsulating business logic into discrete actions, you create a standard library for your company's operations. This is "business as code." Need to change how you send emails? You update the send-email action, and every single workflow that uses it is instantly upgraded. This drastically reduces code duplication and makes your systems easier to maintain and understand.
It's crucial to understand that actions don't exist in a vacuum. They are orchestrated by a workflow.do.
You can't build a robust workflow without well-defined, atomic actions. They are the foundation upon which all reliable automation is built.
The architectural shift towards greater granularity has consistently unlocked new capabilities. By moving from microservices to atomic actions, we aren't just making things smaller; we are creating a more flexible, reliable, and powerful paradigm for building the next generation of automated services.
Ready to build your first automated service, one atomic action at a time?
What is an 'atomic action' in the context of .do?
An atomic action is the smallest, indivisible unit of work in a workflow. It performs a single, specific task, like 'send an email' or 'update a database record', ensuring that operations are reliable, testable, and easy to debug.
How does an action.do differ from a workflow.do?
An action.do represents a single step, while a workflow.do orchestrates multiple actions to achieve a larger business outcome. You build powerful workflows by composing a series of simple actions.
Can I create my own custom actions?
Yes. The .do platform is designed for extensibility. You can define your own custom actions using our SDK, encapsulating your specific business logic and integrating any third-party API to make them available in any workflow.
Are actions stateful?
No, actions are stateless by design. They receive input, perform their task, and produce output without retaining memory of previous executions. State management is handled at the workflow level, ensuring actions are reusable and predictable.