DEV Community

Cover image for I Built a Simple Script to Catch Content Strategy Drift Before It Costs a Quarter
Jack Miller
Jack Miller

Posted on

I Built a Simple Script to Catch Content Strategy Drift Before It Costs a Quarter

I doubled a client's content output last quarter and performance stayed flat. When I finally went back through the numbers, I found the real problem: out of 32 videos, only 4 represented genuinely distinct ideas. This post walks through the diagnostic script I built afterward to make sure I never miss this pattern again, and the underlying metric that actually predicts whether a content strategy is working.

The gap between render count and real testing

Most content dashboards surface one number by default: total pieces produced. That number is trivially easy to track and, on its own, tells you almost nothing about whether a testing program is actually learning anything. Thirty-two videos in a month looks productive on any dashboard. It looked productive on mine. What it was actually hiding: twenty-eight of those thirty-two were surface variants of two ideas we'd already validated weeks earlier, different avatars and slightly different phrasing wrapped around the same underlying argument.

The dashboard had no way to catch this, because render count and genuine angle diversity are two completely different things that happen to correlate loosely under some conditions and diverge sharply under others, specifically once a team starts scaling a known winner instead of exploring new ideas.

The metric that actually matters: the distinct angle ratio

The number worth tracking instead is straightforward to define, harder to track by default because no platform surfaces it automatically.

distinct_angle_ratio = distinct_structural_arguments / total_content_produced

My actual numbers that month:
distinct_angle_ratio = 4 / 32 = 0.125
Enter fullscreen mode Exit fullscreen mode

A ratio near 1.0 means nearly every piece of content represents a genuinely new idea. A ratio approaching 0 means the same handful of ideas are being repeated with cosmetic variation. There's no single universally correct target ratio, since it depends on where you are in a testing cycle, early exploration phases should run higher, later scaling phases naturally run lower, but tracking the number at all is the part most teams, including mine until recently, simply skip.

Why this number is invisible on every standard dashboard

Every analytics tool I've used treats a "piece of content" as an atomic unit, distinct by definition simply because it has a different file, a different ID, a different avatar or thumbnail. None of them ask the harder question: does this piece of content represent a genuinely different underlying argument, or is it a repackaged version of something already tested. That question requires actually reading and classifying each piece of content against its underlying persuasive structure, which is exactly the kind of judgment call a standard analytics dashboard was never built to make.

This is why the distinct angle ratio has to be computed manually, or with a lightweight classification pass, rather than pulled automatically from any existing reporting tool.

Building a simple classification script

Since no existing tool computes this automatically, I built a small script to make the manual classification step faster and more consistent. The approach: reduce each piece of content to a short, structured summary of its underlying argument type, then check for duplicates or near-duplicates within a given time window.

import json
from collections import Counter

# Each entry: content ID, and a manually tagged argument_type
# Argument types: discovery, objection_handling, social_proof, demo, native

content_log = [
    {"id": "vid_001", "argument_type": "objection_handling", "week": 1},
    {"id": "vid_002", "argument_type": "objection_handling", "week": 1},
    {"id": "vid_003", "argument_type": "objection_handling", "week": 1},
    {"id": "vid_004", "argument_type": "discovery", "week": 1},
    {"id": "vid_005", "argument_type": "discovery", "week": 2},
    # ... remaining entries for the month
]

def distinct_angle_ratio(content_log):
    total = len(content_log)
    distinct_types = len(set(item["argument_type"] for item in content_log))
    return distinct_types / total, distinct_types, total

ratio, distinct, total = distinct_angle_ratio(content_log)
print(f"Distinct angle ratio: {ratio:.3f}")
print(f"Distinct arguments: {distinct} out of {total} total pieces")
Enter fullscreen mode Exit fullscreen mode

This is intentionally simple. The actual work isn't the script, it's the manual tagging step, sitting down and honestly classifying each piece of content by its real underlying argument rather than its surface presentation. The script just makes tracking the resulting ratio over time trivial once the tagging is done.

The reduction test behind the tagging step

The tagging step above depends on a specific manual exercise: reducing a piece of content to its underlying argument in one sentence, stripped of avatar, specific wording, and visual choices.

Example reduction:

Original script (paraphrased): "I switched supplements three times 
before finding one with a strain count that actually matched what 
the research recommends."

Reduced argument: "objection_handling - specific, checkable claim 
addressing skepticism about product quality"

