Maintainable Code


I watched a fairly old talk by Nicholas Zakas on Maintainable JavaScript today, and even though it's not new information by any stretch, it's certainly relevant. And you might think that maintainability has nothing to do with security, but maintainability has a substantial impact on security.

First, a couple of statements about the talk. While most of the code samples are in Javascript, and some of the discussion is specific to JavaScript, it is certainly relevant to folks writing any type of code - er. except dead-end code that will never go to production, I suppose. (Unfortunately, you may think it's not going to production, but alas it will).

He mentions the following attributes as being fundamental to maintainable code:

  • Understandable
  • Intuitive
  • Adaptable
  • Extendable
  • Debuggable

These things certainly make it easier to add security to applications that don't have it. While some believe that web application scanning and a web application firewall can cure all that ails you, they're only part of a complete solution - fixing source code and writing good code from the beginning are critical to code defensiveness.

Another couple of attributes that Nicholas didn't mention specifically (but he certainly emphasized them) are consistency and testability.

Consistency is not only making a decision about code formatting (although cosmetic things like that make code review much easier), but consistency should apply to design patterns and library use. Consistency in design patterns helps code reviewers - once they've seen a pattern and how it's used once, they can begin to see "patterns in the patterns" - when particular algorithms are used and when they're not. A code reviewer can provide a much more valuable report when the developers use the same patterns. Consistency in libraries is critical to properly tuning static analysis tools as well as making recommended solutions that fit within the one scheme of frameworks in use.

Before I talk about testability, I'll talk about tests. Unit tests for code are very helpful during code review because they reveal how methods are supposed to behave. Not only should unit tests test for functionality, but also boundary cases and exceptions. If a function is supposed to take a number between 1 and 1,000, what happens when 0.999 is passed or 1,001 or 1000.001? Does the function fail gracefully? These types of tests help to reveal deficiencies in the functions themselves, and are helpful in revealing the desired functionality of those components. So if code is written in a way that it can't properly be tested (say, it's hard-wired to production data sources), there's a good guarantee that the tests are not being written or executed. And if they're not being written or executed, then you're missing two components - the guarantees that negative cases fail gracefully, and the external technical documentation about the desired functionality.

It was an excellent talk, however I don't totally agree with him on all points - most notably the use of closures. While I agree that closures don't solve everything, in many languages, they actually help the readability and understandability of the code. Simple is generally better, and closures properly applied can greatly simplify logic. (Yes, they can be mis-applied). And I'm undecided on extending objects you don't own. Javascript strings need a trim() function, and I've become pretty fond of Groovy - and it adds lots of functions I didn't know I needed (many of which take a closure as an argument).

So, go watch the video. And figure out where you can make things more maintainable. But like in all things - take small steps.