Entries Tagged as Best Practices
These days, I work with CF servers that are home to hundreds of applications simultaneously. We have some nice error reporting going on, with Application.cfc's onError() method to send email notifications, and using a backup of the CFError tags for when all else has failed. But I noticed that it was incredibly annoying waiting for error emails to arrive when my code would have an error in it. So I decided to disable custom error handling and pretty error pages in development.
Of course, to eliminate the potential for human error, we programatically determine if the application is running in dev, staging, or production:
<cfinvoke
component="cfc.AppStatus"
method="getEnvName"
returnVariable="appStatus"
/>
This code is in Application.cfc, and stores the result in a place shared with the entire application. We were already using this value to set things like the datasource password (which is different between your development and production environments, right?!). If it's not obvious, the function will return the string DEV, STAGE or PROD depending on which environment that server belongs to.
Then, in onRequestStart(), I added this little nugget:
<!--- If on dev, don't use error emails, just show errors --->
<cfif app_status neq "prod" and app_status neq "stage">
<cfset structDelete(this, "onError") />
<cfset structDelete(variables, "onError") />
<cfsetting showdebugoutput="true" />
</cfif>
And wrap the backup CFError tags like so:
<cfif app_status eq "prod" or app_status eq "stage">
<cferror
type="request"
template="/error_request.cfm"
/>
<cferror
type="exception"
template="/error_exception.cfm"
/>
</cfif>
Now, when my code throws an error while I'm developing and testing in the dev environment, I see it on screen instead of having to wait a minute or two for the email to come, and I don't have to worry about a rogue infinite loop filling up my inbox.
Posted in
Best Practices |
ColdFusion
| 2 Responses
May 11 2010
This afternoon I have been having a bit of a group discussion on the perils of INNER JOINs in SQL. It started with a tweet that Inner Join's are dangerous and you should use them with caution. A bunch of people asked me what exactly I meant, and of course it's difficult to get that across in chunks of 140 characters or less, which brings us to the present and this blog post.
Consider that you have a relational database setup to store survey responses. Each respondent creates 1 ResponseSet, which gets a ResponseSetId, and then from there you can build up responses to the various questions. There are N questions. Each question can have M responses, all of them free-form text. There are probably a hundred ways to lay out the database to support this, but in the case of the database that inspired the tweet, I've got a Response table, with a foreign key to the ResponseSet table, which has a foreign key to the Survey table. A response belongs to a response set, which belongs to a survey.
In your mind, write some SQL to join these tables and get back the questions and answers for a given ResponseSet for a report (say, ResponseSetId 473240432, if that helps you think). Here is the first thing that pops into my head:
SELECT
QuestionId
, ResponseText
FROM
tblResponseSet rs
INNER JOIN tblResponse r ON rs.ResponseSetId = r.ResponseSetId
INNER JOIN tblQuestion q ON q.QuestionId = r.QuestionId
WHERE
rs.ResponseSetId = @responseSetId
GROUP BY
q.QuestionId
ORDER BY
q.QuestionSort
That's great, right? Unfortunately, no. At least, not in my case. Even assuming that all questions are mandatory (1+ responses required), this does not account for other buggy software I've written (we all do it!) that saves, copies, or modifies the data that may result in blank responses or no responses for a given question. Rather to the point, what I would rather see in the case of this poorly-maintained theoretical data (as modified by my buggy code) is the rows I expect, but with NULL values for missing responses.
In the event of the survey above, it would be more helpful to the developer trying to debug the application to know that NULLs are coming back as survey responses, than it would be to know that the question isn't being included in the result set. The join for the question data is fine — at least in as much as it isn't the cause of this theoretical row being dropped — it's the join on responses that's causing the problem. In effect, by eliminating the row altogether, we're creating a false-negative, or the appearance of a bug that doesn't exist. (The question table should be joined via outer join as well.)
Using this method of querying the data, each row in the result set will give the question and 1 of its responses. The question will repeat with each different response, making it easy to use the group attribute of ColdFusion's cfoutput tag to display each question with the various responses to it for the given Response Set. However, with the above query, if there are no responses for ResponseSetId 473240432 and QuestionId 42, then QuestionId 42 is dropped from the result set. That's a Bad Thing (TM)!
In essence, plan for the worst but hope for the best. Whenever I write a join condition, instead of asking myself "How is this data relationship supposed to work?" I should be asking myself "What do I want to happen if some data turns up missing?" By writing an inner join, I'm answering the second question with "drop the row". The answer I wanted to provide for that question was to "show nulls", which is written in SQL with an OUTER JOIN.
That is the inherant danger of INNER JOINs of which I was speaking.
Posted in
Best Practices |
Databases
| 4 Responses
February 02 2010
I'm giving a presentation at the end of the month on Subversion for my office. It's going to be recorded and I'll be sure to post the video here for anyone interested in watching.
Here's where I need your help: What are some of the craziest, dumbest, most ridiculous things you've ever seen done in Subversion (or some other version control system, as long as the scenario would still apply)?
Anything is fair game. Awful commit comments? Terrible branching or merging practices? I want to hear about it all, and I welcome your comments!
Posted in
Best Practices |
Subversion
| 1 Response
May 11 2009
In ColdFusion, you can loop over the keys in a structure without knowing what the key names are, by using either this syntax:
<cfloop collection="#myStruct#" item="key">
<cfset foo = myStruct[key] />
</cfloop>
Or this syntax:
<cfscript>
for (var key in myStruct){
foo = myStruct[key];
}
</cfscript>
This is useful when you need to loop over a form with dynamically created fields — like editing N person records at a time.
I recently found out that in Webkit — and thus Safari and Google Chrome — form fields without a name attribute are included in a form post, just without a name. Odd, right? I know. I've used nameless form fields in the past, like a select box that's only used for UI functionality, tied to JavaScript. In IE and Firefox, the field is ignored and not posted with the form. In Chrome and Safari, though? Included! Let's look at an example form.
<form action="" method="post">
<select id="test">
<option value="foobar" selected="selected">Foobar</option>
</select>
<input type="submit" name="submitBtn" value="Submit" />
</form>
<cfif structKeyExists(form, "submitBtn")>
<cfdump var="#form#">
</cfif>
This page will submit to itself, and if the form was submitted, dump the form scope. In Firefox, we see what you might expect:

