Module 19: Pattern — Benchmarking
Why Benchmarking Matters in Cast
Section titled “Why Benchmarking Matters in Cast”Telling a customer “Your product usage is 450 sessions” is informative. Telling them “Your product usage is 35% above the industry average for companies your size” is motivating. Benchmarking transforms raw numbers into context — and context drives action.
When built into Cast narrations, benchmarking:
- Makes data meaningful by providing a point of comparison
- Motivates high performers (“Congratulations — you’re in the top tier”)
- Creates urgency for underperformers (“Companies similar to yours average 20% higher usage”)
- Runs automatically — the CS team doesn’t write individual commentary
Pattern 1 — Simple Above/Below Benchmark
Section titled “Pattern 1 — Simple Above/Below Benchmark”The most basic pattern: compare a customer’s metric to an average and branch the commentary.
How it works
Section titled “How it works”{% raw %}{%- assign u = accountUsage | default: 0 | times: 1 -%}{%- assign a = avgUsage | default: 0 | times: 1 -%}
{%- assign percentage = u | divided_by: a | times: 100 | round: 0 -%} Your product usage is {{ percentage }}% of the industry average for companies similar to your size. Congratulations!{% else %} Your product usage is below the industry average for companies similar to your size. Contact your {{ "CSM" | cast_pronounce }} for ideas on how to increase your usage.
{% endraw %}Step-by-step
Section titled “Step-by-step”- Convert both values from strings to numbers with
| times: 1 - Compare: is the customer above or below the average?
- If above, calculate what percentage of the average they’ve achieved
- Display encouraging or actionable commentary based on the result
Real Cast example with data
Section titled “Real Cast example with data”If accountUsage is "680" and avgUsage is "500":
u = 680,a = 500680 > 500→ true680 / 500 * 100 = 136, rounded to 136
Output: Your product usage is 136% of the industry average for companies similar to your size. Congratulations!
⚠️ Guard against division by zero: If avgUsage is 0, the divided_by will fail. Add a check:
{% raw %}{%- if a > 0 -%} {%- assign percentage = u | divided_by: a | times: 100 | round: 0 -%} ...{%- endif -%}{% endraw %}Pattern 2 — Quarter-over-Quarter Change
Section titled “Pattern 2 — Quarter-over-Quarter Change”Compare a customer’s metric between two periods and describe the change — up, down, or flat.
{% raw %}{%- assign current = analysis_current_qtr_avg | default: 0 | times: 1.0 -%}{%- assign previous = analysis_prev_qtr_avg | default: 0 | times: 1.0 -%}
{%- if current > previous -%} {%- assign pct = current | minus: previous | divided_by: previous | times: 100 | ceil | abs -%} Compared to the previous quarter, your monthly average increased by {{ pct }}%. Awesome to see!{%- elsif current < previous -%} {%- assign pct = previous | minus: current | divided_by: previous | times: 100 | ceil | abs -%} Your average monthly usage decreased from the previous quarter by {{ pct }}%.{%- elsif current == previous -%} Your usage level is consistent with the previous quarter.{%- endif -%}{% endraw %}What’s happening
Section titled “What’s happening”- Both values are converted to decimals with
| times: 1.0— critical for accurate division - Three branches: increase, decrease, or unchanged
- Percentage calculation:
(difference / baseline) × 100 ceilrounds up andabsensures a positive number for display- The wording changes based on direction — “increased by” vs. “decreased by”
Why times: 1.0?
Section titled “Why times: 1.0?”Without decimal conversion, current | minus: previous | divided_by: previous would use integer division. If current is 520 and previous is 500:
- Integer:
(520 - 500) / 500 = 20 / 500 = 0→ 0% - Decimal:
20.0 / 500.0 = 0.04→ 4%
The integer version silently rounds to zero. Always use times: 1.0 for percentage calculations.
Guard against zero baseline
Section titled “Guard against zero baseline”{% raw %}{%- if previous > 0 -%} {%- # Safe to calculate percentage change -%}{%- else -%} No previous quarter data available for comparison.{%- endif -%}{% endraw %}Pattern 3 — Storage / License Utilization Percentage
Section titled “Pattern 3 — Storage / License Utilization Percentage”Show what fraction of a reserved capacity a customer is using, with highlighted key numbers.
{% raw %}{%- assign used = LicenseUsed | default: 0 | times: 1.0 -%}{%- assign reserved = LicenseReserved | default: 0 | times: 1.0 -%}
{%- if reserved > 0 -%} {%- assign pct = used | divided_by: reserved | times: 100 -%}
{%- assign pct = pct | round -%}
This site used {{ used | round: 0 | cast_highlight }} TiBs during this period, which is {{ pct | append: "%" | cast_highlight }} of your reserved capacity.
{%- if pct > 90 %} You're approaching full capacity. Let's discuss expanding your allocation. {%- elsif pct < 50 %} You have significant unused capacity. Let's explore how to maximize your investment. {%- endif -%}{%- else -%} No reserved capacity on file.{%- endif -%}{% endraw %}Key techniques
Section titled “Key techniques”cast_highlighton the two most important numbers — usage and percentage- Conditional
round— only round when the percentage is ≥ 1 (preserving precision for very small values) - Three-tier commentary: over 90% (upsell), under 50% (optimization), or neutral
Pattern 4 — Dynamic Benchmark Date Reference
Section titled “Pattern 4 — Dynamic Benchmark Date Reference”Include the current month and year as a benchmark timestamp in your narration:
{% raw %}{%- assign benchmarkMMYYYY = "now" | date: "%s" | date: "%B %Y" -%}Here are your metrics for the {{ benchmarkMMYYYY }} benchmark.{% endraw %}Output: Here are your metrics for the March 2026 benchmark.
💡 Why date: "%s" | date: "%B %Y" instead of just "now" | date: "%B %Y"? Both work, but the double-date pattern ensures you’re working with a normalized timestamp. In practice, "now" | date: "%B %Y" is simpler and produces the same result.
Combining Patterns — A Complete Benchmark Slide
Section titled “Combining Patterns — A Complete Benchmark Slide”Here’s a full narration that combines multiple benchmarking patterns:
{% raw %}{% comment %} Benchmark Comparison Slide {% endcomment %}
{%- # === Setup === -%}{%- assign name = contact_first_name | default: "there" -%}{%- assign company = contact_account_name | default: "your company" | cast_titlecase -%}{%- assign u = accountUsage | default: 0 | times: 1.0 -%}{%- assign a = avgUsage | default: 0 | times: 1.0 -%}{%- assign current = analysis_current_qtr_avg | default: 0 | times: 1.0 -%}{%- assign previous = analysis_prev_qtr_avg | default: 0 | times: 1.0 -%}{%- assign benchmark_date = "now" | date: "%B %Y" -%}
Hi {{ name }},
Here's {{ company | cast_apostrophe }} {{ benchmark_date }} performance snapshot.
{%- # === vs. Industry Average === -%}{%- if a > 0 -%} {%- assign vs_avg = u | minus: a | divided_by: a | times: 100 | round: 0 -%} {%- if vs_avg > 0 %}
Your usage is {{ vs_avg | abs }}% {{ "above" | cast_highlight }} the industry average.Great work! {%- elsif vs_avg < 0 %}
Your usage is {{ vs_avg | abs }}% below the industry average. Let's discuss strategiesto increase engagement. {%- else %}
Your usage is right at the industry average. {%- endif -%}{%- endif -%}
{%- # === vs. Previous Quarter === -%}{%- if previous > 0 -%} {%- assign qoq = current | minus: previous | divided_by: previous | times: 100 | round: 1 -%}
{%- if qoq > 0 %}Compared to last quarter, your monthly average increased by {{ qoq }}%. {%- elsif qoq < 0 %}Compared to last quarter, your monthly average decreased by {{ qoq | abs }}%. {%- else %}Your usage has been consistent quarter over quarter. {%- endif -%}{%- endif -%}{% endraw %}This single template generates completely different narrations depending on whether the customer is above or below average, improving or declining, or steady.
Common Mistakes
Section titled “Common Mistakes”❌ Forgetting to convert strings before comparisons or math:
{% raw %}
{% endraw %}Both are strings — this comparison is alphabetical, not numeric.
✅ Convert first:
{% raw %}{%- assign u = accountUsage | times: 1.0 -%}{%- assign a = avgUsage | times: 1.0 -%}
{% endraw %}❌ Not guarding against division by zero:
{% raw %}{%- assign pct = u | divided_by: a | times: 100 -%}{% endraw %}If a is 0, this errors.
✅ Always wrap division in a zero check:
{% raw %}
{%- assign pct = u | divided_by: a | times: 100 | round: 0 -%}
{% endraw %}❌ Using times: 1 instead of times: 1.0 for percentage calculations:
{% raw %}{%- assign u = accountUsage | times: 1 -%}{% endraw %}Integer conversion truncates decimals and causes integer division, producing wrong percentages.
✅ Use times: 1.0 for any percentage or ratio calculation:
{% raw %}{%- assign u = accountUsage | times: 1.0 -%}{% endraw %}Try It Yourself
Section titled “Try It Yourself”Exercise: Create a benchmark narration for NPS (Net Promoter Score). You have:
nps_score="72"(customer’s score)nps_benchmark="45"(industry average)
Write Liquid that:
- Converts both to numbers
- Calculates how many points above or below the benchmark the customer is
- Displays a message like “Your NPS of 72 is 27 points above the industry benchmark of 45.”
- Adds a congratulations message if above, or an improvement suggestion if below
Click to reveal the answer
{% raw %}{%- assign score = nps_score | default: 0 | plus: 0 -%}{%- assign benchmark = nps_benchmark | default: 0 | plus: 0 -%}{%- assign diff = score | minus: benchmark -%}
Your {{ "NPS" | cast_pronounce }} of {{ score | cast_highlight }} is {{ diff }} points above the industry benchmark of {{ benchmark }}. That's a strong signal of customer satisfaction!{% elsif diff < 0 %} Your {{ "NPS" | cast_pronounce }} of {{ score | cast_highlight }} is {{ diff | abs }} points below the industry benchmark of {{ benchmark }}. Your {{ "CSM" | cast_pronounce }} can help identify opportunities to improve customer satisfaction.{% else %} Your {{ "NPS" | cast_pronounce }} of {{ score | cast_highlight }} matches the industry benchmark exactly.
{% endraw %}Output: Your NPS of 72 is 27 points above the industry benchmark of 45. That's a strong signal of customer satisfaction!
Key details:
plus: 0for NPS (integer values, no decimals needed)diff | absto show a positive number in the “below” messagecast_highlighton the score andcast_pronounceon acronyms- Three branches: above, below, and exactly equal
What’s Next
Section titled “What’s Next”In Module 20, you’ll see how to chain everything together in a complete Welcome Slide pattern — combining cast_titlecase, cast_apostrophe, cast_pronounce, snippets, benchmarks, and conditional logic into one polished narration.
📖 Official documentation: