cswork / libraryOpen practice workspace

Interview guides · Andium

Andium Software Engineer Interview Guide

Andium Software Engineer interview guide: the three reported rounds, system design and behavioral questions, the backend stack and a 7-day prep plan.

The 60-second brief

Andium builds remote monitoring and automated field operations tooling. Candidates describe a mix of its own hardware, computer vision and cloud infrastructure, used by energy companies that want to cut emissions and run operations more efficiently. The Software Engineer role described to candidates sits on the software side of that chain: backend services and data pipelines that move telemetry, sensor readings and video from devices in the field to customers, including in low-bandwidth or harsh conditions. Reported project areas include video streaming, automated alerting and high-frequency data ingestion.

This guide is for backend-leaning software engineers preparing for Andium. Candidates describe the role as asking for 3+ years of professional experience, one major backend language (Python, Go or Java), a cloud platform (AWS or Azure), Docker and Kubernetes, solid SQL or NoSQL design, production APIs and unit and integration tests. The guide covers the three reported rounds, the reliability, security, query, deployment and system design questions candidates report, the behavioral prompts, and a seven-day plan.

Candidates report three rounds over roughly 3-5 weeks: a Hiring Manager Screen, a Technical Deep Dive and Team Sessions. The loop is described as experience-heavy, with practical design discussion in place of puzzle-style coding.

Focus areas

  • System Design
  • Technical Problem Solving
  • Behavioral Questions

Overview

The work described to candidates is backend engineering with a hardware boundary. Services aggregate data from field devices, and candidates report day-to-day contact with product managers, hardware engineers and, in some cases, computer vision teams whose detection models feed the pipeline. For preparation this means your design answers should start at the device: what it sends, what happens when the link drops, and how the cloud side stays correct when data arrives late, twice or out of order.

Candidates describe a loop that centres on your professional history. Expect to be asked why you chose a technology, what a schema cost you, and how you tracked down and prevented a production bug. Reported advice also stresses working with ambiguous or shifting requirements and building solutions that fit real constraints such as deadlines, budget and technical debt. Pick two or three projects you can discuss at the level of individual decisions, and rehearse them until you can give the alternatives you rejected.

The reported question set splits into three groups. Operational fundamentals: high availability in a distributed system, horizontal versus vertical scaling, security for data sent from a remote field device, optimizing a slow production query, and zero-downtime deployment. System design: a rate-limiting service for a public API, a notification system driven by real-time sensor data, tiered storage for millions of high-resolution images, and a real-time telemetry dashboard. Behavioral: a disagreement with a lead, a failed project, staying current with technology, and going beyond your job description. Candidates also report scenarios such as ingesting alerts from 10,000 remote camera sensors and syncing devices with intermittent connectivity.

Nice-to-have items candidates mention are video streaming protocols (RTSP, HLS), MQTT and machine learning deployment. They also report that strong generalist backend skills matter more than prior IoT experience, so spend your time on distributed-systems reasoning first and read up on MQTT and HLS only enough to name where they fit. The plan below moves from your own projects, through fundamentals and design, to behavioral stories.

The Andium interview process

Candidates report three rounds over roughly 3-5 weeks, in the order below. The reported questions apply to the loop as a whole; no source ties a specific question to a specific round.

Hiring Manager Screen

Candidates describe this stage as an opening conversation with the hiring manager about your fit for the role. Reported advice is to pace your preparation so the high-level narrative comes first and the technical deep dives come later. Treat it as the place to present your background clearly: what you built, which decisions were yours, and why this role makes sense next. It is also the natural point to ask about the team's roadmap, structure and project stability, which candidates are encouraged to do.

What to demonstrate

  • Fit for the role, as the stage is described to candidates
  • How clearly you summarize your engineering background and its business impact
  • How well your experience maps to backend services, APIs, data stores and cloud infrastructure

How to prepare

  • Write a two-minute career narrative that ends at backend work involving data from remote or unreliable sources, or the nearest equivalent in your history.
  • Choose two projects and note for each the decision you owned, the alternative you rejected and the measurable outcome.
  • Prepare questions about team structure, roadmap and project stability, plus one about the customers and how the product helps them.

