Book review: 99 Bottles of OOP
A great refactoring book
Last year, I read the amazing *99 Bottles of OOP* by Sandi Metz, Katrina Owen & TJ Stankus. The book explores OOP concepts and how to refactor code while being one
cmd + z away from green tests. It teaches “practical techniques for getting things done that lead, naturally and inevitably, to beautiful code”, by changing one line at a time.
That’s right you read that properly.
For every single line of code that changes your tests should remain green. If they fail, then undo the change and try again. How is this possible? 99 Bottles of OOP explains the technique thoroughly. It is a bit tricky at the beginning but just like any technique, it becomes simpler over time. Here is a gist:
Refactor code one change at a time with retest.
The book is an ode to ‘preparatory refactoring’ by introducing a new feature for the 99 bottles song. How can we replace any references of
6 bottles to
1 six-pack in the song? Simple to understand yet not trivial.
The authors go through the change following the
open/close principle: Code is open to a new requirement when you can meet that new requirement without changing existing code. It is best expressed with this quote from Kent Beck.
For each desired change, make the change easy (warning: this may be hard), then make the easy change. Kent Beck
Make the change easy
The first step to great refactoring is a good test coverage to increase confidence that new changes are preserving the code functionality.
Good test coverage doesn’t mean loads of tests and the book is a good example of that. 99 bottles of OOP handles the entire refactoring with one simple yet reliable integration test: the code must be able to print out the full song after every new code change. One assertion on a single hardcoded string of 100 lines with no dependencies on the code being tested.
Developers tend to write a lot of coupled unit tests while shying away from slow integration tests or feature tests. I find it fascinating that the code in this book can be refactored with only this specific integration test . During the refactoring, the authors create new classes but no new tests. New classes are considered private and don’t deserve any tests (just yet). Mind-blowing.
Keep calm and carry on
The authors tell you to trust the process even if the code seems far from being open to change. Keep calm and continue refactoring, ultimately the code will reach a state where the new feature can be introduced. This refactoring phase is more of a mechanical process as it doesn’t require a vision or sparks of ingenuity. It looks like this:
Change one line
Run the tests
If the tests fail, undo the change
Go back to step 1
The authors teach simple effective rules to help identify areas requiring refactoring like the Flocking rule. Then you repeat simple known atomic changes from Martin Fowler’s book refactoring and your code will become ready for change. Always.
Make the easy change
Only once the code is open do you go back to boring TDD:
Make the tests fail by updating your test
Make the change
Make the tests pass
Make it easy to understand
It’s only at that point that the authors write unit tests to cover most, but not all, newly introduced classes. That is right, every class doesn’t need unit tests when they can still be considered too small or private. This section of the book uses insightful methods to write tests that document and teach future devs how to use the new classes effectively. As tests are written, classes are still being refactored. Amazing chapter.
The infamous test
Finally comes my only point of disagreement with the authors of this great book. They decide after this journey to delete the integration test. They cover the entire refactoring with one single test only to… remove it without an ounce of remorse. Not even a thank you, Marie Kondo would be ashamed.
While not being a unit test, it should be kept. It was testing the only class already in use in the fictive codebase. The test makes sure that printing the 99 bottles of beer song still works which is supposedly an important part of the existing business rules. RIP integration test.
The book is in its 2nd edition. Reading it feels like drinking from a water hose. You can expect to spill most of it but will catch refreshing bits here and there.
It’s one of those books, like POODR, which needs to be read a few times to start internalising the concepts properly. Just like any skill, theory is not enough. Read the book, practise, read the book again, practise again… Trust the process and keep looping. Every time you’ll have new AHA! moments.
I recommend this book to developers with any level of experience and especially to senior developers who haven’t refreshed their OOP basics for at least two years. This book will challenge your deeply ingrained practices and this exercise alone will make you a better developer. Who knows, you might even fall in love with factories again.
The book covers those topics:
Recognizing when code is “good enough”
Getting the best value from Test-Driven Development (TDD)
Doing proper refactoring, not random “rehacktoring”
Locating concepts buried in code
Finding names that convey deeper meaning
Safely altering code by following the “Flocking Rules”
Simplifying new additions with the Open/Closed Principle
Avoiding conditionals by obeying the Liskov Substitution Principle
Making targeted improvements by reducing Code Smells
Improving changeability with polymorphism
Manufacturing role-playing objects using Factories
Hedging against uncertainty by loosening coupling
Developing a programming aesthetic
This book gave me the incentive to create a gem that facilitates this type of extreme refactoring: retest.
Retest is a small command-line tool to help you refactor code and works with every Ruby project (ruby, rspec, rails, rake). Designed to be dev-centric and project independent, it can be used on the fly. No Gemfile updates, no commits to a repo, no configuration files (like Guard) required to start refactoring. The gif at the start shows how to install and use the gem.
$ gem install retest $ retest --auto # Start refactoring by making a change and save the file
If you’re interested in reading 99 Bottles of OOP while going through the changes yourself this gem is for you. It will rerun the infamous integration test after every change you make.
Originally published at https://alexbarret.com