Rich Table
Redesign

Why we're rebuilding the most-used screen in Enginy

Rich Table redesign

What we'll cover

1

๐Ÿ” The Problem

Why this is the most-used screen โ€” and the most complained about

2

๐Ÿ“Š What we found

PoC results, Datadog baselines, three layers of limitations

3

๐Ÿ› ๏ธ How we'll fix it

Architecture principles and measurable success criteria

4

๐ŸŽจ The new design

V1 default view, column set, what's in and what's deferred

5

๐Ÿš€ Release plan

Six releases, organised around user-perceived capability gates

01

The Problem

Most-used screen. Most complained about.

CONTEXT

The contacts & companies table is where customers spend their day.

It has accreted features for months. Each one was justified individually. Compounded, they produce four distinct user pains โ€” three technical, one product.

What we'll argue today: if we only start with UI/UX without redoing the technical layer, we make the situation worse. We need to start with the technical layer and work our way down to the UI/UX.

PAINS WE'VE CONFIRMED

Four user pains, one table

1

โฑ๏ธ Long initial load

First render is gated by data fetching. Monolithic data source returns everything, even hidden columns. Long-polling shares latency with enrichment workflows.

2

๐Ÿ•ณ๏ธ Blank gaps on scroll

Fast horizontal and vertical scroll outpaces the cells to the right of the viewport โ€” visible white areas before columns paint. Column-count ร— column-complexity cliff.

3

๐ŸŒ Slow filter & pagination

Each filter re-fetches through the same monolithic path. Some filtering runs in the browser. Iterative narrowing โ€” the workflow customers actually use โ€” is the slowest case.

4

๐Ÿง  UX โ€” high cognitive load

Every column has equal weight. ICP and Intent exist in the data but never become decision signals. No contact photos. Names truncate. The user reads it as a wall of text.

USAGE FACTS ยท LAST 7 DAYS

This is the screen people live in

261k
SuperTable loads
(7 days)
8.8k
Header clicks
3.7ร— prior window
8.4k
Bulk actions
4.3ร— prior window
~150
Individually-flagged
behaviors today

Lead workflows still dominate (~66โ€“72%), but company share is creeping up. List-scoped behavior is the norm (~81%). When this screen lags, it lags for everyone.

02

What we found

The rendering layer isn't the bottleneck.

FINDING #2 ยท DATADOG BASELINE (NEW INSTRUMENTATION)

The pain is real and measurable

First load ยท P95 (overall) 2,836 ms

P50 is 518 ms โ€” the long tail is what users feel.

Worst slice ยท P95
(companies + list filter)
5,488 ms

5+ seconds, every time, on the most loaded view.

Top 10 filter signatures ยท avg 13โ€“39 s

companyGroup combinations dominate the slow tail.

DB time ยท P95 (companies) 2,671 ms

Overall P95 is 1,002 ms โ€” companies entity is the outlier.

Enginy internal usage 3,906 ms

~38% slower than global. Worst internal case: 12,968 ms.

Error rate 0.54%

1,417 errors over 261,516 loads.

FINDING #1 ยท PROOF OF CONCEPT

We swapped the renderer. It didn't move the needle.

What we tried

Replaced the custom SuperTable with TanStack Table + virtualization in a PoC.

What we measured

Marginal improvement only. Virtualization comes free with TanStack โ€” but it was never the binding constraint.

Conclusion: the real bottleneck is upstream of rendering. The table over-fetches the whole dataset (including hidden columns) on every request, pushing load onto the customer's browser instead of the backend.

Filtering and pagination are the same operation at the data layer. We have to design them together.
โ–ถ๏ธ  LIVE DEMO ยท IN APP

Let's look at
the working PoC.

Design is one thing โ€” here's the real thing running against real data.

๐Ÿ”—  URL

app.enginy.ai/rich-table

๐Ÿšฉ  Required feature flag

Enable companies_rich_table for the page to render.

Watch for #1

First load time vs the legacy table

Watch for #2

Scroll โ€” no blank gaps

FINDING #3 ยท THREE LAYERS OF LIMITATION

It's not a single fixable bottleneck

RENDERING

Inside the component

Row data passed whole to every cell. Reference identity changes during enrichment streaming โ€” same row re-renders ~20ร— per enrichment, invalidating every cell each time. Complex cells (technologies, AI tooltips, badges) lack their own memoization boundary.

DATA

Around the component

Monolithic "one-size-fits-all" data source over-fetches. Background long-polling shares latency with long-running enrichment workflows. Reads and writes share request paths and backend resources.

SOURCE

Upstream of the table

No internal source-of-truth database. LinkedIn provides coarse categories ("tech industry"), not the segmentation customers want ("B2B SaaS startups"). The table does double duty as viewer and filtering workspace โ€” that's why customers scroll through 95 pages.

"We can make the backend more performant, but unless we cut features, we will land probably on 50% better, not 3ร— better."

โ€” Rich Table Kick-Off, 2026-05-04

HONEST CONSTRAINT

Scope must be cut, not just optimized.

A 3ร— experience win requires reducing the per-row / per-cell feature surface โ€” not just a faster engine behind it.

03

How we'll fix it

Four architectural rules and how we'll measure them.

DESIGN RULES ยท NON-NEGOTIABLE

Four invariants for the new table

01

Per-cell render isolation

A cell re-renders if and only if its own value changed. Each cell receives only the slice it consumes, with stable reference identity. Verifiable in React DevTools profiler.

02

Purpose-built data queries

No reuse of the monolithic data source. Two tables get two queries. Filtering and pagination designed together. List-aware paths use the DB's existing bookmark.

03

Reads isolated from writes