Technical Deep Dive

Candidates describe in-depth technical discussions centred on your engineering background and problem-solving. The reported emphasis is on real systems rather than memorized algorithms, so expect to explain why you picked a language, an API style or a database, and to reason through ambiguous requirements by splitting them into components and raising edge cases early. The design questions candidates report (alerting from remote sensors, intermittent device connectivity, tiered image storage, a live dashboard) are good rehearsal material for this kind of discussion.

What to demonstrate

  • Depth in your primary backend language and the tools you use daily
  • Ability to break an ambiguous requirement into components and surface edge cases
  • Reasoning about data stores, API design and data ingestion from remote devices

How to prepare

  • Rehearse the reported design scenarios aloud, starting each with the device side: connectivity loss, retries, duplicate delivery and ordering.
  • Prepare your debugging story in detail, from first symptom to root cause to the change that prevented recurrence.
  • Be able to defend one database schema you chose: the access patterns, the SQL versus NoSQL reasoning and what you would change now.

Team Sessions

Candidates describe this stage as a set of interviews with team members, each covering a different facet of your experience. The reported guidance is that these sessions are broad, so a single polished project story is not enough. Expect a mix of technical conversation and questions about teamwork, conflict, shifting priorities and explaining technical ideas to people outside engineering. Prepare a bank of stories that can be reused across several prompts, and bring questions for the people you would work with.

What to demonstrate

  • Breadth of your engineering background across the facets teammates ask about
  • How you handle technical disagreement and changing priorities
  • How you communicate technical decisions to colleagues and non-technical stakeholders

How to prepare

  • Write four to six behavioral stories in the situation, decision, result shape, covering disagreement, failure, deadline pressure with incomplete requirements and a stretch beyond your role.
  • Practise the reported operational topics in plain language: zero-downtime deployment, high availability and scaling out versus up.
  • Prepare one explanation of a technical concept (for example idempotent retries) pitched at a non-technical stakeholder.

Where candidates lose points

Describing past projects as a list of features and technologies with no decisions

For each project you plan to discuss, write the choice you owned, the two alternatives you rejected and the evidence that settled it. Candidates report that this loop leans on professional history, so practise answering why a database, language or API style was right for that problem.

Designing the alerting or ingestion pipeline as if the network were reliable

Start from the device. Cover store-and-forward buffering on the device, retries with backoff, idempotent writes keyed by device and sequence number, and how late or duplicate events are handled. The reported scenario about intermittent connectivity expects data to arrive eventually and still be correct.

Answering the device-to-cloud security question with a vague mention of encryption

Name the layers: TLS in transit, per-device identity and credentials that can be revoked or rotated, least-privilege access on the cloud side, signed firmware and updates, and what an attacker gains from a stolen device. Then say how you would detect a compromised or misbehaving device.

Stopping a design at boxes and arrows without numbers or failure modes

Estimate load first (for example sensors times message rate times payload size), pick storage and queueing from that, then say what fails first at ten times the load and what the system does when a dependency is down. Do this for the rate limiter, the notification system, the image storage tiers and the dashboard.

Telling behavioral stories in the first person plural, or blaming others in the failure story

Use I for your own decisions, name the specific call you made and what it cost, and in the failure story state your share of the cause plus a practice you changed afterwards. Keep the story to the situation in a sentence or two and spend the rest on reasoning and result.

Questions & practice

Diff a projection against the primary without per-row point reads

Hard · reconciliation · range hashing · throttling

The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.

Approach

  1. Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
  2. Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
  3. For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
  4. Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.

Follow-up

  • The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
  • Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

Collapse a redelivered event batch into per-aggregate high-water marks

Easy · hashing · at-least-once · aggregation

You drain a batch of up to 5,000,000 events, each (aggregate_id BIGINT, aggregate_version INT, event_type, payload). The log guarantees order within one aggregate only; the batch merges 64 partitions, and a relay failover has redelivered a range, so an older version for an aggregate can appear after a newer one. Given a map of last_applied_version per aggregate, produce the events worth applying, at most one per (aggregate_id, version), plus the count discarded. Target O(n) time. State the memory for 2,000,000 distinct aggregates and what you do when it does not fit.

