Here's an approach to writing code that I've been using for years and couldn't quite put into words until now. One of those "This feels wrong but I can't explain why". Now I can!
Smart core, thin interfaces
The best way to avoid a big ball of mud is to use small balls of mud called modules.
But how do you structure those?
The approach I've seen work time and again and that keeps coming up across literature, is to build core business logic modules surrounded by interfaces for different actors. Based on the actor-command-event approach to domain modeling.
Empirically we know that modularization leads to maintainable adaptable software. Tightly coupled components are hard to kill and experience constant churn. Loosely coupled components tend to stabilize and are easy to replace when you need to.
The smart core understands a business domain, its events and commands, and encapsulates the logic required to make it work. Like a billing module that understands the intricacies of your payment plans, discounts, subscriptions, and so on.
The light-weight interfaces understand how different actors want to interact with this logic. Admins need to make exceptions, consumers want clear information, the integrations API cares about fault tolerance, and your backend systems need to be notified when something happens.
Each actor has different needs, but they must all agree on the current state of the system and follow the same set of rules for what's possible when. Push that logic to the edges and mistakes are all but guaranteed. Keep it tightly packed and you stand a chance.
That is to say: Repeat code with abandon. Avoid repeating semantics.
If this sounds familiar, the philosophy flies under many names:
- hexagonal architecture is a way to achieve this in monolithic applications
- service oriented architecture is for when your modules are coupled but talk through an events bus
- microservices architecture happens when your modules are independently deployable and talk through RPCish or RESTful APIs
- fat models, skinny controllers are popular in the world of fullstack web frameworks
- state machines, custom hooks, and global state managers are popular in the world of modern UI frameworks
They're all implementation details of the same idea:
Sanitize at the edges
Keep your business logic contained and hide the messy details behind a pretty interface. Avoid smearing that logic into other parts of the system and validate/sanitize inputs at the edges so your deep logic has fewer exceptions to deal with.
Like this:
function prettyInterface(input = null) {
if (input) {
doTheThingWithInput(input)
} else {
doTheThingWithoutInput()
}
}
Instead of:
function doTheThing(input = undefined) {
const data = db.query(sql`select * from blah where ${input ? 'col = '+input : 'col is null'})
if (data && input) {
return `Search ${input} found ${data}`
} else if (data && !input) {
return `Found ${data} with null values`
} else if (!data && input) {
return `Search ${input} found nothing`
} else if (!data && !input) {
return `No empty values found`
}
}
Without the interface, your deep method has to handle all possible branches in functionality. This makes it brittle. If it doesn't handle those exceptions, then everywhere you use that method has to deal with the exceptions. This makes every change impact a large part of your codebase.
In practice you'd make the prettyInterface
function public and the rest private. This helps you fight Hyrum's law by preventing unintended use. How you do that depends on your language and team conventions.
What's an interface?
An interface is any pile of code that translates public commands into internal function calls. How that looks depends on your context.
For an API it might be a short controller function that takes an API request, cleans up the inputs, and calls an internal module.
For a consumer UI, it can be a whole backend-for-frontend with server-side rendering and oodles of client code creating the app experience.
Cheers,
~Swizec
Continue reading about Smart core, thin interfaces
Semantically similar articles hand-picked by GPT-4
- Finding modules in a big ball of mud
- Why even care about code structure
- Better tooling won't fix your API
- Finding modules in a Big Ball of Mud with ChatGPT
- What microservices are for
Learned something new?
Read more Software Engineering Lessons from Production
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below 👇
Software Engineering Lessons from Production
Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️