YAGNI vs Refactoring

I am currently reading through Growing Object-Oriented Software Guided by Tests by Nat Pryce and Steve Freeman. It is a great read and I highly recommend it. They describe a methodology for building high-quality software from the ground up, using all levels of testing to ensure that quality and functionality. Early in the book, they cover many concepts that are commonly heard in the developer community, including YAGNI and refactoring. Later in the book, they use a "real-world", messy example to demonstrate their methodology.

At the end of chapter 16, about halfway through their real-world example, something struck me in their observations for the chapter. They are leaving the example in an ugly state in need of refactoring. In their observations, they state:

So now that everything works we can get on with more features, right? Wrong. We don't believe that "working" is the same thing as "finished." … [W]e can either clean it up now, while it's still fresh in our minds, or re-learn it every time we touch it.

Too often we hear developers shouting "YAGNI!!!" about anything and everything. Extract a method or class or interface? YAGNI! Create a higher-level abstraction? YAGNI! Make any kind of change to working code? YAGNI! Do the least possible that works! So then where does refactoring fit in the YAGNI mind? Refactoring after writing code, technically, is totally unnecessary. All it does is make working code look cleaner, but it doesn't add any functionality. Often, refactoring leads to more interfaces, more classes, and more methods. Sure, refactoring is necessary when you are going to make changes, but doing it after you finish? YAGNI!

We can refactor after the fact, when we have to make an unexpected change to the code down the line. Often we do, and need to, because maybe the change was not a kind we could anticipate. Or maybe we didn't know that a concept was buried in our code until the change request exposes it. Or maybe the code wasn't refactored when it was written, so we need to clean it up.

But only refactoring after the fact runs contrary the quote we see above. Once code is written (and will need continued maintenance), we should refactor as soon as possible, lest we end up with mangled code we have to re-understand down the road, according to the authors. As I said, the authors are adherents to YAGNI, but they understand that you always need maintainable code*. We can't just write bad code and ship it because YAGNI.

We don't need every possible abstraction. If we did every possible refactoring, we'd never be done, since refactoring is fractal and we could keep finding more and more abstractions. But we can commit to doing some refactoring. We can listen to our code and see what abstractions are already in there, causing pain, that we just need to pull out. We can make preparations for those changes we already know are in the immediate pipeline. We can refactor our code, in the face of uncertainty, to preserve our right to do design later, and make our code more robust to change. Code that is robust to change is something that we always need.

* If the code is to be maintained. A once-and-done piece of code doesn't have the same constraints, but then again, how much once-and-done code is running so many more times than once?


Popular posts from this blog

The Timeline of Errors

Magic Numbers, Semantics, and Compiler Errors

Assuming Debugging