You’ve grasped the core of modern automation: breaking down complex processes into small, reliable, single-purpose functions. These atomic actions are the fundamental building blocks for everything from simple scripts to sophisticated agentic workflows. By invoking send-email or create-user as a managed API call, you're already ahead of the curve.
But as your systems grow, the design of these actions becomes paramount. Simply wrapping a function in an API call is just the first step. To build truly resilient, scalable, and observable systems—the promise of Business-as-Code—we need to think deeper.
Let's move beyond the basics and explore the advanced concepts that transform simple functions into enterprise-grade atomic actions.
In a distributed system, you can’t always guarantee that an action will run only once. Network timeouts, service restarts, or retry logic can cause the same request to be sent multiple times. If your action is charge-customer, this could be disastrous.
This is where idempotency comes in. An idempotent action can be executed multiple times with the same inputs and will produce the same result without creating unintended side effects.
How to Design for Idempotency:
An idempotent action can be safely retried by any part of your workflow automation pipeline, ensuring that a momentary glitch doesn't lead to duplicate data or charges.
A common design trap is to overload an action's payload with too much information. This makes the action brittle and difficult to reuse. A well-designed atomic action makes a clear distinction between its payload and its context.
By keeping the payload lean, your send-email action becomes a universal utility. It can be called from the user-onboarding workflow, a password-reset flow, or a weekly-newsletter batch job. The context, which is invaluable for logging, monitoring, and debugging, travels alongside the action without polluting its core logic. This separation is a cornerstone of building a scalable and maintainable library of business capabilities.
In a complex business process, what happens when step three fails after steps one and two succeeded? Just logging an error isn't enough. For a truly robust system, you need a strategy to undo what's already been done.
This is the principle behind the Saga pattern and is critical for agentic workflows that orchestrate multi-step transactions. For every action that makes a change in a system, you should consider designing a corresponding compensation action.
When your workflow orchestrator (the "agent") detects a failure, it's responsible for invoking the necessary compensation actions in reverse order. By designing your atomic actions in these do/undo pairs, you give your automation the intelligence to clean up after itself, maintaining data integrity and preventing your systems from entering inconsistent states.
Let's look at a code example that incorporates these ideas. Here, our application is calling an idempotent process-payment action. The underlying action, triggered via an.action.do, should also be designed with idempotency in mind using the orderId.
import { Do } from '@do-co/sdk';
import { db } from './database'; // A mock database client
// Initialize the .do client
const an = new Do(process.env.DO_API_KEY);
// Define an idempotent action that handles payments
async function processPayment(orderId: string, amount: number) {
try {
// 1. Idempotency Check
const existingPayment = await db.payments.find({ where: { orderId } });
if (existingPayment?.status === 'succeeded') {
console.log(`Order ${orderId} already paid. Returning original result.`);
return { id: existingPayment.id, status: 'skipped' };
}
// 2. Execute the atomic action via the .do platform
// The `orderId` serves as the idempotency key for the remote action.
const result = await an.action.do('charge-card', {
orderId: orderId,
amount: amount,
currency: 'usd'
});
// Persist the final state
await db.payments.create({ id: result.id, orderId, status: 'succeeded' });
console.log('Action Succeeded:', result.id);
return result;
} catch (error) {
console.error('Action Failed:', error);
// 3. Compensation/Alerting Logic could be triggered here
// For example: an.action.do('notify-fraud-team', { orderId });
throw error; // Re-throw for the orchestrator to handle
}
}
This example shows how a function in your application can ensure idempotency before calling action.do, providing a powerful layer of resilience for your entire workflow.
Atomic actions are more than just API endpoints; they are carefully designed contracts that form the bedrock of your automated business processes. By integrating advanced concepts like idempotency, context separation, and compensation actions into your design from day one, you move beyond simple automation.
You start building a robust, self-healing, and scalable system—a true implementation of Business-as-Code. With action.do, you have the platform to execute, automate, and scale these powerful concepts with confidence.