Skip to content

Tutorials

Returns and Contracts: Making Your Tasks Predictable

I used to have a recurring nightmare. Not the falling kind—the kind where I'm staring at a service object and trying to figure out what it puts into the context. The work method sets context.user on line 12, context.token on line 28, but only if the conditional on line 15 passes. Oh, and there's a context.session_id that gets set inside a private method three screens down.

Every consumer of that task is making an implicit assumption about what the context will contain after execution. When those assumptions break, the error shows up somewhere else entirely—a NoMethodError in a downstream task, a nil where a mailer expected a user object.

That's why I built returns into CMDx. It makes the output contract explicit, enforced, and impossible to forget.

Structuring Large CMDx Codebases

Your first CMDx task is easy. Your tenth is manageable. But what about your hundredth? I've seen projects where the app/tasks/ directory becomes a dumping ground—flat files with no organization, inconsistent naming, duplicated middleware registrations, and base classes that try to do everything.

Scaling a CMDx codebase isn't about the framework. It's about the conventions you establish early and enforce consistently. This post is the playbook I wish I had when my first CMDx project grew from 10 tasks to 200.

CMDx as a Pragmatic Alternative to Event Sourcing

Event Sourcing is one of those ideas that sounds perfect in a conference talk and then bankrupts your sprint when you try to implement it. You need an event store, projections, snapshot strategies, a way to replay history, and a team that understands why you can't just UPDATE a row anymore. For some domains—banking, audit-heavy compliance, truly distributed systems—it's worth the cost. For the rest of us, it's a complexity tax we can't afford.

But the benefits of Event Sourcing are real. An immutable record of what happened. The ability to understand why the system is in its current state. Traceability across complex workflows. I wanted those benefits without the infrastructure.

That's when I realized CMDx already gives you most of it for free.

Why I Switched to CMDx (and How You Can Too)

If you've been writing Ruby long enough, you've probably used at least one service object gem. Maybe you started with Interactor back when it was the default choice. Maybe you moved to ActiveInteraction for its ActiveModel-like validations. Maybe you tried Actor or LightService. I've used all of them in production, and each one taught me something about what I actually need from a command framework.

This isn't a hit piece on any of those gems—they're well-built tools that solve real problems. But after years of using them, I kept hitting the same walls. So I built CMDx to knock those walls down.

Testing CMDx Tasks Like a Pro

I have a confession: I used to skip tests for service objects. Not because I didn't care, but because testing them was painful. Mock the database, stub the API, wrestle with instance variables, pray the test actually exercises the code path you think it does. The friction was real, and it showed in our coverage numbers.

When I built CMDx, I made a promise to myself—if the framework isn't dead simple to test, it's not done. Every task takes data in and pushes a result out. No hidden state, no side-channel mutations, no surprises. That makes testing almost enjoyable. Almost.

Building Production-Ready Rails Applications with CMDx: A Complete Guide

I've been building Ruby on Rails applications for over a decade, and if there's one thing that keeps me up at night, it's the state of business logic in most codebases. You know what I'm talking about—fat controllers, bloated models, service objects that look like they were written by five different people on five different days. We've all inherited that one OrderService class with 800 lines of spaghetti code and a comment at the top that says "TODO: refactor this."

This guide is everything I wish I had when I started taking service objects seriously. We're going to build a complete order processing system from scratch, and by the end, you'll understand how CMDx transforms chaotic business logic into clean, observable, maintainable code.

Mastering CMDx: The Art of Observability with Logging

We've all been there. A production incident report lands on your desk. "Transaction failed for user X." You open the logs, grep for the user ID, and... silence. Or worse, a wall of unstructured text that tells you everything except what you need to know.

Logging is often an afterthought—something we sprinkle in rescue blocks when we're debugging. But in CMDx, the Ruby framework for business logic, observability isn't an add-on; it's a first-class citizen.

Mastering CMDx: Retries, Deprecation, and Internationalization

As developers, we often obsess over the "happy path"—that perfect scenario where networks never time out, requirements never change, and every user speaks English. But the real world isn't so accommodating. Services fail, code evolves, and your application needs to speak more than just one language.

In this post, I want to dive into three CMDx features that help you handle these realities: Retries for resilience, Deprecation for lifecycle management, and Internationalization for global reach. These tools might seem distinct, but together they elevate your business logic from "functional" to "production-grade."

Mastering CMDx Callbacks and Middlewares: Hooks and Wrappers

When I'm writing complex business logic in Ruby, I often find that the core "work" is only half the battle. The other half is everything around it: logging, error handling, notifications, database transactions, and performance tracking.

If you put all that code inside your main method, you end up with a mess. That's where CMDx's Callbacks and Middlewares come in. They let you separate the "what" from the "how" and the "when," keeping your tasks clean and focused.

Let's dive into how you can use these tools to write better service objects.

Mastering CMDx Attributes: Your Task's Contract with the World

Attributes in CMDx are deceptively simple. You define what data your task needs, and the framework handles the rest—coercion, validation, defaults, the works. But there's real depth here. After building dozens of production systems with CMDx, I've found that well-designed attributes are the difference between tasks that "just work" and tasks that fight you at every turn.

Let me show you what I mean.