---

Original script (paraphrased): "I never realized how much my gut 
health was affecting everything else until I started paying 
attention."

Reduced argument: "discovery - general curiosity hook, resolves 
independently of the specific product"
Enter fullscreen mode Exit fullscreen mode

Running this reduction consistently across a batch of content is what actually catches the drift a render-count dashboard misses. Two videos with completely different specific wording can reduce to the identical underlying argument type, which is exactly what happened with 28 of my 32 videos that month.

Tracking the ratio over time, not just once

A single month's ratio tells you where you are. Tracking it over several cycles tells you whether you're drifting, and in which direction.

weekly_ratios = []

def track_ratio_over_time(content_log, weeks):
    for week in range(1, weeks + 1):
        week_content = [c for c in content_log if c["week"] == week]
        if week_content:
            ratio, distinct, total = distinct_angle_ratio(week_content)
            weekly_ratios.append({
                "week": week,
                "ratio": round(ratio, 3),
                "distinct": distinct,
                "total": total
            })
    return weekly_ratios

results = track_ratio_over_time(content_log, weeks=4)
for r in results:
    print(f"Week {r['week']}: ratio={r['ratio']}, "
          f"{r['distinct']} distinct / {r['total']} total")
Enter fullscreen mode Exit fullscreen mode

A declining ratio trend across consecutive cycles, even while total render count stays flat or grows, is the earliest reliable signal that a testing program has drifted from exploration into pure exploitation of an existing winner, usually without anyone deciding that drift should happen.

Why this problem gets worse as generation gets cheaper

It's worth naming the actual mechanism behind why this specific failure mode is becoming more common, not less, as AI-generated content scales. When producing a new piece of content required a real shoot, the cost of that shoot created incidental pressure toward making it count, since pure repetition felt genuinely wasteful at that price point. AI generation removes that friction almost entirely. A new avatar delivering the identical underlying script costs a fraction of a dollar and takes minutes.

That's the entire value proposition of AI generation, and it's also exactly why the discipline that used to be partially enforced by cost now has to be enforced deliberately, through a tracked metric, since nothing about the generation tool itself prevents fifty surface variants of one idea from looking like fifty genuine tests.

Setting an actual threshold worth alerting on

Once the ratio is being tracked, the next useful step is defining a threshold that triggers a real review rather than just watching the number drift downward indefinitely.

def check_drift_alert(current_ratio, baseline_ratio, threshold=0.3):
    """
    Alerts if current ratio has dropped more than `threshold` 
    proportion below the established baseline.
    """
    if baseline_ratio == 0:
        return False
    drop = (baseline_ratio - current_ratio) / baseline_ratio
    return drop > threshold

baseline = 0.5  # ratio from an established, healthy testing period
current = 0.125  # my actual number that month

if check_drift_alert(current, baseline):
    print("Drift alert: distinct angle ratio has dropped "
          "significantly below baseline. Review testing calendar.")
else:
    print("Ratio within normal range.")
Enter fullscreen mode Exit fullscreen mode

Running a check like this at the end of every planning cycle, rather than relying on catching the problem through an unrelated conversation the way I actually did, turns this from a reactive discovery into a proactive, scheduled review.

Category context matters for interpreting the ratio

A raw ratio number needs category context to interpret correctly, since the right target genuinely differs by product type. Trust-dependent categories, supplements, personal finance, need a wider range of angle types, objection-handling, specific claims, social proof, to overcome elevated baseline audience skepticism, which means these categories should maintain a higher target ratio on an ongoing basis. Visible-result categories like skincare can sustain a somewhat lower ratio without severe performance consequences, since the product's own demonstrated outcome carries persuasive weight regardless of angle sophistication.

category_targets = {
    "trust_dependent": 0.35,
    "visible_result": 0.20,
    "low_consideration": 0.10
}

def evaluate_against_category(current_ratio, category):
    target = category_targets.get(category, 0.25)
    if current_ratio < target:
        return f"Below target for {category} (target: {target})"
    return f"On or above target for {category} (target: {target})"

print(evaluate_against_category(0.125, "trust_dependent"))
# Output: Below target for trust_dependent (target: 0.35)
Enter fullscreen mode Exit fullscreen mode

This context matters because a flat 0.25 target applied uniformly across a mixed catalog will incorrectly flag healthy low-consideration accounts as underperforming while under-flagging genuinely concerning drift in trust-dependent accounts that need a much higher bar.

