Testability - The only way to not have legacy code.
If the code you're working in doesn't have tests - it's legacy code. The more tests you have the less legacy it is.
I say this coming from the many conversations I've had about what makes code "legacy code". What the conversation tends towards is that legacy code is code that we won't want to touch, code that we're worried about making changes to.
If you're scared to make changes to a section of code - that code is legacy code. When you have tests covering all of the behavior in the system then you'll have the confidence to do ANYTHING in the code and KNOW if it broke something.
Covering behavior is very different than test coverage metric. I can get 100% test coverage and have almost no behavior tested.
When we have all of the expected behaviors of a system verified by tests - any change to the behavior will break a test - so go wild! You have the safety to change anything - This is the greatest feeling as a developer.
I can focus on the problem I'm trying to solve - nothing else. I don't need to think about if what I'm doing will break anything else - The tests will let me know.
The tests will set you free
I'm not going to get into the power and awesomeness of tests here. No, I want to talk about the beneficial result from developing software in the MicroObjects style - Testability.
There's a lot of ways you can be able to get the same behavior tests in place - TDD is one of the ways that starts to get you there. It makes sure you can test everything - which is great. ... One of the things I find lacking in TDD as an isolated practice is that it doesn't force other elements of the technical practices I advocate and find makes amazing code.
This is totally a personal preference and bias - But it's grounded in recognition of industry 'best practices' for OO code.
TDD doesn't make the code into single responsibility objects - I've seen TDD used and a monster class created.
How many tests does your class have? How many permutations of inputs do you need to ensure that you've covered the code - how many more to ensure, to KNOW, you've covered every single behavior your code can exhibit?
I've previously written classes it would have taken way too many... then - are they valid tests?
It definitely starts to enter a gray area. TDD helps, we're limited to only writing tests we can get to fail first - but when you create monster methods and classes, especially when mutable; there's a lot of accidental, and incidental, behaviors that may not have tests.
These are the sections of code that we fear. Complex things were it's HARD to reason out exactly what it does and what any given modification will do.
MicroObjects just nails it in testability. I think five tests is the most any class needed to validate 100% of the behavior - this includes constructor instantiated collaborators.
We can get EVERY behavior of a class tested quickly, easily, and confidently. We know that if we ever tweak the class - we'll have a test fail if the behavior changes.
This does play into the idea that any change to a MicroObject should be a new object - but that's for another time. :)
MicroObjects are loosely coupled. Other things can be as loosely coupled, but I don't see it as often as I'd like. Everything a MicroObject uses is either a parameter in the method, or instantiated by the constructor. We have no concrete class coupling in the behavior of the class.
MicroObjects are the easier objects I've found to test and get all behaviors tested.
I won't say it's easy to get there. There's a lot of hands-on-keyboard technical things that need to be practiced to become efficent on it. A co-worker on the team I'm helping refactor the code base has described these practices as 'graduate level refactorings'. They're not ones you'll normally see. They're not ones you'll normally find applied until you can't apply them anymore.
I listen to the code. It tells me when there's more I can refactor. Part of this level of refactoring is that the file structure changes as frequently as the code. It's what enables everything to continue to make sense.
In the end - there's a lot of ways to have well tested code. I've found that MicroObjects enable the simplest testing of objects. The work to get to that ease and simplicity of testing is a bit more - but it's a trade off.
When we, as an industry, tend to let testing suffer, often as an after thought - I remove the need to struggle against writing tests, the struggle to arrange the test to get the one behavior you want. Eliminate the challenges you want to avoid - When I did this for tests, MicroObjects are the code result - and in the end, it's easy to write the code - the cognitive load is so low.