What if a listing told you whether it's a good deal, not just what it costs? I designed the layer Roomix is missing
Most real estate search engines treat a property as a place to live: rooms, square meters, neighborhood, price. An investor asks the same listing a different question: is this a good deal, and for which kind of investor? Investor Mode is a layer I conceived and designed end to end to answer that question inside the product, instead of sending the user off to a separate spreadsheet. The whole case fits in one line: showing numbers isn't the same as helping someone decide.
On a sample property (Belgrano, USD 180,000, 30% down), the engine returns:
Net cap rate
2.98%
1-year ROI (cash)
+5.18%
Leverage ratio
3.33×
Roomix solves search and discovery better than anyone; what's missing is answering whether a property is a good deal. That's the gap.
Roomix is Argentina's largest AI-powered real estate search engine: it indexes over 500,000 listings (apartments, houses, land, commercial spaces) for sale and rent, from agencies and owners direct. Its signature is natural-language search ("type what you're looking for in your own words"), plus saved collections and WhatsApp alerts. It's free, AI-first, and covers the whole country. On top of that, it already has a tools layer: a mortgage simulator, rent-adjustment calculators (CPI/ICL-indexed), an online appraiser, a scam detector. It's a product that already believes in "tools that help you decide."
That's exactly where I found the gap. With all of that, no tool answers the question an investor is actually asking: "is this property a good deal?" The appraiser tells you what it's worth; the mortgage simulator tells you your monthly payment. But nothing connects rent, price, appreciation, and financing into a single answer. That user ends up right where they started: in a spreadsheet.
That's where Investor Mode came from: a layer on top of any listing that answers "is this a good investment, and for which kind of investor?" It fits Roomix's DNA (AI-first, tools-oriented) and fills a real gap.
I learned the financial domain from zero and distilled it into a spec that became the engine's source of truth.
Designing this took more than understanding Roomix. I had to learn real estate finance from scratch: gross and net cap rate, NOI, cash vs. leveraged ROI, how financing actually changes returns, and the full Argentine context (UVA-indexed loans, a dual-currency economy, inflation adjustment, the risk of a dollar-denominated payment spiking in real terms).
I distilled that research into a domain spec (domain.md): the formulas, the user inputs, the macro
defaults, and the scenarios. That spec became the engine's source of truth: everything the prototype
calculates traces back to it. I didn't invent numbers to make the demo look good; I derived them from the
domain I researched.
Designing on top of a domain you don't already know starts with humility: understand it well first, then decide how to present it. Half the work was research; the other half was translating that rigor into an experience a non-analyst could follow without feeling dumb. And it didn't land on the first try: every decision below is an iteration.
The central pivot: from a calculator that dumps numbers to an answer that interprets them. Interpreting isn't recommending, it's pointing someone in a direction.
Once I understood the domain, I designed a first version. It showed all three scenarios in parallel, about nine numbers, a comparison against the neighborhood average, and closed with an implicit "you decide." It had all the rigor of the engine, and that's exactly why it didn't work. I tested it, and it felt wrong.
The root diagnosis: I was building a calculator for a user who came to make a decision and doesn't know how to read the numbers. "We show data, not recommendations" was being used as an excuse not to interpret.
The Roomix user isn't a financial analyst. Handing them net cap rate plus cash and leveraged ROI across three scenarios, then topping it off with "you decide," dumps the entire cognitive load on them. It paralyzes: they see numbers and still don't know whether to invest.
The core decision of the project was moving from spreadsheet to interpreted answer. The UI now leads with a plain-language answer ("2.98% net cap rate; that's low for Belgrano, an appreciation market, not a rental-yield one"), the advanced layers are opt-in, and everything closes on an action. Interpreting isn't recommending: it's explaining what the number means and who it makes sense for, without ever saying "buy this."
Before the screens, the principles. Every decision passed through these filters:
I'm not designing pretty screens: I'm pulling a business lever. I treat every decision as a bet on the funnel: get the investor's attention, earn their trust, get them to the action.
Six design decisions, each an explicit trade-off.
The deep-dive was a long, linear report meant to be read top to bottom: no summary up top, the same data point (cap rate) showing up in three different visual formats, and the red card for negative ROI as the visually heaviest thing on the screen, which is exactly the number the copy claimed "isn't a verdict."
I turned it into a dashboard: a KPI scorecard up top (cap rate, ROI, vs. neighborhood, appreciation),
reactive, the same five-second summary every product in this space leads with; a single Stat
component that renders every number the same way across the screen; a re-sequencing (summary, your
input, scenarios, neighborhood, financing, verdict, action); and financing collapsed into an
accordion, which pulls the negative number out of the center of gravity and opens it on demand. The
screen got roughly twice as short.
The copy leads with the conclusion too. Before, descriptive subheadings ("same property, different macro assumptions"). After, conclusions that point somewhere:
It kills the paralysis. The person who tested the original said, word for word, "I just see numbers, I'm overwhelmed, I don't know if I should invest or not." Leading with the conclusion and naming "who it's for" gives them somewhere to stand. Along the way, I also cut the jargon that shuts out the non-analyst: "net of vacancy" became "after accounting for vacant months," "NOI" became "net operating result," "(non-cash)" became "(not money in hand)." Terms that come with their own explanation nearby (cap rate, leverage) stay, each with its own popover.
The comparison used to be "this property vs. the median": two bars. I turned it into a histogram of the neighborhood's cap rate distribution, with the user's property marked and the percentile leading the insight ("28th percentile: 7 out of 10 units in this neighborhood yield more than this one").
That's the difference between a portal and an investment product. "Two bars" tells you a data point; "where you fall in the distribution" gives you the context that actually shapes the decision.
Calculating return with a single number creates a false sense of precision that Argentina's macro volatility can't support. I chose three scenarios (worst case, base case, best case) because that's the minimum needed to define a range with a center. Going to five or seven adds intermediate categories that are hard to communicate ("moderately conservative" doesn't mean anything to anyone).
Once I made the selector reactive, a modeling question came up: does the scenario change rental yield (cap rate), or only appreciation? The decision: the scenario is a bet on appreciation, not on today's yield.
Cap rate (which is NOI over price, pure present tense) shows the same across all three scenarios. That's conceptually correct, and it avoids a dangerous mistake: making the user think "being optimistic" improves what the property earns today. Cap rate is your year-1 reality; the scenario is your bet on the future.
The decision I cared about most. Three moves, all on the user's side:
Cap rate evaluates the asset regardless of how it's paid for. ROI evaluates the investor, where how you pay (debt, mortgage, equity) does matter. Financing turns a weak yield into a bet on appreciation: leveraged ROI moves relative to cash ROI by a factor equal to the leverage ratio (asset value over equity). At 30% down, that factor is 3.33×, and it amplifies both gains and losses:
| Appreciation scenario | Cash ROI (180k) | Financed ROI (54k) |
|---|---|---|
| 2.2% (moderate) | +5.18% | -6.16% |
| 5% (aggressive) | +7.98% | +3.17% |
The question the product answers has two parts: "is this a good investment, and for whom?" I solved the second half along two axes, not with a persona picker.
The obvious move would have been a Conservative / Moderate / Aggressive persona selector. I deliberately didn't build it: those three aren't investor types, they're bets on macro appreciation (a forecast), not strategies. Treating them as personas is a conceptual mistake. So instead of manufacturing personas, I make the real axis impossible to ignore through copy and structure.
Explicit personas: what I ruled out
Reads more like a "product," more configurable. But it manufactures a taxonomy (Conservative / Moderate / Aggressive as personas) the model can't actually support, and over-designs a v1.
Emergent distinction: what I chose
Keeps the real financing axis front and center, doesn't invent categories, and is honest about what the data can support today. The cost: the guidance lives in the copy and structure, so generalizing it to any property is v2 work.
What I deliberately didn't segment: traditional rental vs. short-term Airbnb-style, the segmentation this case is most missing. I left it out because it depends on occupancy, seasonality, and nightly rate (data I don't have and the domain doesn't model), and because half-building it would have been worse than not building it. It's the top candidate for v2: in Buenos Aires, a studio in Palermo can earn 2-3× more on short-term rental than traditional.
The prototype isn't static: it computes. Changing the rent assumption propagates a recalculation through cap rate, scenarios, distribution, and financing. I built a "live engine" block that runs the domain's formulas using Figma's native expressions and conditionals (addition, subtraction, multiplication, division, plus if/else), with the bar and the message recalculating in real time.
The standard I held myself to here was not faking capability: I found the real limits of Figma's
"programming" and designed around them. The characters property only accepts strings (no floats), and
expressions can't format or concatenate, so charts run on width (continuous, computed) while numbers and
messages run on strings and thresholds. Conditionals top out at two blocks (if plus else), so the three
states resolve as "default plus two overrides." It's rare to see a design portfolio prototype that
actually computes instead of simulating pre-baked states.
And the charts, chosen deliberately. I explored four: a fan chart (10-year value projection with an uncertainty band), a stacked area (equity over time), a donut (cap rate composition), and radial gauges (KPIs). I applied the rule "a chart earns its place only if it shows what a number can't," and kept two, cut two:
A restrained visual language reads as product; a showcase reads as a demo. Knowing what to cut is design.
Cap rate in USD doesn't depend on rent alone: it depends on whether devaluation outpaces the rent adjustment. That's the heart of the model.
This is the detail that separates a serious model for Argentina from a generic calculator. The property is listed in USD but the rent is collected in ARS. That mismatch forces two assumptions that don't exist in stable markets like Madrid or Miami: which exchange rate to use to convert the income, and how to project that conversion over the holding period.
The consequence is counterintuitive: USD return doesn't depend only on today's net income, it depends on whether, over the holding period, peso devaluation outpaces the rent adjustment. The engine's core formula:
U₁ / U₀ = (1 + i) / (1 + d)
Where i is the rent adjustment rate (I use ICL, Argentina's official rent index, as a proxy) and d is
peso devaluation against the USD. If i > d, USD rent grows; if i < d, it falls. It's
purchasing power parity applied to a
single asset. That's why cap rate is a single, present-tense number (today's net income converted to USD
over today's price), but the future return isn't: under each scenario, the same property returns
differently depending on whether devaluation outpaces the adjustment. That's exactly what the engine
projects in ROI, not in cap rate.
The defaults (vacancy, inflation, devaluation, appreciation by neighborhood) are built by triangulating public sources: Zonaprop, INDEC (Argentina's national statistics bureau), the Central Bank, and La Nación. There's no officially measured vacancy rate for Buenos Aires, so I built the range by cross-referencing supply data that does exist, and I flag it as a model assumption, not a data point. The regulatory backdrop is DNU 70/2023, the decree that freed up the choice of adjustment index and currency for rental contracts.
A user tested the screen and told me "I don't know what to read." They were right, and the fix was showing only the default answer and hiding the rest behind an opt-in.
Here's the uncomfortable part. I'd applied the reframe (scorecard up top, financing collapsed) and it felt right. But I showed the analysis screen to a user and they told me, word for word: "there's too much information, I don't know what to read." They were right, and it stung: the project existed to kill exactly that (the spreadsheet that paralyzes you), and the screen had filled back up.
The diagnosis: I'd done progressive disclosure halfway. I put a scorecard up top, but left the entire report underneath (engine, distribution, 10-year projection, financing), stacked with equal visual weight. The verdict sat at the bottom, after scrolling past a wall. It was "answer plus spreadsheet," not "answer."
The fix was flipping the logic. By default, the screen now shows only the answer: the verdict in one line, the scorecard (four KPIs), "this works for you if... / it doesn't if...," and the action. Everything else (including the scenario selector) collapses behind an interactive, opt-in "see the full analysis." The depth is still there, one click away, but it's no longer shoved in your face. The default screen shrank to a bit over a third of its original length and fits without scrolling.
What I'm taking from this: half-done progressive disclosure is still overload. And the best feedback is the kind that shows you relapsed into the exact sin you set out to kill. What mattered was listening to it and cutting density without ego, collapsing something I'd built myself.
Stat component, and a
restrained chart vocabulary.Being honest about the gaps is part of the job. Fine for a v1; not for production:
The Figma prototype simulated the calculation (variables plus expressions). This chapter is what happens when the prototype stops simulating and starts actually computing: a TypeScript engine that takes a property and returns its investment metrics. Pure logic, no UI: today it's just the engine and its tests.
The engine is strict TypeScript, no runtime dependencies, framework-agnostic (the UI will import it later; there's no UI yet). I built it with TDD (red, green, refactor), and the detail that makes it demonstrable instead of "trust me" is this equivalence:
Six implementation decisions, all anchored to the domain spec.
domain.md section 3.3 defines the inputs and outputs; the engine implements them as written
(EngineInput to EngineOutput, with all three scenarios running in parallel). Every constant carries
its own @see domain.md section X: no magic numbers, full traceability back to the research.
calcNetCapRate (today's rent) and calcProjectedNetCapRate (projected under macro assumptions) are two
separate functions. The design's yield-vs-return thesis is now enforced by the types and the tests, not by
a comment. ROI uses the projected cap rate because it's a return over a year; mixing "today's yield" with
"this year's appreciation" would mean reading two different clocks. This is where a test caught me getting
it wrong.
projectRent, calcOperatingBreakdown, computeFinancing: each calculation lives in exactly one place.
Cap rate, NOI, and ROI compose those primitives instead of repeating them. I learned this while
refactoring: my first version "un-divided" the cap rate to recover the NOI; I flipped it so NOI is the
building block and cap rate derives from it, not the other way around.
DEFAULT_SCENARIOS encodes the table from section 2.5: what changes between Conservative, Moderate, and
Aggressive is vacancy plus adjustment plus devaluation plus appreciation. The asset's cap rate doesn't
"improve" from being optimistic. And vacancy lives on the scenario, not on the property (it's a macro
assumption, not an asset attribute): a test that ran all three scenarios with different vacancy rates
forced me to move it there. The test drove the design.
Universal financial concepts in English (priceUSD, capRateNet); Argentine-specific terms left
untranslated (expensasMonthlyUSD, ablQuarterlyUSD, ICL, UVA). Cap rate as a decimal ratio (0.0293):
the ×100 lives in the UI, the engine doesn't know about formatting. The boundary between units is
explicit.
Leveraged ROI can come back negative (-6.16% in case E), and the engine returns it as is: negative cash flow is real ("you're paying out of pocket every month"). The engine doesn't dress it up and doesn't dictate: it returns structured data per scenario, not a verdict, the same posture as the design.
analyzeProperty(input) is the only entry point.Next chapter: wiring the engine to the interface, so analyzeProperty feeds a real scorecard,
scenarios, and financing, and closing out v2 (10-year projection, neighborhood distribution, UVA loans
denominated in actual UVAs).
The case has two pieces. On the design side: a navigable Figma prototype with its own design system (foundations and a component library, with states typed against the engine's variables). On the code side: the calculation engine already exists as a pure-logic TypeScript library (cap rate, ROI, financing, and scenarios), covered by tests in Vitest, with no UI yet. The Next.js interface and PostgreSQL persistence are the next chapter.
I'm publishing this partly to have it challenged. I learned the financial domain from zero for this project, so if you work in real estate, finance, product, or design and see something that doesn't add up, I'd like to hear it.
On the design and the product (what you'd cut, add, or change):
On the analysis (the more technical part):
If any of this made you think, or you see it differently, write to me.
At a glance
I took Roomix (Argentina's largest AI-powered real estate search engine) and designed the layer it's missing for buyers investing rather than living: one that doesn't just calculate cap rate, ROI, and scenarios, but interprets them and points toward a decision without dictating one. My first version was a calculator that dumped nine numbers on the screen and said "you decide"; I reframed it into an answer that leads with the conclusion, calculates live, and is honest about risk. I then took it from a Figma prototype to a TypeScript engine with tests. Domain research, interpretive design, and implementation rigor, in one case study.





