Skip to main content

09 / 16

Control flow

Brace every branch, handle every enum value, and keep conditions near their values.

Control flow gets messy as code is edited over time. These rules keep a later change from landing in the wrong branch.

Rules

  1. 01Put braces on their own lines, including single-statement branches.
  2. 02Use nullptr instead of NULL or 0.
  3. 03Use else only when the previous branch can continue. A branch that returns does not need one.
  4. 04Mark intentional switch fallthrough. Empty cases that share the next body remain clear without a comment.
  5. 05Keep a condition close to the values it depends on. A variable initialized 100 lines early becomes hidden mutable state.
  6. 06Mark an intentional fallthrough with the standard [[fallthrough]] attribute. Unreal 5.8 ships no engine macro for it, so the attribute is the portable way to tell a reader the missing break was deliberate.
  7. 07Handle every enumerator explicitly and put checkNoEntry() in the default case when no fallback is safe. A default: that quietly returns a value hides a newly added enumerator from compiler exhaustiveness warnings.
  8. 08Declare each variable at first use and initialize it there. A default-constructed variable declared early is mutable state that a later edit can accidentally start depending on.
  9. 09Return early instead of nesting. Every extra level of indentation is one more condition the reader has to carry in their head to the bottom of the function.

Use braces and handle unexpected enum values

Braces keep later edits inside the intended branch, while the default case gives an unexpected value a defined result.

DamageRules.cppcpp
float GetDamageMultiplier(const EHitZone HitZone)
{
    switch (HitZone)
    {
        case EHitZone::Head:
            return 2.0f;

        case EHitZone::Torso:
            return 1.0f;

        case EHitZone::Arm:
        case EHitZone::Leg:
            return 0.75f;

        default:
            ensureMsgf(false, TEXT("Unhandled hit zone: %d"), static_cast<int32>(HitZone));
            return 1.0f;
    }
}
Why
Adding another line cannot silently move it outside an unbraced branch. An invalid enum value also produces a diagnostic before the function returns its safe fallback.
Costs
A default case can hide a newly added enumerator from compiler exhaustiveness warnings. Use checkNoEntry when every enumerator must be handled and no fallback is safe.
Instead
Use a lookup table when the result is data rather than behavior. Polymorphism fits when each mode owns enough logic to justify its own type.

All 16 rules