Guidelines: Code Readability - Pt1

At work I (with heavy input from the rest of the team) am writing these guidelines for our development practices, so that everyone is working 'on the same page.' I will share this series here for others who may want to know how some folks do it.

In everything there must be balance, two sides to every coin. A debate goes on about code "structure". To indent? Or not indent? One argument persists that unnecessary spaces/tabs within code increase the bandwidth used by our systems, serving up 'empty' content. The other argument is that this cost is minimal, in comparison to the time savings with relation to maintaining 'readable' code.

Our official position is that the maintainability of code is more important. At some juncture, in the future, we may implement other methods for reducing the 'space', possibly eliminating it during the Ant build process during deployment. For now it is more important to be able to quickly scan code, find items for clarification or change, and accomplish our mission.

Proper indentation of code does assist in the maintainability of the code, and should be applied regardless of the language being used (XHTML, ActionScript, JavaScript, SQL, ColdFusion, etc.) Indentation should be applied to logical blocks of code within a document. The following are a few different examples:

ColdFusion CFC:

<!---
*====================================================================================
*   TEMPLATE/CLASS
*   com.DM.testObj
*
*   PURPOSE
*   Just a test object
*
*   AUTHOR
*   Stephen G. 'Cutter' Blades, Jr. [SGB] sbladesATmycompanyDOTcom
*
*   COPYRIGHT
*   MyCompany (c) 2008, All Rights Reserved
*
*   NOTES
*************************************************************************************
*   SGB [02.20.2008]
*   Initial file creation.
*   Dependencies:
*      com.DM.superObj
*   Dependent Objects:
*      com.DM.subObj
*====================================================================================
--->

<cfcomponent displayname="testObj" extends="superObj" output="false">

   <!--- Some props --->
   <cfset VARIABLES.instance = StructNew() />
   
   <cffunction name="Init" output="false" returntype="testObj" access="public">
      <cfargument name="objID" type="numeric" required="false" default="0" />
      <cfargument name="objName" type="string" required="false" default="" />
      <cfargument name="dtCreated" type="date" required="false" default="#Now()#" />
      <cfset var iItem = 0 />
      <cfloop collection="#ARGUMENTS#" item="iItem">
         <cfset Set(iItem,ARGUMENTS[iItem]) />
      </cfloop>
      <cfreturn THIS />
   </cffunction>

</cfcomponent>

Most documents, of any type, will have very similar, base level code stylization. The following example is for a standard ColdFusion template. Most nested elements/tags will further increase the indentation, though there will be exceptions.

ColdFusion CFM

<cfsetting enablecfoutputonly="true">
<!---
*====================================================================================
*   TEMPLATE/CLASS
*   wwwroot/public/time.cfm
*
*   PURPOSE
*   To demonstrate Coding Guidelines
*
*   AUTHOR
*   Stephen G. 'Cutter' Blades, Jr. [SGB] sbladesATmycompanyDOTcom
*
*   COPYRIGHT
*   MyCompany (c) 2008, All Rights Reserved
*
*   NOTES
*************************************************************************************
*   SGB [02.20.2008]
*   Initial Creation
*   Dependencies:
*      com.DM.subObjGateway
*====================================================================================
--->


<!--- Processing code before output --->
<cfimport taglib="/custom/interface" prefix="ui" />