Approach

  1. One pass, one hash map from aggregate_id to the highest version kept, and a discard counter. An event whose version is at or below last_applied_version for its aggregate is dropped without further work, which is the whole reason the event carries its version rather than a delta. O(n) expected time, O(d) space in distinct aggregates.
  2. Keep the maximum, never the last occurrence. The redelivered range means the final appearance of an aggregate in the batch can be an older version than one seen earlier in the same batch, so last-wins applies stale state over newer state and the projection regresses with no error anywhere.
  3. Cost the memory instead of calling it large: an 8-byte key plus a 4-byte version is 12 bytes of payload, and an open-addressed table held at a 0.7 load factor costs roughly 17 bytes per entry before per-slot metadata, so 2,000,000 aggregates is tens of megabytes in a native layout and several times that in a runtime that boxes both key and value.
  4. If the distinct set exceeds memory, partition on hash(aggregate_id) mod P and reduce each partition independently. Every event for one aggregate hashes to the same partition, so the per-partition result is exact and the merge is concatenation rather than a second reduction.

Follow-up

  • The payload is a patch rather than a snapshot, so applying only the highest version loses the intermediate changes. What changes in your reduction?
  • How do you detect that version 7 arrived while version 6 was never delivered, and what should the consumer do about the gap?

How do you optimize a slow database query in a production environment?

Medium · Technical and Domain Expertise

Approach

  1. Measure before changing anything: find the statement in the slow-query log or pg_stat_statements and check total time, mean time and call count. One slow call and an N+1 pattern of many fast calls need different fixes.
  2. Read the plan with EXPLAIN (ANALYZE, BUFFERS) and compare estimated with actual rows. ANALYZE really executes the statement, so run writes inside a transaction you roll back, or on a replica. Look for sequential scans on large tables, nested loops over a big outer side, and sorts spilling to disk.
  3. Cheapest fixes first: an index that matches the predicate (composite order: equality columns, then the range column; a covering index for index-only scans), sargable predicates (a range on ts instead of a function applied to ts, no implicit casts), and refreshed statistics with ANALYZE.
  4. Ship it safely: in PostgreSQL, CREATE INDEX CONCURRENTLY avoids blocking writes, cannot run inside a transaction block, and leaves an INVALID index to drop if it fails. Every new index also slows inserts, which matters on high-write telemetry tables.
  5. If indexing is not enough: keyset pagination instead of large OFFSETs, pre-aggregation or materialized views for dashboard reads, time partitioning for pruning, a read replica or a cache. Confirm the win with the same plan and the p95 latency.

Follow-up

  • The query uses the index on staging but scans the table in production: what could explain that?
  • How would you add an index to a large, write-heavy table without blocking writes?
  • When would you denormalise or pre-aggregate instead of adding another index?

Write the update path that detects a concurrent edit

Medium · optimistic concurrency · transactional outbox · audit history

resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.

Approach

  1. One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
  2. Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
  3. State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
  4. Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.

Follow-up

  • A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
  • Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?

How do you ensure high availability in a distributed system?

Medium · Technical and Domain Expertise

Approach

  1. Start from a target, not a technique: agree an availability objective and what it allows. 99.9% over a 30-day month is about 43 minutes of downtime, 99.99% about 4.3 minutes, and each extra nine changes the design and the cost.
  2. Remove single points of failure tier by tier: stateless replicas behind a load balancer with health checks, spread across availability zones; a replicated data tier with automatic failover. State the trade-off: synchronous replication gives zero data loss but adds write latency, asynchronous can lose the last writes.
  3. Contain partial failures: timeouts on every remote call, retries with exponential backoff and jitter only on idempotent operations (idempotency keys for writes), circuit breakers, bulkheads, and graceful degradation such as serving cached or stale data.
  4. For replicated state, use quorums: N consensus replicas tolerate floor((N-1)/2) failures, and fencing or quorum stops split brain when a primary is partitioned rather than dead.
  5. Prove it: alert on error-budget burn rate, rehearse failover and zone loss, and ship through canaries, because bad deployments cause a large share of outages.

