
Writing Readable Code: The Art of Arranging for Clarity
Your no-BS weekly brief on software engineering.
Join 100,000+ developers
We hear it all the time: readability matters. But why? What is it about well-structured code that makes such a difference? The reality is that future-you—six months from now, staring at a function you barely remember writing—will either thank past-you or curse your name, depending on how readable that code is.
The Problem with Memory
Humans are unreliable narrators of their own past decisions. When writing code, we assume we'll remember the mental model we had at the time, but we don't. We're fallible, just like an AI system hallucinating answers when it lacks context. You've probably seen AI generate plausible but incorrect code—it looks right, but when you run it, something is off.
The same thing happens when we write unclear code: it might work today, but future-you will struggle to understand why. Without clear structure and explicit intent in our code, we end up debugging our own thoughts rather than our software, wasting time unraveling logic that should have been obvious.
In other words, think of yourself as being a forgetful developer.
Arrangement is Everything
When we structure our code effectively, we make it easier to trace, reason about, and modify. What does that mean in practice?
- Highlights the most important details first.
- Establishes a logical order of execution.
- Groups related concepts together.
- Uses naming and formatting to reinforce clarity.
The Hidden ROI of Readable Code
Readable code is an investment that pays off in ways you may not immediately notice:
Debugging is Easier – When code is well-structured, tracking down issues becomes a breeze. You're not left wondering, "Where does this variable even come from?"
Adding Unexpected Functionality is Easier – Requirements change, and code needs to adapt. Readable code makes these transitions smoother.
Removing Code is Easier – Some of the most satisfying pull requests remove more lines than they add. When code is arranged well, identifying redundant or obsolete logic is simpler.
A Case Study: When Simple Code Starts to Break
The best way to understand this is to look at some actual code! Imagine you're a consultant working with a client in the online dating space. One day, the CEO decides annual plans need a discount. Sounds simple enough. Let's start that way with our solution.
class DiscountCalculator
def initialize(plan)
@plan = plan
end
def calculate
return 10 if @plan == "annual"
0
end
end
This is hardcoded of course. Both the percentage discount and also the type of plan required is a hardcoded string. Ick — but hey, it works. In fact, the CFO realized how well it worked and said we can't run the discount all the time. It needs to be able to be turned on and off.
class DiscountCalculator
def initialize(plan, promo_active)
@plan = plan
@promo_active = promo_active
end
def calculate
return 10 if @plan == "annual" && @promo_active
0
end
end
Still hardcoded but you can read it and work with it. Well, that is until marketing came along.
Let's run a special sale in the first two weeks of February before Valentine's Day! If they do, let's give them a 15% discount!
class DiscountCalculator
def initialize(plan, promo_active, signup_date)
@plan = plan
@promo_active = promo_active
@signup_date = signup_date
end
def calculate
return 15 if @plan == "annual" && @promo_active && @signup_date.month == 2 && @signup_date.day <= 14
return 10 if @plan == "annual" && @promo_active
0
end
end
Now our logic is starting to get messy. Every new request forces us to edit this method directly. Let's refactor.
Refactored: A Simpler, Configuration-Driven Approach
Instead of overengineering with abstract rules, we can use a configuration-driven approach. This keeps the logic declarative and easy to extend without introducing unnecessary complexity.
class DiscountCalculator
DISCOUNT_CONFIG = [
{ name: "Valentine's Day", condition: ->(signup_date, plan, promo_active) {
plan == "annual" && signup_date.month == 2 && signup_date.day <= 14
}, value: 15 },
{ name: "Annual Plan Promo", condition: ->(_signup_date, plan, promo_active) {
plan == "annual" && promo_active
}, value: 10 }
]
def initialize(plan, signup_date, promo_active)
@plan = plan
@signup_date = signup_date
@promo_active = promo_active
end
def calculate
applicable_discounts.map { |rule| rule[:value] }.max || 0
end
private
def applicable_discounts
DISCOUNT_CONFIG.select do |rule|
rule[:condition].call(@signup_date, @plan, @promo_active)
end
end
end
Why This Approach Works
Unlike the earlier hardcoded approaches, this configuration-driven design reduces repetition, centralizes logic, and makes adding new rules effortless.
- Declarative Logic: The discount rules are stored as data, making them easy to read, understand, and modify.
- Minimal Complexity: No need to define multiple classes or methods—conditions are defined inline.
- Extensibility: Adding new rules is as simple as appending a new entry to the
DISCOUNT_CONFIG
array. - Debugging Context: Rules can include descriptive names, helping identify which discount was applied.
What could make this even better? Storing the discount codes in a database table! But hey, we're not there yet. For all we know, this may be the extent of our discount codes. For now, let's leverage a principle we wrote about on technical debt: solve today’s problems cleanly and extend solutions only when necessary.
Remember that as developers, planning for scalability isn't just about database tables—it's also about ensuring future maintainability.
The Future You
So next time you’re writing a function, think about future-you. Write as if you’re leaving breadcrumbs for an AI with limited recall, hallucinating solutions unless given clear instructions. Arrange your code to tell a story—one that’s easy to extend, adapt, and understand long after you’ve moved on. Because in the end, that’s what good code is: a story worth rereading.