cswork / libraryOpen practice workspace

Interview guides · Api Group

Api Group Software Engineer Interview Guide

Api Group Software Engineer interview guide: the three reported rounds, answer approaches for reported questions, and a seven-day prep plan for AI app roles.

The 60-second brief

Candidate reports place this Software Engineer role in AI application development at Api Group. The work described is backend and API engineering: building services that put machine learning models and third-party AI APIs behind applications people use, then keeping those services healthy in production through CI/CD, monitoring and troubleshooting. Reports describe day-to-day collaboration with data scientists, who supply the models, and with product managers, who supply the requirements. The reports say little about the company's own product lines, so prepare around the engineering work rather than the business.

This guide is for Software Engineer candidates preparing for the Api Group loop. Candidates describe the role as backend-leaning, typically asking for about three or more years of professional experience, a backend language such as Python, Java or Node.js, REST API experience and SQL plus NoSQL database knowledge.

Candidates report three rounds over roughly 3-5 weeks: an Initial Conversation with the hiring manager, a Team Interview, and a Final Leadership Round with the hiring manager's manager. They describe the style as conversational and practical rather than puzzle-driven.

Focus areas

  • Software Engineering (General)
  • AI Application Development
  • Model Integration (AI/ML)

Overview

The reported topics fall into three groups: building and operating AI-backed services (background processing behind an API, containers, credential handling, putting a model into a web application), architecture trade-offs (scaling to ten times the traffic, database choice, availability, real-time data streams, performance monitoring), and behavioral stories (tight deadlines, process bypasses, code feedback, explaining technical issues, a failed project). Candidates describe the difficulty as easy to moderate, with questions that follow your own projects instead of algorithm puzzles. That makes the quality of your project stories the main variable in your preparation.

Because the loop is described as conversational and sometimes unscripted, you cannot rely on a question list to structure your answers. Build three or four project narratives, each with the problem, your specific decisions, what you measured and what you would change. Each story should be reusable: the same migration or service can answer the database-choice question, the deadline question and the explain-it-to-a-stakeholder question if you know which details to emphasise.

The technical stack candidates mention is Python, Java or Node.js on the backend, REST and sometimes GraphQL APIs, SQL and NoSQL stores, Docker and Kubernetes, one of AWS, GCP or Azure, and message brokers such as Kafka or RabbitMQ in the less common topics. Prepare to talk about AI-specific operational concerns: a model call that is slow, rolling back when a model version changes, and caching responses to cut cost. If you have never shipped a model, prepare the closest real experience, such as a third-party API with unreliable latency, and be honest about where it stops.

The final round is reported as focused on fit with the wider organisation, including long-term goals, shifting priorities and working across teams. Treat it as a separate preparation task: write down where you want your career to go in the next few years and two examples of re-prioritising under pressure, so you are not improvising those answers in the last conversation.

The Api Group interview process

These three stages are what candidates describe; the reports do not name which question appears in which round, so prepare every topic for every stage.

Initial Conversation

Candidates report the Initial Conversation as a talk with the hiring manager about your background and fit for the role. Reports describe the loop as conversational and not tightly scripted, so expect to carry part of the discussion yourself. Open with a short summary of your recent work in backend or AI-backed applications, then be ready to go deep on any project you mention, because the conversation can follow whatever you raise.

What to demonstrate

  • Background and fit for the role, as the stage is described
  • How clearly you can narrate a past project and your own part in it
  • How well you steer an unscripted conversation toward relevant experience

How to prepare

  • Write a two-minute summary of your career that ends on AI-backed or API-heavy work, and rehearse it aloud
  • Prepare three or four project stories, each with the architecture, your decisions, a measured result and one thing you would redo
  • Write down questions for the hiring manager about the team's technical debt, deployment frequency and how success of the AI applications is measured, all topics candidates suggest raising
  • Rehearse the model-in-a-web-application question using a real project so your summary has material behind it

Team Interview

The Team Interview is reported as a deeper conversation with the team you would join, aimed at technical fit. The technical questions candidates report cover backend API design, production debugging, containers, credential handling, scaling and availability, and model integration. Expect to reason about your own past decisions as much as hypothetical ones, so for every design you describe, have the alternative you rejected and the reason ready.

What to demonstrate

  • Technical fit with the team's work
  • Depth in backend engineering, APIs and architecture trade-offs
  • How you reason about operating software in production

