Interview guides · Aunalytics
Aunalytics Software Engineer Interview Guide
Aunalytics Software Engineer interview guide: three reported rounds, backend design, schema migration and caching questions, and a 7-day prep plan.
The 60-second brief
Aunalytics builds analytics and IT technology for established, traditional businesses. Candidates describe the Software Engineer role as work on the company's analytics and insights platforms, which bring together large data sets, cloud infrastructure and AI. The reported role is senior: a backend and full-stack engineer who takes loosely defined requirements through to production, stays responsible for the service afterwards, and works inside technical direction set by the company's software architects.
This guide is for experienced backend-leaning engineers preparing for the Software Engineer loop. Candidates describe the requirements as 7+ years of professional experience, relational data modeling, RESTful services, Git workflows, CI/CD and production operations, with Kubernetes, React/Redux/TypeScript, MongoDB and analytics or AI/ML exposure as extras. It covers the three reported rounds, the four reported question groups (design and architecture, backend and data modeling, scope management and behavioral, production operations), and a seven-day plan. The role is reported as hybrid with three on-site days a week in South Bend, Indiana, so check that before investing time.
Candidates report three rounds over roughly three to five weeks: a Recruiter Screen, an Engineering Manager Interview, and Technical Assessments that cover backend system design, code quality, deployment practices and behavioral interviews.
Focus areas
- Backend Engineering
- RESTful Services
- Distributed Systems
Overview
The reported role sits at the senior end of individual-contributor work. Candidates describe someone operating at staff or principal level who receives an architect's direction and turns it into shipped software: scoping vague requirements, making technical tradeoffs, writing the code, flagging technical debt, reviewing other engineers' changes and informally mentoring. The work feeds analytics and insights platforms, so the data questions are about moving, storing and serving large volumes of data reliably, not about modelling or statistics.
The topics candidates list are backend engineering, RESTful services, distributed systems, production operations and some full-stack work. The stated requirements include relational data modeling, microservice boundaries, failure modes, Git workflows and CI/CD. Kubernetes, MongoDB, React, Redux and TypeScript are described as bonuses, so give them a short refresher but spend your hours on relational schemas, query tuning, API evolution, consistency under failure and release safety.
Roughly half of the fifteen reported questions are experience prompts: the slow-query story, the feature you built from scratch, the production outage, the pushback on a requirement, the vague project and the rescued initiative. These need real projects, told in the first person, with the decision you owned and what it cost. The design questions reward the same habit that candidates' tips describe: begin with the simplest workable design, name the downside of every choice, and say why the tradeoff suits the business case. Practise both modes aloud, because the loop moves from experience discussion toward scenario-based technical questions.
The Aunalytics interview process
Candidates describe three stages that move from background and logistics, to a manager conversation about track record, to technical and behavioral assessments. The question groups in this guide are reported for the loop overall, not tied to one stage.
Recruiter Screen
Candidates report an opening conversation to align on what the role involves, the hybrid working requirement and your general background. Treat it as a fit check with logistics attached: the role is described as senior (7+ years of backend, relational modeling and RESTful service work) and as on-site in South Bend, Indiana, Tuesday to Thursday. Arrive with a short career summary that lands on those requirements, and settle the location question in your own head before the call.
What to demonstrate
- Whether your background lines up with the reported requirements: backend engineering, relational data modeling, RESTful services, CI/CD and production operations
- Whether the hybrid, on-site-three-days arrangement works for you
- How clearly you summarise your own scope and contribution in recent roles
How to prepare
- Write a short career summary that names your most recent system, your role in it, and one outcome, in the first person
- Map your experience against each reported requirement and note which ones are thin, such as Kubernetes or MongoDB
- Decide your answer on the Tuesday to Thursday on-site arrangement and any relocation or commute constraints before the call
- Prepare questions about how the remaining rounds are organised and who you will meet
Engineering Manager Interview
Candidates report a deep-dive discussion focused on your technical track record, how you execute inside architectural guardrails set by others, and your philosophy on scope management. The process description says this conversation may be with an engineering manager or a software architect. Expect to talk through past projects rather than whiteboard. Choose two or three projects where you owned decisions, and be ready to say what the requirement looked like at the start, what you cut, what you shipped and how you watched it in production.
What to demonstrate
- Track record of delivering production systems end to end
- Ability to work within an architecture you did not design, and to question it constructively
- Judgment on scope: pushing back, simplifying, deciding when to take on technical debt
How to prepare
- Build a story bank of three projects, each with the starting requirement, your decision, the tradeoff and the production result
- Write out your answers to the pushback, vague-requirements, rescued-initiative and speed-versus-quality questions in the sections below
- Prepare one example of working within an architect's constraints, including a point where you raised a concern and what happened
- Prepare a concrete example of mentoring through code review, since candidates' tips mention it
Technical Assessments
Candidates describe this stage as a set of technical assessments covering backend system design, code quality, deployment practices and behavioral interviews. The reported question types for the loop include data ingestion design, microservice boundaries, consistency under partition, API evolution, query tuning, zero-downtime schema changes, caching for read-heavy analytics, REST service metrics, deployment pipelines and outage debugging. No algorithm-style coding round is described in the reports, so spend your preparation on design reasoning and operational experience.
What to demonstrate
- Backend system design within stated constraints, including data flow, scalability and reliability
- Code quality and deployment practices: tests, pipeline gates, release confidence
- Behavioral ownership, such as handling outages and delivering without over-engineering
How to prepare
- Rehearse a design answer for the ingestion pipeline: landing raw data, parsing, validation, retries, a dead-letter path, replay and idempotent writes
- Write down your ideal deployment pipeline stage by stage, with the automated gates you would treat as mandatory
- Prepare an outage story with a timeline, the diagnostic steps, the root cause and what changed afterwards
- List the metrics for a production RESTful service and say which ones would page someone
Where candidates lose points
Reaching for fashionable infrastructure in a design answer with no stated reason
Open the microservice-boundaries and ingestion answers with the simplest design that works, such as a monolith with clear module seams and a relational store. Add each service, queue or cache only when you can name the measured need it addresses, and say what it costs to run. Kubernetes and MongoDB are listed as nice-to-haves, so introduce them only if the scenario calls for them.
Drawing the ingestion pipeline as boxes without any failure handling
For high-throughput unstructured data from legacy systems, say what happens to malformed or oversized payloads (a dead-letter store), how duplicates are handled (idempotency keys or a natural dedupe key), how backpressure is applied when downstream is slow, and how you replay raw data after a parser bug. Keep the raw payload so reprocessing is possible.
Answering the consistency-under-partition question with a slogan such as "eventual consistency" or "distributed transactions"
Decide per operation. State which writes must be strongly consistent and which can lag, then name the mechanism: idempotent retries, a transactional outbox, a saga with compensating steps, or single-writer ownership of each record. Say what users see during the partition and how the system reconciles afterwards.
Telling project stories in the plural, so your own decisions are invisible
For every story in your bank, write one line starting with "I decided" that names the choice, the alternative you rejected and the cost. Say "I" when describing your contribution, and credit teammates by role where they did the work. Candidates' tips explicitly ask for the first-person end-to-end narrative from vague requirement to production monitoring.
Giving the slow-query or caching answer with no evidence or invalidation plan
For the query story, quote what the execution plan showed, the change (index, rewritten predicate, schema change), and the before-and-after latency. For the read-heavy analytics cache, say what is cached, the key design, the TTL, the invalidation trigger and what happens on a cache stampede, so the answer includes how stale data is handled and not only where it is stored.
Questions & practice
Archive a resource graph without breaking live references or recursing
Medium · graph traversal · topological order · tenant isolation
Resources reference other resources within a tenant; for the largest tenant the reference table holds up to 2,000,000 nodes and 8,000,000 edges. Archiving a resource must archive everything reachable from it that nothing outside the set still references, refuse when a live external referrer exists, and terminate when references form cycles, which they legitimately do. Produce the archive order and the refusal list, targeting O(V+E). Say what stops the traversal crossing a tenant boundary, and why recursion is the wrong control structure at this size.
Approach
- Load the subgraph with the tenant predicate on both endpoints of the edge, not only on the side you started from. Scoping the left table alone is the classic cross-tenant leak: one mis-entered edge then pulls another tenant's resources into the traversal and, worse, into the archive.
- Traverse iteratively with an explicit stack. A 2,000,000-node graph can hold a chain deep enough to exhaust a native stack in the low tens of thousands of frames, and that failure is a process crash rather than an error you can return.
- Treat cycles as data rather than corruption: compute strongly connected components with Tarjan in O(V+E) using its own explicit stack, then condense. The condensation is a DAG, so a topological order over it gives the archive order, and every member of a component archives in one transaction because no order within a cycle is valid.
- Decide refusals with reverse edges. A candidate is archivable only if every in-edge originates inside the candidate set, so build the transpose or count in-degrees restricted to the visited set, and emit each blocked resource with the id of the external referrer, which is the only part of the answer an operator can act on.
Follow-up
- The graph is read in one query and the archive writes a minute later. What can change in between, and how do you make the write safe?
- The candidate set is 400,000 resources. Is that one transaction, and if not, what does a half-finished archive look like to a reader?
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?
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?
Explain why the owner filter ignores the listing index
Medium · composite index · query plans · sort elimination
The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.
Approach
- Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
- For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
- Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
- Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.
Follow-up
- 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
- A dashboard runs this for 40 owners in one page load. What changes about the design?
How would you design a data ingestion pipeline that handles high-throughput, unstructured data from legacy sys
Medium · System Design & Architecture
How would you design a data ingestion pipeline that handles high-throughput, unstructured data from legacy systems?
Approach
- Start by naming the sources and their shapes before any boxes: nightly flat-file drops over SFTP, fixed-width or EBCDIC exports, CSVs with drifting columns, or a legacy database you can only read through its transaction log (change data capture). Each source gets a thin adapter that only fetches and lands the data.
- Land every payload unmodified in object storage, keyed by source, arrival time and content hash, before parsing anything. That raw zone makes reprocessing after a parser bug a replay instead of asking the legacy owner to resend data.
- Put a durable queue or partitioned log between landing and parsing so bursts are absorbed and parsers scale out on their own. Partition by source or record key to keep per-key order, and accept that at-least-once delivery means every downstream write must be idempotent (upsert on a natural key or the content hash).
- Parse with schema-on-read and validate into a typed staging table. Records that fail go to a dead-letter store with the raw bytes and the error, not into a silent drop, and the parse-failure rate per source is a first-class metric.
- Close with the trade-off: a single batch job can be enough when volume is modest and latency needs are hourly, and I would say what threshold (throughput, freshness requirement) would justify the queue and parallel parsers rather than building them on day one.
Follow-up
- A source starts sending the same file twice with different timestamps. How does your pipeline avoid double-counting?
- How do you handle a legacy export whose column layout changes without notice?
- How would you backfill two years of history without starving the live feed?
Walk us through how you define microservice boundaries. When would you choose to split a service versus keepin
Medium · System Design & Architecture
Walk us through how you define microservice boundaries. When would you choose to split a service versus keeping it monolithic?
Approach
- Draw boundaries around data ownership and business capability: a service owns its tables, and nothing else writes to them. If two candidate services need to update the same rows in one transaction, they are one service.
- Give concrete reasons to split: a component needs a different scaling profile (a CPU-heavy report generator next to a light CRUD API), a different release cadence, a different failure isolation requirement, or a separate team that keeps blocking on shared deploys.
- Give concrete reasons to stay monolithic: a small team, boundaries still moving, heavy cross-entity transactions. Name the costs a split adds: network calls that can fail, distributed transactions replaced by sagas or outbox events, versioned contracts, and tracing across services.
- Recommend a modular monolith as the default: enforce module boundaries in code (no reaching into another module's tables, internal interfaces only), so a later extraction is mechanical. Extract with the strangler pattern, routing one capability at a time to the new service.
- Show the check after a split: if most requests still fan out to the same three services synchronously, the boundary is wrong and the system is a distributed monolith.
Follow-up
- Two services both need customer data. Do you share the table, replicate it through events, or call an API, and why?
- Describe a split you would reverse. What signals told you it was a mistake?
- How do you keep a modular monolith from eroding back into a tangle?
Describe your approach to ensuring data consistency across distributed systems in the event of a network parti
Medium · System Design & Architecture
Describe your approach to ensuring data consistency across distributed systems in the event of a network partition or service failure.
Approach
- Frame it with CAP: during a partition a replicated store must choose between refusing some requests (consistency) and serving possibly stale or conflicting data (availability). Decide per operation, not per system: a balance update and a dashboard read deserve different answers.
- For cross-service writes, avoid two-phase commit, which blocks participants if the coordinator fails after the prepare phase. Use the transactional outbox: write the state change and the event in one local transaction, then relay the event, so a crash cannot leave one without the other.
- Make every consumer idempotent with an idempotency key or a processed-message table, because retries and redelivery are guaranteed under failure. Multi-step business flows become sagas with explicit compensating actions for each step.
- Where a single writer must be guaranteed, use leader election with fencing tokens so a paused old leader cannot write after its lease expires. For quorum-replicated stores, R + W > N makes read and write sets overlap.
- Finish with detection and repair: reconciliation jobs that compare sources of truth and alert on drift, because some inconsistency will slip through and it should be found by a job rather than a customer.
Follow-up
- The outbox relay is down for an hour. What happens to producers and consumers, and what happens when it comes back?
- A compensating action itself fails. What do you do?
- Which operations in an analytics product would you accept as eventually consistent, and which not?
How do you design an API to be both backward compatible and easily extensible for future, unknown requirements
Medium · System Design & Architecture
How do you design an API to be both backward compatible and easily extensible for future, unknown requirements?
Approach
- State the compatibility rule: changes are additive only. New optional request fields with defaults that preserve old behaviour, new response fields, new endpoints. Never rename, remove, change a type or change the meaning of an existing field in place.
- Design for extension from the first version: return objects, not bare arrays, so metadata can be added; use cursor pagination from day one; treat enums as open and document that clients must handle unknown values; require clients to ignore unknown fields (tolerant reader).
- When a breaking change is unavoidable, introduce a new version (URL prefix or header), run both versions side by side, measure which clients still call the old one, and announce removal with a deprecation notice and a Sunset header (RFC 8594) before turning it off.
- Protect the contract mechanically: an OpenAPI spec checked in CI with a diff tool that fails on breaking changes, plus consumer-driven contract tests so a provider change that breaks a known consumer fails before merge.
- Name the trade-off: every optional field and old version kept alive adds testing and maintenance cost, so I would set an explicit support window rather than keep versions forever.
Follow-up
- A field was returned as a string and should have been a number. How do you fix it without breaking clients?
- How do you find out which clients still depend on an old version?
- Where do you put versioning, URL or header, and what does each make harder?
Explain your strategy for caching in a read-heavy analytics application. What cache invalidation strategies ha
Medium · Backend Engineering & Data Modeling
Explain your strategy for caching in a read-heavy analytics application. What cache invalidation strategies have you found most effective?
Approach
- Layer the answer from the data outward: precompute heavy aggregates in the database (summary tables or materialized views refreshed on load), then an application cache such as Redis using cache-aside, then HTTP caching with ETags for responses that do not vary per user.
- Key the cache on everything that changes the result: the normalised query parameters, the user's permission scope or client id, and a data version. Leaving the client or permission scope out of the key leaks one customer's numbers to another.
- For invalidation, tie it to how the data changes. Analytics data usually changes when a load finishes, so I bump a version number per dataset at the end of each load and include it in the key; old entries become unreachable and expire by TTL, with no need to enumerate or delete keys.
- Handle the hot-key problems: request coalescing (single-flight) so one miss does not send a hundred identical queries to the database, jittered TTLs so entries do not all expire together, and stale-while-revalidate where slightly old numbers are acceptable.
- State the trade-off and the measurement: caching trades freshness for latency, so I agree a staleness budget per view and track hit ratio, miss latency and database load to prove the cache earns its complexity.
Follow-up
- A user loads data and immediately refreshes the dashboard but sees old numbers. How do you fix that without disabling the cache?
- How would you refresh a materialized view without blocking readers?
- When would write-through be a better fit than cache-aside here?
Describe a complex backend feature you built from scratch. What were the hardest technical tradeoffs you had t
Medium · Backend Engineering & Data Modeling
Describe a complex backend feature you built from scratch. What were the hardest technical tradeoffs you had to make?
Approach
- Choose a feature where I owned the design decisions and the delivery, and open with the constraint that made it hard: data volume, a latency target, a consistency requirement or an integration with a system I could not change.
- Name two or three real trade-offs with the options I rejected: synchronous API versus a queue and background workers, strict consistency versus eventual consistency, a normalised schema versus a read-optimised copy, building a component versus adopting an existing library or service.
- For each trade-off, say what I gave up and why it was acceptable: for example, I moved report generation to a job queue, which made the feature asynchronous and needed status polling, but removed request timeouts on large clients.
- Show the delivery and the evidence: how I tested it, how it rolled out (flag, migration order), and a number afterwards, such as latency, error rate or support tickets.
- Close with one decision I would change now and why; a story where every choice was right is less believable and hides the judgement.
Follow-up
- Which trade-off would flip if the data volume grew ten times?
- What did you cut from the first version, and did you ever add it back?
- How did you bring the rest of the team along on the design choices?
What metrics do you consider essential for monitoring the health of a production RESTful service?
Medium · Production Operations & CI/CD
Approach
- Start per endpoint with the RED metrics: request rate, error rate and duration. Add saturation from the four golden signals (latency, traffic, errors, saturation), because rate and latency look fine right up until a pool runs out.
- Measure latency as a histogram and report p50, p95 and p99; never average percentiles across instances, aggregate the histogram buckets instead. Track the latency of successful and failed requests separately, since fast-failing 500s can make latency look better during an outage.
- Split errors by class: 5xx are the service's fault and drive alerts; 4xx are tracked separately, since a sudden rise in 401s or 404s often means a client or deploy broke something.
- For saturation, watch the resources that run out first in a REST backend: database connection pool usage and wait time, worker or thread pool usage, queue depth, memory, and the latency and error rate of each downstream dependency.
- Tie it together with SLOs: define availability and latency objectives, alert on error-budget burn rate rather than on every spike, and add a synthetic check that exercises a real request path from outside.
Follow-up
- Your p99 doubled but p50 is flat. What does that suggest and where would you look first?
- Which of these metrics would you page someone for at night, and which would only go to a dashboard?
- How do you avoid high-cardinality labels blowing up your metrics store?
Walk me through your ideal deployment pipeline. How do you ensure confidence when releasing code to production
Medium · Production Operations & CI/CD
Walk me through your ideal deployment pipeline. How do you ensure confidence when releasing code to production?
Approach
- Walk the pipeline in order: short-lived branches and pull requests with review, then CI running lint, type checks, unit tests and integration tests against a real database in a container, then a build that produces one immutable artifact (for example a container image tagged by commit) that is promoted unchanged through every environment.
- Make database changes safe to deploy independently: migrations are backward compatible with the running version (expand, migrate, contract), so code and schema can roll back separately.
- Release progressively: deploy to staging, then a canary or a rolling update gated by readiness probes, compare the canary's error rate and latency with the baseline, and roll back automatically when they breach a threshold.
- Separate deploy from release with feature flags, so risky behaviour ships dark and can be turned off without a redeploy, and remove flags once they are fully on.
- Name what gives confidence and what it costs: each gate adds pipeline time, so fast tests run on every commit, slower suites run before merge or on a schedule, and flaky tests get fixed or quarantined rather than retried until green.
Follow-up
- A canary shows a small error increase that might be noise. How do you decide whether to proceed?
- How would you deploy a change that renames a heavily used column?
- What do you do when the pipeline itself is too slow and engineers start bypassing it?
Talk about a time you had to optimize a severely underperforming relational database query. What was the root
Medium · Backend Engineering & Data Modeling
Talk about a time you had to optimize a severely underperforming relational database query. What was the root cause and how did you fix it?
Approach
- Pick a story with a measured symptom (the query's p95 or a page timing out) and walk the diagnosis in order: capture the exact query with real parameters, then run EXPLAIN (ANALYZE, BUFFERS) or the engine's equivalent rather than guessing.
- Read the plan for the usual root causes and name which one it was: a sequential scan where an index was expected, estimated rows far from actual rows (stale statistics, fixed by ANALYZE), a function or implicit cast on an indexed column that prevents index use, a nested loop over a large outer set, or deep OFFSET pagination.
- Match the fix to the cause: a composite index with equality columns first and the range or sort column last, an expression index or a rewrite of the predicate as a range, keyset pagination instead of OFFSET, or batching an N+1 pattern from the ORM into one query.
- Show the cost of the fix: an index slows writes and takes space, and building it on a large live table needs a non-blocking method (CREATE INDEX CONCURRENTLY in PostgreSQL). Prove the result with the before and after plan and the production latency, not one fast run on a warm cache.
- End with what I changed so it does not recur: slow-query logging with a threshold, plan checks on the heaviest queries, or a review rule for new queries on large tables.
Follow-up
- How did you confirm the planner actually used the new index in production?
- The query is fast for most tenants and slow for one large one. What would you look at?
- When would you denormalise or precompute instead of indexing?
Tell me about the most difficult production outage you’ve had to debug. What was your process, and what did yo
Medium · Production Operations & CI/CD
Tell me about the most difficult production outage you’ve had to debug. What was your process, and what did you learn?
Approach
- Pick an outage I personally drove, and open with impact in concrete terms: what was down or wrong, for whom, and how it was detected (alert, customer report). If a customer found it first, say so and say what monitoring changed afterwards.
- Show mitigation before root cause: I first stopped the damage (rollback, failover, disabling a flag, shedding load), then investigated with the pressure off.
- Walk the debugging sequence: build a timeline of what changed (deploys, config, traffic, data, dependencies), narrow the blast radius with metrics, follow a failing request through logs and traces, form one hypothesis at a time and test it with evidence that could disprove it.
- Separate the trigger from the root cause: a deploy or a traffic spike may have exposed a missing timeout, an exhausted connection pool or an unbounded retry loop that was already there.
- Finish with a blameless postmortem and the action items I owned: the fix, the alert or dashboard that would have caught it earlier, and a guard (timeout, circuit breaker, test) that keeps the same class of failure from recurring.
Follow-up
- How did you know the fix addressed the root cause rather than the symptom?
- What would you change in your on-call process after that incident?
- If the issue had spanned the frontend and the database, how would you have traced a single request across both?
Suggested practice checklist
Each day targets one cluster of the reported rounds and questions, so the three rounds are covered by day seven.
Day 1 — Map the loop and your requirement gaps
- Write out the reported sequence (Recruiter Screen, Engineering Manager Interview, Technical Assessments) and the Tuesday to Thursday on-site arrangement, and decide your answer on location
- Build a table of the reported requirements (7+ years, backend, relational modeling, RESTful services, Git, CI/CD, production operations, distributed systems) against one project of yours each
- List six projects you could use as stories and note which reported question each could answer
Deliverable: A one-page requirements-versus-experience table and a list of six candidate stories tagged by question.
Day 2 — Relational data: query tuning and zero-downtime migrations
- Write your slow-query story: symptom, execution plan finding, root cause, fix, before-and-after numbers
- Write a step list for renaming a column on a busy table using expand, backfill, switch and contract, with lock and replication-lag checks
Deliverable: A written slow-query story with plan evidence, a migration runbook outline, and notes from the two practice questions.
Day 3 — Ingestion pipeline and consistency under failure
- Sketch the ingestion design for high-throughput unstructured legacy data: raw landing store, parsing, validation, idempotent writes, dead-letter path, replay and backpressure
- Write a table of operations that need strong consistency and those that can lag, with the mechanism for each (idempotency key, outbox, saga, single writer)
- Say both answers aloud and note where you hand-waved
Deliverable: One annotated pipeline diagram and one consistency table, each with the failure cases listed.
Day 4 — Service boundaries, API evolution and caching
- Write your rule for splitting a service versus staying monolithic, with three signals for and three against
- List the rules for evolving a REST API without breaking clients: additive changes, tolerant readers, versioning, deprecation
- Design the cache for a read-heavy analytics endpoint: keys, TTL, event-based invalidation, stampede protection, and what staleness you accept
Deliverable: Three one-page answers covering service boundaries, API compatibility and caching, each ending with the downside of your choice.
Day 5 — Production operations and the feature you built
- List the metrics for a production RESTful service: request rate, errors by status class, latency percentiles, saturation, dependency health and database pool use, and mark which would page
- Write your deployment pipeline stage by stage with the mandatory gates, plus the rollback path
- Write the outage story as a timeline with detection, diagnosis, cause, fix and follow-up
Deliverable: A metric list, a pipeline specification, an outage timeline, a feature tradeoff outline and a practice-question attempt.
Day 6 — Scope and ownership stories
- Write the pushback story and the vague-requirements story, each ending with the measured result
- Write the rescued-initiative story with what you cut and what remained afterwards
- Write your answer on speed versus maintainability with the minimum bar you keep, then add a mentoring-through-code-review example
Deliverable: Four written stories and a mentoring example, each with an "I decided" line.
Day 7 — Full mock loop
- Run a manager-style conversation: two project walk-throughs plus the pushback and speed-versus-quality questions, spoken aloud
- Run a technical-assessment-style session: the ingestion design and the service-boundaries answer, spoken aloud
- Review recordings and mark every place you said "we", skipped a tradeoff, or left a failure case out
Deliverable: Two recorded mock sessions and a short list of fixes for each weak answer.
Scope, ownership and delivery stories
Candidates describe the manager conversation and the behavioral part of the technical stage as centred on how you scope ambiguous work, push back on unnecessary complexity, own what you ship and raise team quality through review and mentoring. Prepare stories from real projects in the first person, each with the decision you owned, the alternative you rejected and the measured outcome. The reported prompts come first, followed by one on mentoring.
- How do you decide when to accept technical debt to meet a deadline versus when to push back for more time to build it 'right'?
- Describe a time you raised the quality of a team's code through review or mentoring, and what changed afterwards.
How do you handle schema migrations in a production environment with zero downtime?
Medium · Backend Engineering & Data Modeling
Approach
- Open with the principle: during a rolling deploy, old and new application versions run against the same schema, so every migration step must work with the previous code. That points to expand and contract, also called parallel change.
- Walk the sequence. Expand: add the new column, table or index in a backward-compatible way, with an online index build such as CREATE INDEX CONCURRENTLY in PostgreSQL. Migrate: deploy code that writes both shapes, then backfill existing rows in small, resumable batches by primary-key range. Switch: move reads behind a flag and compare results. Contract: drop the old shape in a later release.
- Name the operational hazards from experience: DDL that takes an exclusive lock or rewrites a large table, so set a lock timeout and retry; adding NOT NULL by first adding a not-yet-validated constraint and validating it separately; backfill load that causes replication lag, which you throttle and watch.
- Frame it as a story, not a lecture. Give the table size and traffic, what you changed, what you measured (lock waits, replication lag, error rate), what went wrong or nearly did, and what rollback looked like. The usual failure is describing the textbook method for a migration you cannot actually describe.
Follow-up
- How would you rename a heavily used column with no downtime?
- What do you do when the backfill starts increasing replication lag?
- How do you roll back after the contract step has already dropped the old column?
Tell me about a time you pushed back on a product requirement because it introduced unnecessary technical comp
Medium · Scope Management & Behavioral
Tell me about a time you pushed back on a product requirement because it introduced unnecessary technical complexity. How did you handle the conversation?
Approach
- Pick a case where the cost of the requirement was concrete, such as an extra service, real-time updates where a scheduled refresh met the need, or a bespoke configuration surface. State the business goal behind the request first, so it is clear you were protecting the goal and not just refusing work.
- Translate the technical complexity into terms the product partner already tracks: delivery date, new failure modes, on-call burden, migration risk, and later change cost. Bring evidence, such as an estimate, a prototype, or an incident from a similar design.
- Offer a cheaper option that still meets the goal, with a stated trigger for building the fuller version later. Describe the conversation: how you raised it, who else was involved, and that the decision stayed with the person who owned the product while the tradeoffs were visible.
- Close with the result and the measurement: what shipped, whether the simple version was enough, and what you would revisit. Include a case where you were partly wrong if you have one. A story where you simply said no, or won by authority, sinks the answer.
Follow-up
- What did you do when the product owner still wanted the original version?
- How did you know the complexity was unnecessary and not just unfamiliar to you?
- Did the cheaper option hold up, and when did you revisit it?
Describe a project where the initial requirements were incredibly vague. How did you drive clarity and deliver
Medium · Scope Management & Behavioral
Describe a project where the initial requirements were incredibly vague. How did you drive clarity and deliver a successful solution?
Approach
- Choose a project where the ambiguity was real and say what was undefined: who the users were, the data volume, the success measure, the source systems, or the deadline.
- Describe the artifacts you produced to create clarity: a short problem statement with assumptions and explicit non-goals, a list of open questions each with an owner, and sample data or example scenarios from the real system to test the interpretations against.
- Show how you cut a v1: a thin end-to-end slice that reached production, with the riskiest unknown tackled first and uncertain choices kept reversible. Name what you deferred and who agreed to that.
- Show the loop afterwards: how feedback from the first release changed the design, and the metric that told you it worked. Avoid saying you simply asked a lot of questions, waited for a perfect specification, or built everything on guesses.
Follow-up
- What did you deliberately leave out of the first version?
- What happened when a stakeholder changed an earlier answer midway through?
- How did you record your assumptions so others could challenge them?
Have you ever had to step in and rescue a failing technical initiative? What was your approach to stabilizing
Medium · Scope Management & Behavioral
Have you ever had to step in and rescue a failing technical initiative? What was your approach to stabilizing the project?
Approach
- Start with diagnosis before action. Say how you found out why the initiative was failing: unclear ownership, runaway scope, an unresolved technical risk, a blocked dependency or poor quality. Name the evidence you used, such as the backlog, defect trend, the plan versus reality, and conversations with each team member.
- Describe stabilisation steps in order: freeze scope and agree a must-have list, attack the riskiest unknown first, set short checkpoints that produce something demonstrable, clarify who owns what, and remove blockers that were not the team's to solve.
- Cover the people side. Keep the original team involved, do not blame earlier owners, and tell stakeholders plainly the new date and what was cut.
- Give specifics on the outcome: what shipped, what was dropped, and what process change stayed afterwards, such as tests around the worst area or a smaller release cadence. Solo heroics, such as rewriting everything yourself, are the story to avoid.
Follow-up
- How did you decide what to cut and who agreed to it?
- Did you ever consider recommending that the project stop?
- How did you keep the original engineers motivated while you changed direction?
How do you balance the need to deliver quickly with the need to write maintainable, high-quality code?
Medium · Scope Management & Behavioral
Approach
- Give a decision rule, not a slogan. Sort changes by how hard they are to reverse. Spend rigor on one-way decisions, meaning data models, public API contracts and anything that migrates data. Accept shortcuts on internal implementation that can be rewritten cheaply.
- State a minimum bar you do not trade away under deadline pressure: tests for the behavior that matters, code review, logging and alerting for new paths, and a flag or other rollback route.
- Make shortcuts explicit. Record each one in a ticket with an owner and a trigger for paying it back, and keep changes small so a late fix does not require a rewrite. Fold refactoring into feature work instead of waiting for a cleanup quarter.
- Back it with one real example: a shortcut you took and what it cost or saved, or a time you asked for more time and what you showed to justify it. Answers that claim you never compromise, or always ship fast, both fall flat.
Follow-up
- Tell me about a shortcut you took and later regretted.
- How do you persuade a team to pay down debt when a deadline is close?
- What is the minimum you require before you merge to production?