Using ColdFusion For Rapid Application Prototyping
People often talk about ColdFusion as a "Rapid Application Development" (RAD) platform; which it is. But lately, I've been using it for rapid application prototyping; and I've been reminded of just how much friction ColdFusion removes from the development process. And of how many native CFML constructs exist to make the collocation of behavior seamless. In this post, I'm not looking at anything fancy — I'm just celebrating what ColdFusion does naturally.
ColdFusion Features For Rapid Prototyping
None of the features here are specific to prototyping; but I'm drawing a distinction between application "development" and application "prototyping" because it allows us to get a little sloppy with our architecture. While these techniques are all valid, some of them won't "feel professional" in a production application architecture. But for prototyping, we're focused on exploring ideas, not creating clean code.
Tag-Based Syntax: one of the oldest, most compelling features of CFML is the fact that it provides an alternate tag-based syntax for all of its language constructs. This allows us to seamlessly weave control flow into our HTML generation. There's no having to jump in-and-out of a syntax mindset to render a page.
Script Islands: that said, CFML does provide
<cfscript>tags for doing just that — jumping in-and-out of a syntax mindset. This is helpful when you want to temporarily use the more concise script-based syntax for defining large swaths of example / prototype data.Function Hoisting: like many modern languages, CFML allows functions to be "hoisted" to the top of the execution context. This allows us to define a bunch of helpful prototyping functions at the bottom of our template; and then invoke them from above, prior to their definition. This leads to much more readable code.
Inline Functions: in rapid application prototyping, collocation is king. We don't want to jump from file to file in order to see how things work. To that end, CFML allows us to place Function definitions inside any template, including our "View templates". This feature, combined with the function hoisting, lets us define helper functions as we need them, right where they're needed.
Function Output: in CFML, functions can either return a value or output a value (or do both). For prototyping, the ability for a function to output data makes them perfect for defining reusable chunks of output logic. No having to build up and return content buffers — just render the view markup right from within the function body.
Effortless Interpolation: if you want to render a variable or a function call into a CFML template, you just wrap it in (
#) symbols. I just want to illustrate how little ceremony there is to so much of the CFML syntax. It just gets out of your way and lets you focus on the prototyping.Seamless Transclusion: the
CFIncludeandCFModuletags allow us to pull existing functionality right into our prototype context. TheCFIncludetag merges both the target template output and the page context into the current template. TheCFModuletag merges the target template output into the current template but leaves a strong separation between the respective page contexts.Shared Scopes: ColdFusion provides a number of shared scopes (memory spaces). For prototyping especially, this lets our page rendering, helper functions, and transcluded templates all work in the same memory space, removing unneeded ceremony from our prototyping logic.
Again, none of these ColdFusion features are specific to prototyping — they can all be used in a production-grade application. But, when prototyping, we can cut some corners knowing that we don't have to think about reusability and maintainability.
My ColdFusion Prototyping Strategy
For the current prototyping effort, I decided to go with a single CFML page that performs "cross-page linking" as anchors. Meaning, instead of linking to a URL like userDetail.cfm, I'm linking to the fragment, #userDetail. This will then navigate the user to a different point on the current page.
This is not the only approach to prototyping. I'm using it because I get to turn the "collocation is king" factor up to 11. With everything in the same template, I can leverage SublimeText's multi-cursor selections to change many parts of the template simultaneously. It also allows SublimeText's intelli-sense to suggest anchor fragments for easier linking.
I started the prototype by outlining 5 "blocks" in my CFML template:
<!--- Global setup for the prototype. --->
<cfscript>
// ....
</cfscript>
<!--- Global CSS for the prototype. --->
<style type="text/css">
/* .... */
</style>
<!--- Rendering of prototype pages. --->
<cfoutput>
<section id="pageOne" />
<section id="pageTwo" />
<section id="pageThree" />
</cfoutput>
<!--- Global JavaScript for the prototype. --->
<script type="text/javascript">
// ....
</script>
<!--- Global ColdFusion utility functions for the prototype. --->
<cffunction name="e" output="false">
<!--- .... --->
</cffunction>
Since the CFML template executes in a top-down manner, the <cfscript> block at the top allows me to define example data structures that I can consume in the "page views". And, since ColdFusion will hoist function definitions to the top of the template, all of my helper <cffunction> tags at the bottom can also be consumed in the "page views".
I'm keeping the CSS at the top and the JavaScript at the bottom to allow for predictable DOM (Document Object Model) querying. This also follows the pattern used by most web pages to reduce FOUC ("Flash of Unstyled Content").
To increase the realism of the single-page prototype, I'm setting each "page section" to have a minimum height of 100vh so that it takes up at least one whole browser screen. Then, each page also has a bottom margin of 100vh to try and maintain this illusion during an over-scroll.
The base CSS looks like this:
<!--- Global CSS for the prototype. --->
<style type="text/css">
.page {
margin-inline: auto ;
margin-bottom: 100vh ;
max-width: 1000px ;
min-height: 100vh ;
scroll-margin-top: 20px ;
}
</style>
This is just an attempt to maintain the suspension of disbelief. In reality, the user can scroll up and down to get from page to page. In fact, this is one of the nice things about having the single-page prototype — you can use the browser's native "Find" feature to quickly look at any page of the prototype.
To better illustrate this, I've put together a super simple prototype with 3 pages (video demo of this is at the top of this post):
- User list.
- User detail.
- User project detail.
At the top, you'll see that I'm defining the sample user and project data. Then, I have my "page simulations". Then, my helper functions at the bottom. I'm trying to keep this as simple as possible while still illustrating the point:
<!--- Global setup for the prototype. --->
<cfscript>
users = [
{ id: 1, name: "Amy", email: "amy@example.com" },
{ id: 2, name: "Jon", email: "jon@example.com" },
{ id: 3, name: "Kim", email: "kim@example.com" },
];
// For user detail page.
xUser = users.first();
xUser.projects = [
{ id: 1, name: "Website Redesign" },
{ id: 2, name: "Mobile App Prototype" },
];
// For project detail page.
xProject = xUser.projects.first();
</cfscript>
<!--- Global CSS for the prototype. --->
<style type="text/css">
body {
background-color: #fafafa ;
color: #202020 ;
font-family: verdana, arial, sans-serif ;
font-size: 18px ;
line-height: 1.4 ;
margin-top: 20px ;
}
a {
color: #ff3366 ;
}
.page {
margin-inline: auto ;
margin-bottom: 100vh ;
max-width: 1000px ;
min-height: 100vh ;
scroll-margin-top: 20px ;
}
.breadcrumbs {
font-size: 90% ;
margin-bottom: 20px ;
& ul,
& li {
display: flex ;
gap: 10px ;
list-style-type: none ;
margin: 0 ;
padding: 0 ;
}
& li:not(:last-of-type):after {
content: ">" ;
}
}
</style>
<!--- Rendering of prototype pages. --->
<cfoutput>
<!--- PAGE SIMULATION. --->
<section id="users" class="page">
#breadcrumbs([
{ text: "Users", anchor: "users" }
])#
#title( "Users" )#
<ul>
<cfloop array="#users#" item="user">
<li>
<a href="##user">#e( user.email )#</a>
</li>
</cfloop>
</ul>
</section>
<!--- PAGE SIMULATION. --->
<section id="user" class="page">
#breadcrumbs([
{ text: "Users", anchor: "users" },
{ text: xUser.name, anchor: "user" }
])#
#title( xUser.name )#
<ul>
<li><strong>ID:</strong> #e( xUser.id )#</li>
<li><strong>Name:</strong> #e( xUser.name )#</li>
<li><strong>Email:</strong> #e( xUser.email )#</li>
</ul>
<div id="user-projects">
#title2( "Projects" )#
<ul>
<cfloop array="#xUser.projects#" item="project">
<li>
<a href="##userProject">#e( project.name )#</a>
</li>
</cfloop>
</ul>
</div>
</section>
<!--- PAGE SIMULATION. --->
<section id="userProject" class="page">
#breadcrumbs([
{ text: "Users", anchor: "users" },
{ text: xUser.name, anchor: "user" },
{ text: "Projects", anchor: "user-projects" },
{ text: xProject.name, anchor: "userProject" }
])#
#title( xProject.name )#
<ul>
<li><strong>ID:</strong> #e( xProject.id )#</li>
<li><strong>Name:</strong> #e( xProject.name )#</li>
</ul>
</section>
</cfoutput>
<!--- Global JavaScript for the prototype. --->
<script type="text/javascript">
// ....
</script>
<!--- Global ColdFusion utility functions for the prototype. --->
<cffunction name="breadcrumbs" output="true">
<cfargument name="links" type="array" />
<nav class="breadcrumbs">
<ul>
<cfloop array="#links#" item="local.link" index="local.i">
<li>
<!--- Last link. --->
<cfif ( i == links.len() )>
#encodeForHtml( link.text )#
<cfelse>
<a href="###e( link.anchor )#">#e( link.text )#</a>
</cfif>
</li>
</cfloop>
</ul>
</nav>
</cffunction>
<cffunction name="e" output="false">
<cfargument name="text" />
<cfreturn encodeForHtml( text ) />
</cffunction>
<cffunction name="title" output="true">
<cfargument name="text" type="string" />
<h1>
#e( text )#
</h1>
</cffunction>
<cffunction name="title2" output="true">
<cfargument name="text" type="string" />
<h2>
#e( text )#
</h2>
</cffunction>
There's nothing "magic" going on here — just native CFML constructs being used to rapidly prototype an application concept. I just wanted to celebrate the ease with which this can be done in ColdFusion. Application development isn't the only thing that ColdFusion makes easy.
Want to use code from this post? Check out the license.
Reader Comments
Hey Ben,
Thanks for the post, a nice reminder of the versatility of CFML.
A developer can take one single file (such as index.cfm) and throw something together in minutes.
You can mix CFML with CSS, HTML, and JS all in one file, pretty darn useful for prototyping.
It might not be pretty, but that's not the point.
Too bad that some folks don't give CFML credibility, but who knows, maybe that will turn around.
Dang it... I jump through so many hoops to do what
output=truesolves natively. How did I not know (or had forgotten) that?!Bye bye cfsavecontent, return content variable 🤦
100% Ben. I have to do a lot of experimentation with a novel architecture, so I use tag-based CFML for rapid prototyping to figure out what works.
CFML is the best.
Sometimes, the simpler the tool, the easier it is to think, and the faster you get there. 😎
@Chris,
Yeah, the
output=truecan be great for this kind of stuff. But, it works best in a tag-based context. If you have script-based functions, it still works (I'm 99% sure); but, you'd have to do a bunch ofwriteOutput()kinds of stuff instead of just having tag-literal syntax.The
outputattribute has three possible values, which is always confusing:true- the function body executes as if its inside a<cfoutput>. This is what I'm doing, which is why the#just work.false- the function body has not output as all.attribute omitted - the function body will output the rendering content; but, it does not imply a
<cfoutput>tag. If you wanted to interpolate variables, you'd have to include an explicit<cfoutput>tag inside the function body.It's good stuff!
Hello Ben
I very much agree with your 'prototyping' template
Our template is similar, with everything unique to a cfm in the cfm itself
Only common code used by others in .cfc's
We came from desktop apps using MS.Access, and then to CF using the same .MDB data file
MS.Access also does layout and code in the same file
We also found that a simple prototype that allows a client to click around was much more productive that a formal specification.
A multiple choice drop-down with real values really helps tease out user requirements
I can see that CF is too easy, and that a strong wrapper like .Net Core is required
But there is also a small-business market that needs to be lower cost and agile.
We can make a user-requested change to a single .cfm, that only affects that .cfm, and within hours ask that user 'is this what you mean'
A strong 'cradle-to-grave' naming convention really helped manage projects - fieldname, control name, variable name, constants
I do want to thank you for all your help over the last few decades
Your annotated production-quality code examples were very much appreciated
[head]
(1) cfscript - local code
(2) handle POST - save,new,delete,calculate,dostuff,etc
(3) queries for output
[body]
(4) html output
@Trevor,
This is the mentality that really gets me through the day. A users asks for something and I make it happen with CFML - no fuss. I think in many ways we've (the industry) have become so process-oriented that we forget that we can just build! I can't tell you how many times I've been at work and had to spend more time "asking for permission" and "justifying my effort" and "running it up the chain" than it would actually take to build the dang thing in the first place!
CFML makes things so easy, I think it's unsettling for people sometimes and they're not used to the pace we can move at when we just decide to build.
Hello Ben
I do agree - I think there is a role for
low-level, low-cost, flexible software.
Really small business, not-for-profits, could
never afford custom software, but it can really help their productivity
And CF is just fantastic for this market
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →