Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2018 (Hollywood, CA) with: Sara Dunnack
Ben Nadel at InVision In Real Life (IRL) 2018 (Hollywood, CA) with: Sara Dunnack

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

By
Published in , , , Comments (8)

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.

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

Reader Comments

2 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)

8 Comments

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.

15,902 Comments

@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.

15,902 Comments

@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

15,902 Comments

@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!

1 Comments

Here's why I disagree with the premise here: when you are working on a shared code-base, each developer will have their own strong opinions of which version of your method is better. Over the years, the method will be modified by several developers. If each one of them shared your take on this issue, they would each reshape the method to fit their idea of what "good" is. This leads to thrashing, increased git diffs, a hard-to-follow history, and ultimately an inconsistent code base where each method is written differently depending on the mood/beliefs of the developer that wrote it.

Because of that, I'm a strong believer in one-way, deterministic formatters such as Python's black or C++'s clang-format.

15,902 Comments

@Gus,

I would argue that what you are saying is going to be true no matter what you do. When you have many people working in a codebase, the codebase will end up looking like it was written by many people - there's no tooling that will ever be able to stop that.

I do want to touch on one thing you said though: increased git diffs. And, I want to call that out because I think it is critical that I add clarity here on what I think is the "good citizen" way to update code. People should not be arbitrarily updating code en masse based on their personal preferences. Meaning, if a developer has to update 20-lines in a 400-line file, they should not, for example, convert indentation to spaces in that file. That could lead to a 400-line git-diff when only 10-lines needed to be changed.

The other one I see is when people have some sort of "trim tailing white space" settings in their IDE; and then they submit a PR in GitHub that has 300-lines of single-space removal in a code change that touched 2-lines in a file.

This is not being a good citizen and will lead to issues that you are referring to. This is lazy. This is a developer who looked at their PR, saw all the extraneous changes they made (probably unintentionally), and thought to themselves, "Meh, I'll just submit it anyway."

I want to work with good, thoughtful people. I don't want to have to rely on tools that allow me to work with people that are less thoughtful. For me, that's solving the wrong problem.

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