advanced 2 min answer Multiple choice

An issue-tracking product lets every customer define custom fields, workflows and screens. Which data model should the platform use for custom fields?

data-modellingeavmulti-tenancyjsonbatlassianarchitecture-selection
Pick one
Show the full answer Hide the answer

Why the pure options fail

Entity-attribute-value. One row per field value is maximally flexible and pathologically slow. Retrieving one issue with 30 custom fields becomes 30 rows to gather; filtering on two custom fields becomes a self-join; sorting on a custom field becomes an aggregation. Types are lost, so validation moves to application code and the database can no longer help. EAV is the classic answer that looks right in a design review and collapses under a query planner.

Table per tenant. Excellent isolation and query performance, catastrophic operational cost. Schema migrations must run across every tenant's tables, so a change that takes seconds for one customer takes days across the estate, with partial-failure states. The catalogue itself becomes enormous, and connection pooling and query-plan caching degrade. It is viable for tens of tenants and not for thousands.

Wide table with generic columns (custom_string_1 … custom_string_50). It performs adequately and is a semantic disaster: the meaning of a column varies per tenant, nothing is self-describing, the limit is arbitrary and eventually hit, and every query needs a mapping layer.

Why the hybrid wins

Split the model by who owns the schema:

  • Platform-owned concepts get real columns. Identifier, tenant, project, status, assignee, timestamps. These exist for every tenant, are queried by every feature, and benefit from constraints, foreign keys and conventional indexes.
  • Tenant-defined fields go in a semi-structured column — a JSON document with a per-tenant field definition table describing types, validation and display. One row per issue, retrieved in one read.
  • Targeted indexes on the document for the field paths that are actually filtered and sorted, which is a small subset. Modern relational engines index inside JSON documents well.
  • A separate search index for the general case: full-text, complex filters and cross-field queries go to a search engine fed asynchronously, not to the transactional store.

The design principle

Put the boundary where schema ownership changes. Anything the platform defines gets platform structure. Anything a tenant defines gets a flexible container with tenant-supplied metadata. Mixing those two ownership models into one mechanism is what produces both EAV and the wide-table disaster.

The trade-off to state explicitly

You accept weaker database-level guarantees on the flexible half — the engine cannot enforce that a tenant's "due date" is a date, so validation lives in application code and must be applied consistently by every write path, including imports and integrations. That is a real, permanent tax, and it is the price of the flexibility the product sells.