A Beginners Guide To Coldfusion and CFCs

Posted By : todd sharp Posted At : August 21, 2006 12:50 PM Posted In: ColdFusion

21

I'm currently in the middle of rewriting some CFCs that I wrote over a year ago for my intranet at work. I thought I'd put together a list of the most common mistakes that I've found in my code to help someone just starting out with Coldfusion or CFCs avoid making them too. As I've stated many times, I'm no expert, but I think that I've learned a lot over the past year. Many of these mistakes are just down right embarrassing, but I think they are worth sharing.

Before I start, let me point out that there are a ton of good CFC resources out there. This post is not meant to replace and/or improve upon any of them. Rather, the purpose is to point out some common mistakes that beginners make when working with CFCs.

Without further rambling, here is my biggest mistakes/lessons learned from revisiting my code.

#1. ALWAYS var your var's.

I'm not the first person to put this at the top of the list, and I certainly won't be the last. I can't tell you how many un "var'd" vars I found in my CFCs. The biggest thing I overlooked were queries. Develop this good habit early when starting to work with CFCs. Go back and look at your code after you've written it - then look at again - then again.....

#2. Redundant Code

Believe it or not, I found a few methods that returned exactly the same thing in separate CFCs (of course, this could probably be avoided if I followed #3 and #4). The beauty of CFCs is that a single, redundant function can be created in one place and called from pretty much anywhere.

#3. Bad (or No) Documentation

Here's another point that a lot of experts will point out to you, and it couldn't be truer. Take advantage of the built in documentation attributes (like descriptions and hints). Why bother you say?

  • They will make your life easier if you ever have to when you revisit your code to figure out what is going on.
  • They will make your coworkers life a little easier when they have to figure out what's going on with your ugly code.
  • As Joe Rinehart points out (see resource links below), you can automagically create impressive, snazzy documentation.
#4. Too Many Components

The experts will warn of having not enough components (AKA the "uber" cfc), however as a beginner, avoid the temptation to create too many components. Combine functions into a single component where it makes sense.

#5. Too Many Methods (Lack of Encapsulation)

If you are calling multiple functions from the same place, consider combining functions and returning a single encapsulated struct. Or if you need to keep the queries separate for some reason, create a function that calls the individual functions and encapsulates the results. For example:

<!--- simple example --->
<cffunction name="getSomething" returntype="query">
    <cfset var getSomeData = "">
    <cfquery name="getSomeData">
    select aColumn
    from aTable
    where aColumn = aCondition
    </cfquery>
    <cfreturn getSomeData/>
</cffunction>

<cffunction name="getSomethingElse" returntype="query">
    <cfset var getSomeMoreData = "">
    <cfquery name="getSomeMoreData">
    select anotherColumn
    from anotherTable
    where anotherColumn = anotherCondition
    </cfquery>
    <cfreturn getSomeMoreData/>
</cffunction>

<cffunction name="getBoth" returntype="struct">
    <cfset var results = structNew()>
    
    <cfset results.a = getSomething()>
    <cfset results.b = getSomethingElse()>
    <cfreturn results/>
</cffunction>
#6. Lack of Helper (Utility) Functions

This may seem basic, but if you have a specific data manipulation function or query that needs to run in multiple places, create a utility function to handle the dirty work. You can store utils in a separate cfc and create that component within your component so that you can call those utilities whenever necessary.

#7. Style Hopping (Between differing conventions)

This is not only an issue that plagues CFCs, but all code really. When learning to program it's a good idea to establish a naming convention or style and stick with it. Code can be pretty hard to read if you keep switching between styles. I found a lot of methods that used the_variable_name while others used theVariableName or TheVariableName. If your company dictates that you use a specific style then your choice is easy. I have taken a liking to theVariableName (known as camel case or "lower" camel case since the first letter is always lower case). I don't care what style you pick, just pick a single style and stick with it.

#8. Be Case Sensitive - Always

Similar to naming conventions and coding styles, here is another issue that is not exclusive to CFCs. I've found it to be a good habit to always pay attention to case when writing code. I now try to always use the same case when referring to a variable (which should be easy if you follow #7). Some people may argue that this practice is irrelevant in Coldfusion since it is not case sensitive, however, when integrating Javascript or Actionscript or any other case sensitive language into your application you will be awfully glad you developed the habit of paying attention to case.

#9. Bad Naming

Naming a query or method "getData" is a bad idea in your CFCs and code in general. Try to be specific and descriptive when naming. Good naming is like built in documentation for your code. It's much easier to figure out what data the query "getCategories" or "getUserData" returns as opposed to "getStuff".

#10. returntype="any"

It's just a bad idea to leave this returntype. This returntype can be helpful when you start to write your method and are unsure what you will be returning (a query or a struct?), but when you're satisfied that your method is complete for the time being, go back and check your returntype and set it to the appropriate type. While your checking your returntype, go check your vars again. Are they all var'd? I didn't thinks so ;)

