Serverless functions changed the game. Whether you call them Lambdas, Cloud Functions, or just FaaS, they gave us an incredible power: the ability to run code in response to events without managing a single server. For years, they've been the go-to tool for everything from simple data processing to powering entire backends.
But as our applications grew more complex, and as we started building sophisticated, multi-step automations and "agentic" systems, the cracks in the serverless-for-everything approach began to show. A sea of disconnected functions, mountains of boilerplate for orchestration and validation, and a lack of clear business context.
What if there was a better abstraction? A primitive designed not just for event-driven compute, but for defining the fundamental steps of any business process?
This is where action.do comes in. It’s time to evolve from writing functions to defining Actions.
Think about a common business process: onboarding a new user. This might involve:
Using traditional serverless, you'd create five separate functions. Then, you'd need another service, like AWS Step Functions or custom orchestration logic, to chain them together. For each function, you'd handle API keys, logging, input validation, and error handling—often in slightly different ways.
This approach has several hidden costs:
Serverless functions are powerful, but they are a low-level compute primitive. For building robust, scalable business automation, we need a higher-level building block.
An action.do is the smallest, indivisible unit of work in a system. We call it an atomic action. It's a fully managed, reusable function that performs a single, specific task.
Each action.do is an encapsulated, observable, and composable unit—the foundation of a reliable Business-as-Code strategy. It's not just a function; it's a declared business capability.
Let's look at what that means in practice.
Imagine you want to create the "Enrich User Profile" step from our onboarding example. Instead of provisioning a generic serverless function, you define an Action on the .do platform.
import { Do } from '@do-platform/sdk';
// Initialize the .do client with your API key
const-do = new Do(process.env.DO_API_KEY);
// Define a new atomic action to enrich user data
const enrichUserAction = await-do.action.create({
name: 'enrich-user-profile',
description: 'Fetches user data from Clearbit and updates the DB.',
inputs: {
email: 'string',
},
handler: async (inputs) => {
// Core logic lives here
const userData = await clearbit.lookup(inputs.email);
const dbResult = await db.users.update({ email: inputs.email, data: userData });
return { success: true, userId: dbResult.id };
}
});
console.log('Action created:', enrichUserAction.id);
Let's break down why this is a leap forward:
"So," you might ask, "is this just a fancy wrapper around a serverless function?"
No. An action.do is a fundamentally different abstraction.
| Feature | Standard Serverless Function | action.do (Atomic Action) |
|---|---|---|
| Concept | Generic, event-driven compute | A specific, self-contained business task |
| Contract | Implicit (function signature) | Explicit (inputs schema, name, description) |
| Orchestration | External service required (e.g., Step Functions) | Designed for composition within a .workflow.do agent |
| Observability | Disparate logs and metrics | End-to-end tracing of the business process |
| Reusability | Ad-hoc, relies on team knowledge | A first-class, discoverable, versioned asset |
The key difference lies in the principle of atomicity and orchestration. An action.do is designed to be atomic—it does one thing. It cannot call other actions. To connect multiple actions into a sequence, you use a .workflow.do agent. This crucial separation of concerns is what makes systems built on the .do platform so robust and easy to reason about. You separate the what (action.do) from the how (workflow.do).
This is the core of Services-as-Software—treating business capabilities as well-defined, managed, and composable software components.
Serverless functions were a necessary step in the evolution of cloud development. They freed us from the server. Now, it's time for the next step: freeing ourselves from the boilerplate and cognitive overhead of managing raw functions.
By elevating your business logic from simple code to defined, atomic actions, you build a more resilient, scalable, and understandable system. Your automations are no longer brittle chains of functions but robust workflows composed of reliable, reusable building blocks.
Stop thinking in functions. Start building with Actions.