But in Chrome or Safari, you'll notice a small difference:

The extra table row shows that there is another key in the structure; but as we can see it doesn't have a key name. I'm not sure why the value isn't displaying, because it's there. I'm guessing it's got something to do with the null key name. (I'm filing this whole thing under odd but true.)
So if you attempt to loop over each key and output it, depending on your method, you'll run into an error. I'm personally a fan of the structName[keyName] notation, so when I first tried this, no error was thrown, and my output was:
<cfif structKeyExists(form, "submitBtn")>
<cfloop collection="#form#" item="key">
<cfoutput>#key#=#form[key]#</cfoutput><br/>
</cfloop>
</cfif>
Outputs:
=foobar
SUBMITBTN=Submit
FIELDNAMES=SUBMITBTN
I know that not having a key name can cause problems though. Let's try outputting each value using evaluate instead of structName[keyName] notation.
<cfif structKeyExists(form, "submitBtn")>
<cfloop collection="#form#" item="key">
<cfoutput>#key#=#evaluate("form." & key)#</cfoutput><br/>
</cfloop>
</cfif>

Now, I'm always saying there's almost never a good excuse for using evaluate because there's almost always a more efficient way around it. For that reason, I can't think of what you might be doing that would put you in this situation… but it's still best that you're aware of the potential issue.
My advice would be to either try to put the nameless form fields outside of a form (which would be considered malformed XHTML), or to just give it a name and ignore it in your processing. The real lesson here is not to ever rely on browser quirks, because they may not exist in your favor forever.
Posted in
Best Practices |
ColdFusion
| 3 Responses
April 29 2009