Skip to content

Tag Design

Good tag design makes invalidation natural. Bad tag design leads to stale data, mass hysteria, and eventually a Hacker News post that nobody will read about why caching is bad.

Structure tags from general to specific:

// Good: hierarchical
const tags = defineTags({
org: (id: string) => ['org', id],
orgTeams: (id: string) => ['org', id, 'teams'],
team: (orgId: string, teamId: string) => ['org', orgId, 'team', teamId],
teamMembers: (orgId: string, teamId: string) => ['org', orgId, 'team', teamId, 'members'],
});
// Invalidate org → invalidates all teams, members
// Bad: flat (no prefix matching benefit)
const tags = defineTags({
user: (id: string) => ['user', id],
posts: (userId: string) => ['posts', userId], // Not under 'user'
});
// Invalidating user doesn't invalidate posts!
// Good: includes both IDs
const tags = defineTags({
postComments: (postId: string, authorId: string) =>
['post', postId, 'comments', 'author', authorId],
});
// Can invalidate by post OR by author