cswork / libraryOpen practice workspace

Interview guides · ASTEK

ASTEK Software Engineer Interview Guide

ASTEK Software Engineer interview prep: the 3 reported rounds, answers on React, REST APIs, security and system design, and a 7-day plan.

The 60-second brief

ASTEK is a consulting company that sells engineering and IT services to organisations in industry and in the service sector. Its software engineers are placed on client projects rather than one in-house product. Candidates describe work that changes from project to project: backend APIs, cloud-native services, and user interfaces. The role requirements candidates describe name TypeScript and React on the frontend, Node.js and Java on the backend, cloud platforms (AWS, Azure, GCP), Docker and Kubernetes, and Agile/Scrum delivery. Day to day, the role includes code reviews, documentation and architecture discussions. Engineers work alongside project managers, business managers and sometimes the client's own team, so a lot of the job is turning a client's business requirement into a feature that has been built, tested and deployed.

This guide is for experienced full-stack or backend-leaning engineers interviewing for a Software Engineer position at ASTEK. The requirements candidates describe ask for a technical degree, at least five years of development experience, React on the frontend and solid backend experience. The guide covers the three reported stages and gives question-specific guidance for each of the fourteen reported questions: React state management, SQL versus NoSQL, REST API design, OOP, production debugging, scaling for high traffic, CI/CD, authentication and authorization, legacy-to-microservices migration, and five behavioral questions about consulting work. A seven-day plan ties all of it together.

Candidates report three rounds over roughly 3-5 weeks: Initial Screening, Technical Evaluations (a coding assessment or an architectural case study, depending on the team), and a Final Client-Facing Validation for a specific project.

Focus areas

  • Algorithmic Problem Solving
  • Working Experience & Technical Background
  • Programming Fundamentals

Overview

ASTEK places engineers on client projects, so the loop checks two things. First, can you deliver in the stack a project needs? Second, can you be put in front of a client? The reported technical questions are mostly practical and stack-specific rather than puzzle-style: React state and effects, relational versus non-relational storage, REST API design, OOP, and production debugging. The topic areas candidates report also include algorithmic problem solving, data structures and programming fundamentals, so do not skip coding practice just because the named questions are conceptual.

The architecture questions candidates report are all open design prompts: serving high traffic at low latency, planning CI/CD, securing authentication and authorization, and migrating a legacy system to microservices. Answers that land name concrete components and the trade-off behind each one: a cache and how it is invalidated, a canary and its rollback trigger, object-level authorization checks, the strangler-fig pattern. Candidates also report questions on code quality, unit, integration and E2E testing, TDD, refactoring legacy code for performance, and defending against the OWASP Top 10. Expect any of these to come up as a follow-up to a design answer.

Half of the reported question list is behavioral, and every item has a consulting angle: explaining technical ideas to non-technical stakeholders, unclear or shifting requirements, team conflict, why you want consulting rather than a product company, and juggling deadlines across clients. The last stage is a client-facing interview for a particular project. Prepare stories that show you clarifying scope, making trade-offs visible to whoever owns the decision, and adapting to a new stack or domain quickly. Candidates report being asked how they move between stacks and industries.

Since projects differ, decide which primary stack you will defend in depth (TypeScript/React with Node.js, or Java on the backend). Be ready to talk about every project on your CV down to individual decisions. Candidates report deep dives into specific technical choices they made, and questions on career goals and how those fit the available projects.

The ASTEK interview process

Candidates describe three stages. Format and pace vary by region, office and the project being staffed, and some processes reportedly finish within about two weeks.

Initial Screening

Candidates describe this as an initial recruiter screen that checks fit for the role, and it is where motivation questions tend to come first. Candidates also report that this is the right place to raise working arrangements such as on-site presence at a client or in the office. Because the process can move quickly when a project needs staffing, have your CV, availability and location constraints settled before the call. Use the screen to ask what form the technical stage will take (a coding assessment or an architecture case study) and, if possible, which project or stack you are being considered for. If you have interviewed with ASTEK before, candidates report that it helps to say so if asked.

What to demonstrate

  • Fit between your experience and the role requirements candidates describe (React frontend, backend development, years of experience)
  • Motivation for consulting work and for moving between client projects
  • Practical logistics: availability, location and willingness to work at a client site

