Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Josh Knutson

ColdFusion 10 - Script vs. Tags And My Coding Methodology

By Ben Nadel on
Tags: ColdFusion

I have traditionally been a fan of Tag-based ColdFusion programming. I think that tags are superior when it comes to integrating SQL scripting and HTML templating. With ColdFusion 10, however, I've needed to resort to Script-based programming as a means to include Closures and Function Expressions within my code. As I've made this transition, I've noticed a number of striking differences in my programming methodology. My ColdFusion code now looks a lot more like my JavaScript code; which means less insight and less [data] typing.

NOTE: At the time of this writing, ColdFusion 10 was in public beta.

To demonstrate this difference, let me present an example ColdFusion component in both tag-based and script-based format. This ColdFusion component doesn't really do anything - it's just a means to demonstrate style.

Old.cfc - My Old Tag-Based Coding Methodology

  • <cfcomponent
  • output="false"
  • hint="I provide functionality around a certain set of features.">
  •  
  •  
  • <cffunction
  • name="init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I return an initialized component.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="dsn"
  • type="string"
  • required="true"
  • hint="I am the datasource for database interactions."
  • />
  •  
  • <cfargument
  • name="storage"
  • type="struct"
  • required="false"
  • default="#structNew()#"
  • hint="I am the storage location for widgets."
  • />
  •  
  • <!--- Set up the internal component properties. --->
  • <cfset variables.dsn = arguments.dsn />
  • <cfset variables.storage = arguments.storage />
  •  
  • <!--- Work with some random local variables. --->
  • <cfset local.startUpDate = dateAdd(
  • "s",
  • (getTickCount() / 1000),
  • "1970/01/01"
  • ) />
  •  
  • <cfset local.startUpDate = dateConvert(
  • "utc2local",
  • local.startUpDate
  • ) />
  •  
  • <cfset variables.startUpDate = local.startUpDate />
  •  
  • <!--- Return this object reference. --->
  • <cfreturn this />
  •  
  • </cffunction>
  •  
  •  
  • </cfcomponent>

Most of you are probably familiar with the above code. It's the reason that plugins like "De-Benification" exist. But now, take that component and compare it to the script-based component below:

New.cfc - My New Script-Based Coding Methodology

  • <cfscript>
  • // NOTE: CFScript tag added purely for Gist color-coding. Remove.
  •  
  • component
  • output="false"
  • hint="I provide functionality around a certain set of features."
  • {
  •  
  •  
  • // I return an initialized component.
  • function init( dsn, storage = structNew() ){
  •  
  • // Set up the internal component properties.
  • variables.dsn = dsn;
  • variables.storage = storage;
  •  
  • // Work with some random local variables.
  • var startUpDate = dateAdd(
  • "s",
  • (getTickCount() / 1000),
  • "1970/01/01"
  • );
  •  
  • startUpDate = dateConvert( "utc2local", startUpDate );
  •  
  • variables.startUpDate = startUpDate;
  •  
  • // Return this object reference.
  • return( this );
  •  
  • }
  •  
  •  
  • }
  •  
  • // NOTE: CFScript tag added purely for Gist color-coding. Remove.
  • </cfscript>

Two very different styles! When I started writing with CFScript syntax, my ColdFusion code immediately fell into a JavaScript-oriented style. This move includes:

No Data-Typing. I have no indication of what data types are required for function arguments. I also have no indication of what data type will be returned from a function.

Less Hinting. Now that I have lost my Hint attributes, I offer much less insight as to what an argument is supposed to be or do. I know that CFScript-syntax offers hint functionality in either pre-function or post-argument format; however, I simply don't feel a strong urge to use it.

No Output Control. Without the tag-based "output" attribute, I stopped caring about whether or not my functions generated output.

Less "Local" Scoping. In ColdFusion 8, I started creating and using a "local" scope as a means to allow just-in-time variable creation without using the "var" keyword. This was useful because in ColdFusion 8, all "var" statements had to be at the top of the function which was annoying. Now that "var" statements can be anywhere within a function, I have much less of a dependence on the local scope. I still love that I can reference the local scope as a struct; but, I don't use it much for declarative statements.

Less "Arguments" Scoping. This is partly due to a lessened dependence on the "local" scope and, partly due to my experience with JavaScript; but now, I find that I hardly use the Arguments scope when referring to function arguments. I simply let the implicit scope-search look in my local scope and then in my arguments scope for variable references.

Clearly, these changes in style translate to less code. Less code is typically a good thing; however, I'm definitely sad that I've become lazy about providing hinting and data-typing. I really should use JavaDoc-style commenting to provide more information. To be honest, I don't care about the actual type-checking; but, I think someone should be able to look at my comments and know what kind of variables are expected.

