So I was writing some code for some data access code and I realized that there was a certain amount of what be considered "boring sameness" about the stuff I was writing. As I added additional methods for manipulating the state of a business object, I realized that I pretty much copied-and-pasted the last method, tweaking it for the new method. In this particular case, I would copy a Perl object method, change the name and POD documentation, change which SQL string it would use (two variable names), change the variable name being added to the array of bind values, and then tweak the return (if at all). All in all, I would copy and paste 100 lines of code and change usually 4-10 lines. Over the course of the past 30 minutes, I had done that 6 times.
If you write software right now, you are probably saying "so what?" You'd say this because it is very common. At some point, you factor out all the tough or repetitive code and you get down to a "diminishing returns" point where nothing else can really be factored out and that you are reusing the same method calls, tweaked for different scenarios, over and over again. This kind of work can actually be a bit boring—even mindless.
The important thing to point out here is that this "boring" copy and paste is a sign of good foundation underneath your code. Or at least it is in business software or code dealing directly with business processes. The reason this work is boring is because you are basically copying and pasting the same stuff over and over again with small tweaks. The reason this is good is because you have gotten your underlying code so consistent and straightforward that you can copy and paste the same basic stuff all over again.
I think this is important to point out because I so often don't see this boring sameness in the code I am asked to update or maintain. I'm not sure why the people who wrote the code originally didn't try to refactor their code to make it more regular and consistent, but again and again it's something I find missing in a lot of code these days. Perhaps they were in a hurry? Or perhaps they thought such optimization was unneeded? Perhaps they (mis)quoted to themselves Emerson, "Consistency is the hobgoblin of little minds." But that's not what Emerson said. He said:
A foolish consistency is the hobgoblin of little minds.He never discussed what "foolish" versus "wise" consistency was, but he did make a qualification for his statement by mentioning "foolish consistency" specifically. While we in software engineering may not be poets (or care about Emerson), I think we need to take this quote (and its converse) to heart when developing software.
I've talked about the need for modular programming in another post. I didn't bother to discuss "going too far" because I rarely see that problem in my work. But it is possible to go too far. For instance, in the copying and pasting I was doing, I wondered briefly if there was some pattern I might extract so that I could eliminate even the copying and pasting. I say briefly because I thought about it and discarded the idea. The project I am on currently does not want to leverage any ORM or database query toolkits; whether this is a good idea or not could be debated, but the fact remains it was a constraint for my current work. So if I couldn't use a DB toolkit, any further optimization would either be creating my own DB toolkit (which would be dumb) or making optimization code that is so specific to the current work that it would either be brittle or not useful beyond the specific work I was implementing.
Here's where my early comment about "diminishing returns" meets Emerson's "foolish consistency". To optimize further in an attempt to eliminate the copying and pasting I was down to at this point would be wasted effort. This is where the proverbial 80/20 rule comes in. How you apply 80/20 depends on the situation, but you can pretty much invent almost an infinite number of ways to find 80 percent of something hiding in another 20 percent of something else. Regardless, the point is that I could have further optimized, but the amount of work required versus how much benefit and reuse I would gain later wasn't worth it—especially since in this case, I would have ended up creating my own limited DB toolkit. To demand additional optimization that has no real end benefit to reuse or clarity would be to insist on an additional level of "foolish consistency."
However, there's a definite balance or gray area about what is foolish and wise consistency in software engineering. I personally feel a lot of the ability to judge what is foolish and what is wise comes from experience and by learning from truly elegant software. Here, working through the classic Design Patterns
The rule of thumb I would leave you with is that design optimization—striving for common, consistent APIs or elegant, concise paradigms—is never "foolish consistency". Taking an extra hour or two to get an API consistent is never wasted effort. Consistent APIs are self-documenting (that is, they are plain to read and using one aspect of the API builds or directly translates into using another aspect). Most code I have found has erred on the side of "there's not enough time", and has regarded most attempts at consistency as foolish. Only painful experience and some training will ever cure those errors.
And who likes pain? So try to aim for boring cut and paste, the kind that isn't duplicating critical code over and over again. If you find that the code you are copying would require you to make a change in more than one place (possibly a dozen places) if some lower API or data structure changed, stop. Pause for a second and ask yourself what kind of consistency you have. Crack open a good book like Design Patterns
Oh, and throw in some documentation while you are at it :-)