The “Principle Of Least Surprise” ( sometimes referred to as “Principle Of Least Astonishment” ) as described by M. F. Cowlishaw in the paper “The design of the REXX language” in 1984:
It is difficult to define exactly how to meet user expectations, but it helps to ask the question: Could there be a high astonishment factor associated with the new feature? If a feature is accidentally misapplied by the user and causes what appears to him to be an unpredictable result, that feature has a high astonishment factor and is therefore undesirable. If a necessary feature has a high astonishment factor, it may be necessary to redesign the feature.
While being a bit on the squishy side, this still a very useful principle.
I recently ran into a violation of this principle while using Number.prototype.toFixed() in JavaScript.
The code was capturing a time value using performance.now(). Since it was already counting in milliseconds, and I was exposing the result as JSON, I didn’t really need 15 decimal places. No problem, I’ll use toFixed()
to reduce it to 3 decimal places. Here is a simplified version of the code and the result:
var now = performance.now().toFixed( 3 );
var share = JSON.stringify( { now: now } );
// share is now {"now": "8.618"}
That is the point where I realized something wasn’t right. When calling performance.now()
you get back a double
( DOMHighResTimeStamp type ). Why then was it coming out as a string in the JSON?
It turns out that toFixed()
“formats a number using fixed-point notation” by returning a string. The MDN docs do indicate that the return value is a string, but I contend that this is completely unexpected. In order to keep the value as a double
, you need to wrap the whole thing in parseFloat():
var now = parseFloat( performance.now().toFixed( 3 ) );
var share = JSON.stringify( { now: now } );
// share is now {"now": 8.618}
It would be clearer, and less work ( parseFloat()
can be a hit in performance ), if toFixed()
preserved the variable type and returned a double.