On a different note, I am pleased with the feel of CFScript-based syntax; I think it, along with features like Closures / Function Expressions and WebSockets will give ColdFusion 10 a renewed street-credit. It's going to be hard for people to say that ColdFusion 10 is a "dead" language when it looks a LOT like the language they program in every day.




Reader Comments

Few comments here:

"No Data-Typing" To be clear, you do know you CAN do that, right?

"Less Hinting. Now that I have lost my Hint attributes" To be clear, you do know you CAN do it, right?

For the above two, I just want to make sure you CHOSE to do so rather than assumed you could not.

@Ray,

Yeah, sorry if that was unclear or misleading. I know that there are several ways to provide data-typing and insights (I think either with JavaDoc-style comments and / or actual inline and post-line syntax). But I find that hard to read. Part of why I LOVED tag-based coding was that everything was so delineated - attributes here, values there. To me, *that* was readable. With things simply separated by spaces, I find my brain gets confused.

Probably just what I am used to vs. not used to. Just noticed that I've been doing this for the last few weeks with ColdFusion 10.

I kinda agree in terms of hinting, it feels a bit awkward in code, but for datatyping I disagree. I think it works well. Compare:

function doitWell(name,gender) {}

public string function doitWell(required string name, required string gender) {}

Yeah, it's more typing, but I think it works well (and is worth the additional validation).

@All,

Also, I'm not saying I don't want to provide more insight / data-typing. I DO. I've just been lazy about putting it in there (JavaDoc comments). That was one of the "bad" ways my coding style has changed.

@Ray,

Yeah, you've got a point. It's really not so bad. I just gotta start doing it and form a habit. I think too much changed at one time and I got sloppy :)

@Scott,

Something about putting code after the ")" just feels so weird to me :)

No output control also pertains to the component itself. There is no need to set output = false at the component level when using script syntax.

Wow. I completely take that back. I swear I was told at one point not to bother but I ran this quick test and both statements are sent to the client. If you add output=false to the user service it does not produce any output. Sorry for the misleading info guys.

http://pastebin.com/ZDCg99rP

Don't find too many limitations with script style coding. Actually think some processes are more easily coded in script than tags (webservice invocations for instance). Script coding also reminds me why the cfquery tag is so great though. One definite plus for me has been in learning Java and .NET as the syntax overlaps in many places.

I feel Tag based coding more neat and very readable compared to script based. I loved CF from the beginning itself due to this Tag based syntax. While working on some others codes (maintenance projects), it helps me a lot - from readability point of view. Iam really sad to see that now everybody moving towards script based style which sometimes makes me to feel like PHP.

@Dan,

You're right in that script-based CFCs don't generate output by default, but this is primarily in reference to whitespace. In your case, since you're explicitly calling writeOutput(), it will still output your string.

@Tony/@Dan - ok, so to be clear then are you saying that placing the:

component output="false"

at the beginning in the script based CFC and/or a function is completely superfluous?

I also thought it unnecessary, yet I see it everywhere - including in Adobe CF help pages - so why even have the option?

Thanks,
Andy

@Tony - thanks for clearing that up!

@Andy - output = false was necessary on tag based components because it would end up producing unwanted white space. What Tony is saying is that no matter what white space will not be generated in a script based component.

@Dan - yes, exactly. So my question was: why does it seem to be std. practice to still put this at the top of script base CFCs? Is it basically just ignored metadata for the cfc at that point?

I don't know that its standard practice. Everyone has their own preference but I never set output on my script components and will continue to not do so.

@Dan - I do the same. I was just trying to see if there was some other possible reason for its existence in a script based component and if not, why leave it as an option at all?

Anyways - thx for the feedback/opinions!

I generally write my CFScript functions like this:

/**
*@hint Function that does stuff
*@strStuff A string with more stuff in it
*/
remote struct function doThings(
required string strStuff='some words'
) {
// function workings
}

The hints stick out and are obvious to read, and with prefixes like str, num, arr I know what type of data is in the variable.

I find this separates out the information I need the most, and keeps the definition in one place.

Ben,
For me data type & access type is more important than anything else specially when you are using your components as a webservices or you r working in hybrid environment. In addition to that, although CF will let you do that, it is good practice to follow coding standards.
Allowing script style coding in CF made it easy on developers from other technology to understand the code. I have been big fan of script syntax & pushing CF more to OOP.

Whilst Adobe has been pouring time and resources into ability to make class/function declarations in script, this feature is an absolute waste of time because of the lack of varscope checker for script.

As we all know, unscoped vars in classes that are in shared scopes cause horrible, hard to track down bugs.

I need a 100% guarantee that code that is deployed does not contain any varscope errors & I simply can't get that with script based functions/classes.

Manually varscoping code is not an option because I have 700k+ LOC and 10 developers working on the same project.

