We've all heard the software engineering dream: building vast, complex applications from simple, reusable "Lego blocks" of code. In reality, we often start with a monolithic giant that becomes difficult to change, or we dive into a microservices architecture so complex that we spend more time managing infrastructure than writing code.
But what if the dream was achievable? What if each "Lego block" was just a single, focused function that instantly became a scalable, secure API endpoint?
This is the core philosophy behind function.do. We believe that by focusing on atomic, composable functions, developers can build robust, scalable, and maintainable systems with radical simplicity.
Before we can compose, we need our building blocks. On function.do, these are atomic functions.
An atomic function is a small, self-contained unit of code designed to perform one specific task perfectly.
Think of functions like sendWelcomeEmail, processPayment, resizeImage, or generatePDF. Each one has a single responsibility. On our platform, a simple export is all it takes to turn this logic into a production-ready, serverless API.
Consider this simple greeting function:
import { Do, Fn } from '@do-are/sdk';
/**
* @description Generates a personalized greeting.
* @param { name: string } - The name to include in the greeting.
* @returns { message: string } - The personalized greeting message.
*/
export const getGreeting: Fn = async ({ name }) => {
if (!name) {
throw new Error('A name is required to generate a greeting.');
}
const message = `Hello, ${name}! Welcome to the platform.`;
// This object is returned as the JSON API response.
return { message };
};
// Deploying this creates a unique API endpoint:
// POST https://your-name.function.do/getGreeting
// BODY { "name": "World" }
This function does one thing: it generates a greeting. It's easy to understand, easy to test, and now, it's a globally available API endpoint. This is our atomic unit.
Now for the magic. Composition is the act of combining these simple, atomic APIs to create sophisticated workflows and complex application logic.
Instead of building one large signUpUser monolith that handles database writes, email notifications, and analytics tracking all in one place, you build smaller, independent functions:
Then, you create a new function that acts as an orchestrator, calling the other three in a sequence. This is the essence of building with composable functions.
Let's build that user signup flow. Imagine we have already deployed three separate atomic functions on function.do:
Now, we can create a single "entry point" function that composes them. This orchestrator function uses the function.do SDK to easily call the others.
import { Do, Fn } from '@do-are/sdk';
/**
* @description Orchestrates the complete user signup process.
* @param { email: string, name: string, pass: string }
* @returns { success: boolean, userId: string }
*/
export const signUpUser: Fn = async ({ email, name, pass }) => {
// 1. Call the `createUser` function
const { userId } = await Do.post('/createUser', { email, name, pass });
if (!userId) {
throw new Error('Failed to create user account.');
}
// 2. Call other functions in parallel (fire-and-forget)
// We don't need to wait for these to complete.
Promise.all([
Do.post('/sendWelcomeEmail', { email, name }),
Do.post('/logEvent', { eventName: 'user_signup', userId })
]);
// 3. Return a successful response to the client immediately
return { success: true, userId };
};
// Deploys to: POST https://your-name.function.do/signUpUser
With just a few lines of code, we've created a complex, multi-step workflow. This signUpUser function is now its own serverless API, but it's built entirely from other, smaller APIs.
This function-as-a-service approach isn't just a novelty; it offers powerful advantages for building modern applications.
The future of backend development isn't about choosing between a single, unmanageable monolith and a hundred-container microservice mesh. There is a simpler, more powerful way.
By embracing atomic, composable functions, you can focus on what truly matters: your application's logic. Let each function do one thing perfectly, and then combine them to build anything you can imagine.
Ready to transform your code into scalable, composable APIs? Get started with function.do today and experience the power of composition for yourself.