Skip to main content
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Steven Neiland
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Steven Neiland

Evolving Code Methodology: IF/ELSE Should Fit On One Screen

By
Published in Comments (6)

When it comes to branching logic in my code, I prefer guard statements with early returns whenever possible. But, such control flow isn't always possible, especially in a View file. In these cases, I'm evolving the heuristics used for my syntactic choices:

  • If the if/else statements fit comfortably on one screen, use them.

  • If the if/else statements can't fit on one screen, use two mutually exclusive if statements instead, one with a positive assertion and one with a negative assertion.

The mental problem that I'm trying to solve here is that if I get to an else statement, and I can no longer see the corresponding if statement, it's one more thing that I have to remember.

This happens often enough that I've noticed myself doing it: I'll have to scroll back up to make sure that this particular else statement is the one that corresponds to that if statement. By repeating the inverse condition of the if statement within the "else" block, it makes it super clear what the "else" block is trying to accomplish.

To illustrate, here's what my ColdFusion control flow would look like if it all fits neatly on one screen:

<cfoutput>

	<cfif records.len()>

		<table>
			<!--- ... content all fits on one screen ... --->
			<!--- ... content all fits on one screen ... --->
			<!--- ... content all fits on one screen ... --->
		</table>

	<cfelse>

		<p>
			<!--- ... content all fits on one screen ... --->
		</p>

	</cfif>

</cfoutput>

And, here's what my ColdFusion control flow would look like if the if block is large enough to force scrolling down in my text editor:

<cfoutput>

	<cfif records.len()>

		<table>
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
			<!--- ... content can't fit on one screen ... --->
		</table>

	</cfif>

	<cfif ! records.len()>

		<p>
			<!--- ... content all fits on one screen ... --->
		</p>

	</cfif>

</cfoutput>

In this second illustration, instead of an if/else control flow, we have two mutually exclusive if statements:

  • One uses the positive assertion: records.len().
  • One uses the negative assertion: ! records.len().

In the latter example, the intent of the code remains clear even as the rendered content pushes beyond the constraint of a single screen.

To be clear, I'm not anti if/else. I'm merely evolving my heuristics:

  1. Prefer guard statements and short-circuiting whenever possible.
  2. Prefer if/else for short blocks of code.
  3. Prefer if/if ! for long blocks of code.

This does mean a minor amount of repeated logic. But, the gain in readability has proven to be worth it.

Aside On Guard Statements In View Files

It's also worth noting that guard statements can be used in View files in CFML. Meaning, you can exit early from the execution of a regular .cfm file by using the cfexit tag. This is especially helpful if you ever want to globally include some logic while deferring the conditional execution of said logic to the context of the include.

For example, let's say you have some sort of dismissable banner that you need to conditionally render based on the existence of a cookie. You might include the banner in every layout, regardless of the cookie:

<cfinclude template="/shared/banner.cfm">

And then, within the template, check for the cookie and early-exit:

<cfif cookie.keyExists( "dismissed" )>
	
	<cfexit />

</cfif>

<div role="alert">
	<p>
		Did you know....
	</p>
</div>

Many programmers would wrap the entire contents of the banner.cfm template in a <cfif>, not knowing that you can exit out of a CFML template without terminating the execution of the overall page. But, by using a guard statement at the top of the View file, it keeps the rest of the template more left-aligned (ie, no "indentation hell"), keeps the intent of the control more clear, and makes the template easier to maintain in the long run.

Want to use code from this post? Check out the license.

Reader Comments

293 Comments

Consider me 🙋 among the novice programmers who would have just guarded the entire banner.cfm. Good to know about cfexit though. There's still so much about this language I don't know to spite my many (many) years working with it. But rather than hit the docs to expand my knowledge, I tend to keep reaching for the tools I already know. Shame on me!

Regarding the if/else vs if/if, I definitely dislike long logic statements that bleed off the page. Nested if/else is the worst offense of all (e.g. is this the else of the first if or second if?). However, if one block is super short such as <if !records.len()> which probably just has simple messaging around "no records yet", then I tend to lead with that because the else block can easily be seen within the context of the if statement. I don't NEED to see the entire else statement.

16,109 Comments

@Chris,

Uggg, I knew "novice" was the wrong word to use, even as I was writing it. I sometimes carry around too much baggage, at it leaks out on the screen. That was 100% me having some residual begrudgel about people hating on Custom Tags (which is where the cfexit tag comes into play the most).

Let me change that word choice right now, it makes me feel like a jerk-face :D

293 Comments

There was definitely no offense taken, @Ben! In some ways, I am still very much a novice. I find that I can be very productive with my 3 tools...screwdriver, hammer, and pliers. But it's also nice to know when to reach for the torque wrench. That's what I'm here for!

3 Comments

I hadn't really thought about the length of my <cfif> blocks. I guess once I get to the <cfelse> and I can't see the original <cfif>, I just use my code editor (VS Code) collapse functionality to collapse the content inside the <cfif>. I do like the approach you're taking with using two opposing <cfif> statements.

I think this gets more difficult to leverage if you're using <cfif>...<cfelseif>...<cfelseif>...<cfelse> blocks that have multiple boolean tests in each.

16,109 Comments

@Carl,

Yeah, with lot of cases, it's gets tricky. The place where I've run into that most (as of late) is when I have to differentiate between "no results" and "no matching results". Basically, to differentiate between the default state and the "search" state. I don't love the way it reads, but I tend to do it like:

<cfif ( ! results.len() && isFiltering )>
	<p>
		No results match your query.
		<a>Clear filters</a>
	</p>
</cfif>

<cfif ( ! results.len() && ! isFiltering )>
	<p>
		You don't have any data yet!
	</p>
</cfif>

Note that one is isFiltering and one is ! isFiltering. Sometimes I wrap them in the same cfif; sometimes I keep them separate (as shown here). I don't love either approach, aesthetically. But, this works well enough.

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel
Managed hosting services provided by:
xByte Cloud Logo