A few more notes worth mentioning. CF - like most programming languages - offers developers many different ways to solve the same problem or perform the same task. I shy away from being the type of person that offers up a single solution for a specific task. I really believe that when learning a new language, programmers should try several approaches to solving the same problem. Here is a simple example of several different approaches performing the same task. For the purpose of this example, assume that you have a CFC named taskBlaster and within that CFC a method named getTasks that accepts a single argument, the userID. Assume that getTasks will return a query containing all tasks for the given userID. Method 1:
<cfset theUser = "12345">
<cfset task = createObject("component", "taskBlaster")>
<cfset userTasks = task.getTasks(theUser)>
Method 2:
<cfset theUser = "12345">
<cfinvoke component="taskBlaster" method="getTasks" returnvariable="userTasks">
    <cfinvokeargument name="userID" value="#theUser#">
</cfinvoke>
What is the difference between the two techniques? Nothing really. There may be some minor performance differences, but as far as functionality goes the two methods perform the exact same task. I suggest you try different approaches and see which one you like best. Don't just pick a techniques because you heard so-and-so says it's the best technique to use. This may be a controversial statement for those coders who have been coding for a long time and prefer the methods that they have gotten accustomed to using. This is also an unpopular approach since it leads to code that can be difficult to read. However, remember that the code we write does not ever have to be permanent. Revisit your code occasionally! We're often too busy to revisit our code, but I think we should all insist on making the time once in a while. You'll be surprised how many times you'll shake your head in amazement that you wrote 25 lines of code for a problem that could have been solved with one CF function that you didn't know existed when you originally wrote the code. In conclusion, if you are a beginner to CFCs or CF, I suggest that you read blogs daily hourly! There is a wealth of CF knowledge online, and most of it is free for the taking. If you read some of the blogs of the more well known members of the CF community, occasionally you'll notice that they don't always agree on techniques or methodologies. My suggestion is to absorb each side of debate and formulate your own decision rather than side with one or the other because you generally agree with their methods. Here's a few helpful CFC articles/blog posts:

Joe Rinehart: CFCs - Ten Spontaneous Tips

