Designing for the data model, not the screen: three product decisions that prevented technical and UX debt at a CRE SaaS startup
CRE SaaS + AI · Duxre · Oct 2025–Jun 2026 · B2B Web · Design Lead · Full-time contract · Role ended when the company lost funding
Duxre is a commercial real estate operating system for brokers: deal tracking, contact management, listing workflows, and an AI layer on top. I joined as Design Lead with a UI designer already on the team. I hired two frontend engineers, built the design system from scratch, and worked with the CPO and one of the engineers to initiate a React migration from the existing ASP.NET MVC product.
My role was the structural layer: product logic, design system ownership, engineering coordination, scope decisions, and daily design review. She owned visual execution. What I want to talk about is three interventions, two identified through reviewing existing screens and one argued at the implementation level, that kept the product from encoding the wrong thing.
Same table, two data owners, no way to tell them apart.
The 1031 Tracker already had screens when I joined. What it didn't have was a design that reflected the underlying data model.
The tracker helps brokers match time-sensitive buyers to listings before a legal reinvestment deadline expires. The table pulls from two completely different data sources interleaved across the same row: fields self-reported by the contact through their own profile, and the broker's private annotations visible only within their own tenant. Some are editable, some are not.
The existing design treated both as a flat table with some read-only columns and some editable columns. Two problems followed.
Editable fields had hidden provenance ambiguity.
The same cell could contain a value the contact reported or a broker estimate from a phone call weeks ago, with no visual difference between the two.
Those carry different confidence levels.
Overwritten values had no memory. When a broker replaced a contact-sourced value with their own estimate, the original disappeared with no way to recover it.
The proposed redesign makes provenance scannable at a glance: a person mark for contact-reported values, a corner mark for broker-entered ones, with the original contact value and a non-destructive revert accessible when something has been overwritten.
This direction was established before the company lost funding. It didn't ship.
A bug surface that hadn't announced itself yet.
The notes situation came to light through design review. Duxre had two note systems: rich text on contact profiles, plain text on smart contact lists. Different rendering paths, different data models, but several API endpoints were shared across both, serialising different formats through the same pipeline. A bug surface that hadn't announced itself yet.
The UX argument for keeping rich text in listing notes was weak. Brokers don't write documents in their notes. They write "Left voicemail, following up Thursday." The only reason the two systems differed was historical. Two features built at different times, never reconciled.
I argued to collapse both into one plain-text system. Engineering got a cleaner pipeline. The product lost a distinction users weren't using. Nobody noticed the difference because there was nothing to notice.
What not to build is also a design decision.
Midway through the React migration, management wanted to add inline company creation to the React contact form. Reasonable on the surface: brokers creating a new contact should be able to create the associated company in the same flow.
But the Companies module was still MVC, scheduled for a full React rewrite two sprints later. Building inline company creation in React at that moment meant writing integration code against the MVC Companies module, two sprints before its React rewrite, throwaway complexity added to a sprint that was otherwise ready to ship.
I presented the case to C-level: ship the contact form with search-only company linking. Unmatched companies go through a user-assisted resolution flow during batch import anyway. Two sprints later, Companies gets rewritten properly.
The contact form shipped on time. The Companies rewrite never happened. The team was let go on budget constraints before that sprint. The throwaway code was never written.
58 component sets. 6,880 variants. 3 months.
The design system was the foundation everything else ran on, built from scratch, adopted as the shared reference across SaaS and AI engineering teams. It cut screen design time from days to hours and gave the frontend pod a single implementation reference.
Three modules. Five months. A system the team adopted.
Three modules delivered via Strangler Fig migration in five months: AI chat, listing manager, contacts. Notes collapsed into one system. The contact form shipped on time without throwaway integration code. The design system became the shared reference across both engineering teams.
The 1031 Tracker provenance redesign was proposed and documented but never implemented; the company lost funding before that sprint.
The decisions that held the product together weren't always the ones that reached production. They were the ones that kept the data model honest and prevented the wrong thing from shipping.