Interview guides · Centuria
Centuria Software Engineer Interview Guide
Centuria Software Engineer interview guide: the reported 3-round loop, coding, design and behavioral questions, and a 7-day prep plan.
The 60-second brief
Candidate reports describe Centuria as a company that builds software for clients in defense, healthcare and federal services. A Software Engineer there designs and implements features, maintains existing systems, reviews teammates' code, and works with product managers and other stakeholders on requirements. The topics listed for the role are software engineering, CI/CD, Power Platform, UC (Unified Communications) engineering and full-stack development. The listed languages are Java, C# and Python, with Git and Agile as the working tools and cloud (AWS, Azure), SQL and NoSQL databases and front-end frameworks (React, Angular) as extras.
This guide is for software engineers preparing for Centuria's Software Engineer loop, whose requirements candidates describe as several years of experience plus solid algorithms and data structures. It covers the three reported stages, a rehearsal path for each of the 25 reported questions (object-oriented versus functional programming, design patterns, a URL shortener, real-time data processing, linked lists, binary search, dynamic programming, graph cycles and the behavioral prompts), and a seven-day schedule.
Candidates report three stages over roughly 3-5 weeks: a Phone Screen, Technical Assessments with coding challenges or system design questions, and In-Depth Interviews with team members or leadership about past experience and problem solving. Details can differ by team.
Focus areas
- Software Engineering
- CI/CD (Continuous Integration / Continuous Delivery)
- Power Platform
Overview
The reported question list is broad rather than deep in any one area. It mixes classic coding problems (reverse a linked list, binary search, cycle detection in a graph, dynamic programming), system design prompts (a URL shortening service, real-time data processing, scaling a web application, database design, SQL versus NoSQL), engineering-judgment questions (object-oriented versus functional programming, design patterns, code quality, RESTful services) and a block of behavioral prompts. Because the questions are short and open-ended, the answer you give matters more than the question: a one-line definition will not carry any of them.
The stack that candidates describe is Java, C# or Python, Git, Agile, and a topic list that includes CI/CD, Power Platform, UC engineering and full-stack development. Pick the one language you can write fastest and most cleanly, and be ready to explain a real pipeline you have used: what triggers a build, which tests gate a merge, how a release is promoted, and how you roll back. If you have worked with Power Platform or unified-communications systems, prepare one concrete example of each; if you have not, say so plainly instead of guessing.
The role is described as work for clients in defense, healthcare and federal services, with code reviews, testing and fixing issues in existing systems among the responsibilities. Nothing reported says there are sector-specific questions, so spend your time on examples where correctness, testing and maintainability mattered: a bug you traced to its cause, a performance problem you measured before changing anything, a review where you pushed back or accepted pushback.
Plan your preparation across three kinds of answers. Coding answers need working code, tested edge cases and a stated complexity. Design answers need requirements, a data model, the read and write paths and the failure modes. Behavioral and experience answers need one specific story per prompt, told in the first person singular, with the decision you made and what changed afterwards.
The Centuria interview process
Candidates describe three stages. The order and content below are what they report, and the exact flow can vary by team or role.
Phone Screen
Candidates describe this stage as an initial call about your background and fit for the role. No technical format is reported for it, so treat it as the place where you present your experience coherently. The role lists Java, C# or Python, Git and Agile as core tools, so your summary should name the languages you have shipped in, the kind of systems you built and your part in them. Keep the summary to the experience that matches the listed topics: software engineering, CI/CD, full-stack work and, if you have it, Power Platform or unified-communications work.
What to demonstrate
- How your background lines up with the reported requirements: Java, C# or Python, Git, Agile and algorithms
- How clearly you describe your own role in past projects
- Whether your motivation for the role and the sectors involved is specific
How to prepare
- Write a short career summary that names your languages, one system you owned end to end and one measurable result you can defend.
- Prepare an honest answer to "What motivates you to perform at your best?" built on two concrete moments, not a slogan.
- List your experience against each listed topic (CI/CD, full-stack, cloud, SQL and NoSQL, React or Angular) and mark the gaps you will name openly.
- Write down questions about the format of the technical stage, since reports do not say whether the challenges are live or take-home.
Technical Assessments
Candidates report that this stage includes coding challenges or system design questions. The coding questions reported for the loop are reverse a linked list, implement binary search, state the time complexity of your solution, solve a problem with dynamic programming and detect a cycle in a graph; a merge of two sorted arrays and a caching strategy for a web application also appear as examples. The design questions reported are a URL shortening service, real-time data processing, scaling a web application, database design for a new application and SQL versus NoSQL. Practise coding in your chosen language with the complexity stated aloud, and practise design answers in a fixed order: requirements, data model, read and write paths, scaling and failure.
What to demonstrate
- Working code with correct handling of edge cases, as a live coding or challenge format requires
- Knowledge of standard data structures and algorithms, with complexity stated
- Ability to turn an open design prompt into components, a data model and trade-offs
How to prepare
- Write the linked-list reversal both iteratively and recursively, then binary search with a tested loop condition, and say the time and space cost of each.
- Solve one dynamic-programming problem top-down with memoisation and bottom-up, and write the recurrence before the code.
- Implement cycle detection for both a directed and an undirected graph, and be clear about which method belongs to which.
- Sketch a URL shortener and a real-time data-processing pipeline on paper: data model, key generation or ingestion path, storage choice, scaling bottleneck and what happens when a dependency fails.
In-Depth Interviews
Candidates describe this stage as interviews with team members or leadership about your experiences and your approach to solving problems. This is where the engineering-judgment and behavioral questions are practised: object-oriented versus functional programming, design patterns you have used, how you ensure code quality, a challenging bug, a performance investigation, handling disagreement, mentoring and ambiguity. Answer with real projects, in the first person singular, and be ready to go a level deeper than your first answer on any decision you describe.
What to demonstrate
- How you reason about past technical decisions and their trade-offs
- Debugging and performance-investigation method, as a discussion of problem solving requires
- Collaboration and communication, which candidates report as part of behavioral fit
How to prepare
- Prepare a debugging story in a fixed sequence: symptom, what changed, hypothesis, evidence, fix, regression test.
- Prepare a code-quality answer from your own practice: review habits, test levels, static analysis and the CI checks that gate a merge.
- Have two design patterns you actually used, with the problem each solved and one place where the pattern made the code worse.
- Rehearse the disagreement, mentoring, prioritisation and ambiguity stories until each has a clear decision and a visible outcome.
Where candidates lose points
Writing the linked-list reversal or binary search without testing the boundaries
After writing the code, dry run an empty list, a single node, two elements, a target at either end and a missing target. For binary search, decide on the loop condition and the mid calculation up front (lo + (hi - lo) / 2 avoids integer overflow in Java and C#), and state the O(n) or O(log n) cost out loud.
Using the same cycle-detection method for directed and undirected graphs
Ask which kind of graph it is. For a directed graph use depth-first search with a visiting state or a topological sort; for an undirected graph track the parent during traversal or use union-find. State the O(V + E) cost and what you do for a disconnected graph.
Jumping to a technology stack in the URL shortener or real-time processing design before fixing requirements
Begin with read and write volume, latency, retention and what happens on failure. Then give the data model, key generation and collision handling for the shortener, or ingestion, processing and storage with delivery guarantees for the pipeline, and only then name tools.
Telling behavioral stories in the plural, with no clear decision of your own
For each prompt (pressure, disagreement, mentoring, ambiguity, prioritisation), write one story in the first person singular with the situation in two sentences, your decision, the trade-off and the outcome.
Answering the debugging, performance and code-quality questions with a list of tools
Tell one real sequence: symptom, what changed, hypothesis, the measurement that confirmed it, the fix and the test or alert that now guards it. For code quality, describe the checks that gate a merge in a CI/CD pipeline you have actually used.
Questions & practice
How would you approach optimizing an existing algorithm?
Medium · Problem-Solving / Case Studies
Approach
- Lock in correctness and a baseline first: tests that pin current behaviour, and a profile or benchmark on realistic input showing where the time actually goes.
- Analyse the hot path's complexity and look for repeated work: a nested-loop lookup replaced by a hash map (O(n^2) to O(n) average), sorting plus two pointers, prefix sums or other precomputation, or memoising repeated subcalls.
- Example: finding a pair that sums to a target drops from comparing every pair to one pass that checks a hash set for target minus the current value.
- Then look at constant factors: avoid allocation inside loops, batch I/O and database calls, use built-in library routines, and improve data locality.
- State the trade-offs: extra memory for speed, readability, and whether n is large enough to matter. Rerun the same tests and benchmark to prove the gain.
Follow-up
- What if you cannot use extra memory?
- How do you show the optimised version gives the same results on edge cases?
- How do you decide the optimisation is good enough and stop?
Write a function to reverse a linked list.
Medium · Coding / Algorithms
Approach
- Iterative three-pointer reversal: prev = null, curr = head; while curr is not null: next = curr.next, curr.next = prev, prev = curr, curr = next; return prev. O(n) time, O(1) extra space.
- Recursive version: reverse the rest of the list, then set head.next.next = head and head.next = null. It is still O(n) time but uses O(n) call stack, which can hit Python's default recursion limit of 1000 on long lists.
- Edge cases: an empty list, one node, two nodes. The old head's next must become null, or the result contains a cycle.
- Test by building a list from an array, reversing it and comparing the walk with the reversed array.
Follow-up
- Reverse only the sublist between positions m and n.
- Reverse the list in groups of k nodes.
- How does the code change for a doubly linked list?
How would you implement a binary search algorithm?
Medium · Coding / Algorithms
Approach
- State the precondition: sorted input, or more generally a monotonic predicate. Loop with lo = 0, hi = n - 1 while lo <= hi, compute mid = lo + (hi - lo) / 2, compare, and move lo = mid + 1 or hi = mid - 1; return -1 if not found. O(log n) time, O(1) space.
- Explain the overflow trap: (lo + hi) / 2 can overflow a 32-bit int in Java or C#, so use lo + (hi - lo) / 2 (or (lo + hi) >>> 1 in Java). Python integers do not overflow.
- Write the boundary variants: lower bound on a half-open range (while lo < hi, hi = mid when nums[mid] >= target) gives the first occurrence or the insertion point. Python's bisect_left and bisect_right do this.
- Know library behaviour: Java's Arrays.binarySearch returns -(insertion point) - 1 when the key is missing and does not say which duplicate it finds; C#'s Array.BinarySearch returns the bitwise complement of the next larger element's index.
- Edge cases: an empty array, one element, duplicates, a target outside the range, and the infinite loop caused by setting lo = mid without + 1.
Follow-up
- Search a rotated sorted array.
- Binary search on the answer: the minimum ship capacity to deliver all packages within D days.
- Find the first index where a monotonic predicate becomes true.
Explain the time complexity of your solution.
Medium · Coding / Algorithms
Approach
- Name the input variables explicitly (n, and m if there are two inputs), count the dominant operations as a function of them, and drop constants and lower-order terms.
- Apply the combination rules: sequential blocks add, nested loops multiply, halving the search space gives log n, and recursion goes through a recurrence (T(n) = 2T(n/2) + O(n) solves to O(n log n) by the master theorem).
- Separate worst, average and amortised cases: dynamic-array append is amortised O(1); hash-map lookup is O(1) on average but O(n) in the worst case with collisions. Report space too, including recursion stack depth.
- Call out hidden costs: repeated String concatenation in a Java loop is O(n^2) (use StringBuilder), Python list.pop(0) and
inon a list are O(n), and slicing copies. - Tie it to the constraints: with n = 10^5, O(n^2) is about 10^10 operations, too slow, while O(n log n) is fine.
Follow-up
- Why is appending to a dynamic array amortised O(1) when some appends copy the whole array?
- What is the complexity of naive recursive Fibonacci, and of the memoised version?
- What is the space complexity of a recursive DFS on a path-shaped graph?
Solve a problem using dynamic programming.
Medium · Coding / Algorithms
Approach
- Recognise the signals: optimal substructure plus overlapping subproblems. Typical problems include coin change, longest common subsequence, 0/1 knapsack and edit distance.
- Define the state, recurrence, base case and evaluation order out loud. For minimum coins: dp[0] = 0, dp[a] = min(dp[a - c] + 1) over coins c <= a, unreachable = infinity, answer -1 if still infinite; O(amount x coins) time, O(amount) space.
- Compare top-down memoisation (easy to write from the recursion, but limited by stack depth) with bottom-up tabulation (allows space reduction: two rows for LCS, or one array for 0/1 knapsack iterated over capacity from high to low).
- Explain why greedy fails here: with coins {1, 3, 4} and amount 6, greedy picks 4 + 1 + 1 (3 coins) while 3 + 3 uses 2.
- Reconstruct the answer by storing the choice at each state, and test amount 0, an impossible amount and a single coin.
Follow-up
- In 1D 0/1 knapsack, why must capacity be iterated downward?
- How does loop order change counting combinations versus permutations in coin change?
- Return the actual coins or items, not just the count.
Write code to detect a cycle in a graph.
Medium · Coding / Algorithms
Approach
- Ask first whether the graph is directed or undirected: the algorithms differ. Build an adjacency list.
- Directed: DFS with three colours (unvisited, on the current path, finished); an edge to a node still on the current path is a back edge and proves a cycle. O(V + E) time, O(V) space. Alternative: Kahn's topological sort, where a cycle exists if fewer than V nodes are ever dequeued.
- Undirected: DFS that skips the edge back to the parent, so any other visited neighbour means a cycle (track edge IDs if parallel edges exist). Or union-find: an edge whose endpoints already share a root closes a cycle, in near-linear O(E α(V)) time with path compression and union by rank.
- Edge cases: disconnected graphs (start from every unvisited node), self-loops, and deep graphs that overflow the recursion stack, which an explicit stack avoids.
- To return the cycle itself, keep parent pointers and walk back from the back edge's target.
Follow-up
- Given course prerequisites, return a valid order or report that none exists.
- Return the nodes of one cycle, not just true or false.
- How does detecting a cycle in a linked list differ (Floyd's tortoise and hare)?
What factors do you consider when choosing between SQL and NoSQL databases?
Medium · System Design / Architecture
Approach
- Start from data shape and queries: relational data with joins and ad hoc queries fits SQL; aggregate-oriented data read and written by key (a document, a session, a time series) fits a document, key-value or wide-column store.
- Compare consistency and transactions: relational databases give ACID multi-row transactions; many NoSQL stores default to single-item atomicity or tunable/eventual consistency, though some add transactions (MongoDB supports multi-document transactions from 4.0).
- Compare the scaling model: Cassandra and DynamoDB partition data horizontally by design; a relational database usually scales up and adds read replicas first, and sharding it is application work.
- Name the schema trade-off: schema-on-write catches bad data at insert time; a flexible schema moves validation into the application. Postgres JSONB is a middle ground.
- Finish with operations: team familiarity, managed offerings on AWS or Azure, cost, and whether more than one store is justified for different workloads.
Follow-up
- How would you keep two related entities consistent in a store without multi-item transactions?
- When would you choose Postgres with JSONB over a document database?
- How would you design DynamoDB partition and sort keys for a given access pattern?
Find version gaps and relay lag with window functions
Medium · window functions · event tables · frame semantics
outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.
Approach
- Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
- Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
- Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
- Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.
Follow-up
- Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
- Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?
Explain the differences between object-oriented and functional programming.
Medium · Technical / Domain Questions
Approach
- Define each paradigm by how it treats state: OOP bundles mutable state with the methods that change it (encapsulation, inheritance, polymorphism through subtype dispatch); FP builds programs from pure functions over immutable values, with higher-order functions and composition.
- Explain why purity matters: a pure function is referentially transparent, so it can be tested with plain input/output checks and run in parallel without locks. OOP instead hides mutable state behind an interface.
- Name the extensibility trade-off (the expression problem): in OOP, adding a new type is one new class but adding a new operation touches every class; with algebraic data types and pattern matching it is the reverse.
- Ground it in the languages the role lists: Java streams and records, C# LINQ and immutable records, Python comprehensions, functools and frozen dataclasses. Real codebases mix both styles.
- State the costs: immutability can mean extra allocation (persistent structures reduce it by sharing structure), and deep inheritance trees are brittle, so I prefer composition over inheritance.
Follow-up
- How would you make a Java or C# class truly immutable, including fields that hold collections?
- Why does a pure function make concurrent code easier to reason about?
- Where in a real service would you choose a functional pipeline over an object model?
What are some design patterns you have used in your projects?
Medium · Technical / Domain Questions
Approach
- Pick two or three patterns I actually shipped. For each, give the problem it solved, the pattern and what it cost: for example Strategy to swap validation or pricing rules without a growing if/else chain, and Adapter to wrap a third-party client behind my own interface.
- Show the code shape: an interface, two implementations, and how the caller receives one through constructor injection (Spring in Java, the built-in container in ASP.NET Core), which lets me unit test with a fake.
- Cover structural patterns too: Decorator to add retries, caching or logging around an existing interface without subclassing; Factory to build the right implementation from configuration; Observer or pub/sub for events.
- Name a misuse: Singleton as hidden global state makes tests order-dependent, so I let the DI container own a single instance instead.
- Close on judgement: a pattern earns its indirection only when the variation is real, not speculative.
Follow-up
- How do you make a lazy Singleton thread-safe in Java (holder idiom, enum, or double-checked locking with volatile)?
- What is the difference between the Strategy and State patterns?
- When would Decorator be a better choice than inheritance?
Describe a challenging bug you encountered and how you resolved it.
Medium · Technical / Domain Questions
Approach
- Structure the story as symptom, impact, reproduction, hypotheses, evidence, root cause, fix and prevention, with numbers where you have them (error rate, users affected, how long it ran).
- Choose a bug with a non-obvious cause: a race condition, a timezone or daylight-saving boundary, an off-by-one at a pagination edge, a stale cache after invalidation, connection-pool exhaustion, or floating-point money.
- Show the tools and the order you used them: a minimal reproduction, logs with correlation IDs, a debugger or thread dump, git bisect to find the change that introduced it.
- Separate the mitigation (rollback, feature flag) from the root-cause fix, and name the regression test that now pins the behaviour.
- Avoid a story where the bug was fixed by luck or by someone else; say what you personally did, using "I".
Follow-up
- How did you confirm the fix addressed the root cause and not just the symptom?
- What check or test would have caught it before production?
- What would you have done if you could not reproduce it locally?
How do you ensure code quality in your projects?
Medium · Technical / Domain Questions
Approach
- Describe layered automated tests: many fast unit tests, fewer integration tests against real dependencies (a test database or containers), and a small set of end-to-end tests, all run in CI on every pull request so a red build blocks the merge.
- Add static checks in the same pipeline: formatters and linters (Checkstyle or SpotBugs for Java, Roslyn analyzers for C#, ruff and mypy for Python) so style debates leave code review.
- Make review effective: small pull requests, branch protection in Git requiring an approval and green CI, and review focused on correctness, naming and test coverage of edge cases.
- Treat coverage as a signal, not a target: a high percentage with no assertions proves nothing, so I look at whether the risky branches are tested.
- Close the loop from production: error tracking and monitoring, and every incident fix ships with a test that reproduces it.
Follow-up
- How would you add tests to a legacy module that has none and is hard to isolate?
- What do you do about a flaky test that keeps failing CI intermittently?
- Tell me about a review comment you pushed back on, and how it was resolved.
What is the significance of RESTful services in modern web applications?
Medium · Technical / Domain Questions
Approach
- Name the REST constraints: client-server, stateless requests, cacheable responses, a uniform interface (resources identified by URIs and manipulated through representations such as JSON), and a layered system.
- Get HTTP semantics right: GET is safe and idempotent, PUT and DELETE are idempotent, POST is not, and PATCH is not guaranteed to be. Use status codes precisely: 201 with a Location header, 204, 400, 401 versus 403, 404, 409, 429 and 5xx.
- Explain why it matters: statelessness lets any instance behind a load balancer serve any request, HTTP caching (Cache-Control, ETag with If-None-Match returning 304) cuts load, and a React or Angular front end can evolve separately from the back end.
- Cover practical design: versioning (path or header), cursor pagination over offset for large tables, idempotency keys so a retried POST does not create duplicates, and one consistent error body.
- Compare alternatives: GraphQL addresses over- and under-fetching for varied clients; gRPC suits low-latency internal calls and streaming; REST can get chatty for deeply nested data.
Follow-up
- What is the difference between PUT and PATCH, and is each idempotent?
- How would you make a payment-creating POST safe to retry?
- How do you version an API without breaking existing clients?
How would you design a URL shortening service?
Medium · System Design / Architecture
Approach
- Fix the requirements: shorten a URL, redirect, optional custom alias and expiry, click counts. The workload is read-heavy. Do the arithmetic: 7 base62 characters give 62^7, about 3.5 trillion codes.
- Generate keys without collisions: base62-encode a unique integer ID, with each app server taking blocks of IDs from a counter so the counter is not hit on every write. Sequential codes are guessable, so scramble them with a reversible mapping if that matters. Hash prefixes or random codes need a uniqueness check and a retry.
- Store code → long URL, expiry and owner in a key-value store or a SQL table with the code as primary key. Put a cache (for example Redis, LRU) in front for hot links.
- Choose the redirect status deliberately: 301 is cached by browsers, which cuts load but hides repeat clicks; 302 or 307 sends every click through the service. Record clicks asynchronously on a queue, never synchronously on the redirect path.
- Edge cases: the same long URL submitted twice, malicious or looping targets, expired codes (return 404 or 410), alias collisions and rate-limiting creation.
Follow-up
- How would you allocate IDs across many servers without a single bottleneck?
- One link goes viral: what happens to the cache and the database?
- How would you delete expired links at scale?
Describe how you would architect a system for real-time data processing.
Medium · System Design / Architecture
Approach
- Clarify event volume, the latency target, whether per-key ordering matters, and whether duplicates are acceptable. These answers decide the design.
- Lay out the pipeline: producers write to a durable partitioned log (Kafka, or managed equivalents such as Kinesis or Event Hubs), keyed so that each entity's events stay ordered within one partition; a stream processor (Flink, Kafka Streams, Spark Structured Streaming) transforms and aggregates; sinks feed a serving store, dashboards or alerts.
- State delivery semantics: at-least-once delivery plus idempotent writes (upsert on event ID) gives effectively-once results; transactional exactly-once exists in some stacks but costs throughput.
- Handle time correctly: window on event time (tumbling, sliding or session windows) with watermarks, and decide what to do with events that arrive after the window closes.
- Plan for failure and scale: checkpoint operator state, send poison messages to a dead-letter queue, and watch consumer lag. Parallelism is capped by the partition count (one consumer per partition per group), and skewed keys create hot partitions, which salting can spread.
Follow-up
- How do you handle events that arrive minutes late?
- A consumer crashes halfway through a batch: what is reprocessed, and how do you avoid double counting?
- One key produces most of the traffic: how do you rebalance?
What considerations do you make for scaling a web application?
Medium · System Design / Architecture
Approach
- Measure before changing anything: p50/p95/p99 latency, throughput, error rate and saturation per tier, so the bottleneck (CPU, database, network, a downstream call) is identified, not guessed.
- Make the app tier stateless (sessions in a shared store or in signed tokens) so instances can scale horizontally behind a load balancer with autoscaling.
- Cache in layers: a CDN for static assets, HTTP caching headers, and an application cache using cache-aside with TTLs and explicit invalidation. Name the stampede risk and its fixes (request coalescing, jittered TTLs).
- Scale the database in order: indexes and query fixes, connection pooling, read replicas (with replication lag and read-your-writes in mind), then partitioning or sharding. Move slow work to queues and background workers.
- Add resilience: timeouts on every remote call, retries with exponential backoff and jitter, circuit breakers, rate limits, and graceful degradation when a dependency fails.
Follow-up
- A user updates a profile and then sees old data because reads go to a replica: how do you fix that?
- When would you shard rather than buy a bigger database server?
- How do you stop a cache stampede when a popular key expires?
Explain how you would approach database design for a new application.
Medium · System Design / Architecture
Approach
- Start from use cases and access patterns: list the entities, the queries each screen or API needs, the read/write ratio and the expected growth.
- Build the model: an entity-relationship diagram, then tables normalised to roughly third normal form to avoid update anomalies, with primary keys (surrogate versus natural), foreign keys, unique, NOT NULL and check constraints so the database enforces invariants.
- Index for the known queries: put equality columns before range columns in composite indexes, and remember each index slows writes and uses space.
- Choose types carefully: DECIMAL for money, timestamps stored in UTC with time zone, and a junction table for many-to-many relationships. Denormalise only for a measured hot read path, and say how the copies stay consistent.
- Plan for change: versioned migrations (Flyway, Liquibase, EF Core migrations, Alembic) and backward-compatible expand-then-contract changes, plus a policy on soft deletes and audit history.
Follow-up
- How would you add a NOT NULL column to a large table that is serving live traffic?
- When is denormalising worth it, and how do you keep the copies in sync?
- How would you model users, roles and permissions?
What steps would you take to investigate a performance issue in an application?
Medium · Problem-Solving / Case Studies
Approach
- Define the problem with numbers: which endpoint or job, p50 versus p95/p99, when it started, whether it affects all users or a subset; then line it up against deploys, traffic and data growth.
- Find where the time goes, layer by layer: client and network, application CPU, database, downstream calls, using APM or distributed traces rather than guesswork.
- For the database: the slow query log, EXPLAIN or EXPLAIN ANALYZE for sequential scans and bad row estimates, missing indexes, ORM N+1 queries, lock contention and connection-pool exhaustion.
- For the application: a CPU profiler or flame graph, GC pause logs (JVM or .NET), thread dumps for contention, heap analysis for leaks.
- Change one variable at a time, verify under a load test and in production metrics, then add an alert or a performance regression check.
Follow-up
- p50 is fine but p99 is ten times worse: what would you suspect?
- It is slow only in production, never locally: what differs and how do you check?
- How would you spot an N+1 query pattern?
Provide an example of how you approached a particularly challenging technical problem.
Medium · Problem-Solving / Case Studies
Approach
- Pick a problem with a real technical constraint (scale, latency, correctness, a legacy limit), and set the context in two sentences with numbers.
- Lay out at least two options with their trade-offs and explain the deciding factor; that reasoning is the core of the answer.
- Go one level deep on the implementation: the data structure, algorithm, query change or architecture shift, and the part I personally built.
- Give the measured result (latency, error rate, cost, time saved) and what I would do differently.
- The answer fails when it is a team narrative with no "I", no alternatives and no numbers.
Follow-up
- Which alternative did you reject, and would you still reject it today?
- What would break first if the load grew tenfold?
- How did you validate the solution before it reached users?
Describe your thought process when debugging a system.
Medium · Problem-Solving / Case Studies
Approach
- Reproduce and characterise first: the exact symptom, its scope, how often it happens and in which environment, ideally reduced to a minimal reproduction.
- Ask what changed: deploys, configuration, dependency versions, data volume or shape, traffic. Pull logs, metrics and traces tied together by a correlation ID.
- Rank hypotheses by likelihood and cost to test, then bisect: git bisect across commits, toggling feature flags, or bypassing components until the fault isolates.
- Check the instrumentation before trusting it (a broken metric can look like an outage), and separate the trigger from the underlying cause.
- Fix the root cause, add a test that reproduces it, write a short postmortem, and add the monitoring that would have caught it sooner.
Follow-up
- The bug disappears when you attach a debugger: what does that suggest and what do you do?
- How do you decide between rolling back and fixing forward?
- How do you debug an issue that occurs only in production, where you have limited access?
Suggested practice checklist
Seven days built from the three reported stages: a day to map the loop and your experience, two coding days, two design days, a day of engineering-judgment answers and a final behavioral day. Each day ends with something you can check.
Day 1 — Map the loop and inventory your experience
- Write the three reported stages (Phone Screen, Technical Assessments, In-Depth Interviews) and, for each, the question types you would prepare for it.
- List your experience against Java, C# or Python, Git, Agile, CI/CD, full-stack work, cloud, SQL and NoSQL, and note anything involving Power Platform or unified communications.
- Pick five projects you can discuss in depth and write two lines on your own contribution to each.
Deliverable: A one-page loop map, an experience-versus-requirements table and five project summaries.
Day 2 — Linked lists, binary search and complexity statements
- Write the iterative and recursive reversal of a linked list, and trace both on an empty list, one node and three nodes.
- Implement binary search with a clearly chosen loop condition, then a variant that returns the first occurrence of a duplicate value.
- For each solution, say the time and space complexity aloud, then write a merge of two sorted arrays and do the same.
Deliverable: Three working solutions in your chosen language, each with an edge-case list and a one-line complexity statement.
Day 3 — Graph cycles, dynamic programming and optimisation
- Write cycle detection for a directed graph (depth-first search with visiting states) and for an undirected graph (parent tracking or union-find).
- Solve one dynamic-programming problem by writing the state and recurrence first, then memoised and bottom-up code, and note the space optimisation.
- Take a brute-force solution you wrote earlier and rewrite it with a better data structure, explaining where the cost drops.
Deliverable: Two cycle-detection functions, one dynamic-programming solution in two forms and one before-and-after optimisation with complexities.
Day 4 — System design: URL shortener, data model and database choice
- Design a URL shortening service on paper: API, key generation and collision handling, storage, read-heavy caching and expiry.
- Write the database design for a new application of your choice, with tables or collections, keys and the main queries, and argue SQL versus NoSQL for it.
Deliverable: A one-page URL shortener design, a schema with its main queries and a solved SQL practice exercise.
Day 5 — System design: real-time processing, scaling, REST and CI/CD
- Design a real-time data-processing system: ingestion, processing, storage, delivery guarantees, backpressure and what you do on failure.
- List the steps to scale a web application, with what you measure first and the caching layer you would add, and design a REST API with resources, status codes and versioning.
- Draw a CI/CD pipeline you have used or would build: build, tests that gate a merge, deployment, promotion and rollback.
Deliverable: A real-time pipeline diagram, a scaling checklist with REST API outline and a labelled CI/CD pipeline.
Day 6 — Engineering judgment out loud
- Explain object-oriented versus functional programming with one example where each is the better fit, and give two design patterns you have used, including a drawback of one.
- Write your code-quality answer from real practice: review habits, test levels, static checks and what gates a merge.
- Prepare a debugging story and a performance-investigation story, each with symptom, hypothesis, evidence, fix and prevention, and one challenging technical problem you can describe in depth.
Deliverable: Spoken answers to seven engineering-judgment questions, recorded once and cut to the strongest version.
Day 7 — Behavioral stories and a full run-through
- Write one first-person story each for working under pressure, a disagreement, mentoring a junior developer, prioritising several projects and ambiguous requirements.
- Prepare a two-moment answer to what motivates you, and practise the ethical-dilemma and team-motivation prompts as well.
- Run one pass through the whole loop: a short background summary, one coding problem, one design problem and two stories.
Deliverable: Five written stories, one motivation answer and a completed full-loop rehearsal with a list of weak spots.
Behavioural preparation
Candidates report behavioral questions on pressure, disagreement, motivation, mentoring, prioritisation and ambiguity, and describe teamwork, communication and adaptability as the fit topics. Reported examples also include a time you faced an ethical dilemma and how you keep a team motivated under pressure. Prepare each story as your own decision with its trade-off and result, with a software example where one exists: a release, a review, a bug, a design choice.
Describe a time when you had to work under pressure.
Medium · Behavioral / Leadership
Approach
- Choose an episode with a real constraint such as a fixed release date, a production failure or a sudden scope increase, where you were the person making decisions.
- Describe the situation in two sentences, then spend most of the answer on what you decided: what you triaged first, what you cut or postponed, and how you told stakeholders.
- Show the trade-off explicitly: what you protected (correctness, a rollback path, tests on the risky change) and what you accepted giving up.
- Close with the result and one change you made afterwards, such as an alert, a checklist or an agreement on scope, so the answer shows learning and not only endurance.
Follow-up
- What would you have cut earlier if you ran that again?
- How did you keep the rest of the team working effectively while you handled it?
- How do you keep a team motivated when the pressure continues for weeks?
How do you handle disagreements with team members?
Medium · Behavioral / Leadership
Approach
- Pick a technical disagreement, for example about a design, a library or a review comment, and state both positions fairly before saying which you held.
- Show how you moved from opinion to evidence: a small benchmark, a prototype, a list of failure cases or a look at similar past incidents.
- Say how it ended: you changed your mind, they did, or you agreed on a smaller step. Include a case where you committed to a decision you disagreed with.
- Name what the relationship looked like afterwards, because the story fails if it reads as winning an argument and ignores the working relationship.
Follow-up
- What if the other person had more seniority than you?
- How do you handle a disagreement in a pull-request thread versus a meeting?
- Tell me about a time you were wrong in a disagreement.
What motivates you to perform at your best?
Medium · Behavioral / Leadership
Approach
- Answer with two or three concrete moments where you worked at your best, such as untangling a hard bug, shipping something users adopted or helping a teammate get unblocked, then name the common thread.
- Keep the answer connected to engineering work: the problem, the people and the feedback loop that energise you, not abstract traits.
- Link it to the role only with facts you can back. Reports mention clients in defense, healthcare and federal services, so you can say what draws you to building dependable software for such users, but do not claim knowledge of the company that you do not have.
- Avoid answers that only mention pay, titles or generic love of challenge; end by saying what you do when motivation dips, so the answer sounds honest.
Follow-up
- Describe a project that drained you and how you handled it.
- What kind of work would make you lose motivation in this role?
- How do you keep your own motivation up when you work on maintenance or bug fixes?
Share an example of how you mentored a junior developer.
Medium · Behavioral / Leadership
Approach
- Describe the junior developer's starting point in one or two sentences, then the specific gap you identified, such as debugging method, reading unfamiliar code or writing tests.
- Name your methods: pair sessions, pull-request comments that explain the reason, a small stretch task with a clear definition of done, then a gradual reduction in your involvement.
- Include the point where it was tempting to do the work yourself and why you did not, because mentoring that replaces their work is a common failure.
- Give a result you can observe, such as the junior owning a feature, giving review feedback or fixing a production issue alone, and one thing you learned about explaining.
Follow-up
- What did you do when they did not improve as quickly as you hoped?
- How do you give code-review feedback to a less experienced developer?
- How did you balance mentoring with your own deliverables?
How do you prioritize tasks when managing multiple projects?
Medium · Behavioral / Leadership
Approach
- State your method in a few steps: list every commitment, estimate the cost of delay and the impact of each, find dependencies, then order the work and confirm the order with the people affected.
- Use a real example of competing projects: say what you did first, what you postponed and who you told, and how you made the trade-off visible, for example in a tracker or a short message.
- Show that you negotiate, not just sort: propose a smaller scope, a later date or a handoff when the load does not fit.
- Cover re-prioritisation: how you handle an urgent request that arrives mid-week, and how you protect time for reviews, testing and technical work that nobody asks for loudly.
Follow-up
- Two managers each say their task is the top priority. What do you do?
- How do you decide when to say no to a request?
- What did you drop in that example, and was it the right call?
How do you handle ambiguous requirements in a project?
Medium · Problem-Solving / Case Studies
Approach
- Choose a project where the requirements were unclear and say what was missing: the goal, the users, the constraints or the acceptance criteria.
- Show your sequence: ask targeted questions of the product manager or stakeholders, write down your assumptions, and separate what is easy to reverse from what is expensive to change.
- Describe how you reduced the risk, for example with a thin end-to-end slice, a prototype or a short list of options with your recommendation, and how you got confirmation before building the rest.
- Finish with how the requirement changed and what you changed in the code or plan as a result; avoid stories where you waited for a perfect specification or built silently on a guess.
Follow-up
- What do you do when stakeholders disagree about what is needed?
- How do you decide between asking another question and making an assumption?
- How do you protect the code when requirements change late?