Follow-up

  • How does your failover avoid split brain when the old primary is unreachable but still accepting writes?
  • Which features would you let go read-only or stale during an outage, and how would clients know?
  • How do you stop client retries from turning a partial outage into an overload?

Explain the difference between horizontal and vertical scaling and when you would use each.

Medium · Technical and Domain Expertise

Approach

  1. Define both: vertical scaling moves to a bigger machine (CPU, memory, faster disk); horizontal scaling adds more nodes and spreads load across them.
  2. Vertical: no code change, keeps single-node transactions and strong consistency, but it hits a hardware ceiling, cost climbs steeply near the top, the node stays a single point of failure, and resizing often needs a restart.
  3. Horizontal: near-linear capacity and fault tolerance for stateless tiers, but the service must hold no local state (sessions in a shared store), and stateful tiers need replication or sharding, which brings cross-shard queries, rebalancing and more to operate.
  4. Typical order for a relational database: scale up first, add read replicas for read load (accepting replication lag), shard by key only when writes outgrow one primary. Stateless API and worker tiers scale out with autoscaling on CPU or queue depth.
  5. Name the limits: adding nodes does not help a shared bottleneck (a hot row, a hot partition key), and a consumer group on a partitioned log cannot have more active consumers than partitions.

Follow-up

  • Your database is at 80% CPU on the largest instance available: what do you try next, in order?
  • What has to change in a service before it can be scaled horizontally?
  • How do you pick a shard key, and what do you do when one key gets hot?

What are the primary security considerations when transmitting data from a remote field device to the cloud?

Medium · Technical and Domain Expertise

Approach

  1. Open with the threat model: the device can be physically captured, the network (cellular, satellite, site Wi-Fi) is untrusted, and the risks are eavesdropping, man-in-the-middle, spoofed devices, replayed messages and tampered firmware.
  2. Transport: TLS 1.2 or 1.3 with strict server certificate validation, plus mutual TLS with a unique X.509 certificate per device, never a fleet-wide shared secret. MQTT runs over TLS on port 8883; plain RTSP video carries no encryption, so tunnel it or use its TLS variant.
  3. Identity lifecycle: keep the private key non-exportable in a secure element or TPM, provision it at manufacture, and support rotation and revocation so one stolen unit can be cut off without touching the rest of the fleet.
  4. Least privilege in the cloud: a device may publish only to its own topics and read only its own commands, enforced by ACLs keyed on the certificate identity. For data buffered offline, add sequence numbers or signatures so replays and duplicates are rejected, and encrypt the local buffer.
  5. Secure updates: signed firmware verified on the device (secure boot, rollback protection). On constrained links, TLS 1.3 needs one round trip for a full handshake and session resumption cuts reconnect cost.

Follow-up

  • One device is stolen from a site: what do you revoke, and what can the attacker still do?
  • How does your design change on a metered satellite link where every handshake costs money?
  • How do you rotate certificates on devices that stay offline for weeks?

Describe your process for performing a zero-downtime deployment.

Medium · Technical and Domain Expertise

Approach

  1. Pick and justify a strategy: a rolling update (in Kubernetes, maxUnavailable 0 with maxSurge for headroom), blue-green (switch traffic between two full environments; instant rollback at double capacity) or a canary (a small share of traffic, compared on error rate and latency, with automatic rollback).
  2. Make instances safe to replace: readiness probes so traffic reaches only ready pods; on SIGTERM stop accepting work and drain in-flight requests within the grace period (30 seconds by default in Kubernetes); a short preStop delay because endpoint removal propagates asynchronously.
  3. Run schema changes as expand and contract: add the new column or table first, deploy code that writes both, backfill in batches, switch reads, and drop the old column in a later release. Set a lock_timeout so a migration cannot queue behind a long transaction and block traffic.
  4. Old and new versions run side by side during the rollout, so API contracts and message schemas must stay backward and forward compatible. Feature flags separate deploying code from releasing behaviour.
  5. Close with rollback and observability: health gates that halt the rollout automatically, and a rollback path that stays valid because the schema is still compatible with the previous version.

