Stephanie
Engineering at Earnest
5 min readOct 21, 2016

--

Pete Hodgson is co-author #pairing

Anne sat down next to Sally, resting her coffee precariously near the edge of her desk.

“OK Sally, you mentioned at standup that you wouldn’t mind a pair. What are we working on today?”

“Thanks, Anne,” said Sally. “So I picked up that story to beef up some of the validation logic in our application form. More specifically, around the employment history section. You know how we have the concept of current employment and past employments?”

Anne nodded as she took a sip of coffee, then placed it back on the desk.

“Well,” Sally continued, “at the moment we don’t take whether an employment is current into account when validating the start and end date of the employment. We require the end date to be valid even if the applicant has specified that this is a current employment. So this morning I was planning to work on making that validation of end date optional, based on whether the employment has been flagged as current.”

“Gotcha”, said Anne. “So where do you think we should start?”.

Sally wrinkled her nose and thought for a moment. “Well actually this reminds me of some of the social security number (SSN) validation Andrew and I were working on last week.”

“I don’t get it,” said Anne with a frown.

“So with SSNs we had a similar issue of wanting to only validate them in certain circumstances. For example we didn’t want to validate the field for a co-applicant’s SSN if there wasn’t a co-applicant involved.”

“Oh, I see,” said Anne. “It’s sort of the same concept in both places — conditionally validating a field based on some other part of the application.”

“Right. I was just about to take a look at how we did that conditional validation for SSN. Here it is.”

Anne and Sally studied the code for a moment.

function isSsn(value) {
if (SSN_REGEX.test(ssn)) {
return valid();
} else {
return invalid('invalid SSN');
}
}
function conditionallyIsSsn(condition,value) {
if( !condition ) {
return valid();
} else {
return isSSN(value);
}
}

“OK, makes sense,” said Anne.

Sally wrinkled her nose again. “Mind if I sketch out how I think this could work for a conditional date?”

Anne gestured to Sally’s keyboard. “Go for it.”

Sally typed away for a moment.

function conditionallyIsDate(condition,value) {
if (!condition) {
return valid();
} else {
return isDate(value);
}
}

“Hmm, that looks legit, but there’s a bit of a smell here,” said Anne.

“What do you mean?” asked Sally.

“Have we talked about the DRY principle before?” asked Anne. Sally shook her head in the negative. “DRY stands for Don’t Repeat Yourself,” Anne continued. “Sometimes you can sort of step back from the code and squint a little and see the same pattern in multiple places. That’s a sign that there could be a violation of DRY. Here, let’s look at these two functions side by side.” Anne clacked at her keyboard for a moment, moving the two functions next to each other.

function conditionallyIsSsn(needsValidation,value) {
if(!needsValidation) {
return valid();
} else {
return isSSN(value);
}
}

function conditionallyIsDate(needsValidation,value) {
if(!needsValidation) {
return valid();
} else {
return isDate(value);
}
}

“Yeah, I see what you mean. Dang, these two functions are almost identical!” Sally exclaimed.

“So, what do you think we should do about this, Sally?” Anne asked, turning to her and taking a sip of coffee.

Sally paused for a moment, weighing up options.

“It looks like the pattern is always the same…” Sally said. “If the condition is met, the value is run through a validation function, which will return valid or invalid. If the condition is not met, we always return valid.”

Sally looked up from her musing to see Anne nodding along.

She continued, “The only difference between the two, the only thing that makes them unique to either SSN or date, is the validation function. And if we’re already passing a condition as an argument, could we also…”

Anne raised her mug to her lips, a smile showing from behind it.

“…pass the validation in as well?”

“Yep!” said Anne. “It’s what we call a higher-order function,” she explained as she started typing. “If we were to sketch it out…” *clack clack clack*

function conditionally(validationFn) {
return function conditionalValidation(needsValidation, value) {
if (!needsValidation) {
return valid();
} else {
return validationFn(value);
}
}
}

Sally looked on as she typed, and asked “So we’re returning a function too?!!!”

“Yes!” Anne answered. “A higher-order function is one that takes a function as an argument, or returns a function, or sometimes both.”

“How many times did you use ‘function’ in that sentence, Anne?!” Sally joked. “But seriously, that’s awesome and I think I get it!” She continued. “We could use our conditionally() function for any field in our form that only needs to be validated in a certain circumstance!”

“Yep!” Anne said. It was a perfect high-five moment. So they high-fived.

“Here’s how we could replace conditionallyIsSSN(),” Anne said as she typed. “We started off with…

function conditionallyIsSsn(needsValidation,value) {
if(!needsValidation) {
return valid();
} else {
return isSSN(value);
}
}

…and now we can express the same thing with this.”

const conditionallyIsSSN = conditionally(isSsn);

“WOW! Look how readable that is,” Sally exclaimed. “And back to the original task, we could manage the employment date validation like this, “ she said, typing:

const conditionallyIsDate = conditionally(isDate);

“Nice!” Anne said.

“A function that takes functions as arguments…that is pretty flippin’ sweet,” Sally murmured as she reached absentmindedly for Anne’s coffee mug and took a long, thoughtful sip. “But…”

“…kind of mind-bending,” Anne said.

“Exactly.”

They sat in silence for a moment, the only sound coming from Anne drumming her fingers on the desk.

Anne raised her mug to her lips, only to realize it was already empty. “Odd…” she mumbled, and started getting up to get a refill when Sally said, “…Wait, this reminds me…”

Anne lowered herself back into the chair. So close.

“..of when we ask the user for their address. We don’t require a ZIP code, but if they provide one we do validate that it’s five digits. Right now that’s implemented as a bespoke optionallyIsZip() validation, but really if you think about it it’s two more fundamental rules combined together in a specific way.”

“Right,” Anne said. “It must EITHER be empty OR be a ZIP code.”

“What if we used this same higher-order function approach to build that optionallyIsZip() validation in a DRYer way?”

A slow grin spread across Anne’s face.

“What??” Sally asked.

It was time.

“That’s exactly right. And let’s get into it…”

“Awesome!” Sally exclaimed.

“…as soon as I get another cup of coffee.”

--

--