To write great code, it’s actually faster to write it badly first (as quickly as possible) then refactor it into great code, rather than trying to write great code in the first place.
Huh? How can writing bad code then rewriting it a 2nd time be FASTER than just writing it once?
The Code Kata Experiment
Recently I hosted a couple of 1-hour coding exercises with my team. Both were simple code katas: The first was to write a simple program but writing clean code using SOLID principles. Each person showed their code to the team afterwards and we discussed how to improve it. (Note: the exercise was to create the game 2048).
The second was very similar, write a simple program to emulate playing Tic Tac Toe (Noughts and Crosses in the UK), but this time, to hack it together as fast as possible — don’t care about code quality. Each person showed their awful, embarrassing code to the team afterwards, and discussed all the shortcuts they took and the refactorings they would make, given more time.
The outcome was very surprising.
In the first exercise (clean code version), there was a huge variation in how far each person got. Some members had barely got any code at all by the end, others had completed the whole exercise.
In the second exercise (hacky version), most of the team had completed the exercise to the same level, regardless of their level of experience.
For some developers the progress in the first exercise was so low as to be embarrassing. I knew the developers were smart and skilled, so what happened, and is this a one-off? Does this ever happen during day to day work? Do some developers get paralysed like this regularly?
What’s going on here and how do we prevent it happening?
As the Tech Lead of the team, I realised that by teaching clean code principles, developers would try to write clean code straight away. Before their fingers hit the keyboard they are thinking about single responsibility principle and dependency injection and testing abstractions.
Exploring this in discussion and pair programming sessions, I saw the tendency to over-think the design before writing any code, thinking for too long about the perfect solution. Developers would write a line of code, then delete it, and think “No, this is the wrong place for this code. What should I call this class (that I haven’t written yet)? Should it live in a folder? How many classes will I end up with?”
This leads to analysis paralysis and developers coding maybe 10x slower (or more!) than they are truly capable of. Tasks that should take a few hours can run into days. In some instances I helped solve a problem in 10 minutes that they had been struggling with for a day, and the solution was not ‘magic’.
There are so many rules and principles to write good clean code that it’s easy to see that your code sucks the moment you write it!
Prevent analysis paralysis — Do 1 thing at a time
This is not the most efficient or optimal way to write software. Kent Beck was attributed with the phrase “Make it work. Make it right. Make it fast” (In that order!).
Write some bad code. Get the fundamentals out of your head and into code. Comment all the bad bits if you want — this will free up mental energy so you dont have to remember everything you want to change. With this you can discuss the code problems with someone else rather than explaining in words what you might have written.
Test Driven Development was, in a way, designed to break this barrier down, to start writing code in small chunks as quickly as possible, and prevent you keeping track of a zillion things in your brain, but you don’t have to follow TDD to fix this problem (although its a great first step to learning how to break down problems and design code).
Programming is complicated. You will never know all the requirements until you start coding, you’ll realise you never asked about edge cases, about dates too far in the future or integer overflows or null checking. If you don’t know all the requirements, you don’t know how complicated your classes are going to be ahead of time, so it’s futile trying to guess the future anyway.
So what’s the answer?
- Write small pieces of functionality at a time. Set yourself small goals which can be coded and completed within a few hours.
- Write it badly first as quickly as possible. Explore the problem space.
- As you explore, you may find lots of other code needs changing before you can really write your feature. It’s OK to revert everything you’ve done and go back to make those changes (because you did it badly and quickly, you haven’t wasted much time!) Discovering new work is PROGRESS!
- Ideally, write a unit test or set of tests (either first if doing TDD, or afterwards if you prefer) to lock this behaviour in place before you refactor
- Once it works, refactor it.
- As with any process, review this process to check you are getting faster and you don’t feel bogged down with process. If you discover something better, use it instead!
Write more bad code. Hack something together. Realise you don’t know anything. Realise other code needs extending before you can do anything. Get answers. Learn as much as you can about the problem you’re trying to solve and the possible solutions as fast as possible, without trying to craft beautiful code at the same time. Maybe you’ll realise the work isn’t even possible and you’ll be glad you didn’t spend a week on it!
When it works, THEN refactor, mercilessly. You have all the code, now put it in the right place, with the right shape, the right classes, the right names.
This is why it’s important to get good at refactoring, because it’s much faster in the long term to write bad code then make it good, than to try to write good code in the first place. If you can make bad code good, you can work successfully on any codebase in any company.