Operability & Maintainability

1. Operability

Operability is the ease of operating the software. Operability has the following aspects:

  • Messaging – Defines how events in the application are expressed to the users and what sorts of events should be expressed to the users.
  • Message Rules – How a message should be formatted
  • Error Handling – Defines how to handle error situations in the application’s flow.

Software which has a high level of operability must have the two following aspects: Validation, Explanation

Validation

Bad user input is prevented as much as possible during configuration/implementation time. Usually this means validations in the GUI. Validations should include:

  • Type Safety (String, Number, Date etc.)
  • Size Constraints (Sting length, Number of digits, Date format etc.)
  • Value Ranges – Where a field has a list of valid values.

User input should be validated during runtime even if it was validated during design time and even if the input was already validated by the GUI.
This should be done both to defend against manual changes and to protect the application against potential security threats.

Explanation

Error conditions should be communicated properly to indicate the following:

  • Their severity (e.g. A Fatal error as opposed to a mere Warning)
  • The cause of the error in such a manner that both the user and a developer can understand and benefit from.

Example:

The message: CFGENV.sh failed!!! Does not provide the developer with any information for debugging and does not provide the user with any useful information.
However, look at the message: [FATAL] The environment configuration script (CFGENV.sh) has failed with error code <5>. Check the log for more details.

This message provides the developer information about the error which occurred, and provides the user with indication that the error is fatal so it should not be ignored and also explains in plain words what part of the process went wrong (by giving a verbal description to the script and not just its name which is usually meaningless to a user).

1.1 Messaging

A message is a means of communicating events that happen during the application’s execution to the user.

Sending a message depends on its purpose and should be done according to the following guide lines.

  • Messages which have a severity of Fatal, Error, Warning and Info should be persisted to an external repository (Properties/XML File, DB Table etc). Those kind of messages should not be stored as hard coded text within the code.
  • It is highly recommended that messages which have a severity of Fatal, Error, Warning and Info will be reviewed periodically by Technical Writers to review their grammar and contents.
  • Parameters in messages should be warped with angled brackets (< and >).
    Example: Unable to load the file <MyFile.txt>.
  • Messages should have a severity which is one of the following: Fatal, Error, Warning, Info, Debug, Trace

Fatal

Fatal messages are messages which are communicated to the user when a critical error occurs during the application’s execution. A critical error is defined as an error from which the application cannot recover, that forces the application to halt its execution.

Example:
An error of insufficient disk space is an event after which, in most situations, the application will not be able to continue.

Error

Error messages are messages which are communicated to the user when a moderate-to-high error has occurred during the application’s execution. Moderate-to -high errors are defined as errors that most likely affect the product of the application’s execution but do not halt its execution.

Example:
After a failure to deploy a certain module, it is not worth to halt the entire deployment process. This event does not affect the deployment of the rest of the modules, and by continuing we are able to learn if there are more problems other than in the current module. The problematic modules can be handled manually later on.

Warning

Warning messages are messages which are communicated to the user when a low severity error has occurred. Low severity errors are defined as errors which do not necessarily have an effect on the application’s product, but might indicate that a certain situation is a potential problem and the user should be aware of it.

Example:
A situation where an input of low importance’s defined incorrectly, like a description which is too long or contains illegal characters. This kind of an event can be logged with a warning and a default value can be provided.

Info

Info messages are notifications about the progress of the application’s execution.

Example:
Starting Migration

Migration ended successfully

Debug

Debug messages are messages which provide the development team with the means of better understanding the flow of execution, both for development time and for investigating problems. Debug information is very useful within loops to give the developer a better insight both on the progress of the execution and on the scenario in which the application has failed.

Example:

Working on process <XYZ>
Process <XYZ> has <7> children

Trace

Trace messages give an indication of the entire flow of execution. (Enter/Exit to each method).

  • Should be done in the main flow of the application.
  • Should not be done in getters/setters.

1.2 Message Rules

A message should be formatted in a manner which makes it the most readable and understandable to the user.

There are several aspects which are important in the message text:

  • Tense – Write in the present tense, not the future tense (will).
  • Arguments – wrap with < and > to easily identify what part of the message is an argument.
    This is especially useful when the argument contains a whitespace which helps to better understand the cause of the error.

Following are some examples for message best practices:

Bad A dependent attribute not defined for activity name {0}, entity type {1} and status {2}.
Good A dependent attribute was not defined for the <{0}> activity name, the <{1}> entity type and the <{2}> status.
Note: Avoid the use of “not”.
Place the label, for example activity name, after the argument.
Bad Account with id:{0} was not found.
Good An account with ID <{0}> was not found.
Note: Don’t use colon before the argument.
Instead of  “id” and “Id”, use ID.

1.3 Error Handling

Proper error handling makes application messages easily understandable and reduces the costs of operation.

This section deals with the following:

  • What is an error?
  • When do we throw an Exception?
  • When do we catch an Exception?
  • What information should an Exception provide?
  • What do we do with an Exception once we get it?

1.3.1 Errors

An error is a condition in the application which is not a part of the standard flow of execution.

