Automation is a double-edged sword. When it works, it’s a silent, powerful engine driving your business forward. But when it breaks—and monolithic scripts always break—it becomes a tangled mess of brittle dependencies, cryptic log files, and late-night debugging sessions. The problem isn't automation itself; it's how we've been building it. It's time to move away from fragile, all-or-nothing scripts and embrace a more resilient, scalable, and modern approach: atomic actions.
This is the philosophy behind action.do, the fundamental building block for creating reliable workflows on the .do platform. If you're tired of your automations collapsing like a house of cards, this guide will show you how to migrate your existing processes to the robust, composable world of atomic actions.
Does this sound familiar? You have a critical business process, like user onboarding, wrapped up in a single, 200-line script. It connects to your database, calls a few third-party APIs, sends some emails, and posts to Slack. For a while, it works.
Then, the Slack API has a momentary hiccup. The entire script fails. The user is left in limbo—half-registered, no welcome email, no trial workspace. You're left digging through logs to find the single point of failure.
This is the core problem of monolithic automation. It suffers from:
action.do solves these problems by treating every step in a workflow as a distinct, independent, and managed service. An Atomic Action is the smallest indivisible unit of work.
Unlike a simple function call in a script, every execution through action.do is a first-class operation. It’s logged, monitored, secured, versioned, and made resilient with built-in retry logic. This is the foundation of Business-as-Code: transforming your core operations into a collection of robust, standalone software services.
Think of it like building with LEGO bricks (action.do) versus carving a statue from a single block of marble (a monolithic script). With bricks, you can easily add, remove, or replace pieces without destroying the whole structure.
Let's migrate that fragile user onboarding script.
First, document every distinct operation your script performs.
Old Monolithic Script: onboard-new-user.js
Next, break that list down into discrete, single-purpose tasks. Each one of these will become an action.do call.
Each action is responsible for one thing and one thing only. email.send doesn’t know or care about the database; it just needs an address and a message. This separation is the key to resilience and reusability.
The .do platform provides many standard actions out of the box. For your unique business logic, you can easily define your own. You can wrap your existing database insertion code into a custom action called database.createUser. This transforms your proprietary logic into a standardized, callable, and managed service on the .do platform.
Now, you simply orchestrate these atomic actions in sequence. Your new "controller" code is dramatically simpler, cleaner, and more declarative. It focuses on the what, not the how.
import { Do } from '@do-sdk/core';
// Initialize the .do client with your API key
const doClient = new Do({ apiKey: 'YOUR_API_KEY' });
// The new, robust onboarding workflow
async function onboardNewUser(name: string, email: string) {
try {
console.log(`Starting onboarding for ${email}...`);
// Step 1: Create user in the database
await doClient.action('database.createUser').run({ name, email });
// Step 2: Send the welcome email
await doClient.action('email.send').run({
to: email,
subject: `Welcome to our platform, ${name}!`,
body: `Hi ${name},\n\nWe're thrilled to have you join us.`
});
// Step 3: Add to marketing list
await doClient.action('mailchimp.addContact').run({
email,
listId: 'YOUR_LIST_ID'
});
// Step 4: Notify the team on Slack
await doClient.action('slack.postMessage').run({
channel: '#signups',
text: `🎉 New user just signed up: ${name} (${email})`
});
console.log(`Successfully onboarded ${email}.`);
} catch (error) {
// A single point for high-level error handling.
// The .do platform handles transient errors via automatic retries.
console.error('Onboarding Failed:', error);
// Optionally, trigger a compensating action, like notifying an admin.
await doClient.action('ops.alert').run({
message: `Failed to onboard user ${email}. Error: ${error.message}`
});
}
}
// Trigger the robust workflow
onboardNewUser('Alex', 'new.user@example.com');
By migrating, you've fundamentally upgraded your automation.
Moving from monolithic scripts to action.do isn't just a refactor—it's a strategic shift. It’s about building your business processes on a foundation of managed, resilient, and composable services. Stop wrestling with fragile scripts and start building automations that last.
Ready to make your workflows unbreakable? Explore the .do platform and start building your first atomic action today.
Q: What is an 'atomic action' on the .do platform?
A: An atomic action is the smallest, indivisible unit of work. It performs a single, specific task—like sending an email or updating a database record—ensuring each step in your workflow is reliable, testable, and independently executable.
Q: How is action.do different from a regular function call?
A: action.do elevates a function to a managed service. Every execution is logged, monitored, secured, and scalable. It provides built-in retry logic, idempotency, and versioning, making it perfect for building resilient, distributed systems as Business-as-Code.
Q: What happens if an action fails during execution?
A: The .do platform has built-in resilience. You can configure automatic retry policies with exponential backoff, define custom error handling logic, or trigger compensatory actions to ensure your workflows can gracefully handle failures without manual intervention.