How to prepare

  • Write a two-minute CV walkthrough that covers one React frontend project, one backend service (Node.js or Java) and anything you shipped on a cloud platform or with Docker/Kubernetes.
  • Rehearse a specific answer to why you want consulting over an in-house product role, with one example of switching stacks or domains quickly.
  • Write down your availability, notice period and any on-site constraints, plus three questions about the technical stage's format and the target project.

Technical Evaluations

Candidates report that this stage may be a coding assessment or an architectural case study, depending on the team. The topic areas candidates report include algorithmic problem solving, data structures and algorithms, programming fundamentals and coding interviews, along with discussion of their working experience and technical background. Prepare for both formats. For coding, practise writing correct, tested code in your primary language and talking through complexity as you go. For a case study, practise structuring an open design prompt: requirements and scale, components, data storage, security, deployment, and the trade-offs between them. The reported design questions (high traffic, CI/CD, auth, legacy migration) are good material for case-study practice.

What to demonstrate

  • Working, readable code in your primary language for algorithmic problems
  • Architecture reasoning on an open case: components, storage choice, scaling and security trade-offs
  • Depth on fundamentals beyond syntax: OOP, data stores, API design, testing

How to prepare

  • Solve the interval-overlap, rolling-failure-rate and windowed top-k practice problems in TypeScript or Java, stating time and space complexity for each.
  • Rehearse the high-traffic design and the legacy-to-microservices migration as whole case studies, each with a diagram and three named trade-offs.
  • For two CV projects, write the main technical decision, the alternative you rejected and why, ready for a deep dive.

Final Client-Facing Validation

Candidates report that the final stage is a client-facing interview to confirm the candidate suits a specific project. Treat it as a meeting with a future client: they want to know whether you can work in their stack and their delivery process, and whether they can trust you in front of their stakeholders. Before it, find out from the recruiter as much as you can about the project's domain, stack and team. Then map your experience to it explicitly, with gaps named and a plan for each. The reported behavioral questions on stakeholder communication, changing requirements and competing client expectations are the right preparation for this conversation.

What to demonstrate

  • Match between your experience and the specific project's stack and domain
  • Communication with client-side interviewers: clarifying questions, plain explanations, professional handling of pressure
  • How you would handle scope changes and competing priorities on a client engagement

How to prepare

  • Write a one-page mapping of the project's stack (as the recruiter describes it) to your experience, with gaps listed and how you would close each.
  • Rehearse aloud the stakeholder-explanation and changing-requirements stories in STAR form, each ending in a measurable result.
  • Prepare four questions for the client about delivery process, environments, team setup and what a first milestone looks like.

Where candidates lose points

Answering the SQL-versus-NoSQL or system design questions with a list of definitions and never choosing anything.

End every comparison with a decision tied to an access pattern, such as 'orders go in Postgres because they need multi-row transactions; sessions go in Redis because access is by key with a TTL'. Name what the choice makes harder and how you would deal with it.

A React state answer that puts everything in one global store and uses useEffect for derived values, with no cleanup or race handling.

Split state into local, server, URL and shared client state. Show an effect with an AbortController cleanup, and explain why server data belongs in a query cache with invalidation rather than copied into Redux.

A security answer that stops at 'use JWT and HTTPS' for the REST API or the auth question.

Cover token validation, token storage in the browser, revocation, and object-level authorization on every request. Tie it to the OWASP Top 10 categories (broken access control, injection, XSS), which candidates report as a topic.

Being unable to defend decisions on your own CV when the technical stage goes deep on past projects.

For each project on your CV, write down the main decision, the rejected alternative, a number showing the outcome, and what you would change. Candidates report deep dives into specific decisions, so anything you list should hold up for several follow-ups.

Walking into the final client-facing stage without knowing the project, then giving behavioral stories that blame a client or teammate.

Ask the recruiter for the project's stack and domain beforehand and map your experience to it. Rehearse the stakeholder, changing-requirements and prioritisation stories so each ends with an outcome and shows the trade-off being made visible to the decision owner.

Questions & practice

Find overlapping job attempts and peak concurrency from lease records

Medium · sweep line · intervals · leases

A day of job_run history yields about 50,000,000 attempt records: (job_run_id, job_type, attempt, started_at, finished_at which is NULL when the worker died, lease_expires_at). Leases expire on a clock, so a job that outran its lease ran twice. Produce (a) every job_run_id whose attempts overlapped in wall-clock time and (b) the peak number of simultaneously running attempts per job_type with the minute it occurred. Target O(n log n). State how you treat a NULL finished_at and what clock skew does to your answer.