Follow-up

  • How do you roll back a release whose database migration has already run?
  • How do long-lived connections such as WebSockets or video streams survive a rollout?
  • What changes when the target is a fleet of field devices instead of cloud services?

Design a rate-limiting service for a public-facing API.

Medium · System Design and Architecture

Approach

  1. Pin the requirements: what the limit is keyed on (API key, user, IP), the limits per endpoint or plan, whether it must hold across all instances, how much latency it may add, and what a limited caller gets: HTTP 429 Too Many Requests with a Retry-After header.
  2. Compare algorithms: a token bucket (capacity sets the burst, refill rate sets the sustained rate, O(1) state of tokens plus last-refill time); a fixed window (simple, but allows up to twice the limit across a window boundary); a sliding log (exact, memory grows with requests); a sliding window counter (weighted previous window, a good approximation).
  3. Placement and state: enforce in the API gateway or middleware, with counters in Redis updated atomically by a Lua script (or INCR plus EXPIRE), keys per client and window with a TTL, and Redis sharded by key. Use Redis server time inside the script to avoid clock skew between app nodes.
  4. Decide the failure mode: if the counter store is down, fail open with conservative local in-memory limits for ordinary endpoints, and fail closed for expensive ones. Local buckets with periodic sync cut latency at the cost of accuracy.
  5. Operate it: return remaining-quota headers, hot-reload limit configuration, log and graph rejections per key so abusive clients and wrongly sized limits are visible.

Follow-up

  • How do you enforce one global limit across several regions without a cross-region call per request?
  • A customer gets 429s while they believe they are under quota: how do you debug it?
  • How would you charge an expensive search more of the quota than a cheap read?

How would you architect a notification system that sends alerts based on real-time sensor data?

Medium · System Design and Architecture

Approach

  1. Size it and lay out the pipeline: devices publish (for example over MQTT) to an ingest tier, readings land in a durable log partitioned by device or site to keep per-device order, a stream processor evaluates rules, and alert events go to a notification service with per-channel queues (email, SMS, push, webhook). 10,000 devices at one reading per second is 10,000 events per second.
  2. Rules need keyed state: beyond simple thresholds, support sustained conditions, rate of change and windowed aggregates per device. Process on event time with watermarks so late or buffered readings are judged correctly, and use hysteresis (separate trigger and clear thresholds) to stop flapping.
  3. Model alerts as incidents with states (open, acknowledged, resolved) and a dedup key such as device plus rule, so one condition sends one notification. Add cooldowns, grouping when a whole site trips at once, and escalation when nobody acknowledges.
  4. Delivery is at-least-once, so sends carry idempotency keys; failed sends retry with backoff and land in a dead-letter queue, with a fallback provider per channel. User preferences and quiet hours live in a database, and every send is audited.
  5. Treat silence as a signal: a heartbeat timeout raises a connectivity alert that is kept distinct from a bad sensor value.

Follow-up

  • A site reconnects and uploads an hour of buffered readings: which alerts should fire, and when?
  • How do you stop a flapping sensor from paging someone fifty times?
  • How do you make sure an alert is not lost if the notification worker crashes mid-send?

Design a storage solution for millions of high-resolution images that need to be accessed frequently for the f

Medium · System Design and Architecture

Design a storage solution for millions of high-resolution images that need to be accessed frequently for the first 30 days and rarely thereafter.

Approach

  1. Estimate first: number of images times average size (a million 5 MB images is about 5 TB), the upload rate, the read rate in the first 30 days and after, and how quickly an old image must come back.
  2. Store blobs in object storage (for example S3) and metadata in a database: id, device, capture time, object key, size, checksum, current tier. Clients upload directly with presigned URLs, using multipart upload for large files, and lookups by device and time range hit the metadata index, never a bucket listing.
  3. Use a lifecycle policy: S3 Standard for the first 30 days, then Standard-IA or Glacier Instant Retrieval (millisecond reads, cheaper storage, per-GB retrieval fees), or Glacier Flexible Retrieval or Deep Archive if waiting minutes to hours is acceptable. Objects must sit in Standard for 30 days before moving to Standard-IA, which fits this access pattern.
  4. Check the cost traps: Standard-IA bills a 128 KB minimum object size and a 30-day minimum duration; Glacier Instant and Flexible bill 90 days, Deep Archive 180; transitions are charged per object. Intelligent-Tiering suits uncertain access but adds a per-object monitoring fee.
  5. Serve the hot set well: generate thumbnails and smaller renditions asynchronously on upload, put a CDN in front, encrypt at rest, and turn on versioning or cross-region replication only if the retention requirements call for it.