There are two types of errors – expected and unexpected.

  • Expected errors are the best kind, since you can anticipate them and handle them elegantly.
    Expected errors should be handled as such – meaning that the user should get a detailed message on why and when the error occurred and if possible, a way to correct the situation.
    Expected errors should not have their stack trace logged because the stack trace will not contain any useful information and because it causes the user to believe that a fatal error has occurred, or even worse – a bug as been found.  Having said that, it is recommended to log the stack trace of such errors in Debug level since we are rarely able to really anticipate all possible scenarios.
  • Unexpected errors are not desired since they are hard to track and when they happen we sometimes need more information than we have to solve them.
    Unexpected errors naturally have to be avoided as much as possible, but since we can never be sure they will not occur we need to trace them properly. In order to do so the main entry to the application (main for batch processes, API methods for EJBs etc.)  should log all unexpected exceptions with their stack trace in the log.

1.3.2 Exceptions

Exceptions are a tool to communicate anomalies/problems between sections of the application.

An exception should be thrown when the method that encountered the problem cannot continue its execution as planned.

An exception should be caught in the following circumstances:

  • The exception is meaningless and can be disregarded.
    • Example (Java Only): When a step before the current one ensures that the exception can never occur but since the API declares throwing the exception it must be caught.
  • The exception is a potential/actual problem that needs to be logged (Warning/Error severities) but the normal flow should continue.
  • The catching method would like to add more information to the exception to make the problem clearer to the next catcher. This is called – adding context to the exception.

An exception should provide the catcher with information that will explain what the problem is and why it was caused.

Example:
A certain method throws an exception with the following message: Could not execute the script <MyScript.Sh> due to <Insufficient permissions>.The catcher of this exception might throw the following exception:The following error prevented the successful installation the environment: <Could not execute the script <MyScript.Sh> due to <Insufficient permissions>>.

1.3.3 Example

Let’s take the following code example:

public static void main(String[] args)
{
	Log.enter(args);

	try
	{
		doSomething(args[0]);
	}
	catch (AppException exception)
	{
		Log.error(ERROR_DOING_SOMETHING,
                args[0],
                exception.getLocalizedMessage());
	}		

	Log.exit();
}

public static void doSomething(String argument) throws ABIException
{
   Log.enter(argument);

   if (“fail me”.equals(argument))
   {
	Log.fatal(FAIL_ON_PURPOSE);
	Log.exit();
	throw new AppException(FAIL_ON_PURPOSE);
   }
   else
   {
	// TODO: Add method logic here (you didn’t actually think of leaving this like this, right???)
	Log.info(DID_SOMETHING,argument);
   }

   Log.exit();
}

For reference, the messages we have in the messages file are:

DID_SOMETHING = “Did Something”
FAIL_ON_PURPOSE = “Failed on purpose”
ERROR_DOING_SOMETHING = “Could not do something with <{0}> due to <{1}> “

The main method calls the doSomething method which might throw an exception depending on the input, now let’s see the output in the log file depending on the execution:

Regular Flow
Executing the main with the argument “hello!!!”
2008-07-06 16:28:11 [TRACE] (Operability.main) (Enter) – Parameter(s): [hello!!!]
2008-07-06 16:28:11 [TRACE] (Operability.doSomething) (Enter) – Parameter(s): [hello!!!]
2008-07-06 16:28:11 [INFO] Doing something with
2008-07-06 16:28:11 [TRACE] (Operability.doSomething) (Exit)
2008-07-06 16:28:12 [TRACE] (Operability.main) (Exit)
Error Flow
Executing the main with the argument “fail me”
2008-07-06 16:28:11 [TRACE] (Operability.main) (Enter) – Parameter(s): [fail me]
2008-07-06 16:28:11 [TRACE] (Operability.doSomething) (Enter) – Parameter(s): [fail me]
2008-07-06 16:28:11 [FATAL] Failed on purpose
2008-07-06 16:28:11 [TRACE] (Operability.doSomething) (Exit)
2008-07-06 16:28:12 [ERROR] Could not do something with <fail me> because <Failed on purpose>.
2008-07-06 16:28:12 [TRACE] (Operability.main) (Exit)

2. Maintainability

Maintainability is the ease with which a software system or component can be modified to correct faults, improve performance or other attributes, or adapt to a changed environment.
The maintenance effort involved in supporting maintainable software is usually shorter in time and cheaper in cost.
Maintainability focuses on the following aspects:

  • Documentation
  • Coding Standards

2.1 Documentation

Documentation is the basis of making an application maintainable.

Proper documentation can reduce the time it takes for a developer to understand existing code and thus reduce the cost both of training of new team members and of fixing defects.

Documentation should provide its reader with insight on how the code works, how it communicates with other modules, what is its scope and more.