Approach

  1. Define the interval before sorting anything: an attempt occupies [started_at, COALESCE(finished_at, lease_expires_at)). finished_at is observed and lease_expires_at is only a promise, so every attempt without a finish contributes an estimate and the whole result is a lower bound on overlap rather than an exact count.
  2. For peak concurrency, sweep: emit 2n endpoints, sort by (timestamp, kind) with ends ordered before starts at equal timestamps, then walk the sequence maintaining a counter per job_type and record each type's maximum with its timestamp. O(n log n) dominated by the sort, O(n) space, or O(1) extra if the sort is external and the walk streams.
  3. For overlap detection, do not compare attempts pairwise. A single global sort by (job_run_id, started_at) gives both the grouping and the order; within a group, keep the maximum end seen so far and report an overlap exactly when the next start is less than that running maximum, which is one linear pass after the sort.
  4. Half-open intervals matter and are easy to get wrong: with closed intervals an attempt ending at the same millisecond another begins reads as concurrency two, and across 50,000,000 records that artefact swamps the real signal.

Follow-up

  • A handler is not idempotent and you have found 400 overlapping jobs. Which of them actually caused damage, and what would you query to find out?
  • Peak concurrency for one job_type is 4 against a configured cap of 4. Is the cap working, or is the data hiding attempts that never started?

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

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

  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?

Can you explain the difference between SQL and NoSQL databases, and when would you choose one over the other?

Medium · Technical and Domain Expertise

Approach

  1. Open by saying that NoSQL is several data models: document (MongoDB), key-value (Redis, DynamoDB), wide-column (Cassandra) and graph (Neo4j). Compare each against the relational model of fixed schemas, joins, constraints and ACID transactions.
  2. Decide from access patterns. Use a relational store for ad hoc queries, reporting, multi-entity transactions and integrity rules (orders, payments, inventory). Use a document or key-value store for known key-based access at high volume, highly variable attributes, or session and cache data. In DynamoDB you design the table around the queries.
  3. Cover consistency and scaling. A single Postgres primary gives strong consistency and scales reads with replicas, while scaling writes needs partitioning. Many NoSQL systems partition natively and offer tunable or eventual consistency. Note that the lines blur: Postgres has JSONB, and MongoDB supports multi-document transactions.
  4. Point out that schema-on-read does not remove the schema. It moves it into application code, so plan document versioning and migrations either way.
  5. Close with a concrete choice from your own work. Mention the cost of polyglot persistence: extra operations and keeping stores consistent through events or change data capture.

Follow-up

  • In a document store, when do you embed a one-to-many relationship and when do you reference it?
  • How would you keep a search index or cache in sync with the primary relational database?
  • Writes grow tenfold on your Postgres design. What do you change first?

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 manage state and side effects in a complex React application?

Medium · Technical and Domain Expertise

Approach

  1. Classify state before picking tools. Local UI state goes in useState/useReducer. Server state belongs in a cache layer (TanStack Query or RTK Query) with deduplication, stale-while-revalidate and invalidation after mutations. URL state goes in router params, and only truly shared client state goes in Context or a store (Redux Toolkit, Zustand). Derive values during render instead of storing copies.
  2. Treat useEffect as synchronisation with an external system, not a place for derived data. Keep the dependency array honest and always return cleanup: unsubscribe, clear timers, and abort fetches with AbortController so a slow old response cannot overwrite a newer one.
  3. Handle mutations with optimistic updates that snapshot the previous cache and roll back on error, then invalidate the affected queries. Disable double submits and surface loading and error states explicitly.
  4. Performance: every consumer of a Context re-renders when its value changes. Split contexts or use a store with selectors, colocate state close to where it is used, and add memo/useMemo/useCallback only where the React Profiler shows a cost.
  5. Testing: keep reducers and selectors pure and unit-test them, then test components with React Testing Library against a mocked network (MSW). Note that StrictMode runs effects twice in development, which exposes missing cleanup.

Follow-up

  • A search box fires a request per keystroke. How do you stop stale results from showing?
  • When would you move shared state from Context into a dedicated store?
  • An optimistic update fails on the server. What does the user see, and how does the cache recover?

How do you approach the design of a REST API to ensure it is both scalable and secure?

Medium · Technical and Domain Expertise