Raymond Camden: CFC Resources. A comprehensive list CFC Resources (including Joe and Rob's articles).

Rob Brooks-Bilson: Top Ten Tips for Developing ColdFusion Components

Mike Schierberl: varScoper (utility to identify variables created within a cffunction that don't have a corresponding (cfset var) statement)

Comments (21)

Rob Brooks-Bilson's Gravatar Hi Todd,

Nice post. Many useful tips there.

This list is a bit old now, but there are still many good tips:

http://www.oreillynet.com/pub/a/javascript/2003/09...

Mike Schierberl's Gravatar Great post, I really like seeing people stress the importance of unscoped variables. Definitely the most important thing to keep in mind with cfcs. For anyone that's interested, I created a utility that can identify unscoped variables(no "var") within a cfc. <a href="http://www.schierberl.com/varscoper">Take a look at it here.</a> (Sorry for the shameless plug)

Raymond Camden's Gravatar While I'm in the camp of "Use the right returnType" as well as "Use the right cfargument type" - I believe Joe, and others, ahve said there is _significant_ performance penalties for this. In fact, Joe removed _all_ typing from MG and got a huge speed boost. (If I remember right.)

charlie griefer's Gravatar ok but to present another side of the coin...

specifying returntypes and argument types are self documenting and (IMO) assist in speeding things up during the ongoing maintenance phase of a project. whether it's another coder or you yourself going back into your code a few months down the road, it does make things that much easier to understand.

my understanding of what Joe did for MG (and what sean did for FB5 if I'm not mistaken) is that they modified core files which (in theory) are not supposed to be modified by the end user. if (in theory) these files are not meant to be modified, there's no need for the extra self-documentation and by all means, tear 'em out and speed things up.

i figure before i get flamed, i should be a little more explicit in stating that I'm not disagreeing with what Ray said. removing the returntypes and argument types will speed up your application. i'm not debating that at all. just saying that the "cost" involved is more ambiguous code which might be harder to maintain.

so...personal preference i suppose. some folks like to optimize the heck out of their code to reap the speed benefits. others will forego speed improvements (to an extent, at least) to write code that (in their opinion) is more easily maintainable.

so um...yeah. that's the other side of the coin :)

Raymond Camden's Gravatar That is a -very- excellent counterpoint. I think you are absolutely right. There is a difference between code like MG/MACHII/FB and more "normal" client code (and by client I even mean CFCs for your applications). As with everything, there are no hard and fast rules. I think you would need to weight the speed benefit over the cost of documentation.

However - two things:

a) I'm definitely NOT one of those "Let's do X cuz its 1 ms faster" type people. I think it is silly. But again, my impression was that the performance gain was _extreme_ (ie, not something you can ignore)

b) It's not like you give up documentation. You could very easily document every method right above it, using something like JavaDoc (or UDFDoc).

todd's Gravatar Rob:

Your article is an excellent resource! I can't believe I never noticed that link on Ray's page before (Come clean Camden, you just added it, didn't you?)

Mike:

I honestly intended to refer to your tool when I sat down to write this yesterday. Can I blame my 5 month old for distracting me?

I've updated my post to include links to both of these resources.

Charlie & Ray:

Thank you for illustrating my point of varying opinions on techniques and approaches ;) You both bring up valid points, both of which have specific benefits and downfalls that a beginner (or even an expert) should weigh before choosing their approach. I think for a beginner though, specifying an explicit type for arguments and returntype for functions is a good idea. Besides internal self documentation, CF will validate the arguments against those types and throw an error if they aren't valid. To me that's definitely a good thing.

Jeff Houser's Gravatar [quote]
This returntype can be helpful when you start to write your method and are unsure what you will be returning
[end quote]

If you are starting to develop a method / function and do not yet know what you will be returning, then you need to stop coding and put your 'application design' hat back on. Figure out what this function is going to do and what it should return before you start coding away.

That said, I've been experimenting with duck typing concepts, and as such have been using returntype="any" quite a lot. I'm pretty sold on the approach, as it makes apps easier to develop.

Jacob Munson's Gravatar "#1. ALWAYS var your var's."

I thought I'd read or heard people say that you don't need to var scope /all/ your variables in a CFC, just those that are internal only. For example, if you are updating an application scope variable from your CFC, you aren't going to var scope that, are you? I think I heard someone say that if a variable would normally fall into the variables scope (or the controversial this scope), you'd want to var it. Am I off base?

One the return type debate...why not go ahead and strictly type all your stuff, and then use an Ant script (or whatever language fits your deployment methods) to strip them on the move the production? Because outside of protecting against sql injection, isn't the main reason for using types to avoid runtime errors? And if you are fully testing your apps in a dev environment, the typing could be invaluable in that case, but then for performance you'd remove them from the production code. At least, that makes sense to me.

Raymond Camden's Gravatar Jacob: If you are using a variable that is function local, you need to var scope it. That's what the var scope is for - function local variables. You would NOT var scope Variables or This scope variables. WHile you typically should not use Application (or other outside vars) in a CFC, you would not var scope them either.

The var scope is ONLY for function local variables. THink look counters, query names, etc.

Elliott Sprehn's Gravatar Very nice list of tips for developing CFC's however I have to say I don't agree that you need always specify a returntype for methods or type for arguments. You should always document the assumed return type and argument type, but specifying it in the code is not necessary, worse its unnecessarily limiting.

For example Coldfusion treats Structs and Queries very similarly. So if you're not using a specific feature of the query object (ex. Query.RecordCount) and just iterating over it don't specify any type for the argument. Then in the code check if the parameter is either a query or a struct, and if not throw an ArgumentException. Now you've created a method that works with both structs and queries completely transparently.

I generally use a modified syntax of JavaDoc to document my code, and a parameter like this would be "@param (Query|Struct) What's expected in this parameter".

This is one of the great things about Coldfusion, it allows the developer to be as flexible or as strict with types as they want. Why lock ourselves into the very restrictive typing box of Java? :)

