Best Practices in ColdFusion Development
By Eben Hewitt
Date: Nov 20, 2001
Article is provided courtesy of Prentice Hall Professional.
Eben Hewitt offers a collection of best practices to follow when using ColdFusion - neatly organized into topics such as file use, speed and performance, database issues, and security.
This article is a compendium of best practices for working with ColdFusion, divided into the following useful categories: files, speed and performance, database issues, security issues, and general issues. That means that these are guidelines for you to follow as you develop. While your code can work if you don't do these, it might not perform as quickly, and it might be harder to follow. I urge you to come up with your own best practices as you work and pass them on to others for consideration.
Files
Writing ColdFusion templates is relatively easy. This is terrific because it means that you can get complex applications up quicklyprobably a fraction of the time it would take you in JSP, for instance. That can make it tempting to rip through your code baseperhaps some developers don't take enough time in the writing phase. So here are a number of easy tips that can keep your code smart, robust, readable, and reliable.
Comment Your Code
At the top of every template, include the following information as comments:
Template name
Author's name
Date created
Date last modified
Use (what pages call the template, the chain of events that the user might set in motion to call this particular page)
Purpose (what does this template do?)
Here is an example:
<!----- Template: act_AdAffiliateInsert.cfm Author: Eben Hewitt, eben@CoreColdFusion.com Purpose: Inserts a new Ad Affiliate profile into the Users table. Use: called from Step 2 of act_AdAffiliateSignUp.cfm Date Created: 11/22/01 Date Last Modified: Thursday, November 22, 2001 3:28:14 PM ----->
You know exactly what will be impacted if you change something, and you can easily debug this and its constellation templates. I urge you to follow this practice.
Always Use Application.cfm and OnRequestEnd.cfm
Even if you do not have a use for these files, ColdFusion will go hunting for them on every request. Your page-processing time will speed up just by including empty files with these names in your site folder.
NOTE
Let us now be disabused of a commonly held yet fallacious belief: The truth is that ColdFusion will search all the way up to the root of your server's hard drive for an application.cfm file. That is, it stops only when it gets to C:\. Don't make it do all of this work, and don't let your applications be potentially exposed to a wandering application.cfm somewhere up the tree.
Set Defaults Only Once
This rule is stated as a corollary of the above because it involves application.cfm. Many developers like to set application-wide variables, such as a title or a datasource in application.cfm. But they don't check to see if their variables are defined, so they end up running the same code over and over again. Use a check to see if a variable has been set. If it has, then move on. This will be slightly faster, and it is better programming form.
Like this:
<cfif NOT isDefined("application.Datasource")>
<cfset application.Datasource = "MyDatabase">
</cfif>
Speed and Performance
The following items are miscellaneous factors that can speed your performance and encourage durability and scalability in your applications.
Rewrite Your Page If It Is Slower Than 2000 Milliseconds
Two thousand milliseconds (two seconds) is the limit. If you use debugging output (as you should), then you will be able to gauge how long it takes CFAS to process your request. I did not write "page" because you may have many levels of included files, queries, and so on that all have to run to make up a single request. If any request takes longer than 2000 milliseconds, it's a candidate for rewriting. We can generally exclude from this rule operations that you know will take a long time and rely on external factors, such as a <CFHTTP> call.
Use <cfswitch> Instead of <cfif>
This rule makes sense only on two occasions (though these are frequently confronted):
When you have a specific expression that you can evaluate against
When you have a set of more than three <cfelseif> clauses
Your code will perform faster.
Use <cfscript> Instead of Three or More <cfset>s
The reason is this: When you use cfscript, the entire block gets sent to the engine at once. So, ColdFusion has to make only one read. When you send three or more <cfset> statements, ColdFusion gets to interpret them once each, or three times. Therefore, it's faster and cleaner.
Eschew IIF
The "immediate if" function (IIF) will process nearly two times slower than a cfif/cfelse block that accomplishes the same thing. Moreover, these are harder to read.
Database Issues
Working with relational databases is a central facet of ColdFusion development. Because all too often the person creating the database is the same person shouldered with development work, it's important to understand the implications of database calls in your applications. Here, we'll look at a few tips to improve performance and keep your applications solid.
Let the Database Do the Work
If you are going to perform some aggregate function on data that resides in your database, do it there. That's what it is made to do. Your code will run faster. You can do formatting of data in your code, however, so you have more control.
For instance, SQL includes a number of aggregate and scalar functions that are also available in ColdFusion.
Some of the functions that are replicated include most mathematical functions, date and time functions, trim string functions, and more. Remember that if you employ conditional logic or other ColdFusion code inside your SQL statement, ColdFusion will create the statement first and then will send the SQL statement off to the database. You do not incur a longer database connection time by writing complex CF code in your <CFQUERY>s.
Use the maxrows Attribute of the <cfquery> Tag
If you know how many rows you're supposed to get (say, you're checking for the existence of a matching username and password that should be unique, and therefore you have only one row returned), set the maxrows attribute to 1, and give your database a break.
Don't Let Query-of-Queries Capability Make You Lazy
You might find it tempting with the query-of-queries capability new in ColdFusion to think about loading your entire 3,000-row product table into Web server memory and start querying that for your e-commerce application. This is not careful designing or prudent use of resources. Determine what you need, and use only that. Determine your trade-offs, and act according to greatest benefit (do some tests).
There are major benefits to the new query-of-queries capability; among these are the ability to perform cross-datasource joins, cross-datasource unions, and in-memory denormalization. And, of course, it is faster to retrieve memory-resident values. All of these can really bring together enterprise business applications. That doesn't make query-of-queries a license to slouch, though.
Security Issues
ColdFusion Administrator has a number of features, such as Advanced Security, built in to protect the development or hosting environment on the server. But what about your code? Here are a few handy things to keep in mind as you write to protect the integrity of your applications.
Do Not Use Hidden Fields to Pass Sensitive Information
You might be thinking, "Duhsomeone can do a view source in the browser and read the information." I'm not talking about people reading information that you do not want them to read (that should be obvious). I am talking about allowing them to modify your data.
Let's say that you have a shopping cart that passes information about the products that your user is purchasing, including the prices, passing in hidden form fields. If I view the source of this page and save it as an .htm file to my desktop, I can then edit the file. Once I have changed the prices to whatever I want, I can then pass the form to an absolute URL (instead of the relative URL specified in the form action attribute), and I can continue to check out normally.
This is not specifically a ColdFusion issue because you might use hidden form fields in this way on an ASP, CGI, or any other kind of site. I'm not saying that don't use hidden fields. I'm saying that you should be careful what you pass in them.
Use Code for Security Contexts
It's a better idea to write your authentication frameworks into your code. If you overuse the CF administrator's security contexts, you can get into porting hassles if you ever want to move your application. Avoid proliferating <cfauthenticate> tags.
General Issues
Finally, here is a miscellaneous set of tips and tricks to enhance your applications' processing. Try keeping a notebook of your own as you notice what improves performance, security, and scalability in your own development to create your own best practices.
Use <cflock> Around <cfhttp> Calls
The <cfhttp> tag is used in a single thread by ColdFusion. Locking them will help cut down on p-code errors.
Use Conditional Logic Logically
This may sound redundant, but it isn't. Conditional logic is a lot more than a couple of if/else statements. You must distill in your applications the sine qua non of each block, and write your logic from that. You shouldn't have to check for more than two circumstances or current states at any given time, and you can often get away with one. The way you do this is by contextualizing your code. Nest your logic in a thoughtful, mathematical manner, and your pages will fly. They will also be simple to read and debug.
ColdFusion's short circuit logic is an effort at speeding things up. You can do your part.
Copy Session and Application Variables into the Request Scope
It is imperative that you write <cflock>s around references to session and application variables. So, instead of having to make sure that you've locked everything in all of the many places where you might refer to variables in both of these scopes, copy them to the REQUEST scope. In application.cfm, write this:
<CFLOCK TYPE="ReadOnly" SCOPE="Application" TIMEOUT=30> <CFSET Request.App = Duplicate(APPLICATION)> </CFLOCK> <CFLOCK TYPE="ReadOnly" SCOPE="Session" TIMEOUT=30> <CFSET Request.Ses = Duplicate(SESSION)> </CFLOCK>
What you're doing is copying the structure that holds all of the application variables into the request scope, and then doing the same for session variables. Because you do it in application.cfm, you can reference these variables without having to lock them. But they act exactly the same way (as long as you copy any changes back again into the application and session scopes at the end of the page). Say that you have a database that you set in application.cfm to be your global datasource, like this:
<cflock type="EXCLUSIVE" timeout="30" throwontimeout="Yes"> <cfset Application.Datasource = "MyDatabase"> </cflock>
Then, when you later try to refer to it, you've got to lock that, too:
<cflock type="READONLY" timeout="10" throwontimeout="Yes"> <cfquery name="getUsers" datasource="#Application.Datasource#"> </cflock>
This can get silly after a while.
Using the above trick, you can just refer to your variables like this instead:
<cfquery name="getUsers" datasource="#Request.App.Datasource#">
Note that you can also use the function StructCopy(APPLICATION) to accomplish the same thing, if you prefer. And remember that you have to copy any potentially changed variables into their proper scope once you've had your way with them in your page. So, in OnRequestEnd.cfm, I usually do this:
<cflock name="RequestToApp" timeout="20" throwontimeout="Yes" type="READONLY"> <cfset Application = StructCopy (Request.App)> </cflock>
My new book, Core ColdFusion 5, gives you the nitty-gritty on all the aspects of ColdFusion development that you need to succeed. It contains a wealth of complete applications, including an e-commerce site and many more best practices tips. Pick up a copy today.