Approach

  1. Model resources as nouns with standard verb semantics. GET is safe, PUT and DELETE are idempotent, POST creates. Use precise status codes (201, 204, 400, 401 versus 403, 404, 409, 422, 429) and a single consistent error body with a machine-readable code.
  2. For scalability, keep the service stateless behind a load balancer. Paginate with cursors rather than offsets on large or changing tables, support filtering and sparse fields, cache with ETag and Cache-Control, return 202 plus a status resource for long jobs, and rate-limit per client.
  3. Make retries safe. Use an Idempotency-Key on POSTs that create or charge, stored with the response, so a retry after a timeout returns the original result instead of creating a duplicate.
  4. For security, require TLS. Authenticate with OAuth 2.0/OIDC tokens and validate signature, expiry and audience. Check authorization per object on every request (broken object-level authorization is the classic API flaw), validate input, use parameterised queries, keep responses minimal, configure CORS narrowly and keep secrets out of code.
  5. Make it evolvable: version the API (/v1 or a header), make only additive changes within a version, publish an OpenAPI contract, and add request IDs and structured logs for tracing.

Follow-up

  • A client times out on a POST that creates a payment and retries. What happens?
  • Rows are inserted while a client is paging. How do offset and cursor pagination behave differently?
  • How do you revoke access for a user whose JWT is still valid?

Explain the core principles of Object-Oriented Programming (OOP) and how you apply them in your daily developm

Medium · Technical and Domain Expertise

Explain the core principles of Object-Oriented Programming (OOP) and how you apply them in your daily development.

Approach

  1. Define the four principles with what each buys you. Encapsulation keeps state behind methods that protect invariants. Abstraction lets callers depend on what a component does, not how. Inheritance reuses behaviour through is-a relationships. Polymorphism replaces type-switching conditionals with dispatch.
  2. Anchor it in your own Java or TypeScript code. For example, a PaymentProvider or NotificationSender interface with several implementations chosen by dependency injection, so tests use a fake and adding a provider touches no existing code.
  3. Mention SOLID only where you applied it: single responsibility in splitting a god class, open/closed through the strategy pattern, Liskov substitution in a subclass that broke callers, dependency inversion through constructor injection.
  4. Give the trade-off. Prefer composition over deep inheritance (fragile base class problem) and avoid interfaces with a single implementation and no seam to test. Say where you write functional code instead, such as React function components and pure utility functions.

Follow-up

  • When did inheritance cause you a problem, and how did you refactor it?
  • What is the difference between an abstract class and an interface in Java now that interfaces can have default methods?
  • Give an example of a Liskov substitution violation that turned into a real bug.

How would you design a system to handle high traffic and ensure low latency?

Medium · System Design and Problem Solving

Approach

  1. Clarify before drawing: peak requests per second, read/write ratio, latency target as a percentile (p99, not average), data size, consistency needs and where users are.
  2. Lay out the path: CDN for static and cacheable responses, load balancer, stateless app tier that scales horizontally (containers with autoscaling), a Redis cache using cache-aside with TTLs, and the database with indexes and read replicas. Shard by a key only when writes outgrow one primary, and say which queries the shard key makes expensive.
  3. Move slow work off the request path with a queue (Kafka, SQS or RabbitMQ). Use connection pooling and batching to cut round trips.
  4. Protect tail latency: timeouts on every call, retries with exponential backoff and jitter, circuit breakers, bulkheads, and cache stampede protection (request coalescing, jittered expiry).
  5. Validate with load tests and percentile dashboards. Name what saturates first and the next step when it does.

Follow-up

  • How do you invalidate cached data when the underlying record changes?
  • One key gets a large share of the traffic. How do you handle the hot key?
  • At ten times the traffic, which component fails first?

What considerations do you take into account when planning for CI/CD and deployment workflows?

Medium · System Design and Problem Solving

Approach

  1. Build once and promote the same artifact. Produce a Docker image tagged with the commit SHA, then run lint and static analysis, unit tests, integration tests, and dependency and SAST scans. Deploy to staging, run E2E tests, and promote that exact image to production.
  2. Choose a branching model on purpose: trunk-based development with short-lived branches, required reviews and feature flags to separate deploy from release, versus long-lived release branches when the client's release cadence demands them.
  3. Pick a deployment strategy with its rollback story: rolling, blue-green or canary, with automatic rollback when error rate or latency crosses a threshold. Keep database migrations backward compatible using expand-and-contract so the old version still runs during rollout.
  4. Treat infrastructure and configuration as code (for example Terraform), keep secrets in a secret manager rather than pipeline variables in plain text, and keep environments as close to production as possible.
  5. Keep the pipeline fast and trustworthy: dependency caching, parallel test shards, and quarantining flaky tests with an owner rather than retrying until green.