What I changed in my actual weekly process

Beyond the script itself, the real fix was procedural. I now commit to a target distinct angle count before generating any content for a given cycle, rather than deciding after the fact whether what got produced was diverse enough. This ordering matters specifically because it's trivially easy to satisfy a render-count target through surface variation alone if the structural target isn't locked in first, which is precisely how I ended up with a 0.125 ratio without ever consciously deciding to stop testing new ideas.

I also caught a second-order version of this same mistake about a month after building the initial fix: counting angles as "distinct" based on loosely different wording, while they actually shared the same underlying persuasive logic. This forced a stricter definition in the tagging step, classifying by actual argument type, not by surface text difference, which is reflected in the script's category-based tagging approach shown above.

Team-level tracking, not just individual

Once more than one person generates content for the same account, the ratio needs to be tracked at the account level, aggregating across contributors, rather than each person tracking their own individual output in isolation. Two people can each maintain a healthy-looking individual ratio while independently scaling the same confirmed winner, producing a combined account-level ratio that's much lower than either person's individual number would suggest.

def account_level_ratio(all_content_log):
    """
    Aggregates across all contributors for a shared account,
    since individual-level ratios can mask team-level drift.
    """
    return distinct_angle_ratio(all_content_log)

# Combine logs from multiple team members before computing
combined_log = team_member_a_log + team_member_b_log
ratio, distinct, total = account_level_ratio(combined_log)
print(f"Account-level distinct angle ratio: {ratio:.3f}")
Enter fullscreen mode Exit fullscreen mode

This is a small code change but a meaningful process one, since it requires actually merging contributor logs rather than letting each person self-report a number that only reflects their own slice of the account's total output.

The honest limitation of this approach

This entire system depends on honest, consistent manual tagging at the classification step, and no script fixes a tagging process that's being done carelessly or inconsistently. If argument types get assigned loosely, generously counting minor wording differences as genuinely distinct structural approaches, the ratio will look healthier than the underlying reality actually is. The script only automates the tracking and alerting layer on top of a manual judgment call that still requires real discipline to execute honestly, which is worth stating plainly rather than presenting this as a fully automated solution that removes human judgment from the process entirely.

Reproducing this on your own content calendar

Pull your last month of content, tag each piece by underlying argument type using the reduction exercise described above, and run it through the ratio calculation. Compare against a category-appropriate target rather than a flat universal number. If the ratio comes back lower than expected, and for most teams running this for the first time it does, that gap is the actual size of the blind spot between what your dashboard shows and what your testing program is really producing.

Extending the script into a full weekly report

Once the core ratio calculation is working, wrapping it into a scheduled weekly report makes the whole system easier to sustain than running the calculation manually every time. This is a simple extension that outputs a readable summary rather than raw numbers, which matters if the report is going to anyone besides the person who wrote the script.

def generate_weekly_report(content_log, category, baseline_ratio=None):
    ratio, distinct, total = distinct_angle_ratio(content_log)
    category_check = evaluate_against_category(ratio, category)

    report = f"""
    Weekly Content Strategy Report
    ================================
    Total content pieces: {total}
    Distinct argument types: {distinct}
    Distinct angle ratio: {ratio:.3f}
    Category: {category}
    Status: {category_check}
    """

    if baseline_ratio is not None:
        drift_flag = check_drift_alert(ratio, baseline_ratio)
        report += f"\n    Drift alert triggered: {drift_flag}\n"

    return report

print(generate_weekly_report(content_log, "trust_dependent", baseline_ratio=0.5))
Enter fullscreen mode Exit fullscreen mode

Running this at a fixed point every week, rather than only when something prompts a manual check, is what actually turns this from a one-time fix into an ongoing discipline. The report format above is deliberately plain text and easy to paste into a Slack message or a shared document, since the goal is visibility across a team, not a polished dashboard nobody actually reads.

What a genuinely healthy trend looks like over a full quarter

It's worth showing what recovery actually looks like once the tracking and the procedural fix are both in place, since a single snapshot number doesn't convey the trend that matters most.

quarter_data = [
    {"week": 1, "ratio": 0.125},   # the month I discovered the problem
    {"week": 2, "ratio": 0.18},    # first cycle after building the fix
    {"week": 3, "ratio": 0.22},
    {"week": 4, "ratio": 0.31},
    {"week": 5, "ratio": 0.35},
    {"week": 6, "ratio": 0.38},    # approaching healthy trust-dependent target
]