<cfscript>
   // Set some variables    VARIABLES.soGW = CreateObject("component","com.DM.subObjGateway").Init(APPLICATION.DSN.myDS);
   VARIABLES.qUsers = VARIABLES.soGW.GetAdminUsers();
   VARIABLES.arrNames = ArrayNew(1);
   /*
    *   Loop over the returned query, and create an array
    *   of names, concatenated from the returned FirstName
    *   and LastName
    */
   for(VARIABLES.i = 0;VARIABLES.i LTE VARIABLES.qUsers.recordCount; VARIABLES.i = VARIABLES.i + 1){
      if(Len(VARIABLES.qUsers.FirstName[VARIABLES.i])){
         ArrayAppend(VARIABLES.arrNames, VARIABLES.qUsers.FirstName[VARIABLES.i] & " " & VARIABLES.qUsers.LastName[VARIABLES.i];
      }
   }
</cfscript>
<!--- The header and footer includes already wrap the output in cfoutput --->
<ui:incHeader>

   <cfif ArrayLen(VARIABLES.arrNames)>
      <cfoutput>
         <cfsilent>
            <!---
             *   Here we wrap a multi-line CF comment in a <cfsilent> tag
             *   to prevent the whitespace from being rendered into the
             *   rendered source. ColdFusion comments would typically
             *   show as whitespace, if included inside an output block,
             *   but the <cfsilent> tag will suppress that processing
             *   output. It is also good to use the tag around display logic
             *   blocks you may have nested within output.
             --->
         </cfsilent>
         <div id="nameDescr">
            This is a breakdown of the various names within our Administrative System - <strong>#APPLICATION.systemName#</strong>.
         </div>
      </cfoutput>
      
      <cfloop from="1" to="#ArrayLen(VARIABLES.arrNames)#" index="VARIABLES.lpIndex">
         <cfif VARIABLES.lpIndex eq 1><cfoutput><p></cfoutput></cfif>
         <cfoutput>Name: #VARIABLES.arrNames[VARIABLES.lpIndex]#<br /></cfoutput>
         <cfif VARIABLES.lpIndex eq ArrayLen(VARIABLES.arrNames)><cfoutput></p></cfoutput></cfif>
      </cfloop>
   <cfelse>
      No names were returned by your request.
   </cfif>

<ui:incFooter>
<cfsetting enablecfoutputonly="false" />

From the previous example, you see a number of examples simultaneously. We start with the standard comment header, come to some processing directives, then get into actual output. The entire document is wrapped within a <cfsetting> tagset. These tags, at the beginning and end of the document, are open (or self-closing) tags, the first stating that any document content after the tag should only be displayed if it is within an explicit output block (either <cfoutput> or a WriteOutput() statement). The second <cfsetting> tag opens the document for non-explicit output, in case this template is included inside another document. By usinging the enablecfoutputonly attribute, the server will reduce the amount of whitespace rendered in the XHTML passed to the browser. Another tool for this is the use of the <cfsilent> tags. Here they are wrapped around a multi-line ColdFusion comment, which would be processed as whitespace to the XHTML document (The above example is for example use only. The above comment would have typically been outside the <cfoutput> altogether). By using the <cfsilent> tags around this, we eliminate that whitespace generation. These tags are also handy if you require a processing block in the middle of your output, as it will hide the whitespace that processing block would produce.

The example above also shows that there are times when you should, and should not, indent. With ColdFusion, JavaScript, ActionScript, etc., simple conditional output (if this then "ok") will typically be inline, while more complex conditional output will be indented as normal. Any looping conditionals will be indented. There are examples above for both tag based, and simple scripted programming.

Our next post will be part II of this topic, where we will show more examples covering XHTML, script, and SQL examples.

Related Blog Entries

Comments
Dav R's Gravatar Hi Steve,

Nice article!. In my project, we usually follow block level indentation like this,

<cfinput
type="text"
name="txtname"
class="txtclass"
value=""
/>

My question is, do you know any DW extention, (or) UDF kind of a thing, can perform this level formatting?. As the traditional "Apply source code formatting" in DW can't able to get me that done.

Plz help!.

Thanks,
Dav R
# Posted By Dav R | 2/22/08 4:16 AM
Steve 'Cutter' Blades's Gravatar @Dav R - I've seen this type of block level indentation before (it displayed properly in the email to me), but I personally think it's overkill. I understand the purpose behind it (easy to see all attributes separated), but it only makes sense, for me, if I'm using a tag with a ton of attributes (like 10), at which point (now) I'd probably just use an attributeCollection anyway.

I can't speak to the formatter question. I gave up on DW a long time ago, switching over to CFEclipse. Whether it could help you, I don't know.
# Posted By Steve 'Cutter' Blades | 2/22/08 6:21 AM
Dav's Gravatar Hi Steve,

Thanks for the reply!. I knew this can be done with "Homesite", but since we are limited to DW here, we've got explore the possiblities within DW alone.

However this blog is definitely an worth reading one, also Please contribute something about your project management standards, process and most particulary the CF tools that you are using out there so that
we ppl know about the different approaches handled out around CF world.

Thanks,
Dav R
# Posted By Dav | 2/22/08 8:31 AM
Steve 'Cutter' Blades's Gravatar @Dav R - Thanx for the kudos. It's a bit of a shame that you are nailed to DW, as Eclipse's plugin architecture really opens up the IDE for the ability to do some great things you just can't integrate into DW (source control with Subversion, unit testing, ANT build and deployment, diagramming, etc.)

At some point in the future I will definitely be getting into management and process, but you can already find a few posts here about some tools. And I always try to share it when I find something new;)
# Posted By Steve 'Cutter' Blades | 2/22/08 8:38 AM
Dav R's Gravatar Thanks Steve!. Will try to utilise eclipse more :-)

Thanks,
Dav R
# Posted By Dav R | 2/25/08 12:28 AM
BlogCFC v. 5.8.001 was created by Raymond Camden. Layout inspired by bluerobot.com., with some JQuery thrown in for fun.