Follow-up

  • A release that included a schema change has to be rolled back. How?
  • A flaky test blocks every merge. What do you do this week, and what do you change for good?
  • Which metrics would gate your canary, and who gets paged when it fails?

How do you ensure the security of an application, specifically regarding authentication and authorization?

Medium · System Design and Problem Solving

Approach

  1. Separate the two: authentication proves who the caller is, and authorization decides what they may do. Then cover each layer.
  2. Authentication: do not build your own. Use OIDC/OAuth 2.0 through an identity provider. For a React SPA, use the authorization code flow with PKCE. Hash any stored passwords with bcrypt or Argon2, add MFA, rate-limit login and lock out brute-force attempts. Use short-lived access tokens with rotating refresh tokens, or HttpOnly, Secure, SameSite cookies rather than tokens in localStorage, which XSS can read.
  3. Authorization: deny by default and enforce on the server for every request at the object level, which prevents IDOR and broken access control (the top OWASP Top 10 category in 2021). Use RBAC for coarse roles and attribute-based rules for ownership or tenant checks. Centralise the policy so it is not scattered across controllers.
  4. Cover the rest of the OWASP basics around it: parameterised queries, output encoding against XSS, CSRF protection for cookie sessions, TLS everywhere, least-privilege service accounts, secrets in a vault, and audit logs of access decisions.

Follow-up

  • How do you cut off a user immediately when their JWT is still valid?
  • How do two backend services authenticate to each other?
  • Where would you store tokens in a React single-page app, and why?

If you were tasked with migrating a legacy system to a microservices architecture, where would you start?

Medium · System Design and Problem Solving

Approach

  1. Start with why. Name the problem the migration must solve (deploy speed, independent scaling, team autonomy) and consider whether a modular monolith solves it more cheaply.
  2. Understand before cutting. Map domains into bounded contexts, trace dependencies and data ownership, and add characterisation tests and observability to the legacy system so behaviour changes are detectable.
  3. Use the strangler-fig pattern. Put a gateway or facade in front, extract one low-risk, high-value bounded context first, shift traffic to it gradually (shadow traffic, then a percentage), and delete the old path once it is retired.
  4. Handle data deliberately: aim for a database per service, keep a shared database only as a temporary step, sync with events or change data capture using the outbox pattern, and replace distributed transactions with sagas.
  5. Budget for the operational cost: CI/CD per service, containers on Kubernetes, distributed tracing, and versioned contracts. Avoid a distributed monolith of chatty synchronous calls.

Follow-up

  • Both the old system and the new service write to the same table. How do you split ownership?
  • How do you know the first extraction succeeded?
  • At what point would you stop extracting services?

What strategies do you use for debugging and troubleshooting issues in a production environment?

Medium · Technical and Domain Expertise

Approach

  1. Mitigate before diagnosing. Size the impact (which users, error rate, latency), then stop the bleeding by rolling back, switching off a feature flag or scaling out. Keep stakeholders updated during the incident.
  2. Build the timeline: what changed and when (deploys, configuration, dependency versions, traffic, data). Correlate it with dashboards before forming a theory.
  3. Use the three signals. Metrics (request rate, errors, duration) show where; distributed tracing with correlation IDs finds the failing hop; structured logs give the detail. Compare failing and healthy hosts, tenants or request shapes to narrow it down.
  4. Check the usual resource culprits: connection or thread pool exhaustion, memory growth, slow queries after a data or plan change, retries amplifying load, expired certificates or credentials. Reproduce in staging with the same configuration and representative data, and bisect commits if a deploy is implicated.
  5. Close the loop with a fix and a regression test, an alert that would have caught it earlier, and a blameless postmortem. Never log secrets or personal data while debugging.

Follow-up

  • The bug affects about one request in a hundred and will not reproduce locally. What is your next step?
  • How do you tell a memory leak from normal cache growth?
  • What actions would your postmortem assign, and to whom?

Suggested practice checklist

