Entropy
Systems in life, it seems, no matter how hard we try as humans to keep them going, tend to entropy. No matter how hard we try we cannot build things that live forever - immortality, even in the abstract sense, seems an unattainable goal.
Systems are built and they exist for a period of a time, a lifetime. More successful systems lasting much longer, and doing more. Such systems can survive the ebb of time, and with maintenance and care the intensity of the erosion of use can be lessened - but not prevented.
There is one crucial component that keeps good systems running for much longer - gardening. The meticulous grooming, watering, and weeding of a garden bed is crucial to its survival. It requires continuous monitoring and vigilance - removing weeds, as they pop up; watering the flowers when the ground becomes dry; fertilising the soil when it becomes infertile.
Gardening
A beautiful garden is a joyful thing. The value of it, the time and care that went into it, are implicit in its existence. There is a certain joy found in its design - and this is the human element of the system.
A garden can only exist with a gardener; and a beautiful garden needs a good gardener that does constant gardening. Without a gardener a garden would wilt and die - just like a system. Gardening is essential, for the creation, maintenance and preservation of a beautiful garden; but gardeners come and pass, and just so gardens do too.
Software to me is the obsession with building beautiful systems, and just like beautiful gardens these systems require constant gardening. Without it they decay - the ground dries up; the soil becomes infertile, and the the weeds take over.
It's up to us as conscientious, well-mannered, gardeners to keep our gardens healthy. Healthy software lives much longer and performs much better. Maintenance is easier, and it responds well to change.
So how do we tend to our software? How do we water the beds of our code, weed out the bugs, and remove the rot?
Everything is on FIRE
Very seldom do we get to start on something new. The majority of what we do is maintaining that which is already done - and usually it's an awful mess. The welcoming committee on these projects always has reasons and excuses as to why this happened. There's always a reason for the mess. but honestly, who cares.
Let's take a minute to acknowledge those well-intentioned fools, ourselves included, without whom the majority of us wouldn't have jobs. Thanks past me, and thanks past you.
Starting on such a project is intimidating and daunting. The code base is big and tangled, and there are always parts of the code that no one touches. With both hands, and I can't stress this enough, reach out and grab those parts. Shake them around; break them some more; figure out how they work and slowly start to understand them.
Doing this will make you a key-player in your team - you're the guy that's willing to work with the icky stuff. It will allow you to fix it, clean it, and scrub away the rot.
Small fixes are fixes too
All fixes are valuable. Renaming variables, restructuring code, or large functional changes all add value to the code. Don't be hesitant because your change would be too small.
But what is Legacy Code?
Legacy code is untested code. Keep a good coverage percentage for your unit tests - choose one at your own discretion - and always ensure that the heart of your system is unit tested completely. This will allow you to chop and change, refactoring to your heart's content, without having to worry about breaking something crucial.
But don't get lost in a testing labyrinth. This can happen in a few ways. The first is when you start writing so many test that you end up testing silly things like controllers. A 100% test coverage is silly - it makes me think of Russell's Paradox what tests the tests? There is a good level of testing, and then there is an arbitrary level of testing - try and stick to the former.
The second maze you may find yourself in, is trying to test code that is coupled tighter than Betty White in the 1956 spandex swim suit catalog. You start injecting, and injecting and injecting dependencies and your test starts looking like a mess. In this case it's best to leave it for a later stage.
The Time Investment
When the weeds pop up take time to weed the garden. The most elegant fixes take time - and the most elegant fixes last the longest. Writing good code takes time - fixing bad code takes even longer. Tending to a bed of code, understand that it takes time and don't rush it (but don't go too slow either).
As a professional software engineer I adhere to the IEEE Code of Ethics. Because of this, I hold myself to a certain standard - and this is a standard my clients can expect. I simply won't rush something and push it into production because business needs it now. It's irresponsible, and unethical. I won't go at a snails pace either, but I'll ensure that I generate minimal technical debt.
A Full Bed of Roses
Reading code is a painful experience; writing code is a joyful one. This can lead us down a very dangerous path - where we start writing all the code that we need even though most of the functionality exists already.
Remember that any code you write is old already. Spend some time before you start, and scour the code base for what's already there. Extend it if you must and write new code if you absolutely have to. Practice your Coding golf.
Coding golf is the art of using as few key strokes as possible to write a function. It's a great exercise in being terse with your code.
Context is key
Ensure that you understand the context of what you're doing. Don't do something because you've been told to. The bigger picture is key to writing purposeful software. Understanding how all the moving parts work together allows you to write your API's cleanly, and to define those contracts strongly. You'll soon figure out exactly what your piece needs to look like to work cleanly in the bigger system.
Terminate with Extreme Prejudice
Another controversial point, very dear to my developer's heart, is eliminating bad code with extreme prejudice. If you can see it, if you can logically explain why it's bad - cut it out like a tumor.
With version control tools like Git removing bad code should be easy, and should be done without trepidation. If you break something, so what? You now have broken code with less rot - fix the thing that broke cleanly and everything is better. Worst case just roll back your change and try again.
Don't be scared to change things - the only way code can get better, healthier, is by taking a zero-tolerance approach to cleaning it.
Take a Break
Breaking things is bad, right? Let me be crystal clear here when I say
BREAKING THINGS IS GREAT (FANTASTIC EVEN)!
Did you just break some crucial part of the code? If the proper unit tests were in place, this code would never have made it into the code base. So write the tests to prevent this next time. fix it, understand it, and restructure the code so it can't break here again.
Good code should be written to do two things well - it should allow for itself break gracefully; and it should allow for itself to be fixed rapidly.
Breaking code might be a controversial topic. Because the concept of breaking is inherently bad. But if you keep breaking something, in different ways, and removed all the flaws that broke it you have a much stronger, more resilient system.
No Good Deed goes unpunished
In your crusade of truth, and honor, and better code, you're inevitable going to be stepping on some toes. Honestly, this is probably the hardest part of gardening. People have feelings, and people write code. These same people see their code as an extension of themselves. I do too - and I really shouldn't. It's going to happen and the best thing you can do is be very vocal about what you're doing and why you're doing it.
Explain yourself with complete clarity. Leave no shadow of a doubt over what you're doing; and make it crystal clear that you do so without contempt or malice. You're making the code better for everyone.
It's a hard cultural change, and it sucks hurting people's feelings - but you have to crack some eggs to make an omelet. And if that omelet is for the betterment of everyone then that's quite alright.
The Naked Truth
In the end we're all human. We err, and make mistakes. Work together, not against one another. You can achieve so much more as a team than an individual. Search for common ground and build unity with your fellow developers. We're all in this together, and a healthy work environment makes life better for everyone involved.
Goodbye, and thanks for all the Fish
This is a softer piece, and a lot less technical than what I usually write - but it comes straight from my heart. I stand by all the points I've made and I hope it helps you in your endeavors to build a beautiful garden.
Now if you don't mind me - I'll just be over here gardening :)
Top comments (7)
Love that analogy... I grew some vining tomatoes this year, and they grew up and out EVERYWHERE because I didn't properly support them. Now I'll think about that as I'm coding 🤣
Hopefully that will keep me disciplined!
There are some real gems in here :-) The IEEE code of ethics and the code golf I had never heard of before. Thanks for sharing!
This is the dev.to equivalent of the Tao of Programming.
Thank you for sharing this and transforming these notions into text. You earned a follower.
Great article!
Today I have been trying to upgrade React to the latest version in the project and thought about this. What makes this "gardening" even harder is this: not only the code of the project itself should be refactored and improved continuously, but the project's dependencies too should be revised and upgraded from time to time.
You started the article like a noble price winner in literature so keep writting and I will keep following you :)
Very well put article, clean, nice and organized (like a garden).
Thank you for this article!
Congrats for this nice article. I recognized a lot of situations that I've passed through when I moved to a new job. Thanks for sharing!