Data pipelines are the circulatory system of modern business. They move critical information from user signups to CRMs, from raw logs to analytics dashboards. But too often, these pipelines are fragile, monolithic scripts—a "big ball of mud" that's impossible to debug, difficult to maintain, and a nightmare to scale. When one small part fails, the entire system grinds to a halt.
There's a better way. By adopting a "Business As Code" mindset and breaking down complex processes into their smallest constituent parts, you can build data pipelines that are modular, testable, and incredibly reliable.
Welcome to the world of atomic actions with action.do.
Think about a typical script for processing new user signups. It probably does something like this:
All this logic is tangled together in a single file. This approach is fraught with problems:
The action.do platform is built on a simple yet powerful concept: the atomic action.
An atomic action is a self-contained, single-purpose function that performs one specific task, like sending an email or updating a database record. It's the fundamental building block for creating reliable and modular agentic workflows.
Instead of one giant script, you encapsulate each piece of business logic into its own reusable, observable, and independently executable action. Each action is a tiny, perfect microservice with a clearly defined purpose.
Let's rebuild our user signup pipeline using this philosophy.
The first step is to ensure the incoming data is clean. We create an action whose only job is to validate the input.
It takes raw data, applies validation rules, and either returns the clean data or a structured error.
import { Do } from '@do-sdk/core';
// Define an atomic action to validate new user data
const validateUserData = Do.action('validate-user-data', {
inputs: {
email: 'string',
name: 'string',
},
handler: async ({ inputs }) => {
const errors = [];
if (!inputs.email?.includes('@')) {
errors.push('A valid email is required.');
}
if (!inputs.name || inputs.name.length < 2) {
errors.push('Name must be at least 2 characters long.');
}
if (errors.length > 0) {
// The action failed its single purpose
return { success: false, errors };
}
// The action succeeded. Return the validated data.
return { success: true, validatedData: inputs };
},
});
This action is now a reusable "data validator" micro-service. We can test it in complete isolation and trust it to do one thing perfectly.
Next, we want to add more context to the user profile. We'll create a dedicated action for this. This action takes the validated user data, calls an external API, and returns the newly enriched data.
// Define an atomic action to enrich a user profile
const enrichUserProfile = Do.action('enrich-user-profile', {
inputs: {
email: 'string',
// ...other validated data
},
handler: async ({ inputs, context }) => {
// API keys are securely injected from the .do platform, not hardcoded
const clearbit = new Clearbit({ apiKey: context.secrets.CLEARBIT_API_KEY });
// Perform the single enrichment task
const enrichmentData = await clearbit.lookup(inputs.email);
// Return a structured result
return { ...inputs, ...enrichmentData };
},
});
Notice how secrets like API keys are managed securely by the .do platform and injected at runtime. Your business logic remains clean and secure.
Finally, we need to format the enriched data for our CRM. This is another perfect candidate for an atomic action. It knows nothing about validation or enrichment; its only purpose is to transform data from one shape to another.
// Define an atomic action for data transformation
const formatForCrm = Do.action('format-for-crm', {
inputs: {
name: 'string',
company: { name: 'string', domain: 'string' },
// ...other enriched data
},
handler: async ({ inputs }) => {
// Transform the data into the target schema
const crmRecord = {
LastName: inputs.name.split(' ').pop(),
FirstName: inputs.name.split(' ').shift(),
Company: inputs.company.name,
Website: `https://${inputs.company.domain}`,
};
return { crmRecord };
},
});
We've successfully broken our monolithic pipeline into three robust, independent, and reusable actions. But how do we chain them together?
This is where the power of the .do ecosystem comes into play. As our FAQ notes:
To orchestrate multiple actions, you use a 'workflow.do', which composes individual actions into a larger, stateful process. This promotes a clean separation of concerns.
A workflow.do defines the sequence of execution, passing the output of one action as the input to the next. It handles the state, retries, and error handling between actions, allowing each action to remain stateless and focused. Your pipeline is no longer a fragile script; it's a resilient, observable, and intelligent agentic workflow.
By moving away from monolithic scripts and embracing atomic actions, you gain unparalleled control and reliability. Your data pipelines become:
Stop wrestling with brittle code. Start building bulletproof data pipelines with action.do. Encapsulate your logic, execute it flawlessly, and transform your data processing from a liability into a strategic asset.
Ready to get started? Sign up for action.do and build your first atomic action!
What is an 'atomic action' on the .do platform?
An atomic action is a self-contained, single-purpose function that performs one specific task, like sending an email or updating a database record. It's the fundamental building block for creating reliable and modular agentic workflows.
How is an action.do different from a standard serverless function?
While similar, an action.do is purpose-built for agentic systems. It includes native integration with the .do ecosystem for state management, observability, and composition, allowing you to treat business logic as a first-class citizen in your Services-as-Software.
Can actions call other actions?
A single action is designed to be atomic and perform one task. To orchestrate multiple actions, you use a 'workflow.do', which composes individual actions into a larger, stateful process. This promotes a clean separation of concerns.
What kind of logic can I run inside an action.do?
Anything you can code. Actions can perform data transformations, integrate with third-party APIs (e.g., Stripe, SendGrid), query databases, or run machine learning models. It encapsulates any unit of business logic and exposes it as a simple API.
How are inputs and secrets handled in an action?
Inputs are defined with clear types in the action's configuration and are passed during execution. Secrets, like API keys or database credentials, are managed securely through the .do platform and injected into the action's context at runtime, never exposed in your code.