The plan covers each reported stage in turn: CV and motivation for the screen, coding and fundamentals plus architecture cases for the technical stage, and client-facing stories for the final stage. Every reported question is practised on a specific day.

Day 1 — CV deep dive and motivation for the screen

  • For each project on your CV, write the main technical decision, the alternative you rejected, the outcome with a number, and what you would change.
  • Write and time a two-minute CV walkthrough covering one React frontend project, one Node.js or Java backend, and any cloud or container work.
  • Write your answer to why you want consulting over an in-house product role, using one concrete example of ramping up on a new stack or domain.

Deliverable: A one-page decision log for your CV projects, a timed two-minute walkthrough, and a written consulting-motivation answer.

Day 2 — Coding fundamentals and OOP

  • Solve the overlapping lease intervals problem with a sort-and-sweep approach and state its O(n log n) cost.
  • Solve the rolling failure-rate and windowed top-tenant problems in TypeScript or Java, writing tests for empty input, ties and window boundaries.
  • Write an interface with two implementations and a test fake from your own domain, then explain aloud the four OOP principles using that code.

Deliverable: Three solved and tested coding problems with complexity notes, and an OOP example class set with a spoken explanation.

Day 3 — React state and side effects

  • Build a small React search page that fetches as you type, using an AbortController (or TanStack Query) so stale responses never render.
  • Add an optimistic update with rollback on error, and a test using React Testing Library with a mocked network.
  • Answer the React state question aloud, grouping state into local, server, URL and shared client state.

Deliverable: A working React component with cancellation, optimistic update and one test, plus a recorded spoken answer on state management.

Day 4 — APIs and data stores

  • Design a REST API for one resource from a past project: endpoints, status codes, error body, cursor pagination, idempotency keys and versioning.
  • Write a one-page SQL versus NoSQL comparison ending in two concrete storage choices from your own work, with the trade-off of each.
  • Work the join fan-out practice problem, then explain how a one-to-many join inflates aggregates and how you prevent it.

Deliverable: An API contract sketch, a storage-choice page with two justified decisions, and a solved fan-out query.

Day 5 — Architecture case studies

  • Whiteboard the high-traffic, low-latency system: clarify requirements, then CDN, load balancer, stateless tier, cache, replicas, queue, and three named trade-offs.
  • Whiteboard the legacy-to-microservices migration using the strangler-fig pattern, including how data is split and which context to extract first.
  • Write a CI/CD pipeline from commit to production with build-once artifacts, test stages, a canary rule and an expand-and-contract migration plan.

Deliverable: Two architecture diagrams with trade-off notes and a written CI/CD pipeline with its rollback plan.

Day 6 — Security and production debugging

  • Write an auth design for a React SPA with a Node.js or Java API: OIDC with PKCE, token storage, revocation, and object-level authorization checks.
  • Map your design against OWASP Top 10 categories (broken access control, injection, XSS) and note one control for each.
  • Write a production incident from your experience as a timeline: detection, mitigation, diagnosis using metrics, traces and logs, fix, and follow-up actions.

Deliverable: An auth design note with an OWASP checklist and a written incident timeline ready to tell aloud.

Day 7 — Client-facing and behavioral rehearsal

  • Write STAR stories for the stakeholder explanation, changing requirements, team conflict and competing deadlines questions, each ending in a measurable result.
  • Tell each story aloud, cutting any line that blames a client or teammate and any ending without an outcome.
  • Write a mapping of the target project's stack to your experience, plus four questions for the client interview.

Deliverable: Four rehearsed STAR stories and a project-fit sheet with client questions for the final stage.

Behavioural preparation

Candidates report behavioral questions on communication, teamwork and fit with consulting work, and a final stage that is a client-facing interview. Your stories should show that you clarify ambiguous scope, make trade-offs visible to whoever owns the decision, resolve disagreements with evidence, and get productive quickly in a new stack or domain. Candidates also report questions on career goals and how they fit the available projects.

  1. Describe a disagreement inside your development team that you helped resolve, and what changed afterwards.
  2. Where do you want your career to go over the next few years, and which kinds of client projects would move you there?

Describe a time you had to explain a complex technical concept to a non-technical stakeholder.

Medium · Behavioral and Soft Skills

