Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Every Line Of Code That You Write Is An Explicit Decision To Make The Application Better Or Worse

By Ben Nadel on

CAUTION: This post is a very soap-boxy.

The other day, I was watching the new Hannah Gadsby comedy special (which is great, by the way), when something she said about Art got me thinking about Programming. She was pointing out that every aspect of a painting reflected an explicit decision made by the painter. Nothing in a painting happens by accident - every color, every arrangement, every stroke of the brush is a calculated part of the final portrait. For me, programming in JavaScript or ColdFusion or SQL is exactly the same thing. Every comment I put in the code, every character I write, every expression that I wrap in parenthesis is a calculated decision intended to make the application better.

Hannah Gadsby saying, Why?

My feelings about this are very strong. It's why I reject the "formatting" aspects of linting. It's why I'll never be a Golang programmer who feels the need to run gofmt. It's why I'll never use Prettier. It's why I'll never create an .editorconfig file. It's why I'll never run a command that ends in --fix. It's why I'll never have anything automatically change my code when I hit CMD+S.

Because, I've already made all of those decisions while I was writing the code.

Part of why I wanted to write this post is because, earlier this week, I had to write an if-statement in ColdFusion. And, the formatting for this if-statement wasn't immediately clear in my mind. I had to pause for a moment and think about what would make the code easiest to understand and maintain.

It might sound silly to pause and consider something as simple as an if-statement. But, let's step back for a moment and think about the fact that the following methods all generate the same output:

<cfscript>

	methods = [
		function( required boolean value ) {

			if ( value == true ) {

				return( "Yes" );

			} else if ( value == false ) {

				return( "No" );

			}

		},

		function( required boolean value ) {

			if ( value == true ) {

				return( "Yes" );

			} else {

				return( "No" );

			}

		},

		function( required boolean value ) {

			if ( value ) {

				return( "Yes" );

			} else {

				return( "No" );

			}

		},

		function( required boolean value ) {

			if ( value == true ) {

				return( "Yes" );

			}

			if ( value == false ) {

				return( "No" );

			}

		},

		function( required boolean value ) {

			if ( value == true ) {

				return( "Yes" );

			}

			return( "No" );

		},

		function( required boolean value ) {

			if ( value ) {

				return( "Yes" );

			}

			return( "No" );

		},

		function( required boolean value ) {

			return( ( value == true ) ? "Yes" : "No" );

		},

		function( required boolean value ) {

			return value == true ? "Yes" : "No";

		},

		function( required boolean value ) {

			return( value ? "Yes" : "No" );

		},

		function( required boolean value ) {

			return value ? "Yes" : "No";

		},

		function( required boolean value ) {

			return ( value == true )
				? "Yes"
				: "No"
			;

		},

		function( required boolean value ) {

			return value
				? "Yes"
				: "No"
			;

		},

		function( required boolean value ) {

			switch( value ) {
				case true:
					return( "Yes" );
				break;
				case false:
					return( "No" );
				break;
			}

		},

		function( required boolean value ) {

			switch( value ) {
				case true:
					return( "Yes" );
				break;
				default:
					return( "No" );
				break;
			}

		},

		// The following methods are the same, but with the check reversed - ie, checking
		// for "False" as the priority.

		function( required boolean value ) {

			if ( value == false ) {

				return( "No" );

			} else if ( value == true ) {

				return( "Yes" );

			}

		},

		function( required boolean value ) {

			if ( value == false ) {

				return( "No" );

			} else {

				return( "Yes" );

			}

		},

		function( required boolean value ) {

			if ( ! value ) {

				return( "No" );

			} else {

				return( "Yes" );

			}

		},

		function( required boolean value ) {

			if ( value == false ) {

				return( "No" );

			}

			if ( value == true ) {

				return( "Yes" );

			}

		},

		function( required boolean value ) {

			if ( value == false ) {

				return( "No" );

			}

			return( "Yes" );

		},

		function( required boolean value ) {

			if ( ! value ) {

				return( "No" );

			}

			return( "Yes" );

		},

		function( required boolean value ) {

			return( ( value == false ) ? "No" : "Yes" );

		},

		function( required boolean value ) {

			return value == false ? "No" : "Yes";

		},

		function( required boolean value ) {

			return( ! value ? "No" : "Yes" );

		},

		function( required boolean value ) {

			return ! value ? "No" : "Yes";

		},

		function( required boolean value ) {

			return ( value == false )
				? "No"
				: "Yes"
			;

		},

		function( required boolean value ) {

			return ! value
				? "No"
				: "Yes"
			;

		},

		function( required boolean value ) {

			switch( value ) {
				case false:
					return( "No" );
				break;
				case true:
					return( "Yes" );
				break;
			}

		},

		function( required boolean value ) {

			switch( value ) {
				case false:
					return( "No" );
				break;
				default:
					return( "Yes" );
				break;
			}

		}
	];

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// Make sure every method gives us the same output.
	for ( method in methods ) {

		echo( method( true ) & " , " & method( false ) & " <br />" );

	}

