So you tell me your pet project has become a burden to maintain? You might be experiencing a Death Goblin infestation. It is a particularly nasty pet. I have several, you may have several, Mark Zuckerberg probably has a horde of them.
Mark Zuckerberg’s Death Goblins (artists imagining)
It’s what comes to bite your arm off when you’re not producing nice code. If you’re lucky. If you’re not it is known to be able to kill you in your sleep. But there are techniques out there to take care of the Goblin and make it weak and harmless. Harmless goblins normally mean a few things:
- less bugs
- cleaner code
- self documenting code
- easier refactoring
- continuous integration
- you know who to blame when the fit hits the shan
- and you get paid more
You don’t care about clean testable code, the goblin grows. As it grows, the fucker multiplies. As much as you are taking care of the monster, if you keep feeding the beast it will never go away, or become a manageable little bugger. So what counts as feeding?
##Feeding the Death Goblin (Technical Debt)
You might also call it technical debt. It is very easy to feed it and it just picks up whatever is left on the table.
What happens it is you rush, maybe you are a startup trying to be as agile as possible, an agency with a deadline or work at a corporation that doesn’t really care about code quality. Or you are an university student and no one has ever mentioned testability to you before. You know what I’m talking about.
this is how a Death Goblin sees your code. source - Wikipedia
So you add a feature. You fix a bug. It takes you a day, two hours. Rinse and repeat 10 times. So you add a feature. It takes you two days. You fix a bug. It takes you four hours. I compounds. That’s because you’re feeding the goblin and spending more and more time battling it and making sure it doesn’t kill you. The Goblin smells your junk code and enjoys it a lot.
So how do I know when I am feeding the goblin, the basic signs.
- No tests
- The code is hard to read and takes ages to understand
- Tracing bugs is hard, fixing one just creates more of them
In code world, the signs might look like static methods everywhere, views and controller that have some serious logic in them, levels and levels of nested ifs and switches… You probably know what I’m talking about.
Any such code in production feeds the meanest, nastiest kind of goblin.
There’s also the goblin that gets fed when you’re prototyping or spiking. That is the less dangerous kind, and we normally let it grow. But it has teeth as well. Every time you launch the app only to get through 3 levels of hierarchy and loading screens just to test a validation in a form it bites you. If you had tests for that component instead the goblin would go hungry.
Stop feeding it, and you are on a good track to capturing and taming it.
##Battling the Death Goblin
Ancient wisdom has it that there are a few clearly defined stages for making sure the Goblin is harmless. In this blogpost we will look at a few steps of how take care of it.
First, you stop feeding it. As it starts getting hungry and weak you can build the cage and prepare your tools for battling it. Once captured, the cage needs to be cleaned regularly, otherwise the monster hides in its own shit and can escape easily. They do that more often than you can imagine. Now we can neuter the thing to prevent it from reproducing and spawning more goblins.
But what if what I am facing seems gruesomer than even the nastiest goblins? With nasty corpses laying about that seem at least a decade old. Then maybe I ventured into the realm of the dreadful King Goblin. Then, you are in a legacy system. It is doable to slay the King Goblin but takes different tactics altogether. We will go through these as well.
##Building the Goblin Cage (Know your Tools)
So we need to catch him, but for that we need to speak his language. Let’s look at some terms we’re going to use:
TDD is a method of writing tests first. Idea is that tests guide your implementation and represent an ad hoc documentation. You might have heard of the red green refactor mantra. Testing is a way to prove your code is valid and the changes you’re making are ok and you know how to break it. Testing can be functional (think espresso, calabash/cucumber), unit testing (public methods, classes are in isolation) or anything in between (integration, etc…). Manual testing isn’t testing, it’s suffer and should be minimised.
Goblin cage assembly tools. source - ikea
Code coverage is percentage of lines of code that has a test associated with it, you can go through the program and see what methods or parts of methods are covered by tests. 100 percent code coverage might as well be served with a unicorn as it doesn’t exists and means that whoever is claiming to have it is inevitably testing their getters and setters. What you should be aiming for is testing the important parts, rule of thumb being the “ifs”, the “elses” and the entry points around interfaces. Oftentimes in practice code with 20-30% coverage is way better than 80%.
What cage can I build, and what can I test? First off, get your tools ready. Cage can’t be built on uneven terrain, so if you are building it from scratch, then make sure you’re preparing for testing out of the box.
As you generate an app, a test case is generated for you in most IDEs. In others, it’s a mere few clicks away. The latest version of Android Studio does it for you.
##Capturing the Goblin
As the beast goes hungry, and your cage is ready and waiting it is time to trap him. He can’t move when your code is clean and tidy, so you have no static methods in your code, you abstract worker classes’ implementations behind interfaces and definitely keep them out of your view and controller logic.
As you ensure testability, your code gets cleaner and the goblin is captured. Contratulations! You’re halfway there.
##Cleaning the Cage (Refactoring)
We need to do this regularly, as the Goblin might escape into the wild otherwise. Plus, code smells less when you are cleaning it every day.
even the monkeys have to battle goblins
Provided codebase is well tested, refactoring is a charm. Moving stuff around, extracting repetitive chunks into private methods, removing static methods and probably the most important part, making sure you stay true to the single responsibility principle.
Tests will tell you when something is missing or you have introduced breaking changes.
##Goblin Neutering (Good Practices)
Luckily, that part is not as hard or ugly as it sounds :) The secret for goblin not reproducing is just sticking to good practice and everything else is smooth sailing.
Danny Trejo is following all the best practices
The practices range from process-based approaches to purely technical ones. First one is to never commit untested code and always running the tests first when you are pulling someone else’s code. It’s like renting a car and checking for dents and scratches before driving it so you don’t pay for any damage done by morons before you. Make use of the blame function in git so goblins are others’ problem.
Second one would be to write your code test first and even better, write functional tests first if possible as they help you to hold the goblin in place as you go along your business.
The more technical best practices are things like mocking time (way too few people do that), thoroughly testing your public interfaces, mocking out external APIs, testing negative scenarios first, rather than positive, never ever try to hide errors in try/catch loops (let it crash, baby crash) and again, always mock time.
flossing is a technical kind of best practices
Your starving, little caged goblin has just had his balls removed.
(But they can and will grow back if you’re not careful, so make sure to keep on sticking to the Good practices)
##Hunt for the Goblin King (In a Legacy System)
Of course, in real life, greenfield projects are about as rare as unicorns. What happens instead is you get pushed into a King Goblin infested hellhole of a legacy project that makes Mordor look like Cabo San Lucas.
It’s the thing that is mostly hacks rather than good practices, documentation was stopped being updated 2 contractors ago and the only test it has is the auto generated one. And it’s up to you to deliver a feature last week. Also, there is a list of bugs stretching across the Channel to France.
What you can do is take the best riot gear and arm yourself with an inevitable battle with the Goblin King. (hint - inevitably you lose).
Tony Stark is ill-equipped for battling the King Goblin
Or you can be smarter and approach the thing as it should be approached. As you enter the lair of the King Goblin, you can start creating a perimeter. Write integration tests. Maybe try the Golden Master technique.
Then take it step by step in the tall grass. Look for signs of the problem, and when you find them, start testing around them. Functional, unit, integration. Only then can you start refactoring it. It might seem like you’re not changing anything but believe, every tiny bit matters. This is a game of attrition.
Look for obvious signs of goblin smell and refactor when appropriate, focusing on the important bits as you identify them. Often they are around the entry points of the app, anything that is out of your control. Maybe the guy writing that sweet API or library wasn’t so clever at all and is long gone killed off by his own Goblins.
Eventually the King Goblin will be cornered, running out of tall grass and shadows to hide in. That’s when you realise it’s nothing special after all just an ordinary little Death Goblin you already know how to fight.
#Winning the Battle
Charlie knows how to win
I intend to go further into detail of each of the steps in the following weeks. My target is one step per week.
Send comments to zan at spacecowboyrocketcompany dot com or tweet me.