Approach

  1. Pick a story with a real decision riding on the explanation, such as why a migration needs extra sprints, a security fix that delays a feature, or a build-versus-buy choice. Name the stakeholder's role (project manager, business manager, client lead).
  2. Situation and task in two sentences, then spend the time on how you explained it. Start from their goal or risk, drop the jargon, use one analogy or diagram, and present two or three options with cost, risk and timeline.
  3. Show how you confirmed understanding: they restated it, chose an option, or carried it to their own management.
  4. State the result as an outcome: the decision made, scope or timeline adjusted, an incident avoided. Say what you would explain differently next time.
  5. What sinks it: a story with no decision at the end, talking down to the stakeholder, or an explanation simplified so far that it misled them.

Follow-up

  • How did you know they had actually understood?
  • What did you deliberately leave out, and why?
  • What did you do when they understood and still disagreed?

How do you handle situations where project requirements change suddenly or are poorly defined?

Medium · Behavioral and Soft Skills

Approach

  1. Show a repeatable process rather than 'I adapt'. List open questions, write down assumptions and acceptance criteria, and get them confirmed in writing by the product owner or client.
  2. When a change arrives, size its effect on scope, timeline and risk, then present options (drop something, move the date, add people) and let the decision owner choose. Record the decision in the backlog or ticket.
  3. Explain how you engineer for uncertainty: thin vertical slices delivered early, feature flags, a quick prototype to surface the real requirement, and keeping the most volatile part of the design behind an interface.
  4. Use STAR with a measurable result, such as a release kept on date with reduced scope or rework avoided because an assumption was caught early.
  5. What sinks it: blaming the client, or a story where you silently absorbed the change and slipped.

Follow-up

  • The change lands mid-sprint with a fixed deadline. What do you do that day?
  • How do you record an assumption so it does not turn into a dispute later?
  • When do you push back on a change instead of accepting it?

Tell us about a time you had to resolve a conflict within your development team.

Medium · Behavioral and Soft Skills

Approach

  1. Choose a substantive technical disagreement (an architecture choice, a library, a disputed code review, test strategy) where you played an active part in the resolution, not a personality clash you watched.
  2. Show the mechanics: you restated the other person's position until they agreed with your summary, agreed on decision criteria, and settled it with evidence such as a spike, benchmark or prototype. If needed, escalate with both options written up and the decision recorded (an architecture decision record works).
  3. Give the result for both the work and the relationship, including whether you were the one who conceded.
  4. What sinks it: casting yourself as the hero and the colleague as the villain, a conflict that never got resolved, or one too trivial to show judgement.

Follow-up

  • What would you have done if the benchmark had shown you were wrong?
  • How did you work with that person after the decision?
  • How would you handle the same disagreement with an engineer on the client's side?

Why are you interested in working as a consultant rather than in an internal product role?

Medium · Behavioral and Soft Skills

Approach

  1. Give reasons tied to your own history, not slogans: working across industries and stacks, ramping up quickly, and working close to the business people who set requirements.
  2. Back it with evidence of adaptability, which candidates report being asked about. Pick a time you joined an unfamiliar codebase or switched stacks (say Java to Node.js, or a new cloud provider), how you got productive, and how long it took.
  3. Acknowledge the trade-offs honestly: less long-term ownership of one product, possible on-site work, frequent context switches. Explain how you handle each.
  4. Connect it to your career goals and the kinds of projects you want, since candidates report questions about how their goals fit the available projects. Do not criticise product companies.

Follow-up

  • How do you get productive on an unfamiliar client codebase in your first weeks?
  • Which industries or kinds of projects do you most want to work on, and why?
  • What would make you want to move back to a product company?

How do you prioritize your tasks when managing multiple deadlines or conflicting client expectations?

Medium · Behavioral and Soft Skills

Approach

  1. Describe your method: list the work, separate hard deadlines from soft ones, weigh impact against urgency, find dependencies that block other people, and make it all visible on a board.
  2. For conflicting client expectations, do not quietly pick a winner. Bring the conflict to the project manager or the stakeholders with the options and what each costs, then get a recorded decision.
  3. Tell one STAR story with two real competing deadlines. Say what you deprioritised, who you told and when, and how it turned out.
  4. Show that you protected quality by negotiating scope, not by dropping tests or reviews without telling anyone.
  5. What sinks it: 'I just worked longer hours' as the whole answer, or a story where nobody was told about the trade-off until it was too late.

Follow-up

  • What exactly did you drop, and how did the affected person react?
  • Two managers both insist their request is the most urgent. What do you do?
  • How do you estimate when you are unsure how long something will take?