Healthy systems aren’t just imagined up front and created as elegant solutions to a problem — elegant solutions are the result of evolution.
When you complete a story, a feature, a part of a system, you are doing so with the knowledge and ability you have at that moment in time.
It might fulfill the requirements. It might look great from the outside. But we are the watchmakers that care about the inside of the clock. We care about how concise and maintainable our systems are, how testable they are, and how well they can react to change.
When you initially complete a piece of a system and go on to add value to other components, you will learn. Inevitably, the problem domain will become clearer. Over time, your techniques mature and you learn new ones. You re-think the work you’ve already done and re-evaluate it against the things you now know.
As you improve, you naturally find ways that you could improve problems that you have already “solved”.
You might think that you’ve solved something elegantly, beautifully. And that might even be true given your current understanding. As far as you know, your code is glorious. But your future self mocks you, feels sorry for you for your ignorance.
So, that’s depressing. Or hopeful?
As you grow and get better at what you do, you’ll inevitably discover better ways to do things. Your future self is embarrassed by the code your present self writes. And that’s a good thing. That means you are improving. So know that whatever you write now is worse than what you will write in the future, but better than what you were ever capable of before.
And so, when you begin to think about solving a new problem, keep this in mind. You don’t have to focus on getting it perfect up front, or even the first implementation. Because you can’t. You’re not ready. It’s OK to move on to the next piece knowing that you will learn and improve, and that knowledge will help you to make the current piece better. You know that your future self will be more able to make good decisions about the problems you have right now.
Always focus on solving problems with the minimum complexity you can get away with. Solve the core problem only. Gold plating is evolution’s enemy. YAGNI. And if you don’t need it or don’t know if you will need it, don’t write it until you do know. The less you write now, the less you’ll have to fix later.
Defer decisions until they are necessary. Prioritize. Always do the most important things first.
Continually re-evaluate. Refactoring is evolving.
That doesn’t mean that it’s always worth your time to go back and refactor or re-write pieces you’ve already completed. Sometimes that just isn’t providing value. But, inevitably as new features are added to a system, some of those changes will have an impact on pieces of the system that are already considered “completed”.
Remember that in TDD, the refactor step doesn’t only apply to the code you just added, but also how that code integrates into the system. Don’t be afraid to continually improve other pieces of the system as you add value. That’s how you keep a system healthy and maintainable under change.