Coding Standards and Best Practices
For www.faa.gov & employees.faa.gov
1. Naming Conventions
1.1 The J2EE/Java Naming Convention
The FAA web development group has adopted the Java naming convention for its applications with some slight modifications to reflect differences in Java and ColdFusion. Basic details of this naming convention will be presented in this document.
1.2 Variable and Function Naming
1.2.1 Basic Variables and Functions
Variable, attribute, and function (or “method”) names should begin with a lowercase letter, with the first letter of each subsequent word being capitalized. All letters not beginning a new word should be lowercase. Underscores and spaces should not be used, and other special characters should be avoided if possible. Numbers may be used when appropriate, but a number should not be the first character of the name.
Variable names should be verbose, providing general information about the purpose of a variable. Avoid nondescript names, such as a single letter, unless the variable is being used as a counter in a loop like the universally accepted “i.” Several examples are shown below illustrating correct and incorrect usage.
Incorrect Variable / Function Names | Correct Variable / Function Names |
---|---|
MyNewVARIABLE |
myNewVariable |
A_Variable_Name |
aVariableName |
V |
descriptiveNameRequired |
%LOUSY_Name |
lousyName |
MyFunctionName() |
myFunctionName() |
1.2.2 Constant Variables
Constants should be in all uppercase letters. Each word in a constant should be separated by an underscore. An example of a correctly formatted constant variable name is “NEW_VARIABLE.” Additionally, the Boolean constants “true” and “false” should always be written in capital letters as TRUE
and FALSE
when writing CFML. This does not apply to JavaScript or other implementations where lowercase letters are required or otherwise appropriate.
1.2.3 Query names
Query names should follow the same convention as regular variables, but should begin with a lowercase “q” to indicate that a particular structure is a query. This allows anyone reading the code to immediately know that a scoped variable starting with a “q” is coming from a query. The example below shows proper naming of a query and referencing its variables.
Code example
<cfquery name="qGetJunkDataForExample" datasource="#request.dsn#"> SELECT null AS junk FROM dual </cfquery> <!--- Reference the variable ---> <cfoutput>#qGetJunkDataForExample.junk#</cfoutput>
1.3 Scope Naming
When referencing scopes, capitalize the first letter of the scope, and then write subsequent letters as lowercase. In the context of the Java naming convention, think of a scope as a Java class. Two exceptions to this are the URL and CGI scopes, which should be written in all uppercase letters because they are initialisms.
More information:
1.4 File Naming
The basic file naming scheme follows the Java naming convention with regards to case and capitalization, but appends a few additional guidelines. Based on the file context, most component files on the web server should have a prefix. The table below shows examples of where each component type should be located and when to use each prefix.
File Type | Description and purpose | Example File name(s) |
---|---|---|
Controller | The index.cfm is the root file in a directory. This file is considered a core file, and does not need any prefix. Other files may also be in the core directory to encapsulate a given function of an application and they may be named the same way, with no prefix. |
index.cfm confirmSubmission.cfm processStep2.cfm |
Query | A query file, part of the model, located in a /model/ directory. Query files should begin with “qry” and should give an idea of the general purpose of a query in the file name. It is recommended, but not required, that SELECT queries use “Get,” INSERT queries use “Insert,” UPDATE queries use “Update,” and DELETE queries use “Delete.” |
qryGetAllSites.cfm qryInsertUser.cfm qryUpdateAircraft.cfm qryDeleteLocationById.cfm |
Action | Actions contain most of the business logic and execute some portion of the request. Action files are located in a /model/ directory. An example of an action file might be validating data submitted via a form and then passing off to another component to perform a database update. Action files should begin with “act.” |
actValidateSearchForm.cfm actVerifyLogin.cfm actLogout.cfm |
Display | A display file should process logic determining the look and feel of a page. This may entail hiding or showing additional information based on a set of rules. Display files are located in a /view/ directory, and should begin with “dsp.” |
dspIndex.cfm dspRightSidebar.cfm dspSearchResults.cfm |
Script | A script file provides the client-side behavior layer for a page. All client-side JavaScript should be implemented unobtrusively, degrade gracefully, and not be relied on for critical tasks. Script files are located in an /assets/js/ or /script/ directory, and do not require a prefix. |
animation.js domSelectors.js |
Style | A style file provides the presentation layer for a page. Style files are located in an /assets/css/ or /style/ directory and do not require a prefix. | newsSearch.css |
Media | Media files include documents, images, video, audio, and other files available for the user to use or download. Media files are located in a /media/ directory. Image files can also go in an /assets/img/ directory. Media file names should not contain spaces, but are otherwise generally free of the file naming requirements for other files. |
banner.jpg CustomerList2021-06.xls |
File names are encouraged to be lengthy enough to explain a file's basic purpose without viewing the contents. For more complex processing, this may not be possible, but in most cases an effort should be made.
2. System Architecture
2.1 Model View Controller (MVC)
The model/view/controller (MVC) paradigm is one of the most widely known and commonly accepted approaches to software development. The MVC architecture is employed by the FAA website to separate the multiple tiers of the application. Each component and how it relates to the FAA website design is discussed in brief.
2.1.1 Model
The model represents the data sets and the bottom tier of an application. This typically includes database queries, system objects, and in object-oriented design includes classes representing the domain model. On the FAA website, model components are found in the /model subdirectory for each directory.
2.1.2 View
The view is the top tier of the application, frequently referred to as the presentation layer. The view file should only include logic to render the page to the user in the correct fashion, and should not be able to affect changes to the model directly. Once the view is rendered, it is a static page that must submit a request to the controller to proceed. On the FAA website, view components are found in the /view subdirectory for each directory.
2.1.3 Controller
The controller is the middle tier that represents the actions an application may perform and determines the flow from one page to another. The controller handles requests and forwards the user to the appropriate destination. It also contains “actions” which perform tasks such as validating user input data. On the FAA website, action components are found in the /model subfolder for each directory. In the FAA website implementation, the main controller for each page is the index.cfm file, located in the root of a given directory. This file is responsible for including the necessary components from the model and view. An exception is made for applications, which should use the FuseBox framework to handle requests to the controller. This is discussed later.
More information:
- Sun: Overview of the Model-View-Controller pattern in Java
- Microsoft: Web Presentation Patterns: Model-View-Controller
3. ColdFusion
3.1 General Practices
3.1.1 Operators
It is recommended that ColdFusion operators (such as greater than, less than, equals, etc.) be written in uppercase. This helps differentiate them easily, making key logic operators easy to discern. This is also the convention typically used by Adobe, developer of ColdFusion. Additionally, it is important to group logical operators explicitly with parentheses. In many cases this may not affect the compiler's interpretation, but makes logic much easier for other developers to follow.
Incorrect
<cfif 1 eq 2 or 3 neq 5> <cfset nothing = "" /> </cfif>
Correct
<cfif (1 EQ 2) OR (3 NEQ 5)> <cfset nothing = "" /> </cfif>
More information:
3.1.2 Boolean variables
Boolean variables are always TRUE or FALSE, and do not need to be tested against a value.
Incorrect
<cfif boolExpression EQ TRUE> <cfif boolExpression EQ FALSE>
Correct
<cfif boolExpression> <cfif NOT boolExpression>
ColdFusion is loosely typed and will silently convert variable types when the context requires it. However, for clarity and safety it is preferable to avoid relying on implicit conversions from numeric or string types to Boolean.
Incorrect
<cfif compareNoCase(x,y)>
Correct
<cfif compareNoCase(x,y) NEQ 0>
More information:
3.1.3 Use of Pound (#) Signs
Pound signs (#) in ColdFusion are used to specify that the content between the pound signs should be evaluated as a variable. Unfortunately it can frequently be unclear to programmers new to ColdFusion which situations warrant the use of pound signs to achieve the correct result.
Pound signs are most often used between <cfoutput>
tags to evaluate and output the contents of a variable as shown below:
Required use of <cfoutput>#variableName#</cfoutput>
pound signs.
Another appropriate place to use pound signs is in attributes of a tag when the value of an attribute is a variable instead of static text. In the following example, failure to use the pound signs would result in the literal string “Variables.listNames” being treated as the list to loop over instead of the list variable's contents.
Code example
<cfloop list="#Variables.listNames#" index="currentName"> <!--- Some processing ---> </cfloop>
There are many places it is unnecessary and often confusing if pound signs are used. Instances where pound signs are frequently used unnecessarily include <cfif>
and <cfset>
statements.
Incorrect
<cfif #len(myString)# EQ #1#> <!--- Some processing ---> </cfif>
Correct
<cfif len(myString) EQ 1> <!--- Some processing ---> </cfif>
The examples below show various misuses of cfset and their cleaner, more concise counterparts.
Incorrect
<!--- Example 1: Standard cfset ---> <cfset myVariable = #anotherVariable# /> <!--- Example 2: Concatenation ---> <cfset myVariable = "#anotherVariable#" & ", " & "#stateVariable#" />
The corrected example achieves the same operation without the use of messy pound signs. The quotes surrounding the values in the concatenation operation are also not necessary in this case, as ColdFusion will automatically evaluate the variables. It is important to note, however, that if the variables are surrounded in quotes, the pound signs would be needed or ColdFusion will treat the variable as literal text. In this case, the best solution is to forgo use of the quotes and pound signs around the variable so that particular issue is not a factor.
Correct
<!--- Example 1: Standard cfset ---> <cfset myVariable = anotherVariable /> <!--- Example 2: Concatenation ---> <cfset myVariable = anotherVariable & ", " & stateVariable />
More information:
3.1.4 Variable Scoping
Scopes should be explicitly defined when variables are set and referenced. ColdFusion allows for very relaxed scoping of variables. If no scope is specified, ColdFusion will search each native structure (Form, Request, etc.) looking for a variable using a predetermined order. In many cases, scoping a variable may not lead to significant performance boosts, but makes the code much more readable. Someone reading your code can immediately know where to find a particular variable without wondering where it is coming from. Local variables should use the “Variables” scope as shown below.
Incorrect
<cfset zelda = 0 /> <cfoutput>#zelda#</cfoutput>
Correct
<cfset Variables.zelda = 0 /> <cfoutput>#Variables.zelda#</cfoutput>
3.1.5 Code Indentation
Proper indentation of code is often one of the easiest steps to take towards creating friendlier, more readable code. A little indentation while writing the code will save time later in debugging and understanding the flow of the program. Block statements should always have code between tags indented. This includes any tag that has an opening tag followed by a separate closing tag (i.e. not a singleton like <cfset />
).
There are some exceptions to the rule, such as <cfoutput>
tags enclosing a single variable, or <cfif>
tags that are used within an HTML element to change a single attribute. Provided below are a few examples of how to implement indentation.
Incorrect
<cfif myString EQ "Hello World"><cfoutput>#myString#</cfoutput></cfif>
Correct
<cfif myString EQ "Hello World"> <cfoutput>#myString#</cfoutput> </cfif>
In the example above, since we're only outputting a single variable with the <cfoutput>
tag, it's acceptable to leave it on the same line. The next example shows an acceptable usage of a <cfif>
tag without indentation in order to avoid breaking the spacing in a text string.
<p>Found #rows# match<cfif rows NEQ 1>es</cfif> in #files.recordCount# file<cfif files.recordCount NEQ 1>s</cfif>.</p>
Often, <cfoutput>
is used in large paragraphs of text to enclose single variables within the paragraph, so it does not help readability to disjoint the paragraph. However, any time <cfoutput>
is used around a large block, for instance as a looping tag, its contents should be indented.
Incorrect
<cfoutput query="qTest"> <td>Looping with no indent</td> <td>Is a bad idea.</td> <td>If we nested another</td> <td>Loop it would be worse</td> </cfoutput>
Correct
<cfoutput query="qTest"> <td>Looping with no indent</td> <td>Is a bad idea.</td> <td>If we nested another</td> <td>Loop it would be worse</td> </cfoutput>
Indentation is often a matter of judgment, but adherence to the guidelines established here will ensure cleaner code. Note that actual tab characters are preferable to multiple space characters.
More information:
3.1.6 Comments
All code should include clear, succinct comments, in order to assist developers who work with the code in the future. Comments should describe what code does and why, leaving aside how it works unless it is not obvious. CFML comments (<!--- … --->
) should be used instead of HTML comments (<!-- … -->
) to prevent them from being visible to the public, and to reduce the weight of pages downloaded by users.
More information:
3.1.7 CFScript
Before the release of ColdFusion MX, many developers heavily used CFScript because of the performance boost it offered over tag-based CFML. However, this performance distinction no longer exists in recent versions of ColdFusion since all code is compiled to Java bytecode and run within the J2EE server.
3.1.8 CFML and HTML Compliance
CFML tags should be written as HTML-compliant, with the following necessary exceptions:
<cfelse>
and<cfelseif>
cannot have a closing tag.<cfif>
does not use anattribute="value"
syntax.<cfreturn>
does not use anattribute="value"
syntax. However,<cfreturn>
should have a self-closing/
.<cfset>
does not always use anattribute="value"
syntax, and non-string values should not be within quotes in<cfset>
. However,<cfset>
,<cfparam>
, and similar tags should have a self-closing/
.
ColdFusion tags and attributes should also be written in all lowercase to be consistent with HTML tags. Some ColdFusion examples on the Internet, particularly older ones, may use capitalized tags. It is preferred that tags are written in lowercase.
Incorrect
<CFPARAM NAME="myVariable" DEFAULT="0">
Correct
<cfparam name="myVariable" default="0" />
More information:
3.2 Forms
3.2.1 Use of <cfform>
When creating forms, use of the ColdFusion <cfform>
tag is not recommended. Although some of the drawbacks of using the tag (including non-HTML markup, clumsy JavaScript validation, limited extensibility, and reliance on poor-performing Java applets) have been reduced to varying degrees with later versions of ColdFusion, it is still preferable to create custom forms using traditional HTML markup.
3.2.2 Naming Form Elements
When naming form elements that correspond to fields in a database, form elements should be named the same as the column in the related database. If the column name contains underscores, remove them from the form element name. Use the Java naming convention to name the form input, for instance a database column called “NUMBER_TIMES_ACCESSED
” would become “numberTimesAccessed
.”
3.2.3 Form Validation
Developers should never rely on client-side validation (such as JavaScript) as the sole mechanism for verifying user input. Client-side validation relies on the user's browser, which might not meet the requirements necessary to perform the validation, or might intentionally circumvent the validation. See JavaScript for more details.
Validation of a form should take place on a server-side page within the controller or “action” aspect of the MVC architecture. Typical validation operations include checking the length of data, verifying that data are within an accepted range, and ensuring integrity between sets of data.
Validation feedback should be provided if the data provided do not meet the established criteria. The system should forward the user back to the form where the data were entered and conspicuously display messages regarding why the data were not accepted. The form should also retain the data the user had previously entered. As an additional option, form fields that contained an error might be highlighted, but this is not a requirement.
3.3 Performance
Much of optimizing ColdFusion performance involves using good development practices, such as those outlined in this document. In general, it is not important to focus on low-level performance tweaking, especially when such tweaking negatively impacts code readability.
Following are several issues which can significantly impact performance:
- Do not use Microsoft Access databases in a production environment. See section 6, “Database and Structured Query Language (SQL) ”, for more information.
- Ensure that your SQL statements are querying the database efficiently.
- Use
<cfquery>
's caching functionality where appropriate. - Avoid the
evaluate()
andiif()
functions.
Additionally, providing visual feedback to users when an application is expected to take a while often gives the perception of better responsiveness.
More information:
- Adobe TechNote: ColdFusion Performance Tuning
- Adobe ColdFusion Developer Center: Performance
- ColdFusion Coding Guidelines: Appendix: Performance Techniques
- Adobe LiveDocs: Avoiding the Evaluate function
3.4 Security
3.4.1 SQL Injection Attacks
An SQL injection attack is the act of embedding partial SQL queries inside of input that is used within a query. For example, the following code is vulnerable:
SQL injection vulnerable code
<cfquery name="qLogin" datasource="#Request.dsn#"> SELECT * FROM users WHERE userName = '#Form.userName#' </cfquery>
The easiest way to protect your applications from an SQL injection attack is to use the <cfqueryparam>
tag. See SQL for more information.
More information:
- Adobe: Securing Database Access Using the cfqueryparam Tag
- Adobe LiveDocs: Enhancing security with cfqueryparam
3.4.2 Cross-Site Scripting (XSS)
Cross-site scripting attacks are among the easiest to enact and most widespread on the Internet today. Two methods to reduce the likelihood of a successful attack are checking HTTP referrer information before processing form submissions, and HTML-encoding any data derived from users or otherwise susceptible to user-tampering (such as URL parameters).
The encodeForHTML()
and encodeForHTMLAttribute()
functions can be used to help protect ColdFusion pages that incorporate user-provided data into the HTML response. The aforementioned functions remove return characters from a string (while preserving line feed characters), and convert certain characters to their HTML entities:
Character | Entity |
---|---|
< | < |
> | > |
& | & |
" | " |
Note that these functions might increase or reduce the length of a string. This can cause unpredictable results when performing certain string functions (e.g. left()
, right()
, and mid()
) against the adapted string.
The following code demonstrates how to check if the HTTP referrer is on the same server as the current page.
Code example
<cfif NOT (len(CGI.HTTP_REFERER) GT 0 AND findNoCase(CGI.SERVER_NAME, CGI.HTTP_REFERER) ... GT 0)> <p>Post from foreign host detected.</p> <cfabort /> </cfif>
In cases where there may be a legitimate reason for an empty HTTP referrer, the code can be modified as follows.
Code example
<cfif len(CGI.HTTP_REFERER) GT 0 AND findNoCase(CGI.SERVER_NAME, CGI.HTTP_REFERER) GT 0> <p>Post from foreign host detected.</p> <cfabort /> </cfif>
However, neither method accounts for subdomains (such as “www”) existing in SERVER_NAME
but not in HTTP_REFERER
. They also fail to verify that the server name is not simply a subdomain or within the directory path of the referrer. For these reasons, a regular expression should be used to validate that the referring page is within the same root domain as the template processing the input. The following code applies this for the faa.gov domain.
Code example
<cfif reFindNoCase("^https?://(?:[a-z\d-]+\.)*?faa\.gov(?:/|$)", CGI.HTTP_REFERER) EQ 0> <p>Post from foreign host detected.</p> <cfabort /> </cfif>
The above code will evaluate the following referrers as shown:
Referrer | Accepted |
---|---|
https://www.faa.gov/ | Yes |
https://www.employees.faa.gov/directory/index.cfm?var=value | Yes |
https://www.faa.gov.example.com | No |
https://example.com/?www.faa.gov | No |
https://example.com/directory/www.faa.gov | No |
(empty referrer) | No |
However, this cannot defeat a technically savvy user who is able to spoof the HTTP referrer while using a foreign host. Ultimately, user data should always be validated and considered inherently untrustworthy.
More information:
- CGISecurity: The Cross Site Scripting (XSS) FAQ
- CERT Advisory CA-2000-02: Malicious HTML Tags Embedded in Client Web Requests
3.4.3 Document Object Model (DOM) Manipulation
DOM manipulation may be used, for example, to modify a form in a way that grants a user special privileges they would not otherwise possess. Two common attack vectors for DOM manipulation are cross-site scripting (XSS) and browser add-ons which allow real-time manipulation of JavaScript or HTML on the page.
To mitigate the risk of these kinds of attacks, applications should not rely on data received from a user (e.g., a hidden input submitted by a form or a URL parameter) for user identification, and should always ensure that the user has appropriate rights to perform the requested action, rather than simply ensuring the user has rights to use an application or page. In other words, it should not be assumed that because an application only presents users with options appropriate for them, the action requested must therefore be within their rights.
3.4.4 Man in the Middle Attacks
Man in the middle attacks involve an attacker or rogue program intercepting and altering communication between a client and server. This can allow an attacker to read or modify messages without either party knowing the link between them has been compromised. To mitigate this risk, sensitive applications should require connections using Transport Layer Security (TLS). Any application that includes an authentication system for users is required to use TLS. Any attempt to use the application over a non-TLS connection should be redirected to the TLS equivalent.
More information:
3.4.5 Passwords and Authentication Mechanisms
OMB policy requires that passwords meet all of the following criteria:
- Have a minimum length of eight characters or the maximum the existing system allows, if less than eight characters.
- Not contain all or part of the user's account name.
- Contain characters from at least three of the four following types: English uppercase characters (A–Z), English lowercase characters (a–z), numerical digits (0–9), and non-alphanumeric characters. Characters cannot be repeated more than once within a succession.
Since the security of all communication media between a user and the server cannot be trusted, passwords and other sensitive data must be transmitted over TLS. On the backend, passwords must be encrypted or stored using a hashing algorithm such as SHA-2 or SHA-3.
Passwords should be changed regularly to limit the usefulness of compromised passwords. Federal policy dictates that passwords have a maximum life of one year, but it is a best practice to limit password life to 30 to 90 days. If a user's password has not been changed by the expiration date, they should be required to change their password when they log in. Additionally, at least five prior passwords should be recorded and the user should be prevented from using these passwords again.
If several invalid login attempts are made in a short period of time, users should be prevented from continuing since it is possible that someone is trying to guess their password. However, user accounts should not be disabled or locked as a result of failed login attempts, because such a mechanism can be exploited to perform a denial of service by systematically locking out many accounts. Instead, the user should be required to wait several minutes or begin a new browser session before reattempting authentication.
Authenticated sessions should be automatically terminated after a period of inactivity.
More information:
- FAA Password and PIN Management Policy (Order 1370.92)
- NIST: Federal Information Processing Standards Publication 112
- P-Synch: Enterprise Password Management
More information about general web application security issues:
- Adobe ColdFusion Developer Center: Security
- W3C: World Wide Web Security FAQ
- Web Application Security Consortium (WASC)
- CGISecurity Application Security Portal
- Open Web Application Security Project (OWASP)
- FAA AIO Documents: Cyber Security
More information about ColdFusion development and best practices:
- Adobe ColdFusion Developer Center
- Adobe ColdFusion Documentation
- ColdFusion Coding Guidelines
- ColdFusion Community Resources: Third-Party Websites
4. JavaScript
4.1 Overview of JavaScript and its Use
JavaScript is very powerful and has numerous uses, but also carries some problems with it. When used on the client-side, execution of JavaScript code is largely dependent on the version of the user's browser and the configuration of the user's options. Some JavaScript may run correctly on one browser, but incorrectly on another. JavaScript may never run at all if a user has disabled it. In still other cases, a user may manually manipulate JavaScript through a JavaScript command-line or other browser add-on. For these reasons, JavaScript should be used sparingly and not relied upon for critical functions or layout instructions.
4.2 General Practices
- Browser support for various JavaScript APIs varies widely. Ensure that your JavaScript code is compatible with the browsers that you are required to support.
- Ensure that JavaScript functionality degrades gracefully for users who have disabled JavaScript or are using older browsers.
- Separate behavior and structure. This is also known as using unobtrusive JavaScript. A developer should only have to add a
class
orid
attribute to one or more HTML tags and use JavaScript to query the DOM and attach event handlers to NodeList items at run time. Utilizing this approach improves usability, accessibility, code readability, modularity, and ease of implementation.JavaScript example (DOM, Level 2)
document.getElementById('pathFromRoot').addEventListener('click', function () { console.log('clicked'); });
jQuery example (v. 1.7+)
$(document).on('click', '#pathFromRoot', function () { console.log('clicked'); });
- Do not use
<noscript>
to provide alternative versions of content. Instead,<noscript>
should only be used to inform users that enhanced functionality is available when JavaScript is enabled. Unobtrusive JavaScript should be used to extend the functionality of existing, accessible content. - Use the same naming convention as established for ColdFusion (the Java naming convention). Keep in mind, however, that unlike ColdFusion, JavaScript is case sensitive. Variables, functions, methods, properties, constructors, and reserved words must all use the correct case or the JavaScript will produce bugs.
- Use semicolons (;) to indicate the termination of a line when appropriate. JavaScript has automatic semicolon insertion, but developeres are advised to supply statement-terminating semicolons explicitly because it may lessen unintended effects of the automatic semicolon insertion. Additionally, inconsistent usage of semicolons is considered bad practice and sloppy.
- Limit or avoid declaring variables in the global scope as much as possible in order to improve performance and avoid conflicts with other scripts and libraries. Use the
var
keyword when declaring variables within functions to ensure that variables are contained within the function scope only. - Use script file includes (e.g.,
<script src="assets/js/thisOneScript.js"></script>
) instead of embedding the JavaScript directly in a page's source code. This improves modularity, encourages reusability, helps to keep code hidden from users, and avoids confusion between JavaScript and<cfscript>
. JavaScript files should be placed in their own subdirectory (assets/js/ or something similar) for a particular page or section. - Do not launch alerts or modal dialogs when a user first visits a page, as this typically annoys users and slows their progress when navigating a site.
- Do not augment the
prototype
of theObject
constructor (Object.prototype
), because this changes the expected behavior when using objects as hash tables and will causefor…in
loops to return unpredictable results. - Opening curly brackets for functions, if statements, and other structures which utilize them should be on the same line as the statement they begin. Closing curly brackets ending a statement should occupy a new line by themselves unless followed by an “else” or “else if.” Curly brackets should always be used, even for statements with a single line. Although the code may execute correctly without them, the inclusion of brackets can significantly improve code readability and maintenance.
Incorrect
// Example 1 if(someVar == 1) doSomething(); // Example 2 if(anotherVar == 2) {doAnotherThing();} else {doSomethingElse();}
Correct
// Example 1 if ( someVar === 1 ) { doSomething(); } // Example 2 if ( anotherVar === 2 ) { doAnotherThing(); } else { doSomethingElse(); }
4.3 Appropriate uses of JavaScript
JavaScript can be useful in moderation. Provided below are a few examples of good uses of JavaScript.
- Confirming the submission of a form. Record deletion is an operation that should always be prompted with a confirmation. However, for deletion of small, relatively inconsequential data, it is burdensome to force the user to click through extra screens of confirmation. One mechanism that allows a quick confirmation is a JavaScript prompt. The code below will prompt the user to confirm a deletion before submitting the data to be removed.
JavaScript example (DOM, Level 2)
document.getElementById('recordForm').addEventListener('submit', function (event) { event.preventDefault(); if ( confirm('Are you sure you want to delete?') ) { this.submit(); } });
jQuery example (v. 1.7+)
$(document).on('submit', '#recordForm', function (event) { event.preventDefault(); if ( confirm('Are you sure you want to delete?') ) { this.submit(); } });
- Supplementary form validation. While HTML5-based form validation has attained near-universal support, it can still be helpful to include some validation through JavaScript. For example: to ensure a selection is made before submission. However, client-side validation should not be relied on and must always be accompanied by validation on the backend.
- Disabling a button after the submit event. Another common scenario is preventing a form from being submitted multiple times. When users have to wait several seconds for a form to submit, they may become impatient and click the submit button again. In some cases this can result in the form being submitted with the same data more than once, causing duplicate records. To overcome this, many sites disable a submission button once it has been clicked the first time:
JavaScript example (DOM, Level 2)
document.querySelectorAll('.content form')[0].addEventListener('submit', function () { this.querySelectorAll('button')[0].setAttribute('disabled', 'disabled'); });
jQuery example (v. 1.7+)
$('.content').on('submit', 'form', function () { $(this).find('button').prop('disabled', true); });
This is obviously not a comprehensive list of the appropriate uses of JavaScript, but is intended to show that JavaScript should be used in a sensible manner and typically in small doses. A system should not rely on JavaScript for any critical functions.
More information:
5. HyperText Markup Language (HTML)
All HTML markup is required to be Section 508 compliant and well-formed according to the HTML document type definition. Developers should strive for a fully semantic approach to markup and maintain a strict separation of structure, presentation, and behavior. For more information, refer to the following resources:
- FAA: Style Guide
- FAA: Section 508 Help Pages
- W3C: HTML specification
- W3C: CSS specifications and drafts
6. Database and Structured Query Language (SQL)
6.1 Database Management System (DBMS) Considerations
6.1.1 Using the Correct DBMS
The selection of the correct DBMS can have a drastic impact upon system performance and response time. FAA has an enterprise-wide license for Oracle, and it has been accepted as the DBMS to use. Under no circumstances should a Microsoft Access database be used in a production environment. Even for small projects, an Access database is not appropriate on the web for numerous reasons, some of which are listed below.
- Scalability: In theory, the JET engine (database engine for Microsoft Access) can support up to 255 concurrent users, but in practice you will never even approach that mark. Performance will suffer with as few as three or four concurrent users.
- Performance: Microsoft Access, in general, performs queries and fulfills requests far slower than other DBMSs optimized for potentially millions of users, such as Oracle.
- Security: An Access database on the network provides additional vulnerability. The database is not intended to stand up to serious security threats, and due to numerous bugs could be exploited to gain access to sensitive data.
If relational data needs to be stored in a database, the appropriate tables and relationships should be created in Oracle.
6.1.2 Porting Microsoft Access to Oracle
If a database schema has already been created in Access and database queries have already been written in code, there are several considerations that may ease the transition to Oracle.
- 1.If using non-ANSI standard SQL functions, some of them will not work in Oracle. For example, instead of
UCase()
andLCase()
useupper()
andlower()
in Oracle. - Do not include spaces in table or field names. Replace spaces with an underscore if necessary. Table and field names may not be longer than 30 characters in Oracle.
- Do not surround column or table names with brackets
[ ]
. - Any instances of a wildcard asterisk “
*
” used with aLIKE
statement should be replaced with the Oracle equivalent, a percent sign “%
.” - Avoid using Oracle keywords in field names, table names, and aliases. Keywords such as
TABLE
,COLUMN
,VALUE
, and numerous others can cause queries to fail seemingly inexplicably. For a full listing of keywords, check the Oracle website. - See Structured Query Language (SQL) for more information.
6.1.3 Alternatives to a DBMS
If the data being used do not require a relational model and are “flat,” then the data may not require a database at all. Data are considered “flat” if they occupy a single database table or spreadsheet, and do not have relationships to other entities. Some viable alternatives to using a database to store these data include extensible markup language (XML) and ColdFusion structures.
6.1.4 Using the Backend — Triggers, Views, and Stored Procedures
When used properly, databases can give applications a performance boost by performing actions quickly on the backend where the data resides. However, placing all of the business logic in the database is generally a very poor idea. It makes it accessible to fewer parties, makes maintenance more difficult, and may be less extensible and reusable throughout the entirety of the application. It is important to be familiar with the facilities a database offers and when it is appropriate to use them. An extremely basic overview of Oracle's abilities is provided, but developers are encouraged to explore further on their own to learn more details.
Object | Description | Usage |
---|---|---|
Views | A view is typically a read-only abstraction of data, combined from several tables or using calculated fields that might be cumbersome to write in a <cfquery> . |
If a particular query utilizing several joins or using fields that are calculated based on functions or procedures is used frequently in an application, it may make sense to make it a view. This view is maintained in the database and can be queried against and have criteria applied as necessary. |
Triggers | Triggers have many usages. One of the most basic uses for a trigger is to auto-increment a primary key field in a table using a sequence. | Triggers should always be used to increment primary keys in the database. ColdFusion <cfquery> tags should never INSERT or UPDATE a primary key field. These triggers should usually be created when a table is created. |
Stored Procedures | Functions and stored procedures are typically written in PL/SQL and can be very powerful. | Stored Procedures are often necessary for scheduled tasks that use backend data, such as sending out a nightly email based on a given criteria, or performing data loading operations. Custom functions that are able to perform complex decisions on the backend may help some queries perform better, particularly if the queries recur throughout the application. Most other processing should be performed in the ColdFusion code unless impractical. |
Using the tools provided by Oracle is primarily a matter of judgment. If there is a sound reason for moving certain functionality into the database, contact the database administrator (DBA) about making use of Oracle's facilities.
6.2 Structured Query Language (SQL)
SQL is the means by which data are retrieved, inserted into, and updated from a database. Sloppy SQL can result in many serious consequences, including security vulnerabilities, drastic performance bottlenecks, and poor portability. These guidelines have been established to try to minimize these issues, and should be used when writing SQL.
6.2.1 Specific SQL and <cfquery>
Guidelines
- Do not use
SELECT *
to retrieve all the columns in a given dataset. This can cause issues with caching the query (in the event of database structural modifications), and can cause improperly aliased columns to display the wrong value or throw an error. Explicitly list out all column names. - Ensure all tables and sub-queries being used as tables are aliased. An alias is a name assigned to a table or dataset. Oracle 10g in particular is very strict regarding aliasing. Field names should be explicitly listed and each should have an alias in front of the column name. See the figure below for an example of correct and incorrect implementations. In the correct example, the aliases used are “t1” and “t2.”
Incorrect
SELECT * FROM Table1 LEFT JOIN Table2 ON Table1.field1 = Table2.field2 WHERE field1 IN (1,2,3) ORDER BY field2
Correct
SELECT t1.field1, t2.field1, t2.field2 FROM Table1 t1 LEFT JOIN Table2 t2 ON t1.field1 = t2.field2 WHERE t1.field1 IN (1,2,3) ORDER BY t2.field2
- Use all uppercase for SQL keywords and Oracle reserved words including
SELECT
,FROM
,LEFT JOIN
,INNER JOIN
,RIGHT JOIN
,OUTER LEFT JOIN
,OUTER RIGHT JOIN
,UNION
,INTERSECT
,WHERE
,HAVING
,ORDER BY
,IN
,ON
,IS
,AS
,TABLE
,COLUMN
,UPDATE
,INSERT
,DELETE
,DROP
,ADD
,MODIFY
,VALUES
, etc. This improves readability and easily calls attention to key portions of the SQL statement. - Apply the Java naming convention when calling SQL and Oracle functions including
trim()
,upper()
,decode()
, etc. Since none of the functions exceed one word, all of the functions will be completely lowercase. - Make proper use of indentation and line breaks. Items following a major keyword should be indented such that the items after each keyword are aligned with the items after the other keywords. See the example below for correct usage.
Incorrect
SELECT t1.f1 FROM Table1 t1
Correct
SELECT t1.f1 FROM Table1 t1
In the correct example the text after the keywords is aligned.
- Each line should contain only one major keyword unless a sub-query is being used. For instance, the following example shows an example of a permissible usage.
Code example
SELECT t1.f1, t1.f2, t1.f3 FROM Table1 t1 WHERE t1.f1 IN (SELECT DISTINCT t2.sid FROM Table2 t2) AND t1.f2 IS NOT NULL
- When writing queries within a ColdFusion
<cfquery>
tag, use<cfqueryparam>
inWHERE
clauses,INSERT
statements, andUPDATE
statements. This has numerous benefits, including increased security, automatic escaping of problematic characters, data typing, and portability. More information about the benefits of<cfqueryparam>
can be found in the ColdFusion documentation provided by Adobe. You must use<cfqueryparam>
for any variables being supplied, but will not be required to use it for static values. Note the one place<cfqueryparam>
cannot be used is aWHERE
clause containing theLIKE
operator and a wildcard (%). In this case,<cfqueryparam>
will ignore the wildcard, so in that instance it should not be used. See the figures provided below for proper usage of<cfqueryparam>
.Incorrect
UPDATE Table1 t1 SET t1.f1 = '#myVariable#' WHERE t1.f2 = #aNumberVariable# AND t1.f3 = 'STATIC_TEXT' AND t1.f4 LIKE '%searchVariable%'
Correct
UPDATE Table1 t1 SET t1.f1 = <cfqueryparam cfsqltype="cf_sql_varchar" maxlength="250" ... value="#myVariable#" /> WHERE t1.f2 = <cfqueryparam cfsqltype="cf_sql_integer" value="#aNumberVariable#" /> AND t1.f3 = 'STATIC_TEXT' <!--- Optional use here ---> AND t1.f4 LIKE '%searchVariable%' <!--- Cannot use here --->
- Multiple criteria in a
WHERE
clause should each be on a new line, including eachAND
andOR
component. The exception to this is nested criteria andBETWEEN
statements. An example of proper usage is shown below.Code example
SELECT t1.f1, t1.f2, t1.f3 FROM Table1 t1 WHERE t1.f1 = <cfqueryparam cfsqltype="cf_sql_varchar" maxlength="250" ... value="#myVariable#" /> AND t1.f2 = <cfqueryparam cfsqltype="cf_sql_integer" value="#aNumberVariable#" /> AND (t1.f3 = 5 OR t1.f4 > 3) AND (NOT (t1.f5 BETWEEN 0 AND 1000))
In this case it would clutter the query to move the
NOT
, nestedOR
, andBETWEEN
to a new line, so they can be left as is. - Use of comments in large queries is acceptable and encouraged in many cases. For instance, in a search results query, the
WHERE
clause might be built dynamically based on a several parameters. It makes the code easily understandable to someone else if there are comments noting each parameter. - A
<cflock>
tag should not be used in a<cfquery>
tag. If a variable from a shared scope needs to be used in a query and its access or manipulation presents a potential race condition, retrieve the value first into a local variable via<cflock>
and then use the local variable in the query. Operations such as file input/output and web services should also be avoided in a<cfquery>
block. In most cases, the only code that is appropriate in a<cfquery>
block is the use of decision making statements such as<cfif>
and<cfswitch>
. Other operations should be performed outside the query first.Incorrect
<cfquery name="qBadExample" datasource="#Application.datasource#"> SELECT t1.field1 FROM Table1 t1 WHERE t1.field1 = <cflock scope="Session"> #Session.someValue# </cflock> </cfquery>
Correct
<!--- Get the value from the session and assign it locally ---> <cflock scope="Session"> <cfset Variables.someValue = Session.someValue /> </cflock> <cfquery name="qGoodExample" datasource="#Application.datasource#"> SELECT t1.field1 FROM Table1 t1 WHERE t1.field1 = <cfqueryparam cfsqltype="cf_sql_integer" ... value="#Variables.someValue#" /> </cfquery>
- Do not make frivolous use of
UNION
. AUNION
operation is one of the more costly SQL operations and can frequently be avoided by intelligent query design. In the example shown below, aUNION
is performed to add a blank value to be shown at the top of a drop-down list. This value could easily be added in the form as an option before looping through the result set or withqueryAddRow()
without the expense of aUNION
operation in the database.Incorrect
SELECT t1.f2, t1.f3 FROM Table1 t1 UNION SELECT 0,' ' FROM Table2 t2 ORDER BY t1.f3
- Related to the previous item, judicious use of the
decode()
function provided by Oracle can drastically increase performance in some situations. Thedecode()
function provides a mechanism allowing Oracle to make a decision, similar to anif…then
or switch statement. This can be extremely powerful and helpful in reducing the burden a query has on the system, but if abused can lead to lazy query design and over reliance. The example below shows an example of a single query being used to sum the number of employees within certain salary ranges grouped by department. Written using thedecode()
function, the query can be performed in a single pass, as opposed to severalUNION
queries or multiple queries to the database.Example
<cfquery name="qDecodeExample" datasource="#Application.datasource#"> SELECT em.deptNo, sum(decode(greatest(em.salary, 3001), least(em.salary,9999), 1, 0)) ... AS sum3001To9999, sum(decode(greatest(em.salary,10000), least(em.salary,19999), 1, 0)) ... AS sum10000To19999 FROM employees em GROUP BY deptNo </cfquery>
- When using domain aggregate functions (such as
SUM()
) as part of aSELECT
statement, use theAS
keyword to rename the column something more appropriate for ease of reference. When renaming the column, use the standard Java naming convention. See the example provided below.Incorrect
SELECT max(t1.f1), t1.f2 FROM Table1 t1 GROUP BY t1.f1, t1.f2
Correct
SELECT max(t1.f1) AS maxF1, t1.f2 FROM Table1 t1 GROUP BY t1.f1, t1.f2
- Do not use data definition language (DDL) to create or modify database structure on the fly. This includes (but is not limited to)
CREATE
,ALTER
, andDROP
statements. This introduces numerous security risks, not to mention concurrency and configuration management problems. If there is a compelling case for use of temporary tables or other structure modification to be effected by code within an application, it must be discussed and approved by a database administrator (DBA) beforehand.
7. Browsers
While browser rendering engines are slowly but steadily converging on their support of web standards, applications and pages must still be tested with many browsers and browser versions. Special consideration should be given to legacy versions of Internet Explorer. It is prudent to support browser versions that are used by at least 1% of your site's visitors. Know your visitors.
Understand the concept of progressive enhancement and how it pertains to web technologies, such as advanced CSS, HTML5 features, new JavaScript APIs, canvas, et cetera.
8. Mobile
Contact Web Management for more information on Mobile standards.