In 1981 Jon Bentley, the computer scientist credited with authoring the heuristic-based partitioning algorithm k-d tree, published a technical report at Carnegie Mellon University titled, “Writing efficient code.” In it, he estimated that as much as 90 percent of a program’s code is written for housekeeping or exceptional, error-processing cases, the majority of which are caused by bad user or computer input. His findings suggested that only 10 percent of code is written for nominal cases. This meant that the total programming effort involved in building an application could be greatly reduced if a strategy were devised to mitigate the amount of programming work related to input validation and error handling.
Bentley’s paper is as relevant today as it was more than 20 years ago. Recently, I was tasked with building an AngularJS web front-end application that required multiple forms for adding records to databases. A few of these required numerous input fields capable of accepting multifaceted input (comma-separated values, wildcard characters, etc.) that was difficult to validate. The volume and complexity of the input-validation was so great, in fact, that when we originally pitched the app to stakeholders, they estimated it would take more than a year to complete and swiftly rejected it. This was due in large part to the work involved in implementing a traditional input validation system: either a) calling a validation function that displays an error indicator/message if the input is bad OR b) waiting until the user clicks the submit button, and then analyzing all fields at once.
The obvious issue with these strategies is the time required to code the validation function for each specific input field, changing the styling of input fields to show the error state, and designing UI elements for displaying error messages to the user. So, rather than using one of these conventional approaches, I decided to try something different and somewhat radical: prevent the user from entering invalid input in the first place.
For my solution I created two mutually inclusive custom AngularJS directives to be attached to an input field: bound-model and bound-model-pattern. The bound-model-pattern directive expected a string containing a regular expression, which defined the pattern for valid input for that input field. The bound-model directive expected a string containing the name of the model object whose value would be tested against the bound-model-pattern regular expression to determine its validity. I encapsulated all of the validation logic in the bound-model directive and made the existence of the bound-model-pattern directive required by the bound-model directive. The way it works is that I apply an angular $watch (an AngularJS way of executing code whenever a model object value changes) to the model object defined by the bound-model directive. In that $watch definition, I test the newValue against the bound-model-pattern. If the newValue passes the test, I don’t do anything — we’re all good; if it fails the test, then I set the value of the bound-model object back to the oldValue (before the change). All of this takes place before the $viewValue is set (i.e. before the user’s input is actually rendered on the screen), effectively preventing the user from entering anything into the input field that would cause its value to violate the bound-model-pattern. The solution also works when users paste input from their clipboard. I also used a jQuery plugin — caret.js — to maintain the user’s caret position in the input field if they entered invalid data (I ran into an issue where it would cause the input field to lose focus after rolling back the value of the input field if the user tried to enter invalid data). Here is the link definition of my custom bound-model directive definition:
This approach to error prevention not only offers an elegant solution to a common problem, but also drastically reduces the amount of necessary dev work. I didn’t have to write a single line of code to account for invalid user input — all I had to do was slap one custom AngularJS directive onto the input element and construct the appropriate bound-model-pattern regular expression for the data. This resulted in a 50 percent reduction in the overall effort required to complete the project. I was able to finish in a couple of months by myself what was originally estimated to take more than a year with a full team.
Best of all, the solution delivers a great user experience. Users know they’re completely protected from accidental typos, which cause errors and workflow disruptions. This affords piece of mind. It also made them feel more comfortable exploring the application more deeply. Instead of learning by trial and error (literally), they got to learn the application just by trial. Previously, users were expected to already know what the valid input should be for each field. Now, all they have to do is attempt to enter a value, and if it’s accepted into the field, then they knew it’s valid.
With up to 90 percent of total programming effort devoted to error processing, it’s clear that a strategy is needed to streamline this workload. The approach outlined above can potentially do away with more than half of this effort. Since the most expensive aspect of software development is paying programmers’ salaries, removing the need for error processing means cutting the total cost of software projects nearly in half. And that’s not even taking into account additional benefits realized by quality assurance: 45 percent less code means 45 percent less testing. With so many software projects today completed late and over budget, a strategy to drastically reduce the workload required for error-processing could be essential for the success of future projects.