FLOWLOGIC
module /data-shaping-and-validation

unit U22 of 5

Falsy values & coercion

?? vs ||, string numbers, string booleans

The most expensive bugs in data shaping are not missing fields — those are loud. They are values that exist, pass a truthiness check, and mean something different from what you assumed. `0` is a real deal size and a falsy value. An empty string is a present-but-useless company name. The string `"false"` is truthy. `null` means "known to be absent" while `undefined` means "never provided", and one API will send you both for the same concept.

The tools are precise, so use them precisely. `??` falls back only on `null` and `undefined`; `||` also fires on `0`, `""` and `false`, which is why a `dealSize || 100` default silently rewrites every genuinely zero-value deal. `?.` stops a lookup safely at a missing link rather than throwing. When a number arrives as a string — which it does, constantly, from form encodings and spreadsheets — convert it explicitly and check the result, because `Number("")` is `0` and `Number("12 units")` is `NaN`, and both will happily flow downstream.

Where it breaks: a default that hides a real value. `score || 50` looks like a sensible fallback and quietly replaces every legitimate score of zero with fifty. Reach for `??` unless you specifically mean "also replace empty and zero", and when you do mean that, say so in a comment so the next person does not "fix" it.

worked example

The same three fields handled carelessly and carefully — the difference is invisible until a zero arrives.

// careless — each line has a case where it silently lies
const size   = input.dealSize || 100;        // a real 0 becomes 100
const name   = input.company  || 'Unknown';  // "" becomes Unknown, fine — but so does a real ""
const active = Boolean(input.isActive);      // the STRING "false" is true

// careful
const size   = input.dealSize ?? 100;               // only null/undefined fall back
const name   = input.company?.trim() || 'Unknown';  // "" IS meaningless here, so || is right
const active = input.isActive === true || input.isActive === 'true';

// numbers arriving as strings, checked rather than trusted
const raw = Number(input.amount);
const amount = Number.isFinite(raw) ? raw : null;   // "" -> 0, "12 units" -> NaN

field checklist

common failure — Every zero-value deal became one hundred

A scoring step used `dealSize || 100` as a default for missing values. Free-trial signups legitimately have a deal size of zero, and every one of them was rewritten to one hundred — so the client’s pipeline forecast counted trials as real revenue for two quarters. `??` falls back only on null and undefined; `||` also fires on 0, "" and false. Choose deliberately and say which you meant.

check your understanding

A step uses `dealSize || 100` to default a missing value. Free-trial leads legitimately have `dealSize: 0`. What happens to them?

next unit opens once this is passed

sandbox validation

The check above confirms you followed the unit. Marking the module COMPLETED takes more: build the automation in your own engine and submit the exported flow and its run evidence, signed, to your unique validation URL. See the module page for that spec.