I'll never forget this lesson I learned from a friend in 6th or 7th grade.
We were building our own Windows clone. In Turbo Pascal, running in DOS, using a textual user interface because we didn't know how to do graphics.
If you're too young to know about textual user interfaces, they looked like this:
It's got windows, buttons, a mouse cursor, and everything else you'd want in a modern user interface. Except graphics. It's all made with ASCII characters.
I used to know the ASCII table by heart by back then 🤓
Now, when I say we were building a new Windows, I don't mean working together on a single project. We didn't know how to do that. There was no Git or CVS or SVN back then. At least not that we knew of. The best we could do back then was to use diskettes to borrow each other's code.
Oh, we also didn't know about putting code in multiple files, so that was fun.
Anyway, the lesson he taught me about business logic being data came when we were both implementing our top row navigation. Like a menu, but for your whole system.
Oh yes, we built dropdown menus and everything. It's amazing what a 13 year old with infinite free time can build in a few months.
Avoid encoding business logic in the shape of your program
Let's say you have 6 items. File
, Edit
, Format
, View
, Window
, and Help
. That's what iA Writer has.
Now let's say you want to have a user action that goes through these items. My system used left
and right
arrow keys, my friend liked j
and k
, while MacOS relies on mouse alone.
You could build navigation like this:
onLeftKeyPress() {
if (currentMenu === "File") {
goTo("Help")
}else if (currentMenu === "Edit") {
goTo("File")
}else if (currentMenu === "Format") {
goTo("Edit")
}else if (currentMenu === "View") {
goTo("Format")
}else if (currentMenu === "Window") {
goTo("View")
}else if (currentMenu === "Help") {
goTo("Window")
}
}
onRightKeyPress() {
if (currentMenu === "File") {
goTo("Edit")
}else if (currentMenu === "Edit") {
goTo("Format")
}else if (currentMenu === "Format") {
goTo("View")
}else if (currentMenu === "View") {
goTo("Window")
}else if (currentMenu === "Window") {
goTo("Help")
}else if (currentMenu === "Help") {
goTo("File")
}
}
Nothing wrong with that. It's readable, gets the job done, everything's perfect. You congratulate yourself on an engineering problem well solved.
Then the PM comes in and says, "We've been running tests, and this sequence doesn't really work. Let's change the order.”
Uh-oh.
"Oh, and we want different apps to have different menus."
💩
Now you have to rewrite that whole logic for every app. Every time the PM decides to change the order. That's not fun at all!
So here's what you can do instead.
items = ["File", "Edit", "Format", "View", "Window", "Help"]
currentItem = 0
onLeftKeyPress() {
currentItem -= 1;
if (currentItem < 0) currentItem = items.length-1;
goTo(items[currentItem]);
}
onRightKeyPress() {
currentItem += 1;
if (currentItem >= items.length) currentItem = 0;
goTo(items[currentItem]);
}
Array of options, pointer to current selection, move pointer when user does something. Now we're talking!
With this approach, your system is flexible. You can change ordering, add elements, remove elements, even have different lists for different apps or users. It's great.
No difficult refactoring every time your PM changes their mind, just a data change.
But Swizec, nobody builds keyboard menus anymore!
That's true! People don't use keyboards as much as they used to. My whole thing above is pointless when a user can directly tap or click whatever they want.
But we still live in a world of sequental flows. More than ever, things are designed as user flows that go backwards or forwards. Onboarding flows are a good example.
Screen1: "Hai", Screen2: "Our app does X", Screen3: "Sign up", Screen4: "Gives us your phone number", Screen5: "Credit card maybe?", Screen6: "Do The Thing"
I simplify, but many apps have that flow.
You can build it as a hard-coded sequence of steps where each step must know who to call next. When your PM experiments, you're in for a world of heavy refactoring.
I had fun like that just this week. Not my fault even! Others on the team decided to build it the not-data way, and now we all suffer. I'm sure they will learn their lesson soon :)
But Swiz, my flow has branches and shit
So?
You're going to go into your super fancy logic and insert a bunch of if statements all the time?
if goNext and currentScreen is 'phone' and userSubscribed and…
Fuck.That.
Here's how you do it:
[() => userSubscribed ? <CreditCard /> : <DoTheThing />, ...]
Put functions in your previous/next logic! Core logic stays simple, end result for user is a complex tree of options.
That can get tricky too, I know. But you know what always works? Tree traversal. You can model your flow as a tree and build a simple walker that goes into different branches of the tree based on stuff. Perhaps based on functions in the tree nodes themselves.
😉
Save time. Make business logic your data. If a Turing machine can do it, so can your app.
Happy hacking.
Continue reading about How to think of your business logic as data
Semantically similar articles hand-picked by GPT-4
- Avoid spooky action at a distance
- 25 lessons from 25 years of coding
- Why dataviz does better for your career than TODOapps
- Livecoding 52: First impressions of Vue
- DRY – the common source of bad abstractions
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 ❤️