Interview guides · Asseti
Asseti Software Engineer Interview Guide
Asseti Software Engineer interview guide: data pipeline, API integration and scaling questions, AI coding tool workflow, HubSpot stack and a 7-day plan.
The 60-second brief
Asseti builds software for real estate operations. Its product pulls scattered property data into one picture that owners of large property portfolios use to decide which repairs come first, where their risk sits and what upkeep will cost. Candidates describe engineering work in two areas: the core intelligence platform (commercial roofing intelligence is one area named) and the growth side, which supports enterprise sales through CRM, enrichment and campaign systems. Engineers on either side own a feature from start to finish: they find the bottleneck, design and ship the fix, then measure the result. Candidates also report close work with GTM leadership, Marketing, Customer Success, Sales and Operations, and an expectation that systems are documented well enough for others to maintain.
This guide is for software engineers interviewing at Asseti, whether the role sits on the property-data platform or on growth and GTM systems. The requirements candidates describe are 3+ years in a technical role, deep HubSpot experience (or a comparable enterprise CRM), SQL, Python or TypeScript, and hands-on work with a warehouse such as BigQuery or Snowflake. Nice-to-haves are growth-stage startup experience, ABM tooling and Supabase. Fivetran and dbt also appear in job descriptions candidates cite. The guide covers the four reported questions on pipelines, third-party integrations, scale and AI coding tools, the lead-scoring scenario candidates mention, and the behavioral material around stakeholders and shifting priorities.
No named rounds or round order have been reported for Asseti. Candidates describe moving from an initial screen into deeper technical assessments, with touchpoints across engineering and cross-functional leadership, and say the assessment centres on practical systems design and integration rather than algorithm puzzles.
Focus areas
- HubSpot (CRM)
- AI/LLM-Powered Agents
- Full-Funnel GTM Systems
Overview
The reported questions sit where Asseti's two engineering areas meet. One side consolidates records from several property management systems into a single model of buildings, assets and work orders. The other side moves prospect and customer data through HubSpot, enrichment APIs and campaign tooling. Both are data-integration problems: many sources, inconsistent identifiers, external APIs that fail in partial and messy ways, and business users who need numbers they can trust. The four reported questions (a single-source-of-truth pipeline, a third-party API integration story, scaling to large enterprise data sets, and how AI coding tools changed your shipping) all test whether you can reason about that kind of system.
Because candidates report a practical, design-and-integration assessment rather than puzzle-style coding, prepare to talk through designs in concrete parts: raw landing tables, staging models, an ID crosswalk, MERGE-based upserts, an outbox table, a dead-letter queue, backoff with jitter, reconciliation jobs. For each one, say what failure it prevents. Still be ready to write SQL and Python or TypeScript, since the requirements candidates describe list both. Know the warehouse side (partitioning and clustering in BigQuery or Snowflake, incremental dbt models) well enough to defend a choice.
AI-assisted development is a reported question group, and the tools candidates name are Claude Code, Cursor and Codex. A strong answer is a specific workflow: which steps you hand to the tool, how you check what it produces, and one feature where it changed your lead time or your testing. If you have only used autocomplete, spend real hours this week building something small end to end with an agentic tool, so that your answer comes from your own experience.
Candidates also report being asked to connect technical choices to business results such as data accuracy, deal velocity or less manual work, and to show they understand what Sales or Operations colleagues actually need. For every design you rehearse, name who consumes the output (a rep reading a lead score, an operations lead ranking roof repairs) and what changes for them when the data is wrong or late.
Where to focus your preparation
No round names or order are reported for Asseti, so this guide treats the loop as one preparation focus built around the four reported questions, the lead-scoring scenario and the stack candidates describe. Ask your recruiter which stages you will face and whether any of them involve writing code.
Preparation focus
Candidates report a progression from an initial screen into deeper technical assessments, with conversations involving both engineering and cross-functional leadership. No stage names, count or durations are reported. Candidates say the technical portion favours practical system design and integration work over algorithm puzzles, and that past projects come up in terms of both technical complexity and business impact. Reported questions cover data-pipeline architecture, third-party API integration, scaling for large enterprise data sets and AI coding workflow; a reported example scenario asks for an automated lead-scoring system built on HubSpot and external enrichment APIs. Approach every answer the same way: state the business consumer and the goal, sketch the components, then spend most of your time on failure modes (duplicates, partial failures, stale data) and how you would detect them.
What to demonstrate
- Designing data pipelines and integrations that hold up under messy sources and failing third-party APIs, as the reported architecture questions require
- Explaining past projects in terms of both technical complexity and business impact
- Describing a concrete, verified workflow with AI coding tools such as Claude Code, Cursor or Codex
- Turning requests from Sales, Operations or Customer Success into technical requirements
How to prepare
- Whiteboard the single-source-of-truth pipeline and the HubSpot lead-scoring scenario end to end, including the ID crosswalk, upsert keys and the dead-letter path
- Write one integration story in STAR form with the error classes, the idempotency mechanism, the reconciliation check and a measured result
- Practise warehouse SQL on BigQuery or Snowflake: MERGE upserts, window-function dedup and partition pruning, and write one in Python or TypeScript that pages through a rate-limited API
- Ask the recruiter for the stage list and whether any stage involves live coding or a take-home
Where candidates lose points
Answering the single-source-of-truth question with 'load everything into the warehouse and join it', with no identity resolution or field-level precedence
Put the crosswalk table, matching rules (deterministic first, fuzzy with a review queue) and per-field survivorship at the centre of the design, and say how a user traces a value back to its source system.
Telling the API integration story as a happy path that ends in 'I added retries'
Name the retryable and non-retryable error classes, the idempotency key or external-ID upsert that made retries safe, the dead-letter path and the reconciliation job, then give one number for the outcome.
Talking about scale in buzzwords ('use Kafka', 'add more servers') with no data volumes or bottleneck
Open with numbers: rows, growth, largest tenant, freshness target. Then tie each technique (partitioning, keyset pagination, incremental models, per-tenant quotas) to the constraint it relieves.
Describing AI coding tools with enthusiasm but no verification method or concrete example
Prepare one feature you shipped with Claude Code, Cursor or Codex: what you delegated, what it got wrong, how tests and review caught it, and what it changed in your lead time.
Designing the lead-scoring or CRM sync with no mention of who uses the output in Sales, Marketing or Operations
State the consumer and the decision the data drives before drawing components. Say how a rep sees a score and why it changed, and who owns each HubSpot property so your sync does not overwrite manual edits.
Questions & practice
Track a rolling failure rate per destination for circuit decisions
Easy · sliding window · ring buffer · circuit breaker
The egress service delivers about 1,500 webhooks per second across roughly 40,000 destinations, each call bounded by a 10 second timeout. Maintain, per destination, the failure rate over the trailing 60 seconds so a caller can ask before dispatch whether the circuit should open. Attempts arrive as (destination_id, finished_at_ms, outcome). Requirement: amortised O(1) per attempt, with total memory bounded by the destination count rather than by traffic. Give the structure, its exact memory, and the rule that stops a destination with three attempts from opening a circuit.
Approach
- Name the exact-deque version and then reject it as the default. Holding timestamps and advancing a tail pointer past anything older than now minus 60 seconds is a correct two-pointer window at amortised O(1) per attempt, but its memory tracks in-window traffic, so one destination in a retry storm holds hundreds of thousands of entries while thousands of quiet destinations hold none.
- Use a ring of 60 one-second buckets per destination, each bucket a pair of counters for attempts and failures. On an attempt, advance the ring by the elapsed whole seconds, zeroing at most min(elapsed, 60) buckets, then increment the head. That is amortised O(1) with a fixed footprint per destination.
- State the footprint: 60 buckets times two 4-byte counters is 480 bytes of payload per destination, so 40,000 destinations is roughly 20 to 25 MB with per-entry overhead, bounded by the catalogue rather than by the rate. The cost is granularity, since the oldest bucket ages out in whole seconds, which is far tighter than the decision needs.
- Require a minimum sample before the circuit may open. A destination with three attempts and three failures reads as 100 percent and is not evidence; a floor of roughly 20 attempts in the window makes the ratio meaningful, and below that floor use a run of consecutive failures as the trigger instead.
Follow-up
- The fleet is 30 instances and each sees roughly a thirtieth of a destination's traffic. Where does the rate actually live, and what does a per-instance answer get wrong?
- A destination answers in 9.5 seconds and succeeds. It is not failing but it is consuming your per-destination concurrency. What signal should open the circuit here?
Identify the heaviest tenants in a five-minute window under memory pressure
Medium · top-k · heavy hitters · streaming
The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.
Approach
- Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
- Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
- State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
- Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.
Follow-up
- The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
- Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?
Merge partitioned event streams into one ordered feed with bounded lateness
Hard · k-way merge · watermarks · out-of-order streams
The read-model service consumes 64 log partitions carrying about 4,000 events per second in total. Each partition is ordered within itself, but partitions drift by up to 30 seconds, and the activity feed must present a tenant's events in occurred_at order. Produce the merge. State its complexity, the buffer it requires in events and in bytes, what happens when one partition is idle, and what you do with an event that arrives after you have already emitted its position. Payloads average 1 KB.
Approach
- Merge with a min-heap over the 64 partition heads keyed on (occurred_at, event_id): O(log P) per event and O(n log P) overall. The tie-break on event_id is what makes the output deterministic when two partitions carry the same millisecond, which matters because the feed is paginated and a non-deterministic order reorders pages under the reader.
- Emitting the heap head is only correct once every partition has produced everything up to that timestamp, so the emit condition is a watermark: the minimum across partitions of the highest occurred_at seen, less the allowed lateness. Events are held until the watermark passes them, which is what turns individually ordered streams into a jointly ordered one.
- Size the buffer from the lateness rather than guessing: 4,000 events per second times 30 seconds is 120,000 buffered events, and at 1 KB each about 120 MB of heap. That number is the real price of the ordering guarantee and belongs in front of whoever asked for it.
- Handle the idle partition explicitly, because it fails the feed rather than corrupting it: a partition with no traffic never advances its own maximum, so the watermark freezes and output stops entirely. Either every partition emits a periodic idle marker carrying the broker's current time, or the watermark falls back to wall clock for a partition silent beyond a threshold.
Follow-up
- The lateness budget is raised to five minutes. What is the new buffer, and what besides memory changes?
- The consumer restarts. Where does it resume from, and what does the feed look like for the first 30 seconds?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
Denormalise tenant onto revisions and backfill it live
Hard · denormalisation · online migration · backfill
resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.
Approach
- Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
- Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
- Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
- Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.
Follow-up
- The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
- How do you verify the backfill actually finished, given rows are still being inserted while it runs?
How would you architect a data pipeline to ensure a "single source of truth" across disparate property managem
Medium · Technical Architecture & Systems Thinkin
How would you architect a data pipeline to ensure a "single source of truth" across disparate property management systems?
Approach
- Start with the consumer and the entities: who reads the unified data (repair prioritisation, risk, cost forecasts) and which canonical entities they need, such as portfolio, property, building, asset and work order. Agree freshness up front: is a nightly batch enough, or does an Operations dashboard need hourly data?
- Ingest each property management system through its own connector (API pull, SFTP file drop, or Fivetran-style replication) into append-only raw tables. Tag every row with source_system, source_record_id and extracted_at, and never transform on the way in, so any day can be replayed.
- Map each source in staging models (dbt) to the canonical schema, then resolve identity in a crosswalk table keyed by (source_system, source_record_id) that points to canonical_id. Match on deterministic keys first (normalised address, parcel or geocode), then fuzzy-match above a confidence threshold, and send the ambiguous cases to a human review queue instead of auto-merging.
- Define survivorship per field, not per record: one system is authoritative for unit counts, another for maintenance history, and the latest timestamp wins only among trusted sources. Keep history as SCD type 2 so you can answer what was believed on a given date, and load with MERGE on canonical_id plus a watermark with a lookback window so reruns are idempotent and late updates land.
- Gate publication on tests: canonical_id unique and not null, referential integrity, row-count deltas per source within bounds. Quarantine failing rows instead of dropping them, and let consumers read only published marts. The trade-off to state: strict matching leaves duplicates; loose matching merges two buildings, which is worse because it corrupts downstream decisions.
Follow-up
- Two systems disagree on a building's square footage. Which wins, and how does a user see where the number came from?
- A source system re-keys all its records after a migration. How does your crosswalk survive it?
- How would you backfill five years of history from a newly added source without double-counting?
Walk me through a time you had to integrate a third-party API into a core business workflow; how did you handl
Medium · Technical Architecture & Systems Thinkin
Walk me through a time you had to integrate a third-party API into a core business workflow; how did you handle error states and data consistency?
Approach
- Choose an integration whose failure had a visible cost: a CRM sync like HubSpot, an enrichment provider, billing. Set the situation in two sentences: the workflow, the volume, and what broke or could break when the vendor misbehaved. Say what you personally owned, in the first person.
- Walk through the error taxonomy you built. Retryable errors (timeouts, 429, 5xx) got exponential backoff with jitter and honoured Retry-After. Non-retryable errors (400/422 validation, 401/403 auth) went to a dead-letter queue with an alert, never into a retry loop. Batch endpoints that return per-record results needed per-record handling.
- Explain consistency. You wrote the intent to your own database in the same transaction as the business change (outbox pattern), and a worker delivered it. Every call carried an idempotency key or upserted on a stable external ID, so a retry after a timeout could not create a duplicate contact or deal. You stored the vendor's ID mapping, and a scheduled reconciliation job compared the two sides and repaired drift.
- Cover throughput limits: client-side rate limiting (token bucket) under the vendor's quota, batching, and a queue so bursts did not cascade into 429 storms. Add a circuit breaker if a vendor outage would otherwise block the core workflow.
- Close with a number and a lesson: sync error rate, duplicates removed, time to detect a failure, hours of manual cleanup saved, and what you would build differently. The story fails if it is a happy-path description that ends in 'I added retries' with no answer for a timeout that actually succeeded.
Follow-up
- The API timed out but the write had actually gone through. How did your system find out, and what did the retry do?
- How did you notice slow drift between your records and the vendor's over weeks?
- The vendor deprecated a field or endpoint you depended on. How did you find out, and how did you migrate?
How do you approach building systems that must scale to handle large enterprise data sets?
Medium · Technical Architecture & Systems Thinkin
Approach
- Quantify before you design: row counts and growth per tenant, the largest customer against the median, query patterns, freshness targets and cost ceiling. Scaling decisions depend on which of these is the binding constraint.
- Split workloads. Keep transactional state in an operational database (Postgres or Supabase) and push heavy aggregation to the warehouse (BigQuery or Snowflake). There, partition by date and cluster by tenant or property ID so queries prune, avoid SELECT *, and use incremental dbt models instead of full rebuilds.
- Make processing chunked and idempotent. Use keyset pagination (WHERE id > last_id ORDER BY id LIMIT n), not OFFSET, which rescans skipped rows and can skip or repeat rows under concurrent writes. Use bulk APIs, and run queue-backed workers that scale horizontally with backpressure. Checkpoint progress so a crash resumes instead of restarting.
- Handle multi-tenancy explicitly: tenant_id in every key and index, per-tenant concurrency or quota so one enterprise account cannot starve the rest, and the option to give the largest tenants their own partitions or job lanes.
- Name the trade-offs and observability. Denormalised read models make reads fast, but updates and backfills get harder. Pre-aggregation trades freshness for cost. Track per-job duration, rows processed, warehouse bytes scanned and data-quality failures, so you can see a scaling problem coming before customers do.
Follow-up
- One customer arrives with fifty times the median data volume. What in your design changes first?
- Why keyset pagination over OFFSET, and what does keyset need from the index?
- The warehouse bill doubled last month. How do you find the queries responsible?
How has your use of AI coding tools changed your approach to shipping features?
Medium · AI-Assisted Development & Workflow
Approach
- Name the tools you actually use (candidates report Claude Code, Cursor and Codex coming up) and map them to steps of your cycle: reading unfamiliar code, scaffolding endpoints or connectors, writing tests, refactoring, writing migrations, writing docs. Be clear about which steps you still do by hand.
- Explain how you verify output, because that is what separates leverage from risk. Tests come first or alongside, you read every diff, run type checks and the test suite locally, and you never merge code you cannot explain. Generated calls to external APIs (for example, HubSpot property names or endpoints) get checked against the vendor docs, because those are where invented fields slip in.
- Give one specific feature as a before/after: what you asked the tool to do, what it got wrong, what you fixed, and the measured effect (time from ticket to merge, PR size, test coverage). One real example is worth more than general enthusiasm.
- Cover limits and safeguards: no secrets or customer data in prompts, extra scrutiny on concurrency, idempotency and auth code, and context files or conventions that keep the agent consistent with the codebase.
- Say what changed beyond speed: smaller, more frequent PRs, more tests written because they cost less, and more time spent on design and on understanding the business problem.
Follow-up
- Tell me about a bug an AI tool introduced that got past you. How was it caught?
- How do you give an agent enough context to work safely in a large, unfamiliar codebase?
- How would you review a teammate's PR that was mostly generated?
Exports duplicate a row range about once a week
Hard · leases · clock skew · idempotency
Roughly once a week an export writes a file containing a duplicated range of rows. The affected job_run rows show attempt = 1, status = succeeded, one started_at, and a lease_owner naming a different host from the one whose logs show the job starting. Leases last 30 seconds and are heartbeated every 10 from inside the handler; lease_expires_at is computed on the worker and compared against the database's now(). Find the mechanism, and give a fix that holds even if you cannot fix the clocks.
Approach
- Start from the fact that eliminates the obvious answer. attempt = 1 means no retry was recorded, so this is not a re-run after failure; two workers ran the same row concurrently and the takeover path never touched the counter. lease_owner naming a host other than the one that started the job is the same statement from the other side.
- Enumerate the mechanisms that cause a premature takeover, then find the signal that separates them. Either the lease genuinely expired because the heartbeat did not fire, which is what happens when the heartbeat runs on the handler's own thread and the handler makes a long blocking call, or it only appeared expired because two clocks disagree, since lease_expires_at is written from the worker's clock and evaluated against the database's. The discriminator is the distribution: incidents clustered on the longest exports indict the heartbeat, incidents clustered on one host indict skew. Measure both, and measure each host's offset against the database directly.
- Read the reclaim query precisely. In PostgreSQL now() is transaction start time, not statement time, so a reclaimer holding a long transaction compares against an older timestamp than expected; clock_timestamp() is the statement-time function. This is worth ruling in or out before you redesign anything, because it changes which rows look expired.
- Remove the second clock rather than trying to synchronise it. Issue and extend the lease in the database, with lease_expires_at = now() + interval '30 seconds' in both the claim and the heartbeat, so exactly one clock is ever compared and worker skew stops mattering to this predicate.
Follow-up
- The displaced worker has already streamed half the file to object storage. What makes that side effect safe to repeat?
- You now count takeovers. What alert fires on that counter, and at what threshold?
Suggested practice checklist
Each day works one reported question or one part of the stack candidates describe (HubSpot, SQL, Python or TypeScript, BigQuery or Snowflake), pairs it with a related practice problem where one exists, and produces something written or recorded that you review on day 7.
Day 1 — Confirm the format and map the stack
- Email your recruiter asking for the stage list, who you will meet, and whether any stage involves live coding or a take-home
- Write a one-page map of the HubSpot data model: contacts, companies, deals, custom properties, associations and workflows, plus how data reaches it through the API
- Write a short gap list against the requirements candidates describe (HubSpot, SQL, Python or TypeScript, BigQuery or Snowflake, dbt, Fivetran, Supabase) and pick the two weakest areas to cover this week
Deliverable: A sent recruiter email, a one-page HubSpot data-model map, and a gap list with two priority areas.
Day 2 — Single source of truth across property systems
- Whiteboard the property-data pipeline: raw landing tables, staging models, crosswalk, survivorship rules, SCD type 2 history and quality gates
- Write the SQL for an idempotent MERGE from staging into a canonical properties table and a window-function query that keeps the latest record per source ID
- Work the denormalise-and-backfill practice problem to rehearse changing a live schema without downtime
Deliverable: A pipeline diagram, two working SQL statements, and written notes on the live backfill.
Day 3 — Third-party API integration story
- Write your integration story in STAR form: error taxonomy, idempotency mechanism, outbox or queue, reconciliation job, measured result
- Work the rolling failure-rate practice problem to rehearse the circuit-breaker logic behind a flaky vendor
- Work the duplicated-export debugging problem and note how the same mechanism (retries or overlapping windows) could duplicate CRM records
Deliverable: A written STAR story with one metric, a working failure-rate tracker, and a root-cause note for the duplicate export.
Day 4 — Scaling for large enterprise data sets
- Outline a scaling answer that opens with numbers and covers the OLTP/warehouse split, partitioning and clustering, keyset pagination, idempotent chunked jobs and per-tenant quotas
- Work the heaviest-tenants practice problem to rehearse bounded-memory aggregation
- Work the per-tenant active-cap practice problem to rehearse enforcing tenant limits under concurrent writes
Deliverable: A one-page scaling outline and two solved practice problems with their complexity noted.
Day 5 — Lead scoring with HubSpot and enrichment APIs
- Design the reported lead-scoring scenario: ingest from HubSpot, call enrichment APIs within rate limits, compute a score in the warehouse, write it back to a HubSpot property, and trigger HubSpot Workflows from it
- Decide how the score stays explainable to a rep (stored contributing factors) and how you would check whether it predicts closed deals
- Outline where an LLM-powered agent could detect signals (research summaries, intent signals), and the guardrails: cost, accuracy checks, human review
Deliverable: A lead-scoring design diagram, a written validation plan, and an agent-and-guardrails outline.
Day 6 — AI coding workflow and behavioral stories
- Record a spoken answer on how AI coding tools changed your shipping, built around one feature with a before/after metric and one bug the tool introduced
- Prepare stories for changing priorities, translating a Sales or Operations request into requirements, and documenting a system for others
- Rehearse the practice stories on unblocking a teammate, losing a design argument, and owning an outage
Deliverable: A recorded AI-workflow answer and six STAR outlines, each with a measured result.
Day 7 — Full mock run
- Answer all four reported questions aloud back to back, recording yourself, then the lead-scoring design
- Replay the recording and note every answer where you never named the business consumer or a failure mode, then redo those
- Write three questions for your interviewers about the data sources, the CRM setup and how engineering splits between platform and growth work
Deliverable: A recorded mock of the four questions plus the lead-scoring design, a list of fixed weak spots, and three questions to ask.
Behavioural preparation
Candidates report that past projects are discussed for both their technical complexity and their business impact, and that questions cover working with non-technical colleagues in Sales or Operations and handling shifting priorities. For this role, each story should show you owning a system end to end and include a measurable result tied to the people who use it.
- Walk me through a time you had to integrate a third-party API into a core business workflow; how did you handle error states and data consistency?
- How has your use of AI coding tools changed your approach to shipping features?
- Tell me about a time priorities changed partway through a project. What did you drop, what did you keep, and how did you explain it?
- Describe a request from a Sales, Marketing or Operations colleague that you turned into technical requirements. What did they actually need that differed from what they asked for?
- Tell me about a system you documented so that someone else could run or extend it without you.
Unblock an engineer without taking the keyboard
Easy · mentoring · leases · at-least-once
A teammate has spent two days on a job handler that occasionally writes duplicate rows. They are certain the queue is delivering twice by mistake. You suspect a lease expiring under a slow handler, so the job is running concurrently with itself. Describe how you have unblocked someone in this position: what you asked before offering a hypothesis, what you showed them rather than told them, and what you left them owning. Then say what you would do if their theory turned out to be the right one.
Approach
- Ask before diagnosing, and ask for things answerable from data they already have: the attempt count on the job rows that produced duplicates, the handler's observed duration against its lease expiry, and whether the duplicate rows share a natural key that a unique constraint could have caught.
- Teach the shape rather than the answer. A lease cannot distinguish a dead worker from a slow one, so a handler that outruns its lease is running twice by design, and deploys deliver the other half by killing handlers mid-run on every rollout. Both of their candidate theories produce identical duplicate rows, which is why the evidence has to come from timings rather than from argument.
- Hand over a checklist they execute: a natural key on every write the handler performs so the second copy collides rather than appends, the record of intent written before any external effect, a lease heartbeat while running, and the metric that shows it working.
- Keep ownership with them deliberately. Pair on the first write, then step back; if you finish it yourself you have closed one ticket and left the same person stuck on the next redelivery.
Follow-up
- How would you distinguish a genuine double-delivery from a lease expiry using only the data already stored?
- Their handler calls an external endpoint before recording that it did. What do you tell them to change first?
Argue against a design, lose, and commit anyway
Medium · disagreement · service boundaries · decision records
Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.
Approach
- State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
- Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
- Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
- Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
- What threshold on that alert would have proved you right, and did anyone ever look at it?
- If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?
Narrate an outage you owned from page to postmortem
Hard · incident response · blast radius · postmortems
Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you cannot name what you measured, choose a different incident.
Approach
- Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
- Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
- Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
- Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.
Follow-up
- What would you do differently in the first five minutes, given the same dashboard and no more information?
- Which follow-up action did you deliberately not take, and why was dropping it the right call?