How to prepare

  • Talk through the heavy-background-processing API question end to end: accepted request, queue, worker, status endpoint, retries and idempotency
  • Practise the memory-leak question as an ordered debugging sequence: confirm the growth, capture heap snapshots over time, compare, fix, verify
  • Outline the ten-times-traffic and real-time stream questions as component lists with the first bottleneck you would measure
  • Rehearse the containers question with a specific application, including what went wrong and what you would do differently

Final Leadership Round

The Final Leadership Round is reported as a conversation with the hiring manager's manager about broader organisational fit. Candidates say it centres on long-term career goals, how you handle shifting priorities and how you work across teams. Pitch your answers at the level of business and team outcomes: what changed for users or for the cost of running a system because of your work, rather than only which tools you chose.

What to demonstrate

  • Fit with the wider organisation
  • How you handle shifting priorities and cross-team work
  • How clearly you explain technical work in terms of its business effect

How to prepare

  • Write a short statement of your career direction and why this AI application role is the next step
  • Prepare a story about re-prioritising when two urgent requests collided, and the criteria you used to choose
  • Prepare the explain-a-technical-issue-to-a-non-technical-stakeholder story with the decision it led to
  • Prepare an honest failed-project story and what you changed afterwards, since reflection questions suit a leadership conversation

Where candidates lose points

Describing model integration in buzzwords with no project behind it

Build one concrete story about putting a model or third-party AI API behind an application: the interface, how requests were served, how a slow or failing model call was handled, and how you versioned and rolled back. If you have not shipped a model, use your nearest real experience and say where it differs.

Designing the AI call as a plain synchronous request with no fallback

Say what the user sees when the model or AI service is slow: queue the work and return an accepted response, expose a status endpoint, set timeouts and retries with backoff, and cache repeat results. Name which of these you would add first.

Over-engineering the architecture answer with microservices and Kafka before the problem needs them

Start with the simplest design that meets the stated load, then add components only where you can name the bottleneck they remove. Candidates describe the interviews as practical and iterative, so show a sensible first version and the next two steps.

Waiting to be asked in an unscripted conversation

Reports describe the loop as conversational. Bring three or four project stories, volunteer the relevant one when a topic comes up, and ask the hiring manager questions about technical debt, deployment frequency and how AI features are judged.

Giving behavioral answers where your own decision is invisible

For the deadline, feedback, bypassed-process and failed-project questions, state what you decided and why, what it cost, and what changed afterward. Avoid stories that are all about the team, the long hours or someone else's fault.

Questions & practice

Canonicalise a request body into a stable idempotency fingerprint

Medium · parsing · canonicalisation · hashing

idempotency_key.request_fingerprint is a SHA-256 over the method, path and canonicalised body, and a retry whose fingerprint differs must be rejected with 422 rather than served the stored response. Write the canonicaliser. Bodies are JSON up to 256 KB nested at most 32 levels; clients vary key order, whitespace and unicode escaping, and some send 64-bit ids as JSON numbers. Produce a deterministic byte string such that semantically identical bodies match and any semantic difference does not. State your complexity and name two normalisations you refuse to perform.

Approach

  1. Parse once into a tree, then re-serialise under fixed rules: object keys sorted, array order preserved, one escaping convention, no insignificant whitespace. Parsing is O(n) and sorting keys is O(k log k) per object, so O(n log n) overall with O(depth) stack, and the 32-level cap is enforced during parsing because hostile nesting is how a canonicaliser becomes a stack overflow.
  2. Sort keys by their UTF-8 bytes and say why the obvious implementation is wrong in some runtimes: a default string comparison that orders by UTF-16 code units places surrogate pairs, meaning code points from U+10000 up, below U+E000 to U+FFFF, which is not UTF-8 byte order, so two services written in different languages disagree on the same document.
  3. Do not re-encode numbers through a double. IEEE-754 binary64 represents integers exactly only up to 2^53, so normalising a 19-digit id through a float changes it, and 1 against 1.0 cannot be reconciled without deciding whether they are the same value. Preserve the literal token, and require ids as strings at the API boundary if you want them comparable.
  4. Reject duplicate keys rather than picking one. JSON permits them and parsers disagree, most keeping the last, so any choice you make ties the fingerprint to a parser detail that the code handling the request does not necessarily share.

Follow-up

  • A client sends the same logical request with an extra field your API ignores. Same key, different fingerprint, so you return 422. Is that the right answer?
  • Where does the fingerprint get computed relative to request decompression and the body-size limit?

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?

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?

Keep soft-deleted accounts from blocking re-registration

