Name Your Conditions, Calm Your Logic!

Thu, June 5, 2025 - 2 min read
Developer simplifying a conditional

🧭 Avoid Complex Expressions Inside Conditionals

We once inherited a module from an external team. Buried inside was an if-statement spanning half the file: if (isUser && hasSubscription && !isExpired && (!isPromo || promo.isActive) && (plan === 'pro' || plan === 'enterprise') && region !== 'blocked' && !isBannedByMarketing).

While the original author was around everything worked. The moment we had to tweak the rules, we spent hours deciphering the chain. The cure is simple: extract intent into named predicates or boolean helpers.


The heavyweight conditional

if (
  user &&
  user.subscription &&
  !user.subscription.expired &&
  (!campaign || campaign.active) &&
  (user.plan === 'pro' || user.plan === 'enterprise') &&
  !blockedRegions.includes(user.region) &&
  !marketingBanList.includes(user.id)
) {
  showStories();
}

It probably means “show stories if the user is premium and not blocked”, yet nobody sees that at a glance. Every change requires scanning each clause.


Give the condition a name

const canShowStories = isPremiumSubscriber(user) && isCampaignAllowed(campaign) && isRegionWhitelisted(user.region) && isMarketingApproved(user.id);
 
if (canShowStories) {
  showStories();
}

And keep the helpers nearby:

const isPremiumSubscriber = (user: User) => user.subscription?.isActive && ['pro', 'enterprise'].includes(user.plan);
const isCampaignAllowed = (campaign?: Campaign) => !campaign || campaign.active;
const isRegionWhitelisted = (region: string) => !blockedRegions.includes(region);
const isMarketingApproved = (userId: string) => !marketingBanList.includes(userId);

Now the code reads like a business rule. Intent lives in function names, and each branch is easy to cover with tests.


Other places that benefit from named predicates

  • 📆 Plans and subscriptions. We manage 30 packages; a helper like canAccessStoryEditor(plan) beats a long list of comparisons inline.
  • 🧪 Feature flags and experiments. Replace if (flagA && !flagB && user.country !== 'DE') with isStoryExperimentEnabled(user).
  • 🔁 Async readiness checks. When timing, queue state, and availability flags matter, wrap them in shouldSyncAnalytics().
  • 🌐 Environment decisions. Dev, stage, trunk, and prod often diverge. isProdEnvironment() reads clearer than env === 'production' && cluster !== 'staging' tucked inside a condition.

Checklist before refactoring a condition

  1. Clarify the rule. State it in plain language: “We show the banner when…”.
  2. Pick a meaningful name. Describe intent, not implementation (shouldShowBanner, canTriggerReminder).
  3. Extract the pieces. Move repeated fragments into helpers or constants.
  4. Add focused tests. Cover each predicate so changes stay safe.

Takeaway

Complex boolean expressions hide intent and slow teams down. Named predicates make logic self-explanatory, ease onboarding, and keep future changes predictable. Keep the magic out of your if — clarity wins every time.