Architecture & DesignCan IDEs Do More to Improve Code Quality? Part II

Can IDEs Do More to Improve Code Quality? Part II

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

In our previous article, we looked at IDE tools for the creation of documentation in the source code. This time, we are going to talk about comment maintenance, among which synchronization and code conventions are major issues. Then, we introduce some custom Javadoc tags and their uses. Finally, we look at miscellaneous uses of comments.

Comment Maintenance

In this section, we discuss aspects and tools that ease the task of working with an existing code base.

We are first going to define an important term that will be used throughout this part.

Refactoring is the part of code maintenance that doesn’t fix bugs or add new functionality. Rather, it is designed to improve the understandability of the code or change its structure and design, to make it easier for human maintenance in the future.

Code Convention

Code conventions are non-absolute rules defined by a group of people. The objective of those rules is to reduce confusion among the group. One can distinguish naming conventions and formatting conventions.

Formatting conventions

A good IDE removes formatting from the programmer’s responsibility. Ideally, one wants incremental formatting when saving a source file. Except from defining the format to use, the programmer is not distracted by the job of formatting and can focus on writing code.

In Eclipse, one has to format the source code manually by executing the “Source Format” action. This formats the source code according to format preferences. One can find them in “Window -> Preferences; Java -> Code Style -> Code Formatter.” In the Code Formatter, one can define a wealth of variables, from lines breaks to braces positions.

Formatting conventions are a very small part of a bigger picture. Indeed, one can glimpse the pattern that was already introduced in Part I: The IDE now supports the developer by implementing checkable aspects of his methodology.

Naming conventions

In Eclipse, the developer can define prefixes and suffixes for the following variable types:

  • Fields
  • Static fields
  • Parameters
  • Local variables

For example, there is a growing trend in the Java community to prefix private fields with an underscore.

private String _ownerName;

One also usually declares constants with capital letters:

public static final int LIGHT_SPEED = ...;

The aim is to increase code readability. Eclipse does not fully implement this aspect. Indeed, Eclipse only uses the above prefixes and suffixes for generated code. Eclipse does not generate a warning for source code that doesn’t comply with the prefix convention. However, plug-ins such as “Checkstyle” (see the Resources section) exist to enforce such naming conventions.

Naming methods and variable identifiers

For methods, by looking at the name, one should guess what the method is supposed to do. It is important to strike a balance between conciseness and strength of meaning.

Key Concept: Names should be self-documenting.

For variable identifiers, the previous argument applies as well. However, it is quite handy to use short variable names. Indeed, many people find such code easier to read. But, other people like longer names better, so how does one solve this dilemma?

One solution lies in renaming variables on the fly when needed. However, this is only imaginable for closed-world variables, variables whose uses are all known to the IDE. Thus, based on meta-data indicating how to construct an identifier, the IDE refactors the code on the fly for the following:

  • Private fields
  • Local variables
  • Parameters

One could possibly do that for package identifiers as well. However, for big packages the operation becomes time consuming.

Protected and public are API variables (i.e. open-world variables). Renaming such variables constitutes an API change and so potentially breaks client code.

Synchronization Between Code and Comments

Once comments are written, one wants them to stay synchronized with the code. In other words, when a change is made in the source code, the comments must be updated accordingly. When a comment is not synchronized, it loses its meaning.

//Comment explaining why D is used
int result = D*F+G;

If someone decides to change the name D to R without updating the comment, it becomes hard to use. Occurrences of D in the comment might point either to F, G, or the new R.

Remark: Broadly speaking, when redundant information is present, synchronization becomes difficult if the system has to stay coherent.

Not so long ago, when you wanted to do a task such as renaming a class or a variable identifier, you had to do it manually. That was error-prone and time consuming. Today, one should use refactoring operations that enforce code and comments synchronization. In Eclipse, when the insertion point is on an identifier, one may rename it with the action “Refactor-> Rename”. The IDE automatically updates the source code in all relevant files.

Nevertheless, Eclipse does not synchronize references in comments. It is a hard problem indeed to solve with the current comment language. Here is a simple illustration of the problem:

/** The car's name is now name
 */
public void setName(String name) {
       _carNameString = name;
}

