← back

ResiQuant

Product Engineer Intern

May 2026 – August 2026 · San Francisco, CA

Visit website →

Background

Natural catastrophe insurance platform. Submissions arrive as email with property schedules and supporting documents attached, and the platform extracts them into structured records, geolocates each property, runs hazard and vulnerability modeling, and moves the submission through a review workflow until an underwriter can act on it.

Python and FastAPI on PostgreSQL, React and TypeScript on the frontend. Extraction and validation run as composable async tasks in a pipeline framework with queuing, retries and scheduled jobs. I owned features across both sides, from the queries up through the UI, and led two enterprise customer integrations end to end.

The platform is multi-tenant, and that shaped most decisions. Workflow definitions and model tuning live in per-tenant configuration rather than in code, so a behavior change is a config change for one customer, not a deploy for all of them.

Finding where a bug actually lives

Most of what I built started as a complaint in the language of someone's job: the wrong document got used, the warnings don't mean anything, my view keeps resetting. Locating that was usually the real work, because a pipeline stage, a Postgres query, tenant config and a React view can all produce the same symptom.

One feature let users exclude specific documents from a reprocessing run. The visible work was a checkbox and a parameter threaded through the pipeline. While verifying it I found the pipeline was inserting new document rows on every run instead of updating the existing ones, so an exclusion keyed to a row id stopped matching anything on the next pass, and duplicates accumulated underneath. The real fix was record identity at the persistence layer: derive it from the document so a reprocess updates in place. Shipping what people asked for meant fixing something they had not reported.

The review surface

Underwriting teams sit in the review workflow all day, so the constraint there is attention, not correctness. One validation step emitted warnings on nearly every record, a dozen signals that were technically true and practically irrelevant, and reviewers had stopped reading warnings entirely. Rather than tune thresholds I worked with the domain side to establish which signals change a reviewer's decision, and cut the check down to those.

The data entry grid had the same shape of problem. Users paste hundreds of rows out of spreadsheets, so the requirement is not strict validation, it is that a paste never gets interrupted. That meant separating input a field genuinely cannot accept from input it should normalize silently, handling regional number and date formats at the point of entry rather than failing downstream, and making a paste and its undo one transaction so a single keystroke reverses the whole thing instead of one cell at a time.

Changing live behavior safely

Config changes here affect submissions that are already processing, so I kept changes reversible in the code rather than only in the deploy: new behavior added in front of existing logic with an early exit instead of replacing it, put behind a flag rather than a branch, and rolled out as an opt-in list of tenants rather than a new default, so one customer could be observed against their own results and rolled back independently.

The clearest case was replacing a manual tuning loop. Part of the matching behavior is governed by parameters in cloud configuration, and tuning them for a customer meant an engineer reading historical outcomes and editing config by hand. I built a scheduled pipeline that reads past outcomes and human corrections from Postgres, evaluates candidate parameter sets against them, and writes the winner back to that tenant's configuration, recording every run so any change traces to the job that made it. Most of the design went into the safety properties rather than the search: because it mutates config that immediately affects live processing, it had to be per-tenant, opt-in, observable and reversible before it was worth running.

Internal support agent

I also shipped a support agent that runs as its own service for the engineering on-call rotation. It picks up Pylon tickets from Slack, pulls the full ticket context from the Pylon API, and keeps a rolling index of resolved tickets to surface the closest past matches. For issues that look like product behavior rather than user error, it searches the codebase, traces the relevant area to the engineer who last worked on it, and posts a technical diagnosis alongside a drafted customer reply in the thread.

The model call was the easy part. Making it safe to leave running was the work: deduplication that holds under concurrent webhook deliveries so it never double-replies, ignoring events older than process start so a restart does not answer a week of stale tickets, filling in context the webhook does not carry, and iterating on the output until a reply was something an engineer sends with a one-line edit. A tool people route around is not shipped.