Monday, June 24, 2002

kwfu: ['cfupdate','cfinsert','session','application','cfform','cfinput']

Someone sent me an email today asking what I think is so wrong with cfinput, cfform, cfinsert, cfupdate and session variables.

Fortunately (or unfortunately depending on your point of view) for them I was in a typing mood. This was my reply:

The main reason for listing those tags is that they put restrictions on the flexibility of what you can do, and they make future maintainability/modification more difficult.

Take CFFORM for example. Although it allows you to add some validation to the cfinput tags that are contained within it, there are several limitations to this. Some examples:

There isn't an easy way to have the validation fired when the focus moves from one of the form fields.
If you are running a version of CF Server earlier than CF 5, you can't use a regular expression to validate the content of a form field.
Although you can specify a Javascript function to fire when validation fails, this sort of defeats the purpose of using cfform in the first place.
Customizing the behaviour of the validation is relatively difficult, and if you do change the validation scripts in the cfusion directory, the changes will also have to be applied to all servers where the application is deployed.
The cfform tag does not permit a method of get. Only post operations are allowed.

Admittedly, none of the above problems are insurmountable if you really insisted on using cfform, but I have found that in general it is easier to read and maintain systems that have been written using standard HTML tags.

In addition to this, the forms generated with cfform can only be used in ColdFusion applications, but the ones which use HTML only can be used in conjunction with any server side technology. I am a strong believer in solving problems properly once, and re-implementing the same solution many times.

The database tags - cfinput and cfupdate, suffer from pretty much the same sort of problem. Although they do a good job of putting data in a database, they are not so nice 6 months down the line for someone who has to maintain the application. i.e. You can't just look at an action page that uses cfinsert and cfupdate and see what is happening. You will have to look at the form from that is posted to the action page, and almost certainly the database itself. Also, if you make a change to a database table, or split a single table into two tables, you have to go with the native SQL anyhow. If you're going to need to use SQL statements in your application then you might as well be consistent and use them throughout.

In essence the problems with these tags are not so much in the functionality that they offer, but in the difficulty of maintaining the applications that use them long after their original purpose has been forgotten.

Session variables I avoid for 3 reasons.

1. It is not possible to get full information about all the sessions that are active on a particular server at any given time. Such information as how many active sessions there are, when the sessions were last accessed, the usernames of the users who have active sessions etc.

This sort of information often is not required in the initial application stages, but often it does become a requirement in the application at some point down the line.

2. Locking. I don't think I have ever seen an application that was completely free of errors with regard to locking access to session (or application) scoped variables. Even turning on the full checking option in the ColdFusion Administrator doesn't catch all the problems. Consider the following code:

<!--- in application.cfm --->

<!--- we'll copy the userprofile structure into the request scope
so we don't have to keep locking as we go along --->
<cflock scope="session" type="exclusive" timeout="2">
<cfset request.userprofile = session.userprofile>


All we have done here is make a reference to the variable in the session scope.

Under heavy load, the following code will almost certainly cause the server to crash due to memory corruption in the session scope.

<!--- in somepage.cfm --->
<!--- something happened, so we increment a counter variable in the userprofile --->
<cfset request.userprofile.counter = request.userprofile.counter + 1>

What we should have done in the first block of code is use the duplicate() function to copy the data from the session scope:

<cflock scope="session" type="exclusive" timeout="2">
<cfset request.userprofile = duplicate(session.userprofile)>

The third reason for not using session scopes is to avoid any problems in the future if the application becomes too busy to run on a single server. Once that happens, you have to make sure that users stay on the same server for the duration of their session, as sessions are not shared between servers. This has changed in CFMX where it is possible to share sessions across clustered servers, but for earlier versions of CF it is a bit of a problem.

In answer to your question about the checklist, I did have a checklist, but it wasn't on a piece of paper. It was a mental checklist of things to look out for.

It is actually more like a set of guidelines than a checklist. It goes something like this:

If a tag like cfform,cfinput,cfselect,cftable is used, is there an overridingly good reason for using it over manually doing the same thing with coldfusion code. In other words, if a tag has been used that encapsulates some programmatic logic, that could otherwise have been written using ColdFusion code is there a good reason for doing so.

The reasons that I feel these tags need to be justified is that they are not so readable when it comes to looking at your own, or anyone elses code in 6 months time, and that they are less flexible should the application need to be changed at a later date.

Having said that, I won't necessarily always decide that the use is not justified, I'd say it's something like 70-80% of the time.

If data has been put into a persistent scope such as session, server, or application, is there a good justification for doing this over putting it in a database, or using the request scope. The main reason for doing this is that the persistent scopes all need to be locked, and as the code example above shows, it can be very difficult to track down problems with locking. The second reason for doing it is that if the application ever needs to run in a clustered environment there may be problems as mentioned above.

If there are calls to tags which reach out from the ColdFusion server to some other system, could those calls become hung threads waiting for a response, or could they cause locking issues. The sort of tags I am thinking of here are cfhttp, cfmail, cfpop, cffile, cfdirectory, cfftp, cfldap, cfsearch, cfcollection, cfobject etc. All these tags can communicate with processes outside the control of the ColdFusion server, and the coldfusion developer. Some of them need to be locked and some of them can cause hung processes which are waiting for a response from the external system. Exactly what can be done with this sort of code is very dependent on the scenario, but it is something I will be looking for as a potential area of problems.

If custom tags are used, are there a lot of nested recursive calls. This can slow down an application to an incredible degree. I have seen coldfusion applications that were built by very competent Java developers. The problem is that ColdFusion has a 'speed bump' of a few milliseconds each time it has to call a custom tag. Multiply that few milliseconds by a factor of several hundred or even a few thousand and you have an application that may not be particularly complex running at a crawl.

Databases and queries. Often applications performance can be improved from several seconds per page request to several tens of milliseconds per page request just by adding a database index in the right table, or by creating a lookup table for id values. Depending on the nature of the application, it may or may not be possible to make changes to the database structure, but it is usually possible to add a few indices to the tables that are regularly queried.

Consistency in naming of variables, naming of files, and structure of the file system. This is so incredibly important for the maintainability of an application that it really should be one of the primary design considerations for all applications. Unfortunately, short of rewriting the entire application there isn't much you can do to change it once an application has been written. The main reason I check this is so I will know for future reference whether or not I should accept any contracts to make changes to the application. It is also useful information for the client for maintaining the application, or for developing new ones.

There are probably some more things that I'd check, but I think that's enough for you to chew on for now. If you've got this far without looking up award yourself top marks for powers of concentration ;-)

NOTE: Ben Forta sent me a mail pointing out that although these are all valid points you should not forget that the majority of CF applications are not mission critical, they are more like reporting or data entry apps. In these cases it often makes a lot of sense to use all the things I have discouraged above.

I should have made it clearer that the mail to which I was responding was asking about a checklist I mentioned that I use when travelling around providing ColdFusion consulting. Since most of the applications I encounter in this capacity are mission critical applications it makes sense for me to have this checklist of things that I would discourage. As much as any other reason because it makes my job easier if I ever have to go back to the same company to do more consulting.

In short, although these things are important for someone like me, that may not necessarily be true for all ColdFusion developers.

Thanks for pointing that out Ben.

Been very busy lately, so haven't had time to do much with the blog.

I've got lots of good material to add when I get a chance which should be sometime in mid July at current rate of mountain shifting.