Tuesday, July 5, 2011

Clean Software

When I say clean software I'm of course talking about software that is:
  • Loosely coupled
  • Highly cohesive
  • Following standard software patterns
  • Easy to read
  • Easy to maintain
  • Dead code free
To be more specific about the points made above:

Loose coupling means that classes are nicely encapsulated, they don't expose their interior to the world, objects communicate through interfaces with an implementation that they don't know about, all they know about is the interface.

The advantage here is that the object only needs to know an objects interface but not its internals, the internals can change as much as they want in the objects class, as long as the class sticks to the interface contract.

Highly cohesive means that all objects in the system have a specific purpose that they fulfill. An object is responsible for a certain task, it knows how to do that specific task, it uses other objects to fulfill other tasks.

Following standard software patterns is good practice and often times takes a while to be engrained in ones mind. Patterns are well proven solutions to common problems, most software systems benefit from patterns and it's always worth at least having a tool set of patterns in the back of ones mind to fall back on. It is also a decent way to communicate the system to other developers as patterns are well known, so in case of an MVC (Model View Controller) pattern it is easy to identify the various parts that make up the application. It allows for abstraction of the problem and for a clean and to the point solution.

Software in my mind is considered easy to read if it follows standard patterns, is highly cohesive and follows standard programming guidelines. This is a view on the code itself, placement of comments to walk a reader through the code is advantageous, however, instead of many comments it is preferable to write readable code by using standard variable naming conventions, class naming conventions, follow the object oriented paradigm and avoid a lot of dead code.
Methods and Classes should contain javadoc comments, an interface should contain a comment for a method that it provides, the implementation should contain a comment specifically how it implements the method.
Classes should have a comment that outlines their purpose.

Easy to maintain and easy to read go hand in hand, however there are some other factors that need to be taken into consideration. For example when it comes to the deployment/development strategy, here some questions that you may want to ask:

  • Is it possible to deploy a sub system of classes without having to reinstall the software?
  • Is it possible to send out a JAR that patches the problem?
  • Is the code kept in a code versioning repository and is it used properly?
  • Is it easy to get the development environment up and running?
  • Is there a strategy to debug a live system?
  • Does the software contain proper logging?
  • Can we deploy software that analyses the system?
There are probably a lot more questions but having been in the situation where a customer faces a problem with a piece of software I never worked on the above are pretty much what goes through my mind.
Having a customer in a production stop situation means that a patch is required quickly, as time is money, and production halt means the system is in an unusable state.
Also one should not assume that it is always possible to access a customers system, lots of customers may have data that is restricted and can't be shown to the support engineer.
An analysis script is a good solution here as the engineer can extract information that might be useful and the results are provided by the customer (the customer actually runs it based on instructions given by support). This gives them ease of mind as they see what is being passed back.

The best solutions are hassle free ones for the customer, if a problem can be solved by the customer performing less than 4 steps and takes less than 5 minutes it is a solution that makes their life easier and thus yours.

Back to the point of maintainable code. Decent logging within the code as well as traceability through it (highly cohesive, loosley coupled) makes it easier to determine what the potential problem could be.

Another important aspect of making debugging and customer support more efficient is to throw the proper Exceptions - at least in languages that have them. This will often times contain information that the implementer felt would be important at the stage the problem occurs and logging the issue will allow the customer to send a log file.

The source maintenance as well as the development environment mentioned before support the engineer to tend to a customers problem faster, rather than wasting hours to get all the source code and more hours to set up an environment that allows for remote debugging.

Software should never contain dead code, a common practice for code that will no longer be supported is called depreciation, in Java a method can be marked as "Depreciated" which means that all API consumers should change their approach as the method will be removed in future releases. Note that depreciated code is NOT dead code! I wanted to list that as an example of code that might still be used by parts of the system but is on its way out.
Dead code refers to methods that have no caller or variables that are never used. All these things take up heap space, a dead local variable of a certain type will take up the types default space for each object created - that might not be a lot but it always helps to start saving memory on a small scale.

Considering the points mentioned above and keeping them in mind will help you to develop your own guidelines, coding standards and deployment strategies that will ultimately produce a clean software system. This system will be easy to maintain which is a big advantage for production systems that are deployed for multiple customers. The faster an engineer understands and can develop a solution for a system the better.

Note: A clean system is not equivalent to a bug free system. Clean code can have bugs - there are various types of bugs, for example a bug that may have fully functioning code might be a requirements miss. The software behaves as implemented and intended but product management miss-communicated a requirement and the code should be doing something else.