Naming conventions fail for a boring reason: they live in a document nobody opens. The only convention that survives is one enforced in code, where a wrong value cannot be typed in the first place.
The rules
- Lowercase, always. Most tools treat
Spring_Saleandspring_saleas different campaigns and will silently split your numbers. - Underscores, never hyphens or spaces. Pick one separator and never revisit it. Hyphens read as minus signs in some spreadsheet tools.
- No dates in campaign names.
spring_salewith a date range in the report beatsspring_sale_march_2026, which fragments year over year. - `{trigger}_{intent}`. What caused the send, then what you want them to do.
booking_confirm,invoice_due,review_request. - Never put the vendor in `utm_source`. Use
imessage. You may switch providers; the channel does not change.
Enforce it in the type system
A union type turns a naming convention from a suggestion into a compile error, which is the only form of documentation that works.
// The full set of campaigns, in one place. Adding one is a deliberate act.export const CAMPAIGNS = [ "booking_confirm", "appointment_reminder", "appointment_nudge", "review_request", "invoice_due", "winback_offer", "gap_alert", "speed_to_lead",] as const; export type Campaign = (typeof CAMPAIGNS)[number]; // tagged() now rejects a typo at build time rather than at reporting time.export function tagged(url: string, tags: { campaign: Campaign; content?: string }) { const u = new URL(url); u.searchParams.set("utm_source", "imessage"); u.searchParams.set("utm_medium", "sms"); u.searchParams.set("utm_campaign", tags.campaign); if (tags.content) u.searchParams.set("utm_content", norm(tags.content)); return u.toString();}The union type is the documentation
Autocomplete shows the whole list at the call site. Nobody has to remember the convention, or find the wiki page, because the editor offers the correct values and refuses the wrong ones.
For the people who are not in the codebase
Front-desk staff sending from a provider inbox cannot import a type. Give them the UTM builder — it normalises to lowercase with underscores automatically, so the output is consistent regardless of what gets typed in.
Auditing what you already have
Export your campaign list, lowercase everything, and look for near-duplicates. Anything within an edit distance of two is almost certainly the same campaign recorded twice. Consolidate, then put the type in place so it cannot happen again.
Next step
Generate a tagged link for whatever you send next with the UTM builder, see what this looks like in your industry, or compare the services that can send it on the providers page.