Jacob Munson's Gravatar Thanks for the clarification, Ray. I knew I was probably butchering that stuff, but I couldn't remember exactly what should and what shouldn't be var scoped.

Anyway, I'd appreciate an explanation for why you shouldn't access the application scope from a CFC. If this is part of the MVC 'rules' (which rules are often the subject of debates in the OO community), I thought that the Model is where your CFCs are, and since that's the place that the complex business logic lives, I'd think you'd need to update application variables from there. But I'm probably butchering things again. :\

todd's Gravatar Jacob: Here is how I understand it. Directly accessing the application scope makes your cfc less 'portable' between applications. Application variables can be passed to a method directly as arguments. I suppose application vars could also be passed to a CFC in an init() method and stored in the variables scope as well. This is where I get a little fuzzy though, so anyone feel free to help me out - am I missing something?

Jacob Munson's Gravatar Oh, I think I get it. You're supposed to pass application variables in to the CFC, modifiy the local variable, and then pass that back to the controller which would then update the application variable? I'm assuming you definitely won't want to update the app var from the view, so that only leaves the controller to do the 'dirty work'. That sure seems like spaghetti code to me, if I'm describing it wright. Ok, maybe 'spaghetti code' is too strong of a description, but if I were updating this application with no prior experience with the code, it would probably take me a good 15-20 minutes to figure out where this application var is getting modified. So I'd have to hunt around for the correct controller file, spend a few minutes reading through it to understand it and find the suspect variable, then hunt around for the correct CFC that the controller is calling, reading through that CFC to figure out how it works, and what's happening to the suspect variable, then returning back to the original controller file, reading through it more to find out what it's role is, and finally learning the truth I was looking for. Not to mention how much time it took to code all that in the first place.

Don't get me wrong, I'm not trying to attack MVC or OO. In fact I'm hoping to be able to use FuseBox for my next project at work. But I have always thought it was a contradiction when MVC proponents claim that a big benefit is easy maintenance, when admittedly they are making code and apps a lot more complex (I realize that this is often a fault of the coder, /not/ the framework in question). And yes, I do understand the 'write once' principle, and portability. But I also believe that some people in the MVC/OO crowd take things a bit too far with their zealous efforts to achieve these lofty goals. In other words, I don't want to throw the baby out with the bath water, so I think a simplified version of a lot the MVC beliefs you hear about today is probably the best way to go.

Jeff Houser's Gravatar Jacob,

It sounds to me like you have it right. But, to me that is encapsulation 101; as opposed to spaghetti code.

Each CFC / function / custom tag should be self contained. If they reference a shared scope variable they aren't self contained. Those 'shared scope' values should be passed in as parameters.

A CFC method or function will [most likely] be returning a value. If you are using that return value to change a shared scope variable, you'd do something like this:

<cfset application.myfoo = foofunction(application.myfoo)>