Easy · soft delete · unique constraints · engine differences

app_user holds user_id, tenant_id, email CITEXT, password_hash (NULL for SSO principals), email_verified_at, auth_version, status ('invited','active','suspended','deactivated'), created_at, updated_at, deleted_at. Two live accounts for one address inside a tenant must be impossible, but an address freed by a soft delete must be reusable, and the same tenant may delete and re-register it repeatedly. Write the uniqueness DDL for PostgreSQL 16, then the equivalent for MySQL 8 where partial indexes do not exist, and say what each permits once three deleted rows already hold that address.

Approach

  1. Start from what is actually unique: not (tenant_id, email), but (tenant_id, email) among live rows. PostgreSQL says that directly — CREATE UNIQUE INDEX app_user_live_email ON app_user (tenant_id, email) WHERE deleted_at IS NULL. A full constraint over the same two columns burns the address permanently the first time someone deletes an account.
  2. Keep case-insensitivity in the type or the index, never in the application: CITEXT as given, or UNIQUE (tenant_id, lower(email)) as an expression index where the extension is unavailable. A case-sensitive unique column is exactly how two accounts for one human appear.
  3. For MySQL 8 the predicate has to move inside the key: add a discriminator column that is a constant 0 while the row is live and is set to user_id on delete, with UNIQUE (tenant_id, email, deleted_marker). Live rows share the constant and still collide; deleted rows differ from each other and stop colliding.
  4. State the NULL variant and its dependency: leaving the marker NULL for deleted rows also works, because a unique index treats NULLs as distinct — true in MySQL, and true in PostgreSQL only under the default NULLS DISTINCT, which PostgreSQL 15 lets you reverse. Check the polarity against the three existing deleted rows: constant-on-live is what preserves the collision you want, and reversing it silently admits duplicate live accounts.

Follow-up

  • A deleted account re-registers with the same address the next day. Do the old resource rows follow the new user_id, and how does the API keep the two principals apart?
  • How do you honour an erasure request while resource_revision.actor_user_id still references this table?

Stop tag and share joins from fanning out a page

Medium · join fan-out · semi-joins · many-to-many

resource_tag is (resource_id, tag_id) with PK (resource_id, tag_id); resource_share is (resource_id, shared_with_user_id, permission). The tagged-and-shared listing inner-joins resource to both, filters tenant_id, tag_id = ANY($2) and shared_with_user_id = $3, orders by updated_at DESC and takes 50. Pages come back with fewer than 50 distinct resources and the total in the header is far too high. Explain the row multiplication, rewrite both the page query and the count query so each is correct, and name the index each one needs. PostgreSQL 16.

Approach

  1. Do the arithmetic against the predicates that are actually there. An inner join emits one row per matching child row, and both joins are filtered: tag_id = ANY($2) admits only the requested tags, shared_with_user_id = $3 admits one user's share rows. So a resource holding three of the requested tags and shared with $3 once yields three rows, not one — the multiplier is its count of matching tags times its share rows for that single user, and that second factor is 1 unless the table admits duplicate (resource_id, shared_with_user_id) pairs. LIMIT 50 then limits rows rather than resources, and COUNT(*) counts pairs — the header is the product, not the population.
  2. Reject DISTINCT as the fix. It deduplicates after the product has been built, so the planner must materialise and sort the fanned-out set before the LIMIT can apply, and it leaves any SUM or AVG in the same select list wrong.
  3. Rewrite both filters as semi-joins, keeping resource as the only row source: AND EXISTS (SELECT 1 FROM resource_tag rt WHERE rt.resource_id = r.resource_id AND rt.tag_id = ANY($2)) and the same shape against resource_share. A semi-join stops at the first match per resource and preserves the driving index order, so ORDER BY updated_at DESC, resource_id DESC LIMIT 50 still stops after 50 rows.
  4. Count with the same predicates and no join at all: SELECT count(*) FROM resource r WHERE r.tenant_id = $1 AND r.status = 'active' AND EXISTS (...) AND EXISTS (...). Nothing multiplies a resource, so the number is the population.

Follow-up

  • The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
  • A resource can be shared with the same user twice under different permissions. Does your count change, and should it?

How do you structure an API to handle requests that require heavy background processing?

Medium · Technical & AI Development