</cfscript>

If we run this ColdFusion code, we get the following browser output:

Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No
Yes , No

These functions all do the same thing. But, I would strongly argue that some of the decisions made in this code are Wrong; and, some of the decisions are Right. However, the wrongness and rightness of those decisions are heavily influenced by context; and variable names; and the expected evolution of the module in which they reside.

It's not cut-and-dry. When I was writing my if-statement, it was correct of me to stop and think about what would make the most sense. Because, that's what programming is - a million little decisions about how to make an application better.

If you think that having to "worry" about decisions like this is going to slow you down, I would posit that you're focusing on the wrong things. If speed of writing code is your North Star, you're leaving a ton of value on the table.

I Don't Always Make The Right Decisions

Please, do not walk away from this post with the assumption that I believe that I always make the right decisions. I often make the wrong decisions. That's part of growing as a programmer. I can look back at the code I wrote 2-years ago and take comfort in the fact that I think it's garbage; because, if I didn't, it would mean that I'm not getting any better at building applications.

Part of what allows me - and encourages me - to evolve is the fact that I'm making explicit decisions every time my fingers hit the keyboard. Every character that I put on the screen is an opportunity to either reaffirm previous decisions; or, to stop and consider new alternatives. And, I don't want anyone else making those decisions for me. I don't want anyone diminishing my ability to grow and evolve and become a better developer.

I Don't Inject My Strong Feelings Into Pull-Requests

At this point, it should be clear how strongly I feel about writing code. But, I will point out that I don't foist my decision making upon other people. At work, I look at a lot of Pull-Requests (PRs) every day. And often times, those PRs include formatting decisions that I don't agree with. But, I'll never push back on a decision simply because it is different. I give my teammates the same courtesy that I believe they give to me: an assumption that every line of code they wrote was written with intent and with purpose and with a robust decision-tree that they've been curating over the last decade.

And, who knows, maybe in 6-months, I'll look back at some of their decisions and realize that they were "right"; and, that I should change part of my decision-tree to match theirs.



Reader Comments

You left out the name of the function which provides the context for each story (code body) and without it, how can you sense the code has the right vibe?

(I couldn't resist the ask to leave a comment)

Reply to this Comment

I couldn't agree with you more. I work hard to balance performance and maintainability. That means different things to different people. On one hand I try to write every line to perform as well as possible but then step back and have to decide if it will make sense in 3 years when I see it next. If I have to give up 2% performance from time to time so that the code is obvious I will do it.

Keep up the great posts.

Reply to this Comment

@Keith,

Thank you, good sir. Striking that balance is always conscious decision. I start to feel this tension more when new operators are introduced to the language. For example, operators like the "null coalescing" (ie, Elvis Operator) or the "safe navigation" operator can throw people off when they haven't seen them before. But, at the very least, they are semantically meaningful; and are good to learn about.

But, yeah, I try to think about the future me and how hard or easy it will be for me to read the code.

Reply to this Comment

@Ted,

Ha ha, it's funny you mention that -- I actually tried that on my first attempt. However, at least in Lucee CFML, you can treat a "function declaration" (which has a name) like a "function expression". Attempting to add a name to the function causes a syntax error.

That said, I could have used a Struct instead of an Array to define the methods, like:

<cfscript>
	methods = {
		a: function( required boolean value ) { ... },
		b: function( required boolean value ) { ... },
		c: function( required boolean value ) { ... },
		d: function( required boolean value ) { ... },
		// ....
	};
</cfscript>

And then just used struct-iteration.

And, I'm glad that someone finally reacted to the commenting call-to-action :D

Reply to this Comment

@KL,

With such a tiny function, I probably would have gone for:

function( required boolean value ) {
	return( value ? "Yes" : "No" );
}

... since there is so little logic and the expression is so tiny.

However, if the logic was a little more complicated, I would have likely started using the explicit if / else syntax:

function( required boolean value ) {
	if ( value ) {
		return( "Yes" );
	} else {
		return( "No" );
	}
}

And, once the logic gets more complicated, or the if condition is much shorter then the else condition, that's when I start using the "guard statement" approach, where I short-circuit the execution:

function( required boolean value ) {
	if ( value ) {
		return( "Yes" );
	}
	return( "No" );
}

Generally speaking, I try to follow these rules:

  • Make it "as long as necessary", but no longer.
  • Use positive conditions as much as possible.
  • Use guard statements when conditions are no longer equal in size.

It's as much Art as it is Science to be sure!

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.