I can't really envision a situation where it's hard to figure out where the value is being changed. ( Although I can envision one where it is hard to figure out what the value is being changed to )

(Disclaimer: None of what I said above is OO or MVC specific; and I agree that a simplified version of MVC would be good )

Michael Dinowitz's Gravatar I am one of those "lets do X for a speed increase" type of guys and removing types and returntypes from CFCs isn't a little speed increase, its a big one. Especially if the types and returntypes your validating are object references. This can run into several hundred milliseconds of savings on some applications and when your using a framework that has overhead to start with, any savings helps. I removed all types and requireds from the cfargument tags and replaced all returntypes (other than void) with "any" on a mach II project I was called in on and the speed increase was very noticable.
There are good reasons beyond speed, but bottom line is that I always use any or void for my return types and never type check (validating really) my arguments using the tag. If I need to validate, I do it by hand so I can control the error and not throw it to the function caller.
But this is a loooong topic that I really have to post one of these days. :)

Jared Rypka-Hauer's Gravatar Hey y'all...

If I may... #11: access="public|private|package"

Having too many public functions can clutter the API of a component and offer a consumer too much power... calling certain functions can really mess up how a CFC is supposed to work, so make sure you've sorted out which methods should be private and make them so. Often I'll start with them all public and go back thru later and mark the ones that don't NEED to be public to private.

Michael/Ray/Charlie/etc.
Yeah, stripping returntypes out of the FB/MG cores made it a LOT faster, like visibly, and it was fast to begin with, so there was a bigtime speed increase. However (someone already mentioned this, I think it was Ray) the cores are pretty safe in terms of type safety simply because nobody's SUPPOSED to mess with the cores.

At the very least these attributes can provide a valuable tool for getting something up and running, so you can make sure you're writing things correctly even if you go back and change required, returnType, type, etc., later on.

Jeff,

I disagree to a certain extent with your commend about needing to put the architect hat back on if you're not 100% sure of what your function will return. I usually start with any because there are times when I'm not sure if I'm going to return a struct or a query, or if I'm going to refactor something into utility functions later, or whatever. Often I'll start with any and then go back thru and change the returnType to whatever it actually turns out to be.

One other nearly-absolute must... adding a dump() cffunction to your cftags/component.cfc... it's save you a great deal of distress later on when you need a peek at your CFC's instance data.

Laterz!

Jacob Munson's Gravatar Jared,

I hate to agree with Jeff...but I'm going to agree with Jeff.

In an ideal world, you've spent enough time analyzing and designing your app, meaning you've done lots of prototyping, user interviews, and diagramming. In this wonderful world, when you finally get to writing code, which comes after the analysis and design phase has fleshed out your coding platform, DB, OS, etc., you will have every ounce of the application defined down to the data types, error messages, GUI widgets, etc.. But this is an ideal world, where none of us over promise in the initial client interviews, and then later have to under deliver. ;)

Sam's Gravatar Two points:

1) I agree with the "you don't need to specify returntypes" crowd. I think if you have automated unit tests, it serves as a good documentation of what the method is expected to return. If you don't, then certainly I agree it makes it more legible to see in the generated docs. But I would also say that anyone reading the code should be able to tell what it is returning.

2) I thought using invoke didn't instantiate an object, whereas create object does. So, if you rely on something happening when the object is created, then using cfinvoke is very different. But, I could be wrong, and in fact, I half expect to be.

Raymond Camden's Gravatar cfinvoke can either make a new ob, or work with an existing one. If you do

cfinvoke component="ray"

it makes a new instance of ray. If you do

cfinvoke component="#ray#"

it uses the CFC defined in the variable ray

Everyone be sure to use Ray! :)

Sam's Gravatar See, I was half-right since I half expected to be wrong!

Sam's Gravatar ... And I suppose it comes from being, as Todd said, one of "those coders who have been coding for a long time and prefer the methods that they have gotten accustomed to using."

Ignorance baby, I've got plenty.