I don't understand why Adobe don't put effort into showing unscoped vars in builder & providing a way to varscope an entire project of script.

@David,

Your comment makes absolutely no sense. A tag based function can also have unscoped variables. There is nothing special about tag, or script, that make it more, or less, likely to screw up var scoping.

"I don't understand why Adobe don't put effort into showing unscoped vars in builder & providing a way to varscope an entire project of script."

Maybe because there is already a VarScoper CFBuilder extension? Maybe because - at the end of the day- it is a developer responsibility?

@Raymond,

Mike's Varscoper extension will only works if both the component and function declarations are declared in CFML.

It does not work if you declare the function and component in script.

Therefore, there is no way to varscope a .CFC if it is declared entirely in script, and that is a problem.

Saying it is a developer responsibility is just a cop-out. Even VB/ASP had an option strict to enable unscoped variables to be picked up.

You say yourself that "The varScoper tool is still an important and necessary part of your testing/deployment strategy.", but we, as a community are left with no option to varscope with the new language features.

If the consequence was a little bit more memory used, or more cpu used, then fair enough, call it a developer responsibility, but the consequences of unscoped vars are huge!!!!

@David,

As a community member, you could also take it upon yourself to update the open-source varscoper project to fully support cfscript. In fact, I'm pretty sure there are people who have already done so.

David, you are right. I forgot that the VarScoper doesn't work for script. However, your statement that you cannot 100% var scope script based components is flat out wrong. I use 100% script based CFCs. I ensure they are properly var scoped. It can be done.

@Tony, if course I could, but I should not have to. I consider this sort of thing to be basic functionality that a compiler should provide.

@Raymond, sorry, your are correct, it's possible to product a 100% varscoped CFC, or even a small project of 100% varscoped CFC's especially if your project consists of developers of the level of experience of yourself.

BUT, I consider it impossible (ok, impractical) to do if you are on a project of the size/scope that I'm working on. It's not that we are not trying, but the reality of having 10 developers working on code is that sometimes they will miss something.

Varscoping is part of our pre-commit and pre-deployment checklists, and the pre-deployment checks will pickup 1-2 unscoped vars every second or third build.

If we simply trusted all of our developers to product 100% varscoped code for every single LOC, that's a lot of horrible bugs that would creep in.

What I don't understand is why community greats and evangelists for CF that understand the importance of var scoping & are pushing features such as 100% script based components don't think we need automated varscope checking. Surely if these people worked on large projects with lots of developers they would see the need and push Adobe for a solution?

Dave

I never said we don't need it. My issue is with your statement that it can't be done. (Which you have retracted.)

Should it be part of the language - or available via some setting? Perhaps. As with all things, the CF Engineers have to make a decision on what features they have time to build/support.

Interesting that you've made the switch to script based components Ben! Out of curiosity, how are you finding the syntax of database queries and queries of queries? I'm still finding them way too clunky and verbose...

