I wouldn't even try
I do admit that it is very hard to write unit tests for these classes. I would find it very difficult to write good unit tests for them too. In fact I wouldn't even try to test this with unit tests. Now what I would do is I would extract the behaviour that I want to modify into other classes or more likely to a completely new class. If I move out a method of say 10 to 50 lines of code then that's very easy to test and things that are easy to test are easy to change by adding behaviour to it. In doing this, besides making things testable, we improve things by splitting things into smaller pieces. Smaller pieces are more easy to understand to test and to assemble into new behaviour i.e. reuse these classes elsewhere.flickr.com/photos/102642344@N02/10203178726/ |
Given that we do split the big classes into pieces as we do features then it doesn't take many stories to make our codebase a lot more maintainable and all the way we been able to work with lower level tests. All we needed to ensure is that the things we split apart are meaningful pieces that are functionally coherent. This is probably the most difficult part - extracting something meaningful. It requires being able to design code and if we don't have much practice doing it won't be very good. On the other hand if we don't start practicing this then all code we create will just be a bigger and bigger mess. I mean there's a reason (almost) all living things grow by cell division, not by cell growth.
The actual act of extracting something is really simple. Very often it is as simple as
- Extract Method (automated refactoring)
- Create class and add it as a member of the existing class
- Move Method (automated refactoring)
As you said, the most difficult part is to extract functionally coherent parts.
RépondreSupprimerVery often, I've seen team undertake large refactoring to build some kind of 'Anti Corruption Layer' around what they are doing. Unfortunately, a team's area of responsibility is often historic and not all functionally coherent! This can end up in an incoherent patchwork of components. Having a target functional architecture in mind can really help there. I've written about how to do it with Event Storming (https://philippe.bourgau.net/drafting-a-functional-architecture-vision-with-ddd-event-storming-part-1/), but I guess it's just one way out of many to do it.
Thanks for the post
Indeed finding Functional Areas through EventStorming is a very good tool. Good short introductory post. Hadn't seen the use of a wool string to draw boundaries! I also perfectly agree that we always benefit from rethinking the solution from the basic need: What are the most rudimentary inputs and outputs?
SupprimerI was mostly referring to micro-functional areas, the size of a method or a class. At this level mistakes are cheap and the best way of learning is by doing, although having a mentor is valuable.