Let’s say you are signing up for a subscription service and you’re told delivery is bi-monthly.
Question: Is that twice a month, or once every two months?
You likely just subconsciously picked the interpretation you prefer. And then you’ll either be surprised to see extra deliveries (and extra charges to your card) or be confused when the delivery did not arrive. Even if you caught the ambiguity and researched the answer, you still had to spend that time and pay the task-switching penalty.
If, instead of using ‘bi-monthly,’ the company spelled it out as “twice a month” or “once every two months,” you would have avoided all of that trouble.
In software, we face a similar problem with naming. There is a reason why this saying exists: “There are only two hard things in Computer Science: cache invalidation and naming things.”
Whereas the subscription service example has low stakes, the code that you modify can lead to performance, security, and behavior issues. Even if you are super careful and avoid the bugs, you are having to pay a huge overhead by second-guessing the variable names. That’s why it pays to use clear, unambiguous names.
Consider this code below. We want it to print “200” since the customer’s premium membership earns 200 points per dollar. But instead, it prints “0”. So somehow our premium members (with double earn rate) are getting less than normal members.
Can you tell where the bug is?
var customerStatus = Status.PremiumMember;
var rewardPoints = CalculateRewardPoints(customerStatus, 1.00)
console.WriteLine(rewardPoints) // Prints “0”; but it should be “200”
…
public decimal CalculateRewardPoints(Status customerStatus, decimal purchaseAmount){
if (customerStatus.IsNotMember) return 0;
if (customerStatus.IsPremiumMember) return purchaseAmount * 200;
return purchaseAmount * 100;
}
If you answered the first if clause “if (customerStatus.IsNotMember…” then correct! But why?
Looking at just the naming, nothing seems off at all. The status is some type of member so we should pass the first if clause, and then trigger the early return on the next if clause. But based on the behavior, it seems the 1st if clause is triggering. It’s nonsensical. Maybe somehow the purchaseAmount is being set to zero? That seems more likely.
Let me show you the missing piece:
public class Status
{
public static Status Premium => new Status { MemberType = PremiumMember};
MemberTypeEnum MemberType {get; set;}
public bool IsNotMember => MemberType != NormalMember;
public bool IsPremiumMember => MemberType == PremiumMember;
}
public class MemberTypeEnum
{
NotMember,
NormalMember,
PremiumMember
}
Cross-referencing with the earlier code, we see the smoking gun is the IsNotMember member; it’s only ever looking at the StandardMember and completely ignores the PremiumMember type. Meaning that as far as the code is concerned, our premium members are also not members!
Now you may point out that this is a dirty trick—that the name is deceptive and how could you have known what was going on.
And to that I say: yes, yes it was a very dirty trick. But I’m not the only one who does this—so does your codebase.
If you’ve worked with any large application, you’ll see parts of the code where “what it says it does” and “what it actually does” can be very different, which forces you to wade through large portions of the codebase. This turns future work from simple implementations into complex problems that require herculean effort (and even when finished, often encourage future bugs).
It is, therefore, vital to be diligent about naming—to help the interpretation line up with the actual behavior. For the above example, we could consider two improvements.
Rename the “IsNotMember” to “IsNotNormalMemberOrIsPremiumMember” as that reflects the true behavior. With that name, you’d probably see the bug immediately.
Keep the name “IsNotMember” but change the implementation to do what it says. “public bool IsNotMember => MemberType == IsNotMember”;
By taking similar steps in your codebase, you’ll be able to remove the ‘dirty tricks’ lying in ambush and reduce bugs while improving your time to deliver and your sanity (personally, I’m a big fan of the last one!).
Like in everyday communication, honesty and clarity matter so much—because #CodeIsCommunication