def plot_trend_summary(data):
    for entry in data:
        bar_length = int(entry["ratio"] * 50)
        bar = "#" * bar_length
        print(f"Week {entry['week']:2d}: {bar} {entry['ratio']:.3f}")

plot_trend_summary(quarter_data)
Enter fullscreen mode Exit fullscreen mode

This kind of simple, unpolished visualization, even just a text-based bar chart printed to a terminal, was more useful to me in practice than a more elaborate dashboard would have been, since it made the recovery trend immediately visible without requiring any additional tooling investment beyond the script already being run weekly.

A note on why I didn't build this into a full dashboard

I want to be direct about a decision I made deliberately: I kept this entire system as a lightweight script rather than building it into a polished internal dashboard, even though I had the option to invest more engineering time into it. The reasoning was straightforward. A polished dashboard takes real time to build and maintain, and that time competes directly against the actual content production and review work the ratio is meant to protect in the first place. A plain script that runs in a few seconds and outputs a readable report accomplishes the same core goal, catching drift before it compounds, without the additional maintenance burden a more elaborate tool would introduce.

This isn't a universal recommendation against building better tooling eventually. For a larger team managing many accounts simultaneously, a shared dashboard pulling this same calculation across multiple content logs automatically would likely be worth the investment. For a solo operator or a small team, the lightweight script version described throughout this post gets most of the real value at a fraction of the setup and maintenance cost, which is exactly the tradeoff that made sense for my own situation when I built this.

The bigger pattern this script is really catching

Stepping back from the specific implementation, what this script actually catches is a general pattern that shows up whenever repeated judgment work gets easier and cheaper to produce: output volume becomes a weak, sometimes actively misleading, proxy for output quality. This isn't unique to UGC video content specifically. The same underlying risk shows up in any workflow where generating more of something has gotten dramatically cheaper than it used to be, faster to produce more variants, easier to justify continuing to scale a known-safe choice rather than investing time in something genuinely new and uncertain.

Building a small, honest metric that specifically measures genuine variety, rather than trusting raw volume as an implicit proxy for it, is a pattern worth applying anywhere this same dynamic exists, not just in the one specific content workflow this post has focused on throughout.

Full script, combined

For anyone wanting to try this directly, here's the complete version combining every piece covered above into one runnable file.

import json
from collections import Counter

CATEGORY_TARGETS = {
    "trust_dependent": 0.35,
    "visible_result": 0.20,
    "low_consideration": 0.10
}

def distinct_angle_ratio(content_log):
    total = len(content_log)
    if total == 0:
        return 0, 0, 0
    distinct_types = len(set(item["argument_type"] for item in content_log))
    return distinct_types / total, distinct_types, total

def evaluate_against_category(current_ratio, category):
    target = CATEGORY_TARGETS.get(category, 0.25)
    status = "below target" if current_ratio < target else "on or above target"
    return f"{status} for {category} (target: {target})"

def check_drift_alert(current_ratio, baseline_ratio, threshold=0.3):
    if baseline_ratio == 0:
        return False
    drop = (baseline_ratio - current_ratio) / baseline_ratio
    return drop > threshold

def generate_weekly_report(content_log, category, baseline_ratio=None):
    ratio, distinct, total = distinct_angle_ratio(content_log)
    category_check = evaluate_against_category(ratio, category)
    report = (
        f"Total pieces: {total} | Distinct types: {distinct} | "
        f"Ratio: {ratio:.3f} | {category_check}"
    )
    if baseline_ratio is not None:
        report += f" | Drift alert: {check_drift_alert(ratio, baseline_ratio)}"
    return report

if __name__ == "__main__":
    content_log = [
        {"id": "vid_001", "argument_type": "objection_handling"},
        {"id": "vid_002", "argument_type": "objection_handling"},
        {"id": "vid_003", "argument_type": "discovery"},
    ]
    print(generate_weekly_report(content_log, "trust_dependent", baseline_ratio=0.5))
Enter fullscreen mode Exit fullscreen mode

Adapt the argument type list and category targets to whatever fits your own catalog. The structure matters more than the specific numbers, tag honestly, track the ratio consistently, and check it against a category-appropriate bar rather than a flat universal target.

Top comments (0)