Role
Design & Development
Context
Enterprise ticket management app
Users
Customers, Engineers, Admins
The Problem
A ticket management system with three user types: customers who submit tickets, engineers who work on them, and admins who oversee everything. When something happens on a ticket, who should know about it?
The challenge
Not everyone should see everything. Internal team discussions need to stay internal. Customers should only see updates meant for them. And nobody wants to be notified about their own actions.
The system needed to be smart enough to figure out who cares about each update, respect visibility boundaries, and deliver notifications through the right channel - all without overwhelming anyone.
Figuring Out Who Gets Notified
The first design decision: rather than calculating recipients on the fly each time, create a persistent list of "watchers" for each ticket. Anyone who cares about a ticket gets added to its watcher list.
Why watchers?
- Clear record of who's following each ticket
- Users can control their own notifications (watch/unwatch)
- Easy to add people when they get assigned or show interest
- Audit trail of who was notified and when
The visibility rules
Comments can be marked as "internal only" or "visible to customer." This creates a matrix of who gets notified:
Customer adds a comment
All assigned staff get notified. The customer doesn't get notified about their own comment.
Staff adds a visible comment
Other assigned staff and the customer get notified. The commenter is excluded.
Staff adds an internal comment
Only other internal staff get notified. Customers never see internal discussions.
Self-exclusion
You never get notified about your own actions. This prevents notification loops.
Transparency Before Sending
When staff write a comment, they see exactly who will be notified before they hit submit. A live preview updates as they change visibility settings.
Email notification will be sent to:
Why show this to staff?
- Builds trust - staff understand what the system is doing
- Prevents mistakes - "Wait, I don't want the customer to see this yet"
- Reduces anxiety - no wondering if the right people got the message
Customers don't see this preview - they simply submit their comment and know it will reach the team. This keeps the staff list private while giving internal users the visibility they need.
The In-App Experience
Bell icon with unread count
A persistent bell icon in the header shows unread notifications at a glance. The count updates in real-time - even across multiple browser tabs. Mark one as read in one tab, and the count updates everywhere.
Notification types
Different events get different visual treatments so users can quickly scan what needs attention:
Click to navigate
Each notification links directly to the relevant ticket. But here's the nuance: different user roles see different views of the same ticket. The notification automatically routes each user to the view they have access to - no permission errors.
Small Details That Matter
Favicon badge
When there are unread notifications, a small red dot appears on the browser favicon. Users can see at a glance - even when the tab is in the background - that something needs their attention.
Cross-tab sync
Mark a notification as read in one tab, and it's marked as read in all your open tabs. No stale data, no confusion about what you've already seen.
User preferences
Not everyone wants the same notification experience. Users can choose: in-app only, email only, both, or muted entirely. The system respects these preferences at the moment of delivery.
Performance That Feels Invisible
The best performance optimizations are the ones users never notice - things just feel fast.
Preloading in the background
When the page loads, notifications are quietly fetched in the background before the user clicks anything. By the time they click the bell icon, data is already there. No spinner, no waiting.
Instant feedback on actions
When you mark a notification as read, the UI updates immediately. The server call happens in the background. If something goes wrong, the system handles it gracefully - but 99% of the time, users just see instant response.
Smart refresh logic
The system doesn't constantly poll for updates. It checks periodically in the background, and only when the browser tab is active. Switching back to a tab after being away triggers a fresh check. Opening the dropdown shows cached data instantly, then quietly refreshes if needed.
Cached across sessions
Notification state persists locally. Refresh the page and your unread count is there immediately - no flash of empty state while waiting for the server.
Security by Design
One architectural decision shaped the entire system: notification logic runs on the server, not in the browser.
Why this matters
If recipient calculation happened in the browser, customer users could potentially see the list of internal staff members - even if they never received those notifications. By processing everything server-side:
- Customers only see data they're meant to see
- Internal staff lists stay internal
- The visibility rules can't be bypassed by inspecting browser traffic
Design Principles
Notify the right people, not everyone
Every notification has a reason to exist. If someone doesn't need to know, they don't get interrupted.
Transparency builds trust
Showing users who will be notified - before they act - eliminates guesswork and prevents mistakes.
Respect boundaries
Internal discussions stay internal. Customer-facing updates go to customers. The system enforces these boundaries automatically.
Users control their experience
Notification preferences let users choose how they want to be reached. The system adapts to them, not the other way around.
Outcome
The notification system handles the complexity of multi-stakeholder communication while staying simple for end users. Staff stay informed about customer activity. Customers get timely updates. Internal discussions remain private. And everyone can tune their preferences to match how they work.