Coding Guidelines
Introduction
This document provides guidelines for developing ColdFusion applications on Oregon Health & Science University (OHSU) ColdFusion-enabled environments.Security
Security considerations in ColdFusion code are not covered in this document, but the importance of writing applications that do not compromise OHSU's security policies is obvious. For a good overview of security best-practices in ColdFusion, please reference the Taxonomy of Coding Errors that Affect Security from fortify.com.
QUICK HITS:
Variables should be named for their type and set in camel-case:
| Prefix | Variable Type (Example) |
|---|---|
| b |
Boolean (bLoggedIn) |
| st | Structure (stUserInfo) |
| lst | List (lstPhoneNumbers) |
| qry | Query (qryUserData) |
| n | Numeric (nPageNumber) |
| ar | Array (arProducts) |
| s | String (sFirstName) |
| d | Date (dToday) |
Functions should also be camel-case, like compareNoCase.
Use CFCs wherever possible for querying databases and heavy processing.
Pay close attention to scope and do not crowd application memory with data that does not need to be universally available to the entire application as this can affect jrun performance. Each scope has a purpose and a specific use – don't just toss variables around scopes until your app works – it will likely break on the weekend if you do this.
Comment your code even more than you think you should. There is no such thing as being too verbose in your comments – let us (and yourself) know what you were thinking – we really want to know.
If you need to return to a file to add functionality later or take out a hard-coded value or logging or something, identify these spots in your code with <!--- TO DO: ---> comments.
Do not use pound signs when they are not necessary (outside of outputting to the screen).
When treating a value or a function's attribute as a string identify it with double quotes ("") – reserve single quotes for SQL, javascript and strings inside of double quotes.
If a page or file will error in the absence of a variable use cfparm to initialize a default value. You cannot use cfparam too much! If the presence of a variable is conditional, use isDefined checks. You cannot check isDefined too much!
If a block of code relies on forces outside of your code's control such as database connectivity, SOAP responses or the existence of a file wrap your processing in cftry / cfcatch blocks. Throw custom errors for yourself where necessary.
Please pay attention to whitespace. One line between blocks of code can aid readability but mulitple lines just make debugging difficult.
Please nest tags appropriately – perhaps nothing else improves readability and speeds debugging more. Indent with one tab.
Where possible put function attributes on the same line as the function, ala:
<cfwhatever name="this" attribute="that" parameter="theother">.
Use compare and compareNoCase for string comparisons. ColdFusion will allow you to evaluate strings with such nonsence as <cfif sMyString NEQ ""> but that is because it is lazy – EQ, NEQ, GT, LT, etc. ARE FOR MATHEMATICAL OPERATIONS. Compare, find, len, etc. are for strings and strings alone. This will make your code much more portable and make you feel generally better about yourself.
Similarly, do not evaluate anything except a boolean with <cfif x>. Again, lazy ColdFusion might let you get away with this as a way for checking a variable's length, but use <cfif len(x) GT 0> instead. Also do not evaluate it as <cfif len(x)> - len returns a number, so compare it to a number.
While we're on the subject, find, findNoCase, listFind, listFindNoCase, findOneOf, compare, and compareNoCase also return integers – use them accordingly.
If you are writing values to a database use cfsqlparam. Always. You can't be too safe.
If a value or a chunk of output can be cached, then cache it. There are many ways to cache values (memory, filesystem, database, XML…) and your code (and your users!) will love you for it.
Style: Naming Conventions, Performance Considerations & Preferred Programming Techniques
This section provides guidelines on developing ColdFusion code that will improve the reliability and performance of applications developed for OHSU. These guidelines are based on industry adopted and documented practices. The optimal methods described here are a result of comparing performance and execution times on alternative, less favorable methods.
Variables
Initializing variables
When using <cfparam> to initialize variables, specify a variable type. Although ColdFusion will attempt to determine the type by the value, it is more readable and decreases overhead to set the type when initializing.
Example Initialization:
<cfparam name="bInitialized" default="No" type="boolean">
< cfparam name="nTotalCount" default="0" type="integer">
< cfparam name="sErrMsg" default="" type="string">
When possible, initialize variables to a default value and the check for a valid value, rather than continuously checking to see if the variable is defined.
<!--- BAD --->
<cfif IsDefined("form.nEventID")>
<!--- execute some conditional code --->
</cfif>
<!--- more cf code here --->
<cfif IsDefined("form.nEventID")>
<!--- execute some conditional code --->
</cfif>
<!--- GOOD --->
<cfparam name="form.nEventID" default="0" type="numeric"> <!--- set the variable to an initial value and type --->
<cfif form.nEventID GT 0>
<!--- Do conditional code here --->
</cfif>
Scoping variables
All variable references with the exception of local variables (also known as variables scope) must include the scope prefix prior to the variable reference. This includes variable references with in a cfoutputwith a query attribute and a cfloop with a query attribute. By properly scoping variables, you can avoid stepping on variables with the same name but different scope. By scoping your variables, you make you applications run faster and easier to understand.
Listing of all ColdFusion scopes requiring a scope prefix:
Request - request.sDSN
Session - session.nUserID
Application - application.environment
Query - qryeventselect.Eventid
Attributes - attributes.sAction
Form - form.this_event
Url - url.eventid
Caller - caller.error
Cookie - cookie.nUserID
Client - client.settings
Examples:
<!--- BAD --->
<cfloop query="qryEventSelect">
<cfoutput>#EventID# - #EventName#</cfoutput>
</cfloop>
<!--- GOOD --->
<cfloop query="qryEventSelect">
<cfoutput>#qryEventSelect.EventID# - #qryEventSelect.EventName#</cfoutput>
</cfloop>
Setting variables
When setting and/or using a string that contains both text and variables, you should separate the string component from the dynamic variable component and concatenate them together using an ampersand (&).
Example:
<!---- BAD --->
<cfset sMessage ="Thank you #form.fname# for downloading the software">
<!--- GOOD --->
<cfset sMessage = "Thank you " & form.fname & " for downloading the software">
When the Value section of a cfset only has a dynamic variable or a function, it should not be placed in pound signs (#) and quotes ("). This is commonly done.
Example:
<!--- BAD --->
<cfset sMessage ="#sOutputMessage#"> <!--- this is a value parameter of cfset , not an attribute. Don't enclose in quotes and #'s --->
<!--- GOOD --->
<cfset sMessage = sOutputMessage> <!--- don't need #'s and quotes here. Just set it --->
Certain ColdFusion functions may be run without any concern being given for any sort of return variable. When these functions are run, it is more efficient and cleaner to use the following syntax for them.
<!--- BAD --->
<cfset temp = Function() >
<!--- GOOD --->
<cfset Function() > <!--- no need to set this to a dummy or temp variable --->
The following ColdFusion functions can be used in this way:
|
ArrayAppend |
ArrayClear |
ArrayDeleteAt |
|
ArrayInsertAt |
ArrayPrepend |
ArrayResize |
|
ArraySet |
ArraySwap |
CF_SetDataSourceUsername |
|
CF_SetDataSourcePassword |
CFUSION_SETODBCINI |
CFUSION_SETTINGS_REFRESH |
|
CFusion_DBConnections_Flush |
QueryAddRow |
QuerySetCell |
|
SetLocale |
SetProfileString |
SetVariable |
|
StructClear |
StructDelete |
StructInsert |
|
StructUpdate |
WriteOutput |
|
Any user defined function may be used in this way as well.
Examples:
<cfset structClear(session)>
<cfset querySetCell(qryEventSelect,"EventID",form.EventID,1)>
When a large number (3+) of cfsetS are used in a row, it is more efficient to place them within a cfscript block. The savings is .2 ms and increases with each cfset added. When 4 or 5 are used, the savings was .4 ms.
<!--- BAD --->
<cfset FirstName="John">
<cfset LastName="Smith">
<cfset Email="john_smith@OHSU.edu">
<cfset UserID=form.userID>
<!--- GOOD --->
<cfscript>
FirstName="John"; // remember to use this comment style
LastName="Smith"; // end each cfscript line with a semicolon, like JavaScript
Email="john_smith@OHSU";
UserID=form.userID; // don't need #'s here.
</cfscript>
Setting boolean variables
The preferred boolean attribute values are true and false. In cfset, they should always be unquoted., they should always be unquoted.
Example:
<!--- BAD --->
<cfset bInitialized = "true">
<!--- GOOD --->
<cfset bInitialized = true>
Setting numeric variables
When setting numeric values, do not use quotes around the value.
<!--- BAD --->
<cfset nTotalCount = "100">
<!--- GOOD --->
<cfset bTotalCount = 100>
Using pound signs around variables
The use of additional pound signs is inefficient and causes unnecessary processing. Pound signs should never be used in an evaluation zone.
Examples:
<!--- BAD --->
<cfset myvar = #thisvar# + #thatvar#>
<cfset myvar=#Evaluate(#thisvar# + #thatvar#)# > <!--- this will break --->
<cfset lstNames ="Jack,Joe,Jason">
<cfloop list="lstNames" index="Name"> <!--- this will work, but the list here is literally "lstNames" not the value of the variable with that name --->
<cfloop list="lstNames" index="#Name#"> <!--- this will break --->
<!--- GOOD --->
<cfset myvar = thisvar + thatvar>
<cfset myvar = Evaluate(thisvar + thatvar) >
<cfset lstNames="Jack,Joe,Jason">
<cfloop list="#lstNames#" index="Name">
Quotes in Queries
Use single quotes when passing strings/parameters into a SQL query:
<cfquery name="Search" datasource="Company">
select firstname, lastname, phone
from Employees
where LastName= '#LastName#'
</cfquery>
No quotes should be used for numeric values:
<cfquery name="Search" datasource="Company">
select emloyeeId, firstname, lastname
from Employees
where employeeId = #empID#
</cfquery>
ColdFusion Tags
All ColdFusion tag attributes must be lower case and surrounded by quotes similar to XHTML standards.
The ColdFusion tag name should use the same case throughout the document. If you choose to capitalize ColdFusion tag names, it tends to more easily separate CFML tags from HTML tags. However, it takes longer to type if you're shifting all your tag names to uppercase. However you prefer to type tag names, just be consistent throughout your application.
Example ColdFusion tag usage:
<CFQUERY name="qryEventSelect" dbtype="query" > <!--- attributes name and dbtype are lower case. Values for these attributes are surrounded by quotes --->
<cfswitch expression="#form.eventid#"> <!--- attribute expression is lower case. Value is a CF variable surrounded by # and quotes --->
<cfswitch expression=#form.eventid#> <!--- incorrect style --->
<cfswitch expression=form.eventide> <!--- incorrect style --->
<cfswitch expression="#form.eventid#"> <!--- correct style --->
All attribute values to all tags should be quoted, usually with double quotes ("). Single quotes (') may be used if the attribute value already contains a double quote.
ColdFusion Functions
All calls to ColdFusion functions should use camel-case with the first letter of the word uncapitalized.
Example ColdFusion function calls:
<cfset thisFile = getFileFromPath(GetBaseTemplatePath())>
<cfset msg = replaceNoCase(msg,",","","All")>
Type Names
The names used to reference ColdFusion types (e.g., in type= and returntype= attributes) shall be lowercase for built-in types (e.g., boolean, string). The names used to reference user-defined types (i.e., ColdFusion components) shall exactly match the case of the implementing filename, e.g., Article, NewsItem, MachII.framework.Listener.
Booleans
Booleans will always be true or false - do not test a boolean expression against a value, just test the expression:
<!--- BAD! --->
<cfif bInitialized EQ "true">
<cfif bInitialized EQ "false">
<!--- GOOD --->
<cfif bInitialized>
<cfif NOT bInitialized>
Performance
Performance "Do"s
Comparisons
Use EQ, LTE, NEQ, GTE, LT, GT for comparison operators only. Do not use IS, IS NOT. Capitalize all comparison operators.
Use len() for testing nulls. It is significantly faster.
Example:
<!--- BAD --->
<cfif sMyVar EQ "">
<!--- GOOD --->
<cfif len(sMyVar) GT 0>
Note:
<cfif len(trim(x))> can be useful to trap values that are completely whitespace as well as empty strings.
Use functions to compare string values.
Use compareNoCase() for comparing two values
Use compareNoCase() or compare() instead of the NEQor is not operator to compare two items. They are significantly faster. (Remember though, that these functions return numeric values - 0 if the values match, which could be evaluated as false).
Example:
<!--- BAD --->
<cfif sFirstName EQ "John">
<!--- GOOD --->
<cfif CompareNoCase(sFirstName,"John") EQ 0>
<!--- code that is executed if there is a match --->
<cfelse>
<!--- not a match --->
</cfif>
Use listFindNoCase() for OR comparisons
Use listFindNoCase() or listFind() instead of the EQ and OR operators to compare one item to multiple items. They are much much faster (order of magnitude for 5+ options).
Example:
<!---BAD --->
<cfif x EQ "a" or x EQ"b" or EQ is "c">
<!--- GOOD --->
<cfiflistFindNoCase("a,b,c", x) NEQ 0>
Lock memory-based state management variables
In ColdFusion, you have to lock the setting of memory based state management variables (Server, Application, Session). If you don't, you may find a situation where a value is being written to memory at the same time as it's being read. This will result in a PCode error.
In order to avoid problems with locking, performance and potential cfid collisions, in a clustered environment, application, client, server and session scope should be avoided in general. Where application or server scope is required, use cflock around all updates.
When using cflock be sure to use the proper scope attributes and type attributes. Type="EXCLUSIVE" is the default type, causing unnecessary exclusive locks if not explicitly specified in the type="" parameter. A timeout in the range of 2 – 5 seconds should be acceptable. Too long of timeouts will cause other applications to be locked out on a long running process. A timeout too short will cause excessive errors if application resources are not instantaneously available.
Cflock code should surround only small amounts of CF processing and should never span a query, call to a custom tag, or an include file. Where possible, web applications should be stateless.
Example:
<!--- BAD --->
<cflock timeout="2">
<cfset sDSN = application.sDSN> <!--- This is a readonly access with an unscoped, untyped lock. Default lock type is exclusive – an unnecessary exclusive lock --->
</cflock>
<!--- GOOD --->
<cflock scope="application" type="readonly" timeout="5" throwontimeout="No">
<cfset sDSN = application.sDSN>
</cflock>
Programming with loops
Never nest loops deeper than 3 levels - Doing so will result in major slowdowns. Be careful of the code between an opening and closing cfloop tag. Avoid making successive calls to a module or custom tag within a cfloop.
Example:
<!--- BAD --->
<cfloop Query="qryEventSelect">
<cfloop LIST="#qryEventSelect.EventNames#" INDEX="sName">
<cfloop List="#lstEventNames#" INDEX="sEVName">
<cfloop Query="qryEvents">
<cfif NOT CompareNoCase(qryEvents.EventName,sEvName) AND FindNoCase("FPGA",sName)>
<!--- Do some conditional code here --->
</cfif>
</cfloop
</cfloop>
</cfloop>
</cfloop>
Programming with Queries
Restrict your query returns - If you're expecting a single row back from a CFQUERY, set the maxrows attribute to 1. This gives you more information about what you're doing when it comes time to debug or rewrite. Additionally, it may result in a slightly faster query, as you're restricting what you should get back.
Similarly, only select the values you need. Don't use "select *" in your SQL statements simply because it's fast & easy. If your table has 16 columns and you only the values of 5, explicitly select those 5.
Examples:
<!--- BAD --->
<cfquery name="qryPerson" datasource="#request.dsn#">
select *
from People
where id = '#nPersonID#'
</ cfquery >
<!--- GOOD --->
<cfquery name="qryPerson" datasource="#request.dsn#" maxrows="1">
select fname,lname,phone,fax,email
from People
where id = '#nPersonID#'
</ cfquery >
Cached Queries
A cached query allows you to query the data in a database once, and use the results for a specific period of time. For example, if you have a form with a drop-down list of items from a table, you can query the table when originally loading a page and have the results available in cache for a set time period (i.e. 30 minutes). This cuts down on processing for data that changes rarely.
Two attributes are used to enable persistent queries:
CACHEDAFTER – used to specify a certain date and time to use cached query data.
CACHEDWITHIN – used to specify a timespan for using the cached query data (example, you can specify to the cached data for a span of 10 minutes)
Sample use:
<cfquery name="myQuery" datasource="myDataSource" cachedwithin="#CreateTimeSpan(0,0,10,0)#">
select value1,value2
from myTable
where field="fieldname"
</cfquery>
Use cfqueryparam to increase query performance
You can use cfqueryparam to optimize a query that looks something like this:
select *
from myTable
where
column = #variable#
If this query is executed repeatedly with different values for variable then using a SQL 'bind' variable will be faster. Cfqueryparam creates these 'bind' variables:
select *
from myTable
where
column = <cfqueryparam cfsqltype="cf_sql_xxx" value="#variable#">
This allows the optimizer to compile the query once and reuse it every time the query is executed. It is also more secure since it prevents rogue SQL from being passed into a query (because it validates the type of the data).
Performance "Don't"s
Don't slavishly convert lists to arrays
Even though manipulating an array is generally faster than manipulating a list in CFMX, if you simply need to iterate over a list of items and process each one in turn the faster construct is <cfloop list="#itemList#" index="x"> ...< /cfloop>. Don't convert itemList to an array and then loop over that - it's not worth it because it probably won't be faster.
Avoid using StructFind()
Under the hood, StructFind()tells ColdFusion to collect the list of keys of a structure, step through each entry in that list and compare it against a given string (the target key). If it matches, then pull the value that's associated with that key. If you know the name of the key you want to access, why not cut out the middleman, and simply access the key by it's name using the dot notation:
<cfset somevar = structname.keyname>
or, if the name of the key is dynamic (stored in a variable), access the key using the bracket syntax:
<cfset somevar = structname[keyname]>
Note: Using the keyname in the bracket notation is commonly used to access a dynamic form variable, since form is a structure. For example, let's say you had a form that had n number of event name fields (sEventName_x, where x=1 .. n), you would use the bracket notation to access the values from this form field:
<cfset i=1>
<cfloop condition="structKeyExists(form,"sEventName" & i)">
<cfoutput>Event Name = #form["sEventName" & i]#</cfoutput>
<cfset i = i + 1>
</cfloop>
If you're using StructFind to determine if a key exists, use the structKeyExists() function instead:
<cfif structKeyExists(structname,keyname) > <!--- Checking to see if it exists , use NOT if checking to see if it doesn't exist --->
Avoid String comparisons
When ColdFusion compares two strings, it needs to analyze the strings much like we had to when we were learning to alphabetize things in school; it has to look at the first character of the first string and see if it matches the first character of the second string, if it does it has to move on to compare the second character of the first string to the second character of the second string, and so on, until it finds a character that isn't an exact match, etc etc. It does this by actually converting each individual character into it's numeric equivalent (know as an
ASCII value) and comparing the numbers. ColdFusion provides a much more optimized way to compare strings, called the Compare() function. The syntax is Compare(string1,string2). The function takes two strings and returns a -1 if string1 is less than string2, a 1 if string1 is greater than string2, or a 0 if the two strings are identical. In most cases, you're only really concerned with whether two strings are (or are not) an exact match... so you're only checking for returned values of zero or non-zero (respectively). Only if they are exactly alike will it return a 0. In Boolean terms, a 0 is FALSE. To make it TRUE and useful within a cfif, we have to use a NOT logical modifier before the function. This says, NOT FALSE, which is the same as true. Because of this, it is not as intuitive for people to look at the code and understand it, which is, for me, its only drawback.

