I've written other posts about the benefits of striving against constraints, even those that are artificially created, and I've not been quiet about my disdain for code that performs poorly either. While quality and performance are good topics in themselves, they are not directly the focus of this post...even though I will use a conversation with colleagues regarding performance.
When critiquing a portion of code, I have been approached by those who argue, and with some cause, that if a particular block of code executes in less than 1ms its performance is "good enough".
This concept of "good enough" is dangerous. While I'll agree that such code may perform better than other approaches an engineer may have used - it may even have better than average performance - what are we really saying when we say it's "good enough"?
Perhaps it will help to mention that I tend to think of my code, not so much in terms of "good enough" but rather in a stable evolutionary stage - primarily because "good enough" implies if it does not evolve further, that would perfectly acceptable. "Good enough" implies that we can get better [insert your property here] if we kept working, but we're not going to keep working because it's acceptable.
While I'm familiar with the Law of Diminishing Returns and the 80/20 Rule, frankly, as someone who's been developing software for more than two decades, one of the biggest problems I've seen in the industry is this very idea of "good enough". Even if I were to ignore thought around exploring outliers to improve our code, in my experience "good enough" tends to be what we say when something other than the customer is given priority.
Enough theory...let's have a real world example and see if we can tell the difference between "good enough" and "best". (Note: for this example, I'm going to use one of those JavaScript libraries I mentioned earlier - specifically, the YAHOO! YUI library - because it will help show how we tend to become complacent.)
How the "good enough" code (very nearly) lives in the wild:
MyObject = { element: document.getElementById("mylabel"), highlight: function() { YAHOO.util.Dom.removeClass(MyObject.element, "mask"); YAHOO.util.Dom.addClass(MyObject.element, "hilite"); } };
...and how better code might live in the wild...
MyObject = { element: document.getElementById("mylabel"), highlight: function() { this.element.className = this.element.className.replace(/\bmask\b/, " hilite "); } };
Of course this snippet, while faster also makes the potential errors apparent. While it's also "good enough" we can do better...
MyObject = { element: document.getElementById("mylabel"), highlight: function() { if (this.element) { this.element.className = this.element.className.replace(/\bmask\b/, " hilite "); } } };
...and if your code is going to be used by others you should make your private members private...
MyObject = function(id) { var element = document.getElementById(id); this.highlight = function() { if (element) { element.className = element.className.replace(/\bmask\b/, " hilite "); } } };
...and finally, if you want to actually unit test your code, return a value like this...
MyObject = function(id) { var element = document.getElementById(id); this.highlight = function() { var ret; if (element) { try { element.className = element.className.replace(/\bmask\b/, " hilite "); ret = (/\bhilite\b/).test(element.className); } catch (err) { ret = null; } } return ret; }; };
While our first example from the wild is "good enough", it's clearly not as good as it could be, and even worse, writing a unit test to validate it becomes onerous. On the other hand, by going further...by doing better, we can achieve not only better performance and greater reliability, but also improved quality and maintainability - all of which combine to make a better user experience, and a better user experience is what helps make our application a success.
That's why we say success is built on better than "good enough".
No comments:
Post a Comment