Building a Console Tool to Validate Progression Systems
One of the quiet challenges of running a living campaign system is progression balance.
When advancement rules change, the real question isn’t “Does the code work?” — it’s:
How does this change affect real characters who already exist?
In my Westmarches campaign platform, I recently added a console command designed specifically to answer that question.
This post walks through the motivation, implementation, and lessons behind the Game Advancement Comparison Command.
The Problem: Progression Changes Are Hard to Validate
Westmarches campaigns evolve.
Rules shift.
Advancement systems get tuned.
Experience pacing changes.
When that happens, I need to know:
- Do characters level faster or slower?
- Who benefits from the new system?
- Who gets unintentionally penalized?
- Is the change fair across the player base?
Manual comparison isn’t realistic once dozens of characters and sessions exist.
So instead of guessing, I built tooling.
The Solution: A Console Comparison Command
This commit introduces a new Yii console controller command:
php yii command/compare-game-advancement <oldCampaignId> <newCampaignId>
The goal is simple:
Calculate character advancement using two different campaign configurations and compare the results.
Rather than modifying data, the command performs a pure analysis pass.
Why a Console Command?
This isn’t an admin UI feature.
It’s developer tooling.
A console command gives me:
- Fast execution
- Scriptability
- Repeatability
- Zero risk to production data
- Easy integration into migration workflows
It becomes a diagnostic instrument rather than an application feature.
How It Works
1. Validate Campaign Inputs
The command first ensures both campaigns exist.
If either campaign cannot be found, execution stops immediately with a clear message.
This protects against accidental comparisons and bad input.
2. Gather Characters from the Original Campaign
We treat the old campaign as the source of truth:
- Load all characters belonging to the original campaign
- Abort early if none exist
This ensures we’re comparing identical player histories.
3. Recalculate Advancement Twice
For every character:
- Fetch all games played
- Run advancement calculation using:
- Old campaign rules
- New campaign rules
Conceptually:
oldLevel = advancement(oldRules)
newLevel = advancement(newRules)
No database updates occur.
We are strictly observing outcomes.
4. Output Machine-Readable Results
The command prints structured output:
{
"Aria": ["3", "4"],
"Thorn": ["5", "5"],
"Mira": ["2", "3"]
}
Each entry represents:
Character Name: [Old Level, New Level]
This format makes it easy to:
- Paste into spreadsheets
- Diff results
- Feed into scripts
- Visualize balance changes
Why This Matters
Game systems often fail not because mechanics are wrong, but because migration effects aren’t understood.
Changing progression without analysis can:
- Accidentally nerf long-time players
- Inflate power levels
- Break encounter balance
- Create perceived unfairness
This tool turns progression design into something measurable.
Instead of arguing about theory, I can now answer:
“What actually happens to my players?”
Hidden Design Principle: Tooling Over Guesswork
This commit reflects a philosophy I try to follow:
If I need to check something twice, it deserves a tool.
Rather than manually inspecting characters, I invested a small amount of engineering effort to create permanent infrastructure.
Future benefits include:
- Testing experimental advancement systems
- Comparing seasonal campaign rules
- Running balance audits before launch
- Supporting automated regression testing
What I Like About This Approach
A few intentional decisions stand out:
Stateless Analysis
The command reads data but never mutates it.
Safe tools get used more often.
Reuse Existing Logic
Instead of duplicating advancement rules, it calls:
CampaignCharacter::advancement(...)
One source of truth means fewer bugs.
Developer-First UX
Clear stdout messages and structured output make the command pleasant to use.
Small touches matter when tools become part of daily workflow.
Future Improvements
Some ideas already on the roadmap:
- CSV export option
- Level delta summaries
- Percentage change reporting
- Highlight characters most impacted
- Automated balance reports in CI
Eventually, this could evolve into a full campaign simulation toolkit.
Final Thoughts
Game design often focuses on creativity, but scalable campaign management demands engineering discipline.
This command bridges the two.
It lets me experiment with progression systems confidently, knowing I can measure the real consequences before players ever feel them.
And honestly, that’s one of my favorite kinds of code:
Not flashy.
Not user-facing.
Just quietly making better decisions possible.