In the preceding Javadoc comment, how can the IDE know which occurrence of “name” refers to the parameter? Our brain knows it because it understands a complex language. In “The car's name is now name“, Eclipse sees text with no special meaning. String equality as a criterion is not acceptable, because comment text could then become “The car's aliceName is now aliceName“. One has to use a syntactic construct that enables the IDE to distinguish between template text and normal text. In the Javadoc paradigm, one could define an inline Javadoc tag for variables.

/** The car's name is now {@var name}
 */
public void setName(String name) {
       _carNameString = name;
}

With the extended Javadoc definition, the refactoring tools are now able to synchronize comments correctly. Nevertheless, such new syntactic conventions can be tedious to use. Eventually, there is a need for designing ergonomic tools that diminish the overhead generated by those notational conventions. As discussed in Part I, the overhead generated by the {@var } construct can be diminished with the Code Completion feature.

The inline @var tag is not officially supported. One needs an IDE supporting custom Javadoc tags.

Custom Javadoc Tags for Better Code

A few IDEs fully support custom Javadoc tags. The current Eclipse milestone is not among those. One can expect custom tags support in the near future, though.

Custom tags enable developers to more tightly integrate the use of design by contract concepts; the most important ones are pre conditions, post conditions, and class invariants.

Pre-conditions and post-conditions

A condition is an expression whose result is a Boolean value. Conditions are applied to Java methods in the following way:

  • Pre-conditions are a set of conditions one has to comply with before using the method protected by the pre-conditions.
  • Post-conditions are a set of conditions that must hold when the method returns.

In the current official Javadoc tool, there are no pre-condition and post-condition tags. That information, if not missing, is often spread over the whole Javadoc comment. In the official setup, one can write anything because there are no templates to follow after the tag name. The IDE is a possible place for defining an extended documentation language and enforce its syntax rules.

The main benefit of extending the documentation language is a better specification structure that:

  • Enables tools to compute things in background and inform the developer of any results or errors.
  • Allows one to find relevant information more quickly.

Invariants

Class invariants express general consistency constraints that apply to each instance as a whole. Invariants are conditions that must hold:

  • after a constructor returns.
  • before a method call.
  • after a method returns.

Possible use of invariants: case study

This section describes a mechanism that can reduce occurrences of the ubiquitous NullPointerException in Java. The following code defines the Car class implementing the IWeighable interface.

/**
 * Defines a Car
 * @author Charles-Philip Bentley
 */
public class Car implements IWeighable {
   /**
    * Constant to use for null owner names
    */
   public static final String NULL_OWNER = "NOBODY";
   /**
    * @def the name of the car owner
    * @inv _ownerName!=null use {@link Car#NULL_OWNER} for
    *                  creating a car without an owner
    */

One can see two custom tags. The @def tag gives a natural language description of the _ownerName field. The @inv tag defines a class invariant on the _ownerName field. Provided the IDE is aware of the meaning of those tags, it can take charge of many tasks and automatically report problems to the user. If one declares the following constructor:

public Car(String ownerName) {
   _ownerName = ownerName;
}

An IDE is able to generate a rich skeleton:

/**
 * @pre ownerName!=null use {@link Car#NULL_OWNER} for
 *                 creating a car without an owner
 * @param ownerName the name of the car owner
 */
public Car(String ownerName) {
       _ownerName = ownerName;
}

Let’s further illustrate with the following method:

public void setOwnerName(String newOwnerName) {
       _ownerName = newOwnerName;
   }

Again, most of the documenting job can be automated and the IDE will write the following skeleton:

/**
 * @pre newOwnerName!=null use {@link Car#NULL_OWNER} for
 *                    creating a car without an owner
 * @param newOwnerName the name of the car owner
 * @post true the name of the car owner is set
 *       to {var newOwnerName}
 */
public void setOwnerName(String newOwnerName) {
       _ownerName = newOwnerName;
}

The real useful feature kicks in when one is using the Car class as shown here:

/** Gives examples of uses for the Car class
 * @author Charles-Philip Bentley
 */
public class ExampleCar {
   public static void main(String[] args) {
      Car car = new Car("Bob");
      String name = null;
      car.setOwnerName(name);
   }
}

This code snippet above compiles normally without errors. It also runs without any exception being thrown. Indeed, that’s normal from the Java Language Specification (see the Resources section). However, from the designer viewpoint, the programmer is making an error; he is breaking a pre-condition by calling the setOwnerName method with a null value. It is thus desirable for the IDE to generate an error and warn the user. One could use an Exception scheme, throwing a ConditionNotMetException when pre-conditions are not respected. But that would only offer runtime error checking and still generate those custom exceptions defined by the scheme. Error reporting at compile time is much better.

Three possibilities are possible to make this kind of tool a reality:

  • As an Eclipse plug-in (most likely).
  • Integration of the feature in the current Java Development Tool plug-in
  • Incorporation of those checks in the compiler used by Eclipse (less likely)

Key point: By having a well defined environment for documentation and comments, the IDE leverages the documentation and improves the developer’s experience.

Synchronization Between Class Comments and Sub Class Comments

The case of the Liskov Substitution Principle

When one is sub typing by implementing an interface or extending an existing class, it is a good practice to follow the Liskov Substitution Principle.

Barbara Liskov first wrote it as follows:

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

Stated more simply, programs using references to base types must be able to use references to derived types without knowing the difference.

In Java, this principle fits especially well when one is using the pre-conditions and post-conditions constructs that were introduced in the previous section. In this paradigm, the principle easily translates into:

On a synchronized method specification, one may

  • remove one or several pre-conditions
    • The method does not rely on those pre-conditions to successful return.
  • add one or several post-conditions
    • The method is giving more when it returns

Let’s show an illustration of the rule of thumb by taking back the method definition of the IWeighable interface; pre-conditions and post-conditions have been integrated.

/**Returns the weight of this object when empty
 * @pre unit != null
 * @param unit the unit to use for the weight
 * @return w = the weight value of the object in the units defined by unit.
 * @post w >= 0
 * @throws WeightException when at least one of the contained objects 
 * does not implement IWeighable
 */
public int getWeight(IUnit unit) throws WeightException;

From the viewpoint of the person who implements this interface, the comment cannot be changed in IWeighable.java. Let’s synchronize the Javadoc comment and see what can be changed in Car.java;

/**Returns the weight of this object when empty
 * @pre unit != null
 * @param unit the unit to use for the weight
 * @return w = the weight value of the object in the units defined by unit.
 * @post w >= 0
 * @throws WeightException when at least one of the contained objects
 * does not implement IWeighable
 */
public int getWeight(IUnit unit) throws WeightException {
   //implementation
}

After implementing the method, the developer might then strengthen the post-conditions by adding @post w >= 1, because any instance of his Car implementation always carries a weight above zero. From the IWeighable perspective, this new post-condition does not change the contract. The LSP is respected. Indeed, when w >= 1, one also has w >= 0.

Is it be possible to remove the pre-condition @pre unit != null? The answer is no. Without this condition, one cannot fulfill the contract defined in the @return tag that states the returned value must be expressed in the units defined by the variable unit parameter. Not respecting such constraint is the kind of mistake that leads Mars probes to crash unexpectedly.

What about exceptions and rules one shall follow? Based on the getWeight definition in the IWeighable interface, for an implementation of getWeight in Java, one