Approach

  1. Make it asynchronous: POST /jobs validates the input, stores a job row with status queued, enqueues a message and returns 202 Accepted with a Location header pointing at GET /jobs/{id}. The client polls that resource, or gets a webhook or server-sent event when the status moves to succeeded or failed.
  2. Make retries safe. The client sends an Idempotency-Key header, and a unique constraint maps that key to one job id, so a retried POST returns the existing job and does not enqueue a second one. Workers must be idempotent too, because SQS standard queues, RabbitMQ with manual acks and Celery with acks_late all deliver at least once.
  3. Avoid the dual write between the database and the broker. Either use a transactional outbox (job row and outbox row in one transaction, with a relay that publishes) or let workers claim jobs straight from the table with SELECT ... FOR UPDATE SKIP LOCKED.
  4. Worker contract: keep a visibility timeout or heartbeat longer than the task, retry with exponential backoff and jitter up to a maximum number of attempts, then send the job to a dead-letter queue. Record each state change (queued, running, succeeded, failed) with error detail, and put large outputs in object storage with a URL on the job.
  5. Backpressure: cap queue depth and per-client rate, return 429 or 503 with Retry-After when the system is saturated, and autoscale workers on queue depth or the age of the oldest message, not on API CPU.

Follow-up

  • A worker crashes halfway through a job. What happens to the job, and what does the client see?
  • How would you let a user cancel a job that is already running?
  • How does the client get its result without polling every second?

How would you securely manage API keys and sensitive credentials in a cloud environment?

Medium · Technical & AI Development