Follow-up

  • A customer needs an eight-month-old image for an incident report today: what does your tier choice mean for them?
  • How would the design change if most images were under 128 KB?
  • How would you enforce a deletion policy, for example removing images after a set retention period?

Walk through the design of a telemetry dashboard that updates in real-time.

Medium · System Design and Architecture

Approach

  1. Clarify scale and freshness: number of devices and metrics, how fresh the screen must be, how many concurrent viewers, and the history ranges the charts need.
  2. Store data in a time-series database (TimescaleDB, InfluxDB or similar) with rollups: raw points for short retention and 1-minute and 1-hour aggregates for long ranges, so a 30-day chart reads aggregates and never millions of raw rows. Cap points per panel at roughly the chart's pixel width.
  3. Push live updates with Server-Sent Events (one-way over HTTP, automatic reconnect, Last-Event-ID for resume) or WebSockets when the client also sends messages. A gateway tier subscribes to a pub/sub channel and forwards only the series each client has subscribed to, batching updates rather than sending every point.
  4. Load the historical range by query first, then apply live deltas; on reconnect, resume from the last timestamp or sequence number to fill the gap. Slow clients get the latest value instead of an unbounded queue.
  5. Show data quality honestly: render gaps as gaps rather than zeros, place late points by event time, and mark devices whose last reading is stale. Gateway nodes hold no state beyond open connections, so they scale horizontally.

Follow-up

  • A thousand operators open the same dashboard during an incident: what breaks first, and how do you protect it?
  • How should a chart handle a device that reconnects and backfills an hour of data?
  • When would you choose plain polling over a push connection?

p99 jumped on one listing filter while p50 stayed flat

Medium · query planning · composite index · percentiles

After a release that added an owner_user_id filter to the resource listing, p99 rose from 90 ms to 1.9 s while p50 stayed at 40 ms. Traffic and row counts are unchanged. resource carries the index (tenant_id, status, updated_at DESC, resource_id DESC). The new query filters tenant_id and owner_user_id, orders by updated_at DESC, resource_id DESC, and takes 20 rows. On PostgreSQL, explain the shape of the regression, prove it from a query plan, and give the index you would add.

Approach

  1. Start from the shape. A flat p50 with a moved p99 means a subset of requests changed cost, not all of them, so the first job is naming the subset. Bucket the endpoint's latency by the tenant's row count; the natural hypothesis is that large tenants are a small share of requests and all of the tail.
  2. Get the plan for the new query on a large tenant with EXPLAIN (ANALYZE, BUFFERS). Expect an index scan over the tenant's range, a filter discarding most of it, then a Sort feeding the Limit, possibly reporting Sort Method: external merge Disk. Read actual rows on the scan node, not estimated.
  3. Explain why the existing index cannot serve it. A composite B-tree is seekable only as a left prefix, and with no equality predicate on status the scan cannot treat updated_at as an ordering, because rows in the tenant's range are ordered by status first. Everything matching must be read and sorted before LIMIT 20 can apply, so a tenant with 400,000 rows pays 400,000 rows to return 20.
  4. Add (tenant_id, owner_user_id, updated_at DESC, resource_id DESC). Equality on the first two columns leaves the index ordered by updated_at within that pair, so the plan becomes an index scan that stops after 20 rows with no Sort node. PostgreSQL can scan a B-tree backwards, so the DESC markers matter only if the two sort columns ever disagree in direction; keeping them explicit documents the order the keyset cursor depends on.

Follow-up

  • The endpoint paginates with OFFSET. What does page 500 cost with your index, and what does the keyset version cost?
  • How would you have caught this before release, given that a 10,000-row seed database produces the same plan shape at an unnoticeable cost?

