How to Write Readable Code
Episode 8: Designing Code That’s Resistant to Change
(Last updated: April 30, 2025)
Readable Code
This article takes 5 minutes to read!
(Rotate your phone horizontally if the image is too small)
Make your code resistant to change!
“The moment a spec changes, I end up rewriting code everywhere.”
Have you ever had that experience? If you aim for readable and maintainable code, designing it to be resistant to change is essential.
In this article, we’ll cover key principles and design approaches to make that happen.
[Table of Contents]
What is OCP (Open/Closed Principle)?
Functional vs Procedural: Which Handles Change Better?
How to Minimize the Impact of Changes
What is Data-Driven Design?
Summary
1. What is OCP (Open/Closed Principle)?
We've discussed OCP in previous articles too. Here are the links: Open/Closed Principle (1): Designing for Extensibility, Open/Closed Principle (2): Using Inheritance and Polymorphism.
In short, the principle says: "Open for extension, closed for modification."
This means new features should be added without modifying existing code. Instead of increasing conditionals, handle changes via substitution, inheritance, or delegation.
Let’s look at an example. Here's a bad one: every time a new user type is added, you have to rewrite the function.
Bad Example
Now here's a better example using a dictionary to map user types to discounts.
With this structure, you don’t need to modify the function itself when adding new user types—just extend the dictionary.
This clearly shows the strength of OCP.
Good Example
2. Functional vs Procedural: Which Handles Change Better?
Which paradigm handles change better: functional or procedural?
The answer is: functional programming is generally more robust against changes.
However, for embedded systems or scenarios involving state control, procedural code may be more suitable.
That said, you can still write robust code using procedural style with the right practices.
Comparison of Functional and Procedural Styles
Aspect Functional Procedural
State Management Stateless (immutable) Often relies on global state
Side Effects Minimal (pure functions) More frequent side effects
Reusability Easy with function composition Harder within sequential flow
Learning Curve Slightly steeper Gentler (easy to get used to)
3. How to Minimize the Impact of Changes
To reduce the scope of impact from changes, focus on these four points. Points 1, 3, and 4 are common advice, but point 2 is especially crucial in Python.
Break functions into small, clear units: Smaller responsibilities mean less impact when modified
Use data structures instead of conditionals: Abstract logic using dictionaries or mappings instead of more if statements
Depend on interfaces and abstractions: Avoid relying on concrete classes directly
Layer your architecture for change: Separate UI, logic, and data access; consider clean architecture
4. What is Data-Driven Design?
Data-driven design means modifying behavior without changing the code.
For example, using config files (JSON, YAML) to change workflows or thresholds.
Benefits include:
Behavior can be changed without touching the code
Non-engineers can adjust settings
Friendly for test and production environments
Here's a JavaScript example of data-driven design.
The `discountRules` section is data-driven. You can easily add a new rule like `executive` with a 0.15 discount rate by updating the data only.
Example of Data-Driven Design
5. Summary
This article introduced how to make code resilient to change.
By following principles like OCP, adopting functional style, reducing conditionals, and applying data-driven design, you can achieve more maintainable code.
This time, we especially emphasized data-driven design, which enables even non-engineers to maintain and extend behavior. Give it a try!
▼References
"**Readable Code**" by Dustin Boswell and Trevor Foucher, translated by Masanori Kado, O'Reilly Japan