CSC/ECE 517 Fall 2009/wiki3 14 12

From Expertiza_Wiki
Jump to navigation Jump to search

Principle of Self-Documentation

The Principle of Self-Documentation (aka Self-Commenting) states that "The most reliable document of software is the code itself. In many cases, the code is the only documentation. Therefore, strive to make your code self-documenting, and where you can't, add comments." [1] With the advent of Agile Programming methodologies the need for Self-Documenting software is on the rise. Previous design methodologies (like the Waterfall Design Methodology) required the programmer to develop a design document/contract with the customer before the program is undertaken. The advent of Extreme Programming and Agile's Test Driven Design (TDD) requires the software engineer/programmer to pay more attention to the actual code written since the methodologies openly shun design specifications.[2][3] Extreme Programming values Self-Documentation and TDD believes that the tests are the contracts/documentation for a program to adhere to.

Tenets of Self-Documentation

Self-Documenting programs should be the goal of any programmer. Software that is self-documenting should be easy to read with design intent easy to understand and comprehend. Code density will be as dense as it needs to be without being too dense. Essentially it should never fail the initial "sniff" test.

Use meaningful/descriptive variable/method names

Variables are more than memory location abstractions, they are the building blocks on which the program is built. They represent some real world component that is being modeled by the program (a person's name, the force of gravity, number of doors to a room); the intent of the variable should be derived based upon its name.

Methods are responsible for performing actions upon the real world data abstractions. The name of the method represent the action that the method is performing.

The following example set is from Stack Overflow[4].
Example of non-descriptive code:

   float a, b, c; a=9.81; b=5; c= .5*a*(b^2);

Example of Self-Documenting Code:

   const float gravitationalForce = 9.81;
   float timeInSeconds = 5;
   float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2)

Example using coherent method name:

   const float accelerationDueToGravity = 9.81;
   float timeInSeconds = 5;
   float displacement = CalculateDisplacement(accelerationDueToGravity, timeInSeconds);

As with most things in life it is possible to go overboard on the descriptive naming convention: http://thedailywtf.com/Articles/CodeThatDocumentsItselfSoWellItDoesNotNeedComments.aspx

Use Language Constructs when Possible

Code should be readable not only to the author but to reviewers as well. Modern programming languages have been adding native language constructs to allow for additional readability. Ruby added the "unless" operator to replace the "if not" syntax. The use of List/Array iterators allows each list item to be referenced as its native name.

List iteration has move from C's:

   for(int i = 0; i < list.length(); i++) {
       operation on list[i]
   }

To :

   for object in list :
      operation on object

Or:

   list.each { |object| operation on object }

The use of dictionaries (python) or hashes (perl/ruby) instead of vanilla arrays allows for elements to be referenced by native names instead of array position.

Use Design Patterns

The use of language constructs is an intermediate level of self-documenting. The use of common Design Patterns allows for the programmer and reviews to instantly know what the design intent is. Deviates from the patterns should be documented/commented so that the reviewers know that pure patterns weren't used. Rails is a good example of the use of design patterns (MVC). Controllers typically have controller embedded in the name and file/class function are segregated via directory structure makes the code intent easy to infer.

Languages like Scala have design patterns that can be inherited. The inherited functionality allows for the reviewer and programmer know how the inherited class should behave.

   import scala.actors.Actor
   import scala.actors.Actor._
   class Counter extends Actor {
       var counter: Int = 0;
         def act() = {
           while (true) {
           receive {
               case Inc(amount) =>
                   counter += amount
                   case Value => println("Value is "+counter)    
                   exit()
               }
           }   
       }
   }

Comments are evil; but not pure evil

Agile programmers tend to eschew since comments are one of the first things to entropy in a code base. The feeling is that comments are rarely updated during refactoring which cause the code and comments not to match up which causes confusion[5]. Beginning programmers forget that comments are intended to provide intent insight not exact behavior insight.

   # returns a bool value
   def is_equal_to_two(value)
       return value == 2
   end

The code doesn't need a comment since the intent of the code can be derived via the method name.

Not all comments are evil. Comments are good when they explain the why but not the how or what.

   # compute displacement with Newton's equation x = v0t + ½at^2
   def compute_displacement(gravitational_force, time_in_seconds)
       return (1 / 2) * gravitationalForce * (timeInSeconds ^ 2)
   end

Conclusion

Agile programming practices make sense for non-mission critical applications where time-to-market is the main driving force. Limited customer requirements or moving requirements require a programming practice this is flexible and extendable. Rapid prototyping allows for the customers to have feedback and to get a "good-enough" product out to market and then iterate. Self-documenting code fits nicely with this practice since it allows for evolving code and the intent is always preserved in spite of application fluidity.


Definitions

Test-Driven Development (TDD): A style of programming where tests for a new feature are constructed before any code is written. Code to implement the feature is then written with the aim of making the tests pass. Testing is used to understand the problem space and discover suitable APIs for performing specific actions.[6]
Agile Development: refers to a group of software development methodologies based on iterative development, where requirements and solutions evolve through collaboration between self-organizing cross-functional teams. [7]
Waterfall Design: is a sequential software development process, in which progress is seen as flowing steadily downwards (like a waterfall) through the phases of Conception, Initiation, Analysis, Design (validation), Construction, Testing and maintenance.[8]

References

[1] http://www.developerdotstar.com/mag/articles/read_princprog.html
[2] http://www.softwarereality.com/lifecycle/xp/four_values.jsp
[3] http://jjinux.blogspot.com/search/label/agile
[4] http://stackoverflow.com/questions/209015/self-documenting-code
[5] http://www.flamingspork.com/blog/2005/08/08/comments-are-evil/
[6] http://doc.silverstripe.org/doku.php?id=testing-guide-glossary
[7] http://en.wikipedia.org/wiki/Agile_programming
[8] http://en.wikipedia.org/wiki/Waterfall_model

External Links

http://www.developerdotstar.com/mag/articles/read_princprog.html
http://blogs.agilefaqs.com/2008/11/08/self-documenting-code-example
http://www.clariusconsulting.net/blogs/kzu/archive/2009/10/01/171565.aspx
http://www.embedded.com/design/202602883