Urgent.News

What's breaking now, across thousands of outlets.

Tech

Modelling a shared inbox: why the message is the primary key, not the conversation

Every shared-inbox implementation starts with a conversations table and ends up regretting it. Here is the model that survives contact with real support traffic. The problem with conversation-first A conversation is a derived thing. It is what you get after you decide which messages belong together. If you store conversations as the source of truth, every new channel forces you to write merge…

Every shared inbox starts with a conversations table and often ends up regretting it. The model that survives real support traffic stores messages as the primary key, not conversations. A conversation is derived from messages, and storing conversations as the source of truth requires complex merge logic that can lose data. Messages, on the other hand, arrive once and never change, making them the natural primary key.

The model consists of immutable fact messages (id, channel, external_id, direction, sent_at, body, raw), contacts who sent them, and threads that group messages. The key properties are that thread_messages is append-only, allowing merges to insert rows without rewriting history, and that threads can be rebuilt from messages and merge rules.

Identity resolution is challenging, as a single person may send emails from different addresses during a work rebrand. A compromise involves exact matches on email, phone, or channel-specific IDs, followed by domain-and-name heuristics to suggest merges rather than performing them automatically. Conversation state is stored as a separate message_delivery table, tracking outbound mail lifecycle per message and recipient.

This approach ensures accurate answers to support leads about why emails are grouped together, and that delivery state belongs to the message, not the conversation. With this schema, channel integrations become writers of messages, and AI summarisation or tagging attaches to messages or threads without mutating the record. Deleting a thread is simply a policy operation, enabling enforceable retention rules.

When migrating to a new database, it's a straightforward row copy, not a logic rewrite. The most important early step is ensuring messages.external_id is unique per channel and deduped on insert, preventing duplicate replies from webhooks that customers remember as failures.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

Treat a Model Upgrade Like a Schema Migration

The most common LLMOps incident I've seen is not a prompt bug or a jailbreak. It's a one-line diff. Someone bumps gpt-4o to gpt-4o-2024-08-06 (or claude-3-5-sonnet-latest to a newer snapshot), CI…

More from Thursday 29 October →