I don't know if anyone else has found that queries don't have to be awful in CFScript. I do something like this:

  • var my_widget_query = new Query(datasource=getDatasource(),
  • sql = "
  • SELECT widget_id, widget_name
  • FROM widgets
  • WHERE widget_type = :widget
  • ");
  •  
  • my_widget_query.addParam(...)
  •  
  • return my_widget_query.execute().getResult();

For myself I always use CFscript in the model and controllers now. I am a big fan. I leave tag-based syntax to the view (in my mind it helps distinguish and separate presentation versus business logic).

I also love javadoc style comments for metadata. I have a standard unit test helper method to assert I haven't left anything out :) And to one of the commenters above (Pete) - do you know you don't need to include @hint? if you open with '/**' ie

  • /**
  • * Creates killer orangutans
  • * @returns the number of killer orangutans created
  • */

sets the hint metadata to 'Creates killer orangutans'

I can definitely see how queries aren't as simple, but, having done SQL in JavaScript and other languages, I'd say it is pretty similar. (Not horrible, just not quite as simple as a tag.)

@Kevin,

Yeah, SQL is definitely something that I will miss in the script-based syntax. Clearly, you can do SQL statements with script - but not nearly as easily or [more importantly] as readable!

@Andy, @Dan,

Before I generate my output, I typically use a CFContent tag to set the mime-type. This ALSO resets the output buffer, so any generated space is also cleared out.

@Shirak,

Yeah, with remote-access you'll definitely need to put access in there. Remote access is still something I have not embraced in ColdFusion components. I tend to keep all of my "controller" logic in CFM templates and leave the CFCs for my domain model / service layer. Something just feels so natural about a procedural/cfm-based controller.

@David,

This is similar in JavaScript (my other love). Unscoped values become global. In JavaScript, you can use a "use strict" statement to throw errors for that kind of code. It would be cool to see ColdFusion implement some sort of "strict" featureset that was less lenient when it came to things that *can* cause buggy behavior.

@Justin,

Database stuff is the one thing I haven't really done yet since I'm just investigating the updates to ColdFusion 10. I'm not really looking forward to writing SQL in cfscript. Once I start, it might end up be a deal-breaker for me. I write so much SQL that its important for it to be as easy / readable as possible. It cannot add friction to my life :D

@Ben D, @Ray,

The multi-line feature of ColdFusion strings might be the saving grace for me when it comes to SQL in script. My biggest concern is that I often have SQL that looks like this:

  • WHERE
  • name = 'ben'
  •  
  • <cfif someCondition>
  •  
  • AND
  • hasAccess = 1
  •  
  • </cfif>

Tag-based SQL statements make that kind of stuff a no-brainer.

Ben,
You can accomplish the same thing like this.

<code>
var q = new Query();
var sql = "Select firstname,lastname FROM USERS";

if( arguments.age > 0){
sql += "WHERE age > :age"
}

q.addParam(name="age",value=arguments.age,sqltype="integer");
q.setSQL(sql);

return q.execute().getResult();
<code>

Don't forget that for especially complex SQL you can - and I'm not saying this is necessarily a good idea - switch back to tag based syntax by using an include. Ie, include a tag based file into a script based file. I did that in my last job with a SQL script that was way too huge to convert.

Again - not saying it makes sense - but it worked.

Ben - I have noticed with CF10 my code has changed the same way. Seems very interesting. Prior to CF10, I vary rarely (almost never) used cfscript; however, now I'm finding that I use it extremely often. And now am annoyed at using cfset tags for things that aren't really setting anything!

As an example:
<cfset entitySave(abc)/>

@Ray,

On a side note, I do have a few files like that (that I was working on with someone else who did use CFScript) and I noticed that my ColdFusion Builder (v1) just gives up on color coding. It hits the CFScript tag half way down and just stops color coding or doing any file outline. Haven't tested in CFBuilder v2 yet.

@Cameron,

I think it has a two-fold influence - on one hand, there is stuff in CF10 that is script-only (ie. closures and function expressions); and on the other hand, JavaScript is making such a powerplay on the world of development. My style from JavaScript is starting to merge over into the CF world.

Now doubt, however, I'll still use tags whenever I have to do View-based output. Nothing integrates with HTML like CFML!!

@Ben,

Agreed entirely! I love CF for that reason, it integrates extremely well into HTML. But it is definitely amazing how similar JS and CFScript have become. It's just amazing that before CF10, there's no way I would have ever even thought of creating a script based CFC, and now I'm using them more often than not. But I do agree CFQuery tags do look so much cleaner in tags... at least for anything more complicated than 1 line.

Recently I wrote a CFHTTP request in CFScript and it's definitely not quite as nice looking as the tag based method.

But, I have noticed that everything that we could do in tag based CFCs can definitely be done in a script based cfcs as far as the functions and arguments are concerned, just the layout of them is a bit different. I'm not sure whether it's more or less clear with the layout, but I am definitely finding that I am enjoying it, and writing much less code than the tag based counterpart.

@Cameron,

The love/hate thing that I have with new versions of a language is that 1) it's still in pre-release and 2) so much of my existing code is on older servers. So, I'm all jazzed up about the CF10 stuff; but, even when it comes out, my VPS and everything is still on (shhhhh) CF8 :(

I'm slowly bringing everything together - dropping MS SQL databases for MySQL so I can consolidate on one database engine. Looking forward to a good spring-cleaning of a whole bunch of stuff.

Thanks Ben. I've been trying to learn to like script based cf and I just have not been able to and could not find the correct words to express what I didnt like about it.

You hit the nail on the head. Yes you can do everything and more in script that you could in tags, but it loses a lot of semantics by the new structure.

Also some of the more important tags <cfquery> are clunky in script based. Yes they work but not as easily.

Now if the different cfml engines would allow you to write tags inside script then that would make the transition easier.

Ben, this post makes me so very happy :D

SQL is indeed the greatest challenge, but I tend to use tag-based gateway CFCs and script-based CFCs for services and just about everything else. Sure, I may have query-of-query situations in my services, so I'll either use script-based query for simple stuff or mix up tags and script in a CFC if I must.

Script style coding is a bliss for me. I leave tag-style only for the view and SQL processing components (gateway CFCs etc). Nowadays I even think of cfml (tags) as a view engine. The rest, e.g. controllers and the model, is all in cf script.

I prefer scripting over tagging because I can scan code much more quickly to get the meaning of it and follow the logic. Tags weigh down the amount of information my eyes have to process to get to the meat of the matter.

And everything you pointed out that you assume scripting is lacking is in fact available. Shall I teach you correct scripting syntax? Step into my classroom ;)