Perfectionism may be good and bad when programming.
- When and where do you draw the line when you are problem solving?
- When do you decide when a solution is overkill, too general or simply too futuristic?
Please comment if the question is unclear.
Software Engineering Stack Exchange is a question and answer site for professionals, academics, and students working within the systems development life cycle. It only takes a minute to sign up.Sign up to join this community
Only engineer a solution for the things you know you're going to need soon. Don't engineer it for the things that might be needed in two years, because most likely you're going to need very different things and will have to re-engineer it anyway.
The moment you start talking about "with this design at some point in the future we could do X, or even Y", instead of "this design allows us to do customer requirement Z in the next release", that's when you're getting into architecture astronomy.
In response to the comments:
A solution is overkill when the extra time taken to complete it is worth more than the potential negative impact from when the easier solution is finished to when it would next be naturally upgraded/amended.
Basically you are trading time now with time later. If your spending more time now then you will save later, you are doing it wrong. If you're really over engineering, you are spending time now that doesn't affect how much time (or even makes it more) you spend later.
You get better at working this out the more experience you have. The best way to go about things (from my experience) is to do what you need now, but in a manner that is most easily augmented should later requirements demand it. Working out how to do that is the tricky bit.
I used to be very perfectionist (spending time creating frameworks, not solutions).
But the thing that really helped me accelerate my production was learning and following BDD/TDD principles, including the outside in principle (which I have found particularly hard to learn to embrace).
This really taught me not to write a single line of code before a test exists for it. But the unit tests doesn't exists either before an acceptance test exists for it. And the acceptance tests comes from real user needs.
So therefore, all lines of code originate from a real user need.
If you're not familiar with the outside in principle, it dictates that you start writing tests for the outermost layer in your application (i.e. the GUI in virtually all cases) using test doubles to simulate the behavior of the lower layers. Then you implement just enough for the tests to pass. This implementation of the top layer then dictates the tests you need to write for the next layer, etc, until you hit the bottom layer of your application.
My boss does actually :)
I must admit I'm getting better, but I'm still not much for compromise. Thankfully I've got my boss to rein me in ;)
I'd like to raise another problem than overengineering though, as overengineering is quite easy to detect.
My main problem is with refactoring. The problem is that most of the times, even though I tried to write the code as good as I could, I didn't know back then what I do know now (seen more codes, more patterns, new idioms, new issues, new solutions). And so, even though it works, I now know better ways to do it:
However, it's working as it is, and therefore refactoring it is not a priority, and the truth is that it's nagging at me; I understand the economic reasons, and I understand the client expectations (they don't see the code and prefer new features and bug fixes), but I do wish I had time to work on it still.
For now, I just follow my boss' order, but I must admit I'm feeling uneasy knowing that the code delivered into production is not the best I could come up with now. Perfectionism, I guess.
Both professionally and personally, the standard I try to apply to myself is:
Be content with winning.
If my code solves the problem it's meant to solve and doesn't create any new problems*, it's very likely time to move on. When you learn to set the bar as high as it needs to be set, "Good enough" becomes, well, good enough.
Perfection is like the speed of light: you'll never get there, but there's no limit to the energy you can expend trying.
(* -- Note that "Buggy" and "Difficult to maintain" both firmly fall under the heading of "New problems." So I don't call it complete until the code has been tested, had the superfluous bits trimmed, and had the comments/API documentation brought up to date.)
With experience I've realized that perfectionism is not even possible until I've got at least a few years under the belt in any particular context (language, framework, platform, standard). As a newbie there will be all sorts of idiosyncrasies which you will not be aware of (escaping, precedence, reserved words, syntactic sugar, timeouts, asynchronous calls, undocumented features & bugs), so I just try for a good solution, all the while learning as much as possible. Importantly, I always try to make it simple to refactor the result - Modular architecture, comments where needed, and no clever tricks.
I, like many other programmers, have a lot of legacy code to maintain. The temptation to redo it all will always be there, but I've essentially boiled it down to one principle:
Am I (or someone else) going to have to figure this out again?
That usually takes care of a lot of spaghetti code into somewhat more manageable spaghetti-chunk code. Abstract away some of the chunks, throw in your tests, and now it doesn't look so much in need of perfection.