“Wow. Look at this code, it looks so nice! And it’s so clear and easy to read!” Clarity and readability are admirable things to aim for, but is it possible that the quest for “perfect” code is preventing you from actually RELEASING real functionality to the user?
When my perfectionism is owning me more than helping me, I remind myself:
“Yesterday is already gone. Tomorrow is not yet here. Today is the only day available to us; it is the most important day of our lives.”
– Thich Nhat Hanh
What Thich Nhat Hanh is referring to is the Buddhist concept of Impermanence.
“Impermanence..?!?” What a crazy thing to be talking about in relationship to software. “Aren’t we trying to build something that will stand the test of time?” is something a rational human might say. But what impermanence refers to is the fact that all things decay and/or change. Maybe that notion is scary or sad to you, but that’s not what we’re trying to do– CubicleBuddha.com is all about lifting you up! So instead of being sad about the staleness of yesterday’s effort, remember that your users want the code to change to support new use cases. So why not focus on what your users need today– after all, “[today] is the most important day of our lives.”
How Do I Know When Fear Is Preventing Me From Helping My Users?
It doesn’t take very much time to find a company that is afraid of change. Here are some clear markers of that “fear of change” and how it can lead to a slow development process:
- Products that are delayed because “it wasn’t all ready.” (Who cares if it’s not all done? Release what you have!)
- Code that never gets released because of poor understanding of the requirements/ acceptance criteria. (If you’re trying to add value for your users, you should probably ask them what they want. And better yet, try to limit the number of people that the message has to flow through between the user and the developer. Whisper down the lane is a killer.)
- A product that never gets released because the contract was canceled before it had a chance to “go live” to the customers. (This means that the company clearly waited too long to get the work out to the users.)
- Code that is rewritten all of the time because new requirements come in all the time– i.e. “swirl” or “churn.” Again, this is a clear sign that it took too long to get the product out to the users. Software isn’t made out of stone like I used to think– it’s very easy to change. And if you don’t think it’s easy to change, then you’re not writing with an eye towards refactorability. Think about it this way: a contractor who is building a porch or a roof can’t build it quickly and then ask the home owner if he likes the work. They’d get fired and/or taken to court if their work was deficient. But a house contractor can draw up plans and get feedback on these low-fidelity mockups of the work. We have almost no excuses in software since not only can we have the users review our plans, but we can also get the work out and then change it almost immediately.
What do these scenarios have in common? They’re all examples of how embracing change would have made for more pleasurable experiences for all involved.
So, How Can I Embrace Change?
Maybe you’ve recognized that you want to make the jump, but you don’t know how. You ask, “what does embracing change mean in software?”
- It means writing code that lets you make quick changes without breaking the past. This means using good design patterns (like we’ll discuss in future articles).
- It means writing unit tests so you can make new updates without worrying about whether or not you broke your last updates.
- It means removing “Dead Code” so that the past doesn’t slow you down and creep up as unexpected bugs.
- It means worrying less about the “beauty” of your code and more about if it makes your users happy and if it makes future developers happy to build upon it.
- It means loving and understanding your peers, even when your Product Owner comes to you and says that we misunderstood the customer and therefore we have to remove the code you just worked so hard on. That’s change. And as long as your listening to the needs of the customer, you’re changing for the better.
We’ll be touching on many of these solutions in future articles, but let’s unpack that last point a little bit:
How I Learned To Be Happy Deleting Code
In my years of professional programming, I have had to delete functions, features, and entire products countless times when my bosses “refined” their understanding of the customer’s needs or the market’s needs. So when that happens, did I get sad over the loss of my code? Heck yea I did! I’m only human. But eventually you realize that every time we write code, we’re producing our best guesses of what the customer needs. As we refine that guesstimate, we have to remove or delete the parts of the product that were bad assumptions. Sometimes that means deleting your favorite parts– yes, even the parts that you really thought were beautifully written. But was it really beautiful if it wasn’t helping the customer?
You might have read my critique of “beautiful code” and thought, “is he asking me to not worry about readability?” Of course I’m not! I am simply recommending that you don’t let yourself become slowed down or paralyzed by thoughts on how the code should be structured.
The best way I find to unburden myself from these thoughts is to ask myself, “Do you think that future developers will see your code and think this is beautiful?” The answer is almost certainly “no.” The dirty secret about software maintainability is that most of us only like the way our code looks.
Ignoring “pretty” and perfection
What we’re trying to do is make a tiny amount of room for acceptable failure so that perfectionism doesn’t control you. When you’re thinking from the perspective of balance you can consider thoughts like “I want the code to be maintainable but I also want to get value out to the users today!” We would be wise to remember the words of Confucius who said:
“Better a diamond with a flaw than a pebble without.”
So my wish for you is that you accept and learn from the flaws so you can grow your work into a diamond day by day. And if your peers have flaws, accept them too– they’re also trying to grow and learn too. If you don’t let go of the past, then you can’t make room for what really matters today.
If this article helped you, you might interested in other articles from CubicleBuddha.com such as:
Doing nothing: The cure for procrastination
Cubicle Buddha ・ Jun 26 '19 ・ 6 min read
Samsara: 5 Agile Techniques to End Suffering And Increase Learning
Cubicle Buddha ・ Apr 7 '19 ・ 6 min read
Thanks again for reading. I’d love to know what has helped you to accept change in the workplace?
Oldest comments (8)
Thanks for this article, I really needed this :)
You’re welcome! Thank you for your kind and generous comment. Comments like this are why I write this non-profit blog! I also need to give myself this advice sometimes. It takes work to remind ourselves that control is and illusion. But there’s a lot of happiness in embracing the lack-of-control.
So keep on reading the blog since I’ll have more posts about letting go and getting happy at work. :)
A bit of wisdom from someone who has deleted a lot of code; including my own code.
Backend code has a longer shelf life than frontend code. I've been doing Java for over two decades. A lot of the early libraries and tools are still around and are still getting active maintenance and I still use them. I have code that I wrote close to a decade ago that is still used and relevant. Good backend code can stay relevant for a long time. I use some Java libraries that predate anything you might consider super dated on the frontend.
Frontend code has a shelf life of about 1-2 years before people start getting nervous about new frameworks, a new design, or some other excuse to start from scratch. Frontend code that survives beyond that is generally expensive to maintain. This is OK. It means you have an reason to not over-engineer. YAGNI and KISS are good principles.
Other people's code almost always ends up being retired early. I've learned that the problem is that good handovers are rare in our industry. Getting this right and gradually transferring ownership can create enough buy-in from the new people that they may end up being confident doing maintenance. Failing to do that they will end up replacing rather than fixing code.
Code that tries to be clever is the worst. Your pretty is my unreadable. Cryptic one-liners that show of your mastery of dark generics voodoo or typesystem hackery is not a good thing. I like stuff that does exactly what it says it does and nothing more. I hate languages that encourage these things and seem particularly susceptible to code rot: calling out Ruby and Scala here. These languages seem to attract exactly that kind of behavior.
Prototypes and mvps have a habit of becoming the thing. Starting from scratch two months into a project is a hard sell in most places.
Many startups fail in the phase where they are trying to rewrite their MVP because it is unfixable and only few have the runway to survive long enough to pull that off. Deleting code to fix code rot is a risky business and kills companies.
Maintenance makes up the vast majority of cost in our industry. Given that, developing from scratch can be expensive and still end up saving cost. If your existing design is holding you back, it basically becomes a tax on new development. Understanding cost is helpful in deciding what to delete and what to fix.
Understanding code metrics and their influence on maintainability can help you produce better code for the same cost. Anything with lot of lines of code and a huge number of imports has high coupling and low cohesiveness. You want the opposite. Some people refer to these metrics as the solid principle, which basically translates as doing things that help improve these metrics. It's not an OO thing and applies to any language. Lots of imports == bad. Thousands of lines of code in a single thing (module, class, method, function, lambda, etc) == bad. That simple. Any idiot can see at a glance of an eye what are the problem points in any code base. Simply look for the biggest files: that's where all the bad code is going to be.
Refactoring is key to keeping code relevant. Anything that you can't refactor will accumulate cruft and rot over time. Using languages and tools that facilitate this helps keep code stay relevant longer. You don't allocate time for refactoring it is part of what you do. Similarly writing tests is not a separate task.
If it is messed up or making your work harder, and you are not fixing or refactoring it, you are adding to the problem by working around it. That means you are piling technical debt on top of existing debt and are making it more likely that the code will be deleted sooner rather than later; including the stuff you are doing right now. It's that simple.
The boy scout rule is easy to explain and apply. Good developers leave a code base in a better shape than they found it with every commit.
Tactics and strategy of software development are such that planning for replacing what you are building causes you to build things such that that is easy and probably less likely to be needed.
Requirements are based on assumptions. Assumptions are subject to change as facts change. They always change and will end up breaking assumptions. No code is ever finished and you should judge it on its ability to be fixed for requirement changes. Those are inevitable. So, is code rot.
Thank you for reading and for your great response. It’s amazing what experience teaches us. I agree with your points.
A+ for your last point about adaptability. That’s the main reason why I write this blog. Happiness is based on freedom, and if your code prevents you from making easy changes... then you’re not free!
This is such an awesome idea for a post! So well written. Nice work!!
Thank you so much @michaeltharrington . Sincerely. It's really so wonderful to be able to share the things I've learned in my career. It's great knowing that my tough times have transformed into some advice that can help people. But I wouldn't be able to do that without an incredible community! So thank you @michaeltharrington for helping to coordinate that community! :)
Did you get it out of your system?
I totally agree, write code that works poorly today, make it better tomorrow.
Making mutable prototypes have always worked better for me, regardless of where that code lives.
Developers should not be afraid to break things, even accepted convention, just do it sensibly. :)