All Java applications should be documented with JavaDoc (HTML format). Documentation should be done in the following scope:

  • Class Mandatory
    • Should include the purpose of the class and the scope of its usage.
  • Methods
    • Should include the following:
      • General description – including purpose and, if applicable, the scope of usage.
        Complex methods should include a detailed explanation of their logic.
      • Input parameters and their meaning
      • Output of the method – Type and meaning
      • Exceptions – when they happen
    • Public Methods – Mandatory
    • Protected Methods – Mandatory
    • Private Methods – Optional but recommended
    • Getters and Setters – Optional, recommended if the logic is not trivial.
  • Fields Mandatory
    • Fields which have none trivial data must contain examples of that data.
    • Maps must contain a definition of what is the key and what is the value.
    • Collections must contain a definition of the items in the collection.

2.1.1 Java Doc

JavaDoc is HTML based and has relatively simple and strict structure.
Since it is HTML based it is not whitespace sensitive, so layout needs to be done with html tags: &nbsp; for spaces (one space works fine but two or more require this tag) and <br/> for a new line.
Each aspect of the documented code has certain tags which represent it and allow the java doc to be formatted into HTML.

Some examples:

  • Method Arguments – documented as: @param <argument name> <argument documentation>
  • Return value – documented as: @return <return value documentation>
  • Exceptions –  documented as: @throws <Exception Type> <Exception reason>

Note: More information and examples can be found at http://java.sun.com/j2se/javadoc/writingdoccomments/index.HTML

2.1.2 Example

Following is the HTML version of the documentation for the method:  IMigrationProperties.getModuleLogicalName

Description Gets the Logical name of a module based on its Physical name.

  • The Physical name of a component is how it is named on the file system or in the database when referring to it.
  • The Logical name of a component is how it is named for a user to identify it.

Example: The Physical name BL is mapped to the Logical name BLBE.

Note: The Physical and Logical names can be identical.

Parameters Parameters:
modulePhysicalName The Physical name of the component.
Return Value Returns:
The Logical name of the component.

The syntax of the documentation behind this method is:

Description /**
* Gets the <b>Logical</b> name of a module based on its
* <b>Physical</b> name.<br/>
* <ul>
* <li>The <b>Physical</b> name of a component is how it is named on the file system or in the database when referring
* to it.</li>
* <li>The <b>Logical</b> name of a component is how it is
* named for a user to identify it.</li>
* </ul>
* <b>Example:</b> The <b>Physical</b> name
* <code><b>BL</b></code> is mapped to the <b>Logical</b> name
* <code><b>BLBE</b></code>.<br/>
* <br/>
* <u><b>Note:</b></u> The <b>Physical</b> and <b>Logical</b>
* names <u>can</u> be identical.
* <br/><br/>
Parameters * @param modulePhysicalName The <b>Physical</b> name of the
* component.
Return Value * @return The <b>Logical</b> name of the component.
*/

2.2 Coding Standards

Proper coding standards make code easily readable and understandable by any developer and allows new team members to be productive faster than when working with non-standard code.

There are several coding standards in the industry. Below is the standard I find the most productive.

2.2.1 Naming Conventions

  • Class name starts with upper case (file name equals to the class it contains)
  • Method name with lower case
  • Members starts with lower and should not contain prefixes (As opposed to the Hungarian Notation)

2.2.2 Coding Conventions

  • Log the start and end of methods (Trace level)
  • Log input parameters (Debug level)
  • Log critical sections in the flow (Info level)
  • Give meaningful names to variables and methods (adhere to the naming conventions)
  • Each class should be properly documented (adhere to the Documentation Standards)
  • Write comments for important parts in the method – explain special considerations where needed
  • Constants are maintained in one or more global class (usually one global class per area).  Constants that are for private use only, are defined in the same class that used them.
  • Check for null references, or use assertions to validate your flows.
  • Throw exceptions with detailed descriptions of the problem.  If the exception is thrown due to another exception always add the causing exception to the stack trace.
    Example:

    try
    {
      doSomething();
    }
    catch (ThirdPartyException tpe)
    {
      throw new MyAppException("Something went wrong!", tpe);
    }
    
  • Coding which is to be completed later should be marked specifically in the code. Use the following for things you need to complete:
    • // TODO: <developer name> – Something to be done
    • // FIXME: <developer name> – Something important to be done

2.2.3 Look & Layout

Class structure:

  • Enums
  • Static Fields
  • Non-Static Fields
  • Static Methods
  • Constructors
  • Non Static Methods
  • Internal Classes

References to members in the class should be done with the ‘this.’ and ‘super.’ prefixes to increase readability.

2.2.4 General recommendations

  • Keep methods small – split when needed
  • Better to have long but readable code than short and unreadable code.  If performance is an issue – find a compromise.
  • Think of what can go wrong in the flow and take it in account – use Automatic Unit Tests to test your code for all possible scenarios.
  • Write the description of the method before writing the code – in that way you will know better what the method should perform
    You can prepare templates in your code so that you don’t forget things later – example:

    private void init(Map parameters)
    {
           // FIXME: Ron – Delete temp files from previous execution
           // FIXME: Ron – load property file
           // FIXME: Ron – Initialize environment variables
           // FIXME: Ron – open threads
    …
    }
    

    This will help you breakdown each method’s functionality and ease its implementation. Also, from these comments you can extract the method’s documentation later on.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: