During a routine ponderance on software engineering, I was thinking about a conversation recently in which we discussed how and when to copy and paste code. The team agreed that it’s OK to copy code once or twice, and to consider refactoring on the third occasion. This led me to wonder about the size of the code we copy, and how it might indicate adherence to the single responsibility principle.
For the uninitiated reader, the single responsibility principle – one of the first five principles of Object Oriented Programming and Design – can be succinctly summed up as:
“A class should have one reason to change”
The subject of many an interview question, often recited as rote, yet often misunderstood, the core premise can – I think – be understood by just talking about the code block, or class, at hand, in the form of a response to the question: “Tell me all the reasons you might need to edit this class?” Responses such as:
- “If we want to change the backing store, we have to change this class.”
- “If we want to change the business rules for persisting, we have to change this class.”
- “If we want to change the fields used in the response, we have to change this class.”
When recited in one’s mind, are all that are required. And by considering the answers carefully, we can make an informed decision about whether to refactor, or that we’re actually happy with what we have, and are left with the option to change the class easily at a later date. The latter part of this sentence is critical to becoming a better developer, because what we have might be acceptably incomplete, and refactoring might take an inordinate amount of time and fail to offer significant business value to justify the expense.
This said, is copying and pasting code OK? Mark Seemann wrote an excellent blog post on the subject – which I won’t attempt to better – suffice to say I agree, and that it’s OK to copy and paste under a certain set of circumstances. The primary concern is the tradeoff: to suitably generify code requires at most a deeper understanding of the abstractions in play, and at least the ability to introduce dependencies between classes and modules that might not have otherwise been required. A quick copy paste of code that’s unlikely to change is not going to kill anyone. It might introduce an overhead should the code’s underlying understanding change, however volatile concepts do not in the first place represent good candidates for copying and pasting.
Now to wistfully return to the subject at hand – how can we use copying and pasting to judge our code’s adherence to the single responsibility principle? Quite simply: if we can copy only a line or two, then the surrounding code within the method body is perhaps not doing as targeted a job as we might hope. If we can copy entire classes, we can say that we’ve adhered strictly to the core tenets of the single responsibility principle: this class has such a defined person it can be lifted and shifted around the codebase with ease.
This means we can judge any of our code in a couple of ways: answer the question “what reasons does this class have to change?” as well as being honest with ourselves about our ability to copy and paste this code into a different codebase without being refactored. Would half of the class be thrown away? Would we have to change a bunch of code in order to fit a different persistence model, say copying from a SQL Server backed system to a system backed by Event Store? I think it’s an interesting idea, and definitely one I’m going to keep trying in the coming days.