  • Cannot add any throws CheckedException clause.
  • Can remove the throws WeightException clause if the implementation finds a way not to throw any instance without breaking the contract of specification.
  • Can add one or several throws UncheckedException clauses (an unchecked exception is any exception sub classing the RunTimeException class).

Clearly, when one is modifying a Javadoc comment according to the LSP, one needs the whole specification under his eyes to avoid mistakes. A complete Javadoc comment synchronization tool is thus needed, a tool that would help the developer at enforcing the LSP. Such a tool does not yet exist for the Eclipse IDE, but can be expected in the future as a plug-in.

Re-synchronization upon API changes

Changes in interface specifications are rare. However, when changes do happen, one is supposed to iterate through all interface implementations to check the compliance with the new specifications, and make modifications if necessary. This process is not necessary if the specification is changed in such a way as to make it more general. One makes a specification more general by adding a pre-condition or removing a post-condition.

In the current state of affairs, Eclipse does nothing when one modifies the Javadoc comment in an interface. Automatically generating todo tasks is a useful improvement to ease the maintenance work. Such a task may include information such as the modification date and the interface in which the modification was made.

Miscellaneous Uses and Features for Comments

We have seen how to write and maintain good documentation in the source code. However, it would be pointless to make these processes efficient if there were no productive ways to benefit from this documentation. Here are a few examples of what is possible to do in the current version of Eclipse.

Non-intrusive popup window

Indeed, with minimum time wasted, Eclipse can fetch relevant documentation and display it in a non-obtrusive popup window. Take, for example, the following code snippet:

public static void main(String[] args) {
   Car car = new Car("Bob");
   String name = null;
   car.setOwnerName(name);
}

To display the specification of the setOwnerName method, one puts the insertion point inside the method call setOwnerName and presses the F2 key. One also can hover the mouse cursor over the method name for half a second. Easy access to information allows one to make sure the objects and methods are used correctly.

Generating and Displaying the Javadoc

The IDE also uses those comments to generate API documentation in HTML. A browser can be started from inside the source editor for displaying this documentation. In Eclipse, the action in menu Navigate -> Open External Javadoc will show the API documentation relevant to the object selected.

Javadoc comment Folding and Unfolding

Comments take space in the editor. Thus, once one knows a class pretty well it is legitimate to hide the comments by folding them. Code folding is scheduled for the next Eclipse release in June 2004. One can try to improve the feature by defining behavioral preferences enabling the IDE to know when to fold or unfold a comment. For example, one may tell the IDE to fold comments after two visits.

Conclusion

The goal of an IDE is to help developers. The IDE is especially useful for undertaking dull and easily automated tasks. It can do straightforward operations in the background while letting one focus on interesting things.

We have seen what already exists in the Eclipse IDE, while pointing out what developers can expect to see improved. In Part I, we saw the creation of comments issue and explained the Javadoc skeleton. In Part II, we showed how managing existing documentation within the source code becomes easier and faster. We analyzed the kind of improvements custom Javadoc tags can generate, among which reducing occurrences of the NullPointerException is outstanding. Even though IDEs are already doing a great job at helping developers write quality code, there is room for improvements. Because the Eclipse platform is extensible, one can use plug-ins to implement those improvements. However, one should not dream too much. In the current state of technology, developers will still have to write the informal content in the comments for quite some time.

It was our premise that good comments increase quality. However, code quality does not exclusively depend on static documentation; good design and tests contributes to code quality as well. Indeed, one should look on a large scale when evaluating tools. In an IDE, one should look at the supported aspects of one’s methodology. With its extensibility, the Eclipse platform is especially well equipped for supporting custom methodologies within a coherent and seamless environment.

References

B. Le Charlier and P. Flener. Specifications are necessarily informal, or: Some more myths of formal methods. Journal of Systems and Software, Special Issue on Formal Methods Technology Transfer 40(3):275-296, March 1998. http://user.it.uu.se/~pierref/publications.html

Barbara Liskov and John Guttag. Program Development in Java: Abstraction, Specification, and OO Design. Addison-Wesley Pub Co 2000 ISBN: 0201657686

Barbara Liskov, “Data Abstraction and Hierarchy,” SIGPLAN Notices, 23, 5 (May, 1988).

Resources

Source code for this article: Source.zip

Java Language Specification: http://java.sun.com/docs/books/jls/

Checkstyle: http://checkstyle.sourceforge.net/

CodeGuide: http://www.omnicore.com/index.htm

Borland JBuilder: http://www.embarcadero.com/products/jbuilder

Eclipse: http://www.eclipse.com

Net Beans: http://www.netbeans.org/

MS Visual Studio: http://msdn.microsoft.com/vstudio/

IntelliJ IDEA: http://www.jetbrains.com/idea/

Oracle JDeveloper: http://otn.oracle.com/products/jdev/index.html

About the Authors

Charles-Philip Bentley is a computer science engineering student at the Catholic University of Louvain (UCL). He is currently writing his dissertation titled “Research on tools for code specification and commenting.” His main interests are OOP methodologies, user interface ergonomics, and software engineering techniques.
cbentley@student.info.ucl.ac.be

Baudouin Le Charlier is a professor at the Catholic University of Louvain (UCL). His research interest includes program analysis by abstract interpretation, program verification, intrusion detection, and interoperability of programming languages. He is teaching programming methodology, programming languages concepts, and formal syntax theory.
blc@info.ucl.ac.be

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories