Validation & Naming topic
Validation & Naming
analytics_gen treats the tracking plan as production code. Parsing fails fast, generated files stay deterministic, and CI can block regressions before they land. This document explains the schema, common validation errors, and how to troubleshoot them.
Command Reference
| Command | Purpose |
|---|---|
dart run analytics_gen:generate --validate-only |
Parse every YAML file without writing outputs. Use in CI pre-checks. |
dart run analytics_gen:generate --plan |
Print the parsed plan: domains, events, parameters, fingerprint. Great for debugging. |
dart run analytics_gen:generate --docs --exports |
Generate code, docs, and external exports with the deterministic fingerprint. |
YAML Schema Cheatsheet
domain_name:
event_name:
description: Human readable details
deprecated: false # Optional, mark deprecated events
replacement: auth.login_v2 # Optional pointer when deprecated
event_name: "Screen: {name}" # Optional override sent to providers
identifier: auth: login # Optional canonical identifier
parameters:
param_key:
type: string # Types: string, int, double, bool, map, list, custom
description: Why this matters
identifier: userId # Override generated Dart parameter
param_name: user-id # Override provider payload key
allowed_values: [card, paypal]
Accepted Types
The generator maps YAML primitive names to Dart types. Prefer lower-case primitives in your plan for consistency.
| YAML Type | Dart Type | Notes |
|---|---|---|
string |
String |
|
int |
int |
|
double |
double |
|
bool |
bool |
|
list |
List<dynamic> |
Use list<String> etc. if supported by your provider adapter. |
map |
Map<String, dynamic> |
|
DateTime |
DateTime |
Custom types pass through as-is. |
Nullable parameters: Append ? to the type (e.g., string?, int?).
Rules of thumb:
parametersmust be a map (use{}when none).- Nullable parameters append
?(e.g.,string?). - Custom types (like
DateTime) pass through directly; ensure your providers can handle them.
Naming Strategy
Configured under analytics_gen.naming:
enforce_snake_case_domains/enforce_snake_case_parameters: keep filesystem-safe keys and predictable Dart APIs. Disable only for legacy plans.event_name_templateandidentifier_template: control the canonical strings when an event omits overrides.domain_aliases: map snake_case domains to human-friendly labels for doc/export placeholders.
Uniqueness enforcement is performed on the resolved identifier (override > template). Duplicate identifiers abort generation so no two YAML entries can emit the same analytics payload.
Migrating from legacy naming
Many teams inherit plans with camelCase or kebab-case. Recommended path:
- Freeze identifiers – set
identifier_template(or per-eventidentifier) to the legacy strings so analytics payloads stay stable. - Disable enforcement temporarily – set
enforce_snake_case_*: falseso the parser ingests existing YAML without blocking. - Normalize domain-by-domain – pick a domain (
marketingLaunch→marketing_launch), rename the YAML key, and updateidentifieronly if the canonical string should change. Regenerate + review artifacts each time. - Re-enable enforcement – once every domain/parameter follows snake_case, flip the flags back to
trueto prevent regressions.
Document the migration in analytics_gen.yaml comments or README so future contributors know why the temporary relaxation existed.
Placeholder Interpolation
Placeholders declared in event_name ("Screen: {screen_name}") map to generated Dart variables:
logger.logEvent(
name: "Screen: ${screenName}",
parameters: {
"screen_name": screenName,
if (previousScreen != null) "previous_screen": previousScreen,
},
);
- Placeholder keys must match YAML parameter names exactly.
- Unknown placeholders stay as-is; this keeps the YAML explicit and predictable.
Common Validation Errors
| Error | Why it happens | Fix |
|---|---|---|
Domain "Auth" ... violates the configured naming strategy |
Domain keys must satisfy the configured casing rules. | Rename to auth or set enforce_snake_case_domains: false. |
Parameter identifier "userId" ... violates the configured naming strategy |
Parameter identifiers default to snake_case. | Use identifier: userId to expose camelCase in Dart or relax enforcement globally. |
Duplicate analytics event identifier |
Two events resolve to the same canonical identifier. | Provide unique identifier values or adjust the template. |
Parameters ... must be a map |
YAML indentation/structure is invalid. | Ensure parameters points to a map (use {} when there are no parameters). |
Allowed values must be a non-empty list |
allowed_values was empty or not a list. |
Provide at least one allowed value or remove the guard. |
CI should call --validate-only to catch these failures without touching generated files.
Runtime Guards
allowed_valuesproduces runtime assertions in generated methods. Passing a disallowed value throwsArgumentErrorimmediately, making mistakes obvious in tests.- Deprecated events include their replacement in generated documentation and mixin comments so you can migrate safely.
- Custom types (e.g.,
DateTime,Uri) pass through unchanged. Ensure your providers can serialize them; otherwise, transform to primitives before logging (e.g., convertDateTimeto ISO 8601 strings). Add unit tests exercising provider adapters so unsupported types fail fast instead of silently dropping data.
Privacy & Compliance (PII)
Following industry best practices (including Google’s recommendations for Analytics), Personally Identifiable Information (PII) should never be sent to your analytics providers.
- What is PII? Email addresses, phone numbers, precise location data, full names, or any data that can identify a specific individual.
- Why? Sending PII leads to data pollution, legal risks (GDPR/CCPA), and can result in your analytics account being suspended.
- Best Practice: Use hashed identifiers (like
user_id) instead of raw data. If you need to track user identity, do it through secure backends or dedicated CRM systems, not via event parameters.
Before adding a new parameter, ask: "Could this data be used to identify a specific person?" If the answer is yes, do not include it in your tracking plan.
Deterministic Outputs
YAML files, domains, and events are sorted before emission. Docs, JSON, SQL, and SQLite exports embed a fingerprint derived from the plan content (no timestamps). Byte-for-byte consistency keeps PR diffs reviewable, and you can safely re-run generation locally or in CI without noisy churn.
Troubleshooting Checklist
- Run
--planto inspect the parsed structure. - Confirm naming strategy values in
analytics_gen.yaml. - Search for duplicate
identifierstrings across the plan (rg "identifier:" events). - Re-run
--validate-onlybefore generating code to ensure errors are resolved.
Still confused? Pair this doc with the Onboarding Guide and share plan context in PR descriptions so reviewers can spot issues faster.
CI/CD Integration
To ensure your analytics plan and generated code stay in sync, add a validation step to your CI pipeline.
GitHub Actions Example
Create .github/workflows/analytics_check.yml:
name: Analytics Check
on:
pull_request:
paths:
- 'events/**'
- 'analytics_gen.yaml'
- 'lib/src/analytics/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
with:
sdk: stable
- name: Install dependencies
run: dart pub get
- name: Validate Analytics Plan
run: dart run analytics_gen:generate --validate-only
- name: Check for Uncommitted Changes
run: |
dart run analytics_gen:generate --docs --exports
if [[ -n $(git status --porcelain) ]]; then
echo "Error: Generated files are out of sync. Please run generation locally and commit changes."
git status
git diff
exit 1
fi
This workflow:
- Validates the YAML schema.
- Regenerates all artifacts.
- Fails if there are any uncommitted changes (ensuring generated code matches the plan).
Strict Event Naming
By default, analytics_gen enforces strict event naming to prevent high-cardinality issues. This means you cannot use string interpolation (e.g., View ${page_name}) in event names.
Why? Analytics providers like Amplitude and Mixpanel treat each unique event name as a separate event type. If you include dynamic values in event names, you can quickly exceed your event limit and make your data unmanageable.
Bad Practice (Blocked by Default):
view_screen:
event_name: "View Screen: {screen_name}" # ❌ Dynamic event name
parameters:
screen_name:
type: string
Good Practice:
view_screen:
event_name: "View Screen" # ✅ Static event name
parameters:
screen_name:
type: string
If you absolutely must use dynamic event names (e.g., for legacy reasons), you can disable this check in analytics_gen.yaml:
analytics_gen:
rules:
strict_event_names: false
Libraries
- analytics_gen Overview Onboarding Validation & Naming Capabilities Migration Guides Scalability & Performance Code Review
- Analytics Gen - Type-safe analytics event tracking with code generation.