Suggested practice checklist

The plan starts with your own projects, since candidates describe an experience-heavy loop, then covers the reported operational and design questions, and ends with behavioral stories. Practice-library questions are included where they match the data-pipeline themes.

Day 1 — Map the loop and inventory your projects

  • Write the three reported rounds (Hiring Manager Screen, Technical Deep Dive, Team Sessions) and one line on what you must show in each.
  • List every project from the last few years and pick the two with the most architectural decisions and the clearest outcomes.
  • Write the career narrative for the Hiring Manager Screen and a list of questions about roadmap, team structure and customers.

Deliverable: A one-page loop map, a ranked project list with two chosen projects, a written career narrative and a list of questions for the team.

Day 2 — Rehearse experience walkthroughs

  • For each chosen project, write the architecture, the schema, the decision you owned, the rejected alternative and the result.
  • Write your production-bug story from symptom to root cause to prevention, including the test or alert you added.
  • Explain aloud why you chose your database (SQL or NoSQL) for the schema in your best project, tied to its access patterns.

Deliverable: Two project write-ups, one debugging story and one spoken schema trade-off explanation, each with decision, alternative and outcome.

Day 3 — Reliability, scaling and deployment fundamentals

  • Answer aloud how you ensure high availability: redundancy, health checks, failover, graceful degradation and the cost of each.
  • Write a comparison of horizontal and vertical scaling with a concrete case each, and name what makes scaling out hard for stateful services.
  • Outline a zero-downtime release for a containerized service: rolling update, readiness probes, backward-compatible schema changes, and rollback.

Deliverable: A spoken high-availability answer, a scaling comparison with two cases and a written zero-downtime release checklist.

Day 4 — Device-to-cloud security and slow queries

  • Write a security checklist for data sent from a field device: transport encryption, device identity and rotation, authorization, update integrity and abuse detection.
  • Walk through optimizing a slow production query: reading the plan, indexes, query rewrites, statistics and verifying the fix safely.
  • Work the practice question on a p99 latency jump on one filter while p50 stays flat, and note how it changes your query-tuning sequence.

Deliverable: A device-security checklist, a written query-tuning procedure and notes from the p99 practice question.

Day 5 — Design: rate limiting and alert notifications

  • Design the rate-limiting service: algorithm (token bucket or sliding window), where counters live, behavior across multiple nodes and what happens when the counter store fails.
  • Design the real-time alert notification system: ingestion, rule evaluation, deduplication, delivery channels and retries.
  • Rehearse the reported scenario of ingesting real-time alerts from 10,000 remote camera sensors, starting with load estimates.

Deliverable: Two design write-ups (rate limiter and alert notifications) with load estimates and failure modes, plus a spoken ingestion walkthrough.

Day 6 — Design: image storage, telemetry dashboard and sync

  • Design tiered storage for millions of high-resolution images that are read often for the first 30 days and rarely afterwards, covering lifecycle rules, metadata indexing and retrieval from the cold tier.
  • Design the real-time telemetry dashboard: ingestion, aggregation, push versus polling, and how stale data is handled.
  • Answer the reported question on devices with intermittent connectivity that must eventually sync all data, then work the practice coding question on collapsing a redelivered event batch into per-aggregate high-water marks.

Deliverable: Written designs for image storage and the dashboard, a device-sync answer and a solved redelivery practice question.

Day 7 — Behavioral stories and a full run-through

  • Write and rehearse the four reported stories: a disagreement with your lead, a failed project, how you stay current and decide what to adopt, and going beyond your job description.
  • Add the deadline-with-incomplete-requirements story and the explain-to-a-non-technical-stakeholder story.
  • Run the whole loop aloud: career narrative, one project deep dive, one design and two stories, then fix the weakest answer.

Deliverable: Six written stories in situation, decision, result shape and a recorded or spoken full run-through with a note on the weakest answer.

Behavioral stories for the Andium loop

