This document is intended to be a concise summary of best practices for anyone building ColdFusion applications within our team. Several external resources used when creating this document. Please note that this is a guideline based on past development experience and industry standards. Please use common sense when applying them and note that this document is ever changing as development trends continue to change.
Use good names for components, methods, arguments and local variables. This can sometimes be a disaster if developers choose random names or non qualified names for methods, arguments and local variables. Naming is very important and will most of the time document your code. Always remember to use meaningful names and stay away from cryptic abbreviations or naming strategies.
AVOID abbreviations if possible. For example, calculateSalary() is a better method name than calcSalary(). Although you can use well known abbreviations, please try to avoid them if possible. Here are some examples NOT RULES:
Acronyms should be avoided in names, but if they must be used, then all acronyms must be capitalized no matter where they are located on a string name.
-- DO THIS -- URLScanner.cfc parseHTTPString() -- NOT THIS -- url-scanner.cfc UrlScanner.cfc parseHttpString() ParseHttpString()
Package names should be unique and in lowercase letters. Underscores may be used or hyphens if necessary. You can package your objects/files using two well known approaches:
The best practice is to use packaging by functionality if at all possible. This creates better packaging layout and maintenability. Here is an example from an application's model or business layer folder:
+ model + security + remote-api + products + users + conversions + util
Class/Component/Interface names should be nouns, as they represent most likely things or objects. They should be written in camel case with only the first letter capitalized for each word. Use whole words and avoid acronyms and abbreviations if possible. Examples:
-- DO THIS -- URLConverter RSSReader Serializable ISearchEngine -- NOT THIS -- urlConverter rssreader serializable iSearchEngine
Methods should be verbs, in mixed camel case with the first letter lower cased and then each internal first letter of words capitalized. Examples:
-- DO THIS -- run() doThis() executeInBackground() isLocated() -- NOT THIS -- RUN() dothis() executeINBackGround() ISLocated()
All ColdFusion type names in arguments, return types and the like should all be in lower case when they are native ColdFusion types. If they are components they should be the EXACT name of the component. This is extermely important if for some reason the code executes in a case-sensitive system, then the code will not work. ALWAYS have the exact case of components and definitions.
-- DO THIS -- <cfargument name="paths" type="array" > <cfargument name="user" type="model.users.User"> <cffunction name="getSecurityService" returnType="model.security.SecurityService"> -- NOT THIS -- <cfargument name="paths" type="ARRAY" > <cfargument name="user" type="model.users.user"> <cffunction name="getSecurityService" returnType="model.security.SECURITYSERVICE">
All CFML and custom tags should be writing in lower case form, just like HTML tags. Attributes for CFML tags should follow the same behavior as arguments and variables as seen below. If attributes can all be placed in one line, then do that. However, if they will span and cause breaks, consider breaking the attributes into multiple lines and aligning them to the first attribute.
-- DO THIS -- <cfhttp url="..."> <cfabort> <cfdump var="#session#"> <cfhttp url="#urladdress#" method="GET" resolveurl="Yes" throwOnError="Yes"/> -- NOT THIS -- <CFHTTP> <CFABORT> <CFDump Var="#session#"> -- Unecessary Multi Line -- <cfhttp url="#urladdress#" method="GET" resolveurl="Yes" throwOnError="Yes"/>
They should be descriptive lowercase single words, acronyms or abbreviations. If multiple words are necessary they should follow camel case with first letter lowercase. Examples:
-- DO THIS -- niceLocation = "Miami"; results = ""; avgSalary = "323"; -- NOT THIS -- NICELOCATION = "Miami"; Results = ""; average-salary = "323";
They should all be in upper case separated by underscores "_". Examples:
-- DO THIS -- INTERCEPTOR_POINTS = ""; LINE_SEP = "-"; MAX = "123"; -- NOT THIS -- interceptor-points = ""; line_sep = "d"; max = "123";
This section indicates some best practices when creating ColdFusion components. It also introduces several areas for development convetions and guidelines.
Components can have instance data that can be placed in two different visibility scopes: private and public. Private variables are declared in the variables scope and public variables in the this scope. This means that the variables in the this scope will be available for modification from the outside world, while the variables scope is not accessible from the outside world directly. The only way to manipulate these private variables would be through methods that your object will expose to the outside world. This is called data-hiding or encapsulation, which you can read in the following point.
<cfset address = CreateObject("component","address").init()> <cfset instance = structnew()> <cfset instance.firstname = "Luis"> <cfset instance.lastname = "Majano>
Note: The variables scope is the default scope in ColdFusion and therefore it is implied, so do not write it out.
Be very careful of when to make internal properties public as you will be violating encapsulation (look at next point). One of the best reasons for making variables public is if they do not change and can act like static constants. If your variable does not meet this criteria, then DO NOT expose it as public.
<cfset this.OPTIONS = "add,remove"> <cfset this.NOT_FOUND = '_NOTFOUND_'> <cfset this.EVENT_CACHEKEY_PREFIX = "cboxevent_event-">
Note: Even though you are treating these variables like final static variables they CAN still be modified as ColdFusion does not support final or static variables. It is more of a convention and agreement to use this approach.
For instance data, create a "virtual scope" inside the variables scope. For example, in the first line of your init() method you might have:
<cfset instance = structNew()>
You can then reference your instance data as "instance.foo". The benefit is that it separates your stateful instance data from the variables scope, which also contains references to all methods (public AND private) as well as a reference to "THIS" -- this becomes really handy if you need to do things like return a memento of your instance or do global operations on all instance data. It's also very handy if and when you need to clone a CFC, since you can move state data in one chunk rather than worrying about which keys in variables are your instance data and which are built-in keys. You would likely still use the variables scope for non-stateful instance data such as references to other CFC's your instance uses. ColdFusion 9 has the potential to change this best practice as they allow for the creation of instance data via the cfproperty tag and placing them in the variables scope. They also create implicit getters/setters for these properties, which saves time. Therefore, this best practice could potentially be removed in later editions.
Below is a way to implement the state pattern on an object to get and set an entire object's instance data.
<--- Getter/Setter memento ---> <cffunction name="getMemento" access="public" returntype="struct" output="false" hint="Get the memento"> <cfreturn variables.instance> </cffunction> <cffunction name="setMemento" access="public" returntype="void" output="false" hint="Set the memento"> <cfargument name="memento" type="struct" required="true"> <cfset variables.instance = arguments.memento> </cffunction>
Encapsulation provides the basis for modularity by hiding information from unwanted outside access and attaching that information to only methods that need access to it. This binds data and operations tightly together and separates them from external access that may corrupt/change them intentionally or unintentionally. Encapsulation is achieved by declaring variables as private in a CFC (variables scope). This gives access to data to only public/package member functions of the CFC. You can then create their mutators (setters) and accessors (getters) via public methods. Some benefits of encapsulation are:
-- THESE ARE GOOD -- <cfset stuff = myCFC.getStuff()> <cfset myCFC.setStuff(stuff)> -- THESE ARE BAD -- <cfset stuff = myCFC.stuff> <cfset myCFC.stuff = stuff>
Always have an init() method that acts as your constructor and returns this (unless you are building a web services/flash remoting facade). Even if the method has a simple return statement, it is always best practice that every object have a constructor method.
<cfcomponent name="Converter" output="false"> <cffunction name=”init” access="public" output=false returnType=”Converter” hint=”Constructor”> <cfreturn this> </cffunction> </cfcomponent>
If you are using inheritance then you must call the parent constructor by accessing the super scope.
<cfcomponent name="Converter" output="false" extends="BaseConverter"> <cffunction name=”init” access="public" output=false returnType=”Converter” hint=”Constructor”> <cfset super.init()> <cfreturn this> </cffunction> </cfcomponent>
Always, always, always use "var" for local variables inside your methods, including ALL loop counters, temporary variables, queries, etc. This is called “var scoping”.If you do not do this, your component will not be thread-safe. This means that if somebody persists (stores) this component in memory, succinct calls can and will override variables and create all sorts of memory problems. There is an open source project called varscoper that can check all of your components for var scoping issues, even if they are using cfscript. ALWAYS VAR SCOPE.
Var scoping applies to methods inside components and also to UDF's in order to comply with best practices.
-- DO THIS -- <cffunction name="myFunction" access="public" returntype="void" output="false" hint="This methods does nothing"> <cfset var i = 0> <cfset var qGet = ""> <cfquery name="qGet"> </cfquery> <cfloop from="1" to ="20" index="i"> </cfloop> </cffunction> -- NOT THIS -- <cffunction name="myFunction" access="public" returntype="void" output="false" hint="This methods does nothing"> <cfquery name="qGet"> </cfquery> <cfloop from="1" to ="20" index="i"> </cfloop> </cffunction>
Always (with rare exceptions) use output="false" in your cffunction and cfcomponent tags. Do not output directly to the buffer inside a CFC method; instead return a string from the method. The main reason is that you don't want to break encapsulation. By outputting directly to the output stream you assume knowledge of the external environment of the CFC. However, if you return a string then you get the exact same behavior when you do #myCFC.someHTMLGeneratingMethod()# but you gain the advantage of not assuming that's how your method will be used. For instance, what if the method that returns the string is used inside of a big cfscript block where someone is building a string via concatenation? Everything will break.
<cfcomponent output="false"> <cffunction name="getContent" output="false" access="public" returnType="string"> <cfreturn "This is my content"> </cffunction> </cfcomponent>
Document your component, methods and arguments by using the hint attribute in those tags. This will help fellow developers and even you, when determining what a method, argument or component does, can do, etc. You can also use several tools to create cfc documentation according to your component metadata. This should be done for the following tags:
Use the returnType attribute of the cffunction tag and the type attribute of the cfargument tag to add documentation and for runtime type checking. Also remember that void is the return type when your method call does not return anything.
Duck Typing is when you use the return type or type of any in a cffunction or cfargument tag. This is a useful technique when dealing with a dynamic language such as ColdFusion. This means that the argument or object returned can be ANYTHING, which then your caller needs to determine what to do with it and what it is based on pre-determined conventions. A side effect of not using a strong type is a speed enhancement, since ColdFusion does not check the validity of the types. This side effect should not be used to get more performance, unless absolutely necessary.
This dynamic nature of arguments and return types brings forth great power in a dynamic language, but it also opens holes for runtime exceptions. However, thanks to unit testing, these runtime exceptions should be minimized. So as a followup guideline to duck typing is that you must have unit tests for these components.
Do not directly reference external scopes, i.e.: session/application/client/server/request variables, inside a CFC. If you reference external scopes you will be breaking the encapsulation and cohesiveness of the component at hand. You have now binded the component to an external scope that must exist in order for this component to work. This also provides difficulties when unit testing.
However, the one exception to referencing external scopes is when building facades, especially for web services/flash remoting, in which case ALL references to shared scope variables should be encapsulated within the facade. This means, for instance, passing in the dsn instead of referring to application.dsn when doing database queries. If you do not know what a facade is, then please search for facade pattern to learn more about it. It basically encapsulates a shared scope such as application,session, etc into a CFC.
Note: This is not a golden rule, but try to adhere to it
Getters or accessors are simple methods in a CFC that can access instance data. These are very simple methods that basically just retrieve data and follow a convention: get{property name}()
// Property name is firstname <cffunction name="getFirstName" output="false" access="public" returnType="string"> <cfreturn instance.firstName> </cffunction>
Setters or mutators are simple methods in a CFC that can modify instance data. These are very simple methods that basically just set data and follow a convention: set{property name}(value). They have one argument with the same name as the property or a generic name, i.e: value. The return type for such methods is void
// Property name is firstname <cffunction name="setFirstName" output="false" access="public" returnType="void"> <cfargument name="value" type="string"> <cfset instance.firstName = arguments.value> </cffunction>
In general, non-required arguments of a CFC method should have a default value specified, unless you will be programmatically checking for existence using structKeyExists(arguments, "key").
<cfargument name="isReadOnly" type="boolean" default="false" required="false"> <cfargument name="maxRows" type="numeric" default="10" required="false">
Use inheritance only when describing an "is-a" relationship, not for a "has-a" relationship (composition) or for code reuse only. For a nice summary, visit http://cnx.rice.edu/content/m11709/latest/
Do not use a component as a huge glorified set of methods and call that code reuse. Components are synonymous to objects, they should have an identity upon themselves. Put in practice your Ontology skills and define what the CFC will do for you and what is their identity.
Always prefer object composition over inheritance. This is where another component is created or injected as a property of the object at hand. There are several reasons of why to choose composition over inheritance in order to make your designs more flexible and not coupled at compile time, which inheritance does. Composition brings in functionality at runtime as you can switch implementations, etc. Some resources are:
It would be of best practice to add a document header that can follow the following standard or something similar to any CFC or template:
<------------------------------------------------------------------------
Author : Luis Majano
Date : 3/13/2009
Description :
This is my component that does fantastic stuf!!
----------------------------------------------------------------------->
ColdFusion variables must be scope according to where they are created and located unless for good dynamic reasons. This will improve performance and readability when diagnostics are needed. The default scope that can be omitted is the variables scope, which is by default implied. Even scoping variables/columns in queries is mandatory to avoid collisions.
-- DO THIS -- <cfoutput>#url.name#</cfoutput> <cfoutput query="qCountries"> <li>#qCountries.name#</li> </cfoutput> -- NOT THIS -- <cfoutput>#name#</cfoutput> <cfoutput query="qCountries"> <li>#name#</li> </cfoutput>
ColdFusion treats separate applications by looking at the application.cfc name property. This has to be unique, especially when dealing with multiple applications in a server. If collisions occur, then scopes will be shared and nasty things could happen. It is imperative to distinguish applications uniquely. This will even be more of a thing to watch out for in future versions of CF. Therefore, try the following technique:
this.name = "MyApp_" & hash(getCurrentTemplatePath());
This will provide you with a unique hash for your application according to where it is located on the server. This way, no collisions will apply.
Be very careful when using session scoped variables in your applications, especially if used under heavy load or high traffic. Remember that session variables are per user and consume memory. If they are misused or you issue session variables randomly, your RAM usage will increase exponentially. Consider always having low timeouts for your session scopes and always disallowing bots to have session variables.
Use cflock whenever you need to make your code thread safe. This applies to variables in shared scopes such as: server and application scope. You sometimes want to even lock session scope if you are working with framesets, but usually locking session scope is not necessary anymore. Also remember to use cflock whenever you are accessing shared resources, such as file operations, cache operations, etc.
-- DO THIS -- <cflock name="FileOperation" timeout="20" throwOnTimeout="true"> <cffile action="write" file="#filePath#" output="#content#"> </cflock> <--- application scope is exclusively locked on the cache ---> <cflock type="readonly" scope="application" timeout="10" throwOnTimeout="true"> <cfset myVar = application.cache.getValue("x")> </cflock> <cfquery name="variables.qUser" datasource="#request.dsn#"> SELECT FirstName, LastName FROM Users WHERE UserID = #request.UserID# </cfquery> <cflock scope="application" timeout="2" type="exclusive"> <cfset application.qUser=variables.qUser> </cflock> -- NOT THIS -- <cflock name="FileOperation" > <cffile action="write" file="#filePath#" output="#content#"> </cflock> <cfset myVar = application.cache.getValue("x")> <cflock scope="application" timeout="2" type="exclusive"> <cfquery name="application.qUser" datasource="#request.dsn#"> SELECT FirstName, LastName FROM Users WHERE UserID = #request.UserID# </cfquery> </cflock>
There will be cases where you need to do a double test in order to avoid race conditions on shared resources. This strategy can be applied when you need to test, for example, if a resource is created, an object is configured, etc. What this strategy does is provide two if statement criterias that can verify behavior on the resource, squished between a cflock tag. This prevents threads that have already entered the locking stage and are waiting execution, to re-execute the locked code.
-- DO THIS -- <cfif structKeyExists(application,"controller")> <cflock name="mainControllerCreation" timeout="20" throwOnTimeout="true" type="exclusive"> <cfif structKeyExists(application,"controller")> <cfset application.controller = createObject("component","coldbox.MainController").init()> </cfif> </cflock> </cfif> -- NOT THIS -- <cfif structKeyExists(application,"controller")> <cflock name="mainControllerCreation" timeout="20" throwOnTimeout="true" type="exclusive"> <cfset application.controller = createObject("component","coldbox.MainController").init()> </cflock> </cfif>
As you can see from the previous code snippet, if you do not have the double if statements, then code that is waiting on the lock, will re-execute the creation of the controller object. Therefore, since we can test the resource state, we can provide a multi-thread safety net.
Pound signs are most often used to output variables to their set values or evaluate them. There are many places where you DO NOT need to place hash signs. This only delays the evaluation and is not best practice. Most likely you will only need to use pound signs when using cfoutput or when dealing with certain tag attributes that require the evaluation of a variable.
-- DO THIS -- <cfset name = request.firstname> <cfif isValid></cfif> <cfset SomeVar = Var1 + Max(Var2, 10* Var3) + Var4> -- NOT THIS -- <cfset name = #request.firstname#> <cfif #isValid#></cfif> <cfset #SomeVar# = #Var1# + #Max(Var2, 10* Var3)# + #Var4#>
Try to always use tabs and spacing correctly when spacing code and formatting it. Always indent your tags when they are nested, it provides readability and consistency.
-- DO THIS -- <cfif isValid> <cfset test = luis> </cfif> <cffunction name="getValue" access="public" returnType="any" output="false"> <cfreturn test> </cffunction> -- NOT THIS -- <cfif isValid> <cfset test = luis> </cfif> <cffunction name="getValue" access="public" returnType="any" output="false"> <cfreturn test> </cffunction>
-- DO THIS -- <cfif structKeyExists(arguments,"car")> </cfif> -- NOT THIS -- <cfif isDefined("arguments.car")> </cfif>
-- DO THIS -- <cfset value = form["field#i#"]> -- NOT THIS -- <cfset value = evaluate("form.field#i#")>
-- DO THIS -- <cfif len(firstName)></cfif> <cfif NOT obj.isEmpty()></cfif> <cfif query.recordcount></cfif> <cfif arrayLen(myArray)></cfif> -- NOT THIS -- <cfif firstName eq ""></cfif> <cfif obj.isEmpty() eq false></cfif> <cfif query.recordcount gt 0></cfif> <cfif arrayLen(myArray) gt 0></cfif>
-- DO THIS -- <cfoutput> <html> <head> #head# </head> <body> #leftBar# #content# #footer# </body> </html> </cfoutput> -- NOT THIS -- <html> <head> <cfoutput>#head#</cfoutput> </head> <body> <cfoutput>#leftBar#</cfoutput> <cfoutput>#body#</cfoutput> <cfoutput>#footer#</cfoutput> </body> </html>