Table read paths separated from long-running enrichment / AI generation. The user's view is never bottlenecked behind background work or long-polling.

04

Scope cut, not just optimized

The rewrite ships a smaller default surface for the daily user. Power-user complexity stays reachable via Advanced Mode while we earn the right to replace it.

HOW WE'LL KNOW WE WON

Success metrics โ€” measurable, defensible

Metric Legacy baseline Target
First load ยท P95 (frontend) 2,836 ms overall
worst slice 5,488 ms
Strictly lower
no slice > 3,500 ms
Top 10 filter signatures ยท avg 13โ€“39 s < 5 s avg
DB time ยท P95 1,002 ms overall Strictly lower on same window
Internal cohort ยท P95 3,906 ms
worst slice 12,968 ms
โ‰ค 2,836 ms
worst slice < 4,000 ms
Error rate 0.54% โ‰ค legacy at R1 cutover
Blank gaps on scroll Qualitative โ€” present today No visible gaps at normal scroll speed

We are not claiming a direct product-metric move. The honest goal is reducing churn driven by "the software feels laggy" and "the table doesn't look professional" โ€” neither cleanly attributable.

04

The new design

A new default view for contact lists. Legacy stays reachable.

MVP ยท V1 DEFAULT VIEW

Ship a read only new default view for contact lists, with the legacy table one click away.

How users land on it

New default when a customer selects a contact list. CSV-imported lists open in the legacy view by default to avoid breaking sparse-data layouts.

Escape hatch

"Edit Mode" button switches back to the legacy SuperTable. Power users keep everything they have today while we earn the right to remove it.

Constraint

Testing and engineering overhead. Every cell we keep means real QA surface.

V1 SCOPE ยท IN AND OUT

What we ship โ€” and what we deliberately don't

โœ… In V1

  • Columns for default view (Contact, ICP, Intent, Email, Phone, Company, etc.)
  • Hard-coded Contact pin
  • Basic search filtering visible rows
  • Default actions: Enrich, Add to Campaign, Add Contacts, Export
  • Bulk toolbar swap on selection
  • New ICP & Intent pill design (consistent with the pill redesign spinout)
  • Cell search
  • Retrofit selected new-design components into the legacy table for visual consistency

โญ๏ธ Deferred from V1

  • Moveable columns (PoC exists but UX not ready)
  • Pinned columns (beyond the hard-coded Contact pin)
  • Enrichment workflow status bar
  • Inline editing of complex / action-heavy cells
  • Views (filter + sort + columns)
  • Per-cell memoization, long-polling replacement, isolated read paths
Why deferred: data fetching is V1's binding bottleneck โ€” not render fanout. Revisit memoization and long-polling when V1 metrics arrive.

RISKS WE'RE TRACKING

What could go wrong

Customer flexibility expectations

Power users are accustomed to high customization. The single biggest source of complaint risk is the transition UX: how users land on the new view, and how they find the old one. Damian owns the flow.

Testing overhead is binding

Engineering capacity is fine. QA surface isn't. Email, phone, and the enrichment-adjacent cells each add real QA volume โ€” which is why so much was cut from V1.

"Simple + Advanced" loop

Risk: customers ask us to port every advanced feature into the simple table, defeating the scope cut. Mitigation: R5 (inline editing + legacy removal) is the forcing function.

AI-variables performance cliff

The column combo including aiVariables averages 9.6 s frontend in the PoC vs 261โ€“584 ms without. Flagged for R4 column rollout.

05

Release plan

Six releases organised around capability gates, not column count.

FRAMING

Editing is the legacy-killer, not column count.

The previous V1โ†’V4 plan was anchored on column coverage. The 2026-05-11 sync rebuilt it around user-perceived capability gates instead.

The order is deliberate: every release before R5 has to stand alongside the legacy table, so we earn the right to remove it. Nothing customer-visible regresses; everything risky stays behind a fallback until R5.

SIX CUSTOMER-FACING RELEASES

Contact table: from view-only default to legacy removal

R1
View-only default
New default view for contact lists. "Advanced Mode" button switches back to legacy.
May
R2
Views: filter + sort + columns
Filters and sorting inside column headers; column selection bundled as a "view". Pinned columns beyond Contact.
June
R3
Live activity notifications
In-table feedback for enrichment / scraping. Trust gate before customers stop reaching for legacy.
TBD
R4
Full column set
Every legacy default-view column available in the new view. Closes the parity gap.
TBD
R5
Inline editing + legacy removal
Edit cells inline. Legacy SuperTable removed for contacts. The cut-over release.
Killer release
R6
UX improvements
Unified search input, nesting columns, contact โ†” company toggle.
TBD

SCOPE BOUNDARIES

What's deliberately not in this plan

Companies table

R1โ€“R5 are scoped to contacts only. Companies SuperTable continues to exist after R5. Companies migration is a separate project.

Surrounding page refactor

The list-selector, page filters, and adjacent page chrome are explicitly out of the customer-facing release stream โ€” scheduled separately after R5.

Spun-out projects

Improved ICP / Intent Pills, Enrichment Packs, Heat Score + Smart Enrich โ€” tracked separately. Pills design influences V1 visuals but the project is its own track.

Deferred V1 Foundation items

Per-cell memoization, long-polling replacement, isolated read paths, explicit virtualization work. Revisit when R1 / R2 metrics arrive.

IN ONE BREATH

A faster table that surfaces decision signals โ€” shipped behind a fallback until we've earned the right to remove it.

P95 < 3.5s
First load target
vs 5,488 ms worst slice today
< 5s
Top filter signatures
vs 13โ€“39 s avg today
6 releases
R1 ships now, R5 removes the legacy table

Questions?

Let's talk through the open questions on the prior slide.