Approach

  1. Keep secrets out of source control, container images and committed .env files. Store them in a managed secret store (AWS Secrets Manager or SSM Parameter Store SecureString, GCP Secret Manager, Azure Key Vault, or HashiCorp Vault), encrypted at rest with KMS-managed keys.
  2. Give workloads an identity, not static keys: IAM roles for EC2 or ECS tasks, IRSA or EKS Pod Identity, GCP Workload Identity, Azure managed identities. Scope least-privilege policies to the specific secrets each service needs, and fetch them at startup or through a sidecar or the Secrets Store CSI driver.
  3. Rotate automatically: Secrets Manager rotation functions, or Vault dynamic database credentials with short leases. During a rollover keep two valid versions so rotation causes no outage, and cache secrets in the app with a TTL so a rotated value gets picked up.
  4. Detect and audit. Log every secret access (CloudTrail or the provider's audit log), run secret scanning in pre-commit and CI (gitleaks, GitHub secret scanning), redact secrets from logs and error traces, and keep a revoke-then-rotate runbook for a leaked key.
  5. For third-party AI or SaaS API keys, keep the key server-side only and never ship it to a browser or mobile client. Proxy the calls through your backend with separate keys per environment and spending limits or quotas on the provider side.

Follow-up

  • Are Kubernetes Secrets enough on their own? (By default they are only base64-encoded, so ask whether etcd encryption at rest and RBAC are configured.)
  • A key was pushed to a public repository an hour ago. What do you do, in order?
  • How do developers get credentials locally without copying production secrets?

Explain how you have previously integrated a machine learning model into a web application.

Medium · Technical & AI Development

Approach

  1. Open with the architecture and the reason for it: model in-process, a separate inference service (FastAPI, TorchServe, Triton, or a managed endpoint such as SageMaker), or a hosted model API. Give the latency and throughput target that drove the choice.
  2. Serving correctness: load the model once at process start, not on every request. Run the same preprocessing at training and serving time (shared code or a serialized pipeline such as an sklearn Pipeline) to avoid training-serving skew, validate inputs against a schema, and log the model version with every prediction.
  3. Latency handling: call fast models synchronously, send slow work (LLM calls, document batches) through a job queue, batch requests on GPU, and set a timeout with a fallback (cached answer, rules-based default, or a degraded UI) so a slow model does not stall the page.
  4. Rollout and rollback: version artifacts in a model registry, try a new version in shadow mode or as a canary before full traffic, and roll back by pointing the service at the previous version. Monitor latency, error rate, input drift and the distribution of predictions.
  5. If a hosted LLM API was involved, explain how you handled 429s (backoff and client-side rate limiting), validated structured output against a JSON schema, tracked prompt versions and cost per request, and kept personal data out of prompts.

Follow-up

  • How did you know the model performed as well in production as it did offline?
  • The service is healthy but prediction quality dropped. How do you detect that and roll back?
  • What changes if inference needs a GPU and traffic is spiky?

If we need to scale an application to handle 10x its current traffic, what components would you evaluate first

Medium · System Architecture & Problem Solving

If we need to scale an application to handle 10x its current traffic, what components would you evaluate first?

Approach

  1. Clarify what 10x means: requests per second, data volume or concurrent users, the read/write ratio, peak versus average, and the latency target. Then measure each tier's utilisation at today's peak. A tier at 30% now needs three times its capacity at 10x, and contention means growth is rarely linear.
  2. Use Little's law (in-flight requests = throughput x latency): 10x the request rate at the same latency means 10x the concurrent requests. That puts thread pools, database connection limits (for example Postgres max_connections, often fronted by PgBouncer), file descriptors and downstream rate limits on the list to check first.
  3. Check the database early, since it is often the first bottleneck: slow queries (EXPLAIN ANALYZE), missing indexes, lock contention and IOPS. Add read replicas for read-heavy load and accept replica lag where it is safe. Use cache-aside in Redis with TTLs and stampede protection for hot reads, and a CDN for static content.
  4. Make the app tier stateless (sessions in a shared store, not local memory) so it scales horizontally behind a load balancer with autoscaling. Move slow or bursty work to queues. For heavy writes, look at batching and partitioning, and say which queries become cross-partition.
  5. Validate with a load test at 10x in a production-like environment, and check cloud quotas, third-party API limits and the cost of the scaled setup before calling it done.

Follow-up

  • Read replicas are already in place and writes are now the bottleneck. What next?
  • What happens to the database when the cache cluster restarts cold?
  • How would you load test at 10x without affecting real users?

How do you ensure high availability and fault tolerance in the systems you build?

Medium · System Architecture & Problem Solving

Approach

  1. Start from targets: an availability SLO (99.9% allows about 43 minutes of downtime in a 30-day month) plus RTO and RPO, because those decide whether one region with multiple availability zones is enough or whether multi-region is worth its cost.
  2. Remove single points of failure: stateless app instances across availability zones behind a load balancer with health checks, a managed database with a synchronous standby and automatic failover (for example RDS Multi-AZ), and backups you have actually restored in a test.
  3. Handle partial failure in code: set a timeout on every network call, retry only idempotent operations, with exponential backoff, jitter and a retry budget, and use circuit breakers on flaky dependencies. Separate connection pools (bulkheads) stop one slow dependency from exhausting everything. Degrade gracefully by serving cached or stale data and shedding load.
  4. Decouple with durable queues and idempotent consumers so a downstream outage turns into a backlog rather than lost requests, with a dead-letter queue for messages that keep failing.
  5. Verify it works: failover drills and game days, alerts on SLO burn rate, and runbooks. Name the trade-off: asynchronous cross-region replication means RPO is above zero, and synchronous replication adds write latency.

Follow-up

  • Retries made an outage worse. Why, and how do you prevent it?
  • What do synchronous and asynchronous database replication each cost you?
  • How do you test failover without causing an incident?

Walk me through how you would design a system to ingest and process real-time data streams.

Medium · System Architecture & Problem Solving

Approach

  1. Clarify event rate and size, the latency target (sub-second or minutes), ordering needs, delivery guarantees, retention, and who consumes the output.
  2. Ingest into a durable, replayable log (Kafka, Kinesis or Pub/Sub). Partition by a key such as device or user id so per-key order holds. The number of partitions caps consumer parallelism, because each partition goes to at most one consumer in a group. Add a schema registry (Avro or Protobuf) so producers and consumers can evolve their schemas safely.
  3. Process with a stream processor (Flink, Kafka Streams or Spark Structured Streaming): stateless transforms plus windowed aggregations on event time, with watermarks for late events and checkpointed state for recovery.
  4. Delivery semantics: committing offsets after processing gives at-least-once delivery, so sinks must be idempotent (upsert keyed on event id). Otherwise use Kafka transactions or Flink two-phase-commit sinks for exactly-once within those systems. Send poison messages to a dead-letter topic rather than blocking the partition.
  5. Sinks and operations: a low-latency store for serving (Redis, Cassandra or Elasticsearch) and object storage or a warehouse for history. Monitor consumer lag and backpressure, and reprocess by replaying from the retained log after a bug fix.

Follow-up

  • One key is far hotter than the rest. How do you keep its partition from falling behind?
  • How do you handle events that arrive an hour late?
  • A bug corrupted a day of aggregates. How do you backfill?

What strategies do you use to monitor application performance and identify bottlenecks?

Medium · System Architecture & Problem Solving

Approach

  1. Cover services with RED metrics (rate, errors, duration) and resources with USE (utilisation, saturation, errors). Report latency as p50, p95 and p99 from histograms rather than averages, and do not average percentiles across hosts.
  2. Propagate trace ids across services with OpenTelemetry and write them into structured logs, so one slow request can be followed hop by hop to the span that took the time.
  3. Find hot paths with sampling profilers and flame graphs (py-spy, async-profiler, pprof). On the database side use the slow query log, pg_stat_statements and EXPLAIN ANALYZE, and look for N+1 query patterns, connection-pool wait time and GC pauses.
  4. Alert on user-facing symptoms such as SLO error-budget burn rate rather than on every internal cause. Keep a dashboard per dependency, and for AI features also track model latency, token usage and cost per request.
  5. Investigate one hypothesis at a time: reproduce under load, change one variable, and compare the same percentile before and after so you can show the bottleneck actually moved.

Follow-up

  • p99 latency doubled while p50 stayed flat. Where do you look?
  • CPU is low but throughput has plateaued. What are the likely causes?
  • How do you keep metric label cardinality (for example user ids) from overwhelming the metrics system?

Walk me through your process for debugging a memory leak in a production application.

Medium · Technical & AI Development

Approach

  1. Confirm it is really a leak. Graph heap and RSS per instance over time: a leak shows a sawtooth whose post-GC floor keeps rising, while a cache warming up levels off and traffic-driven growth tracks request rate. Line the start up against deploys, config changes and traffic shifts, and check whether OOM kills (container exit code 137) and restarts are hiding the trend.
  2. Mitigate before you diagnose. Roll back if a deploy lines up with the start, or schedule rolling restarts to buy time. If you can, take one affected instance out of rotation so you can inspect it without user impact.
  3. Capture comparable evidence: two heap snapshots taken some time apart, diffed by object count and retained size. Per runtime that means a Java heap dump (jmap, or -XX:+HeapDumpOnOutOfMemoryError) read through the Eclipse MAT dominator tree, Node heap snapshots in the DevTools comparison view, or Python tracemalloc snapshots compared with compare_to.
  4. Check the usual culprits: unbounded in-process caches or memo dicts (functools.lru_cache(maxsize=None), or lru_cache on a method, which keeps self alive), global maps keyed by user or request, event listeners that are never removed, closures holding large objects, thread-locals in pooled threads, unclosed streams or connections, and an ML model or tokenizer loaded per request instead of once.
  5. Fix and prove it. Bound the structure (LRU with a max size, TTL, WeakMap or weakref), run a soak or load test that shows the heap reaching a plateau, and add an alert on the per-instance memory growth rate. If RSS grows while the managed heap stays flat, look at native memory instead (Java NMT, allocator fragmentation, native extensions).

Follow-up

  • RSS keeps climbing but the heap snapshots look flat. What do you check next?
  • How would you reproduce the leak outside production?
  • How do you tell a leak from a cache that is working as designed?

Suggested practice checklist

The plan moves from your project inventory through API and operations topics, AI model integration, architecture and database choice, to behavioral stories and a full three-round rehearsal.

Day 1 — Map the loop and inventory your projects

  • Write the three reported rounds in order (Initial Conversation, Team Interview, Final Leadership Round) and mark which one you are least ready for
  • List every project from the last few years and pick three or four that involve APIs, databases, deployment or any model integration
  • For each chosen project write the problem, your decisions, one measured result and one thing you would change

Deliverable: A one-page loop map plus a table of three or four project stories with decisions, results and regrets

Day 2 — API design for background work, and credentials

  • Sketch an API for a long-running job: accepted response, job id, status endpoint, queue, worker, retry rules and an idempotency key
  • Write a spoken outline for storing and rotating API keys in a cloud environment: a managed secret store, least-privilege roles, no keys in images or source, and rotation

Deliverable: One job-API sketch, one credential-handling outline and a solved fingerprint practice problem

Day 3 — Production debugging and performance monitoring

  • Write the memory-leak debugging sequence: confirm steady growth, take heap snapshots at two points, diff retained objects, find the owner, fix and verify under load
  • Write down the signals you would monitor for an API service: latency percentiles, error rate, saturation and queue depth, plus where each is surfaced

Deliverable: A leak runbook, a one-page monitoring checklist and a solved memory-pressure practice problem

Day 4 — Putting a model into an application, and containers

  • Outline how you would expose a trained model as a web service: request validation, model loading at start-up, timeouts, a fallback response, version tags and rollback
  • Write notes on caching AI responses and handling a slow upstream AI service
  • Prepare the containers answer from one real application, with two benefits and two drawbacks you actually hit

Deliverable: A model-serving outline and a containers story with benefits and drawbacks

Day 5 — Scaling, availability, streams and database choice

  • Draw the ten-times-traffic answer: measure first, then separate read and write paths, cache, scale the stateless tier, and check the database
  • Outline availability and fault tolerance: redundancy, health checks, timeouts, retries with backoff, graceful degradation
  • Sketch a real-time stream pipeline with a broker, consumers, a failure queue and idempotent processing

Deliverable: Three architecture sketches, a database-choice story and a solved SQL practice problem

Day 6 — Behavioral stories

  • Write bullet outlines for the tight-deadline, bypassed-process, code-feedback, technical-explanation and failed-project questions, each naming your decision and the result
  • Record yourself answering two of them and trim anything that describes the team instead of your own choices
  • Write your answers on career direction and re-prioritising under competing requests for the final round

Deliverable: Five story outlines, two recorded answers and a short career-direction statement

Day 7 — Full rehearsal of all three rounds

  • Run the Initial Conversation as a spoken walk-through of your career summary and one deep project
  • Run a Team Interview round: pick three technical questions at random and answer each aloud as a walk-through with trade-offs
  • Run the leadership round on career goals, shifting priorities and cross-team work, then finalise your list of questions to ask

Deliverable: Three rehearsed rounds, notes on weak answers and a final question list for each interviewer

Behavioural preparation

The behavioral prompts reported for this loop centre on deadlines with missing information, process and stakeholder friction, feedback, communicating with non-technical people and a failed project, and they pair with a conversational, project-based loop. Prepare stories where you made the decision and can show the result.

Describe your experience with containerizing applications. What are the benefits and drawbacks?

Medium · Technical & AI Development

Approach

  1. Answer with one real application, not a general lecture: what you containerised, why (reproducible builds, matching dev and production, simple rollback by image tag), and your part in it. If you only used Docker locally or in CI and not Kubernetes, say so plainly and describe exactly what you did.
  2. Cover specifics: a multi-stage build to keep the runtime image small, a pinned base image, configuration and secrets injected at runtime and not baked in, a health check, logs written to stdout, and the image built and tagged in CI.
  3. Give the drawbacks from experience: image bloat and vulnerable base layers, orchestration complexity once you add Kubernetes, harder local debugging, awkward persistent data and volumes, and memory limits that kill a process (for example a runtime that sizes its heap from the host, not the container limit).
  4. For AI-backed services, add one relevant point such as large model files making images heavy, so weights are better fetched from storage at start-up or mounted, trading start-up time against image size.
  5. Close with a decision rule: containers pay off when several services or environments must behave identically; for a single small script they can be overhead.

Follow-up

  • How did you keep image size and build time under control?
  • How did you handle secrets and configuration across environments?
  • When would you not containerise a service?

Describe a time you had to choose between two different database technologies. What drove your decision?

Medium · System Architecture & Problem Solving

Approach

  1. Choose a decision you owned and state the constraint in two sentences: the data shape, read and write patterns, consistency needs and expected growth. The reasoning is the answer, not the technology names.
  2. Compare on access patterns, not preference: SQL for relational data, joins, transactions and flexible ad hoc queries; a NoSQL store for a known key-based access pattern, flexible documents or horizontal write scale. Name the specific engines you compared.
  3. Show how you tested the choice: a prototype with realistic data, a load test on the key queries, or a review of operational cost and the team's familiarity with running it.
  4. State what you gave up and how you contained it, for example a lack of cross-document transactions handled by a different schema, or a managed service chosen over self-hosting to lower operations work.
  5. Say how it turned out and what would make you revisit the decision, so the story ends in evidence and not in a verdict.

Follow-up

  • What would have made you pick the other option?
  • How did you migrate or load data, and what did you do about consistency during the move?
  • Did the choice survive the growth you expected, and what changed?

Tell me about a time you had to deliver a project on a tight deadline with incomplete information.

Medium · Behavioral & Team Dynamics

Approach

  1. Set the scene briefly: the deadline, what was unknown, and who was affected. Spend most of the time on how you decided what to do with the gaps.
  2. Sort the unknowns into those that block the work and those you can safely assume. Write the assumptions down, share them with the stakeholder, and ask quick targeted questions of the one or two people who could resolve the blocking ones.
  3. Cut scope to a thin end-to-end version that proves the risky part first, favouring reversible choices such as a config flag or an interface you can swap, so a wrong assumption is cheap to fix.
  4. Describe how you communicated: early warning of the risk, a clear statement of what would and would not ship, and updates when an assumption changed.
  5. Finish with the outcome and what you changed afterwards. Avoid a story that only shows long hours or blames missing requirements; show judgment.

Follow-up

  • Which assumption turned out to be wrong, and how did you find out?
  • What did you decide not to build, and who agreed to that?
  • What would you do at the very start of the next project to avoid this situation?

How do you handle situations where a hiring manager or stakeholder bypasses standard protocols?

Medium · Behavioral & Team Dynamics

Approach

  1. Interpret it as a manager or stakeholder skipping an agreed process: approvals, review, tickets, security checks. First work out why the process exists and why this person went around it; urgency, ignorance and habit call for different responses.
  2. Separate a genuine emergency from a pattern. For a real emergency, help get it done through an expedited path, for example a hotfix with a review immediately afterwards and the ticket filed the same day, so the control is deferred, not dropped.
  3. Talk to the person directly and privately first, framing the process in terms of the risk it prevents, such as an unreviewed change to production or sensitive data sent to an outside AI service.
  4. Document the decision and what was skipped. Escalate only when there is real risk to security, data or customers and a direct conversation did not resolve it.
  5. Avoid both extremes in the telling: rigid refusal that blocks the work, and quiet compliance that leaves the risk unowned. Do not criticise a named person; describe behaviour and outcome.

Follow-up

  • What if they insist and the person is more senior than you?
  • When would you escalate, and to whom?
  • How would you change the process so the shortcut is no longer tempting?

Describe a time you received critical feedback on your code. How did you respond?

Medium · Behavioral & Team Dynamics

Approach

  1. Pick feedback with real substance, such as a race condition, a missing test, an N+1 query or an unclear abstraction, not a trivial style comment. Describe the code and what the reviewer found in two sentences.
  2. Show your first reaction honestly and what you did next: restated the concern to confirm you understood, reproduced or verified it yourself, then fixed it or explained with evidence why you disagreed.
  3. Show a lasting change: a regression test, a lint rule, a checklist item or a different habit when you open a pull request. Feedback that changes nothing is a weak story.
  4. Mention how it changed the way you review other people's code, for example giving specific, actionable comments and asking questions first.
  5. Avoid the humble-brag, where the feedback was that you were too thorough, and avoid blaming the reviewer.

Follow-up

  • Did you ever disagree with a review comment, and how did you settle it?
  • How has this changed how you give feedback to others?
  • What did you add to your process so the same issue is caught earlier?

Give an example of how you explained a complex technical issue to a non-technical stakeholder.

Medium · Behavioral & Team Dynamics

Approach

  1. Choose an issue with real stakes, such as an outage, a slow page, a memory leak or a model giving poor results, and name the stakeholder and what they needed to decide.
  2. Start from impact and the decision, not the mechanism: what users or the business experienced, what the options are, and what each costs in time, money and risk. Then give one short analogy for the cause, with no jargon.
  3. Offer two or three options with a recommendation and say what you would do if they chose differently, so the discussion is about trade-offs they can weigh.
  4. Check understanding by asking them to say it back in their own words or by agreeing next steps, then follow up in writing.
  5. Avoid the failure mode of explaining the architecture. Also avoid sounding condescending; the point is that they can decide, not that they learn the system.

Follow-up

  • What did you leave out on purpose, and why?
  • How did you know they understood and were not just nodding?
  • What happened when the stakeholder pushed back on your recommendation?

Tell me about a project that failed. What did you learn from the experience?

Medium · Behavioral & Team Dynamics

Approach

  1. Choose a project that really failed or was cancelled, where you had a meaningful role. Say what failed in plain terms: a missed target, low adoption, a shutdown or a rewrite. A near-miss that ended well will not satisfy the question.
  2. Own your share. Name two or three contributing causes, including at least one that was yours, such as an untested assumption, skipping an early check with users, or underestimating integration work.
  3. Name the signals you missed or ignored and when the first one appeared. That shows you now know what to look for.
  4. Give concrete changes you made afterwards and a later example where they worked, such as a smaller first release, an explicit risk review at the start, or an earlier demo to the people who would use it.
  5. Keep the tone factual and avoid blaming colleagues, leadership or tools.

Follow-up

  • When did you first suspect the project was in trouble, and what did you do?
  • What would you do differently in the first week if you restarted it?
  • Where have you applied that lesson since?