Candidates report that the behavioral side of the loop covers conflict resolution over technical decisions, adaptability when priorities shift, and interest in the industrial and sustainability space. The Team Sessions are described as broad, so prepare stories you can reuse across prompts. Each should show a decision you made, the evidence you used and the result, and avoid blaming colleagues.

  1. Tell me about a time you had to deliver a project under a tight deadline with incomplete requirements.
  2. Describe a situation where you had to explain a technical concept to a non-technical stakeholder.

Describe a time you disagreed with a technical decision made by your lead. How did you handle it?

Medium · Behavioral and Leadership

Approach

  1. Choose a decision with real technical alternatives, such as a message queue versus polling, a schema shape or a rollout method, and state each option in a sentence so it is clear the disagreement was substantive and not a matter of taste.
  2. Show how you raised it: a private conversation first, then a short written comparison backed by evidence such as a benchmark, incident history or operational cost. Say what your lead knew that you did not, which shows you weighed their view.
  3. Give the resolution honestly. If you changed their mind, say what evidence did it. If you lost, say that you committed fully, delivered the chosen approach and what happened later. A story where you were proven right but damaged the relationship is a weak one.
  4. Close with the habit it left you with, for example writing a decision note with the criteria for revisiting it.

Follow-up

  • What if your lead had still said no after seeing the data?
  • How did the working relationship change afterwards?
  • What did your lead's approach have going for it that you underrated?

Tell me about a project that failed. What did you learn, and what would you do differently?

Medium · Behavioral and Leadership

Approach

  1. Pick a project where you owned meaningful decisions and say plainly what failed: a missed goal, a shelved launch or an incident it caused. A failure that is really a disguised success, such as working too hard, fails the question.
  2. Explain the root cause in terms of your own choices, such as unvalidated requirements, no load test, an unclear owner or an oversized first release, and when the first warning signs appeared and what you did about them.
  3. State the cost honestly (time, customer impact, rework) and what you did to limit the damage once you saw it.
  4. End with a concrete change you have applied since, such as incremental rollouts, written success criteria or earlier checkpoints with stakeholders, and where it worked on a later project.

Follow-up

  • When did you first see warning signs, and why were they not acted on sooner?
  • What did the failure cost, and how did you communicate it to stakeholders?
  • Where have you applied the lesson since, and what was the result?

How do you stay current with new technologies and decide which ones to bring into your work?

Medium · Behavioral and Leadership

Approach

  1. Describe a repeatable process in place of a newsletter list: release notes and postmortems for the tools you run, reading source or specs for protocols, and learning from colleagues and incident reviews. Tie it to your stack, for example how you follow your language runtime, Kubernetes releases or cloud-service changes.
  2. Show the decision method: a time-boxed spike against a real problem, then criteria such as operational burden, failure modes, team familiarity, lock-in and whether the existing tool already does the job.
  3. Give one example where you adopted something and how you rolled it out, and one where you evaluated and rejected it. The rejection shows judgment and makes the answer more credible than one adoption story.
  4. Name the mistake you avoid: adopting a technology because it is new or looks good on a resume, with no measured problem it solves.

Follow-up

  • Tell me about a technology you chose not to adopt and why.
  • How did you bring a new tool to your team and handle people unfamiliar with it?
  • How do you retire a technology that is no longer paying for itself?

Give an example of a time you went above and beyond your job description to solve a problem.

Medium · Behavioral and Leadership

Approach

  1. Pick a problem outside your assigned scope and say why it became yours: nobody owned it, it was blocking a team or customer, or it was recurring. For a field-device product, a plausible case is diagnosing flaky devices, building the missing alert or writing the runbook after an incident.
  2. Say what you did concretely and which colleagues you worked with, such as support, product or another engineering group, so it reads as initiative within a team and not as bypassing one.
  3. Quantify the result (errors removed, hours saved, a customer unblocked) and say how you balanced it against your committed work and whether your manager knew.
  4. Finish with the handoff: documentation, tests or ownership transferred, so the fix did not depend on you. Avoid stories of unsustainable overtime or unrelated side projects.

Follow-up

  • How did you decide it was worth your time compared with your assigned work?
  • Who else did you involve, and did anyone object?
  • What happened to it after you moved on?