Notwithstanding the credit given to MTV's Real World, I think reality TV took off with that Fox special that featured a deer knocking a hunter on his rear. That guy just never saw it coming.
Same thing seems to happen too often when we start making architectural adjustments around a corner case in a software application.
The real problem is how to tell when an exception to the initial model -- the corner case or edge case -- is truly a strange bird, versus when it is a big piece of the model that was missed the last time through.
Sounds like something that should be easy to resolve with proper analysis, whether of the thick-stack-of-paper variety, OO variety, or an agile story-based style.
But it's not.
I've recently been doing some work on a project where this mistake was made. At some point in the past, this team looked at an odd duck, thought hard, and realized it was a big piece of their world. Big enough that the underlying infrastructure -- database, network services, etc. -- should be designed with sufficient generality that this corner case would become a mainstream case.
I understand how the analysis went, and why it made sense.
Only problem is, with 20/20 hindsight we can now see it was wrong. Bringing this edge case into the tent made the system an order of magnitude more complex, and it should have been left as an odd hack in a dozen places ... but as development happened over time, on several different module teams, the problem of the added complexity wasn't clear until it was too late.
To be a little more specific, I'll offer an analogy: you're writing a driving game or simulation. A big part of the fun is the spots where the players catch air, jumping their vehicles over obstacles to get ahead. But how to model the behavior of the cars as they fly through the air? A quick hack? or ... integrating elements of flight simulation or 3-D physics simulation?
Well, this team went the equivalent of the flight simulator route, and their hard-enough-to-code 2-D driving game has become a brutal 3-D simulation. All for a few jumps that in retrospect should have been hacked.
My point is not to complain about a historical decision that probably didn't seem bad given the info available at the time. It's to ask about how to avoid these problems in the future.
If you grant my assertion that, in some cases, no straightforward analysis reveals the problem at the moment the "how do we handle this case?" question is asked, then it seems like we need a set of milestones for a posteriori analysis.
We make a design call when we need to make a call, and then we need some specific questions -- and times or situations to ask them -- that will tell us if we're on the right track.
Specifically: What is the best and earliest way to ascertain if we're
- monkeyhacking in an edge case that really demands a proper refactor
- doing a refactor that way overengineers a solution that really just needs a monkeyhack
What do you think?