Interview guides · Amulya
Amulya Software Engineer Interview Guide
Prepare for the Amulya Software Engineer interview: reported rounds, Java, DSA, SQL, Terraform and cloud networking questions with worked approaches.
The 60-second brief
Candidate reports for Amulya describe the engineering role rather than the business, so this guide makes no claim about what Amulya sells or who buys it. The Software Engineer role, as candidates describe it, follows software across its whole life: designing it, writing the core logic, deploying it and keeping it running. The work spans backend code in an object-oriented language (Java is the example candidates give), SQL and relational database design, and the delivery side: Docker containers, Jenkins pipelines, infrastructure managed with Terraform, and cloud networking such as subnets, NAT gateways and internet gateways. Candidates also describe the role working with product managers and operations teams, and needing to explain technical choices to people outside engineering.
This guide is for engineers preparing for a Software Engineer interview at Amulya, particularly those whose background is stronger on either the coding side or the infrastructure side and who need to cover the other. It covers the three reported stages, all fifteen reported questions grouped as candidates report them (data structures and algorithms, CS fundamentals and databases, DevOps and cloud infrastructure), the Core Java depth candidates mention (multithreading, memory management, garbage collection), and a project walkthrough for the technical interviews.
Candidates report three rounds over roughly 3-5 weeks: a resume screen, a technical assessment of coding challenges (some describe an initial phone screen at this point instead), and technical interviews that go deep on past projects and domain expertise.
Focus areas
- Terraform (Infrastructure as Code)
- Data Structures
- Algorithms
Overview
Candidates report questions across data structures and algorithms, CS fundamentals and databases, and DevOps and cloud infrastructure. The data structures work covers graphs and trees, heaps, hashmaps, sorting and searching. The fundamentals are framed in Java terms (the four OOP principles, static members and memory), alongside database questions (join types and their cost, normalization and ACID, SQL tuning). The DevOps block covers Terraform workspaces and backends, Docker in production, Jenkins in CI/CD, Git merge conflicts and cloud network layout. Candidates note the DevOps questions come up for roles involving infrastructure, and the requirements they describe list Docker, Terraform and Jenkins as nice-to-haves, with an object-oriented language such as Java, SQL and DBMS, and core data structures as the must-haves.
For preparation, that means two different kinds of fluency. The technical assessment is described as coding challenges judged on algorithmic speed and accuracy, so you need to write correct Java quickly with the collections you will reach for: HashMap, PriorityQueue, ArrayDeque, TreeMap. The other questions are explanation questions where a correct answer is concrete: the actual join algorithms the database chooses, the isolation level that prevents a lost update, the route table entry that makes a subnet public, the flag that stops a JVM from outgrowing its container.
Candidates also report example scenarios that show the expected depth: writing a program that demonstrates polymorphism and explaining where it is used for real, optimizing an algorithm for a large dataset, troubleshooting a failed deployment in a Terraform environment, and securing a database connection inside a VPC. Practise each of these as a spoken answer with one specific example from your own work, because the technical interviews are reported to dig into past projects as well as domain knowledge.
Topic labels candidates attach to this loop are Terraform, data structures, algorithms, Core Java and networking concepts. If you are a backend engineer who has never touched Terraform or VPC routing, budget real time for those; if you come from infrastructure, budget it for timed Java coding.
The Amulya interview process
Candidates describe three stages, from a resume review through a coding assessment to in-depth technical interviews, over roughly 3-5 weeks.
Resume Screening
Candidates report that the first stage is a review of the resume against the qualifications and fit for the role. No questions are reported for it. Treat the resume as the agenda for the later technical interviews, which candidates say go into past projects in depth: every technology you list (Java, SQL, Docker, Terraform, Jenkins, a cloud provider) is something you should be able to discuss at the level of the reported questions. List the must-haves candidates describe (an object-oriented language, SQL and database work, data structures and algorithms) where your experience is real, and the infrastructure tools only if you can answer the Terraform, Docker and Jenkins questions in this guide.
What to demonstrate
- How well the resume matches the role requirements candidates describe: an object-oriented language such as Java, SQL and DBMS, core data structures
- Fit for the role, as candidates describe the stage
How to prepare
- Rewrite each project bullet as what you built, with which technology, and a measured result, so it can anchor a deep project discussion later.
- For every tool you list (Terraform, Docker, Jenkins, Git, a cloud platform), write the one concrete thing you did with it and check you can answer the matching reported question in this guide.
- Pick two projects and note for each the main design decision, the alternative you rejected and why.
Technical Assessment
Candidates describe this stage as a set of coding challenges that evaluate algorithmic speed and accuracy; some report an initial phone screen at this point instead. The reported data structures and algorithms questions (graph and tree search, heaps for priority handling, hashmaps for fast lookup, a logic-building process under complexity limits, and sorting or searching algorithms) are the type of material a coding stage like this exercises, though candidates do not say which ones appear here. Approach each problem in a fixed order: restate the constraints, state the brute force and its cost, pick the structure that removes the repeated work, write it in your strongest language (Java is the language candidates name), then dry-run edge cases before saying you are done.
What to demonstrate
- Working, correct code, with attention to algorithmic speed and accuracy
- Choice of algorithm and data structure, and stated time and space complexity
- Handling of edge cases such as empty input, duplicates and overflow
How to prepare
- Do timed sets on BFS/DFS, a PriorityQueue top-k problem, a HashMap counting problem and binary search with duplicates, writing each in Java without an IDE.
- Learn the Java collections API cold: PriorityQueue with a Comparator, ArrayDeque as stack and queue, HashMap.getOrDefault and merge, TreeMap floorKey and ceilingKey.
- Practise the reported scenario of optimizing an algorithm for a large dataset: take an O(n^2) solution and narrate the move to O(n log n) or O(n).
Technical Interviews
Candidates describe the technical interviews as in-depth discussions of past projects and domain-specific expertise. They do not report which questions are asked at this stage, so prepare two things: a project walkthrough you can defend at any depth, and the domain material reported across the process, which covers CS fundamentals in Java (OOP, static members, memory management, multithreading, garbage collection), databases and SQL, and DevOps and cloud infrastructure. For a project, be ready to go from the architecture down to a single query, deployment step or class design and say why you chose it over the alternative. Candidates also report being pressed on the trade-offs behind their designs, so attach a trade-off to every decision you describe.
What to demonstrate
- Depth of understanding of your own past projects and the reasons behind technical decisions
- Domain knowledge in the areas candidates report for this role
- Ability to explain trade-offs clearly
How to prepare
- Write a one-page walkthrough of your strongest project: components, data flow, the database schema, how it is built and deployed, and one failure you handled.
- Practise explaining the four OOP principles with one connected Java example, and the polymorphism scenario candidates report: write the program and say where it is used in a real system.
- Rehearse spoken answers to the reported Terraform failed-deployment and database-in-a-VPC scenarios, each tied to something you have actually run.
Where candidates lose points
Writing Java in the coding assessment that fails on details: a PriorityQueue assumed to be a max-heap, a comparator written as a - b that overflows, an int sum that overflows on large input.
Drill the collections you will use until the API is automatic: PriorityQueue (min-heap by default, Comparator.reverseOrder or Integer.compare for others), ArrayDeque, HashMap.merge, TreeMap. Before declaring a solution done, dry-run it on empty input, one element, duplicates and the largest values, and use long for sums.
Answering the joins question with Venn diagrams and nothing on performance, or missing that a WHERE filter after a LEFT JOIN turns it into an inner join.
The question explicitly asks how joins affect query performance. Prepare the physical side: nested loop, hash join and merge join and when each wins, row multiplication from one-to-many joins, indexes on join keys, and reading an EXPLAIN plan. Add the NULL traps (LEFT JOIN filter placement, NOT IN with NULLs).
Describing Terraform workspaces as full environment isolation, with no mention of remote state, locking or the risk of applying to the wrong workspace.
Prepare backends and workspaces as separate answers: the backend is where state lives and how it is locked and versioned; workspaces are multiple states for one configuration. State the trade-off against per-environment directories, and rehearse the reported failed-deployment scenario with real commands: force-unlock, plan -refresh-only, import.
Mixing up the NAT gateway and the internet gateway, or placing the database in a public subnet when asked to lay out a cloud network.
Draw one VPC from memory: public and private subnets per zone, the route table entries for each, where the NAT gateway sits and why traffic through it is outbound only, and which security group may reach the database port. Practise the reported VPC database question against that drawing.
Walking through a past project as a team summary, so it never becomes clear which decisions were yours or why they were made.
For each project you will discuss, write down the components you personally built, one decision with the alternative you rejected, one thing that broke and how you found it, and a number that shows the result. Speak in the first person singular and name the trade-off behind each choice.
Questions & practice
How would you implement and traverse a graph or tree structure to optimize a search?
Medium · Data Structures and Algorithms
Approach
- Pin down what kind of search it is first: membership or range lookups in ordered data point to a balanced BST (O(log n), TreeMap in Java); fewest hops in an unweighted graph points to BFS; reachability or cycle detection to DFS; cheapest path with non-negative weights to Dijkstra with a heap.
- Store the graph as an adjacency list (List<List<Integer>> or Map<Integer, List<Integer>>): O(V+E) memory, against O(V^2) for a matrix, which only pays off for dense graphs or constant-time edge checks.
- Write BFS with an ArrayDeque and mark a node visited when you enqueue it, not when you dequeue it, so it never enters the queue twice. BFS and DFS both run in O(V+E).
- For trees, show recursive and iterative traversals; an explicit stack avoids StackOverflowError on a deep, skewed tree. In a BST, discard the half that cannot hold the target, so search is O(h): O(log n) balanced, O(n) skewed.
- Edge cases: disconnected components (loop over every start node), cycles without a visited set, self-loops, an empty graph, and a target equal to the source.
Follow-up
- The edges now carry weights; what replaces BFS, and why does Dijkstra break with negative weights?
- How would bidirectional BFS or A* reduce the nodes explored, and what must the A* heuristic satisfy?
- The graph no longer fits in one machine's memory; how do you store and traverse it?
Can you explain and implement a solution using heaps for priority management?
Medium · Data Structures and Algorithms
Approach
- Explain the structure: a complete binary tree stored in an array, children of index i at 2i+1 and 2i+2, parent at (i-1)/2. Insert sifts up and poll sifts down, both O(log n); peek is O(1); heapify of n items is O(n).
- In Java, PriorityQueue is a min-heap by default; pass a Comparator for a max-heap or custom priority. Use Integer.compare or Comparator.comparingInt, never (a, b) -> a - b, which overflows for large or negative values.
- Code a concrete priority problem: a job scheduler that always runs the highest-priority task, top-k frequent elements with a size-k min-heap (O(n log k)), or merging k sorted lists (O(N log k)).
- A heap is not stable: add an insertion counter as a tie-breaker when equal priorities must run first-in, first-out. Iterating a PriorityQueue does not visit elements in priority order; only repeated poll does.
- Changing a priority: PriorityQueue.remove(Object) is O(n), so either push a new entry and skip stale ones when polled (lazy deletion) or keep an index map for an indexed heap with decrease-key.
Follow-up
- How do you maintain the running median of a stream with two heaps?
- Make the scheduler safe for several threads: what does PriorityBlockingQueue give you, and what does it not?
- Why is building a heap O(n) rather than O(n log n)?
How do you leverage hashmaps to optimize lookup times in a large dataset?
Medium · Data Structures and Algorithms
Approach
- Start from the access pattern: repeated lookups that cost O(n) each in a list become O(1) average in a HashMap or HashSet, which turns an O(n^2) nested loop (two-sum, duplicate detection, matching two lists by id) into one O(n) pass.
- Explain why it is O(1): hashCode picks the bucket, equals resolves collisions. Java 8+ HashMap doubles its table at a 0.75 load factor, and a bucket with more than 8 entries becomes a red-black tree once the table has 64+ buckets. Tree bins give O(log n) only when the colliding keys are Comparable; keys that are not Comparable and share a hash can still need a scan of the whole bin.
- State the correctness rules: override equals and hashCode together, and never mutate a field used in hashCode while the object is a key. Pre-size the map when the count is known to avoid repeated rehashing.
- For a large dataset, count memory: each entry is an object plus boxed key and value, several times the raw data. Alternatives are primitive-keyed maps, sorted arrays with binary search for static data, a Bloom filter in front of a slower store, or an indexed database lookup.
- Choose the right map: HashMap for speed, LinkedHashMap for insertion order or an LRU cache, TreeMap for ordered and range queries at O(log n), ConcurrentHashMap when threads share it.
Follow-up
- Build an LRU cache with O(1) get and put; which Java class gets you most of the way?
- What happens to lookup time if every key returns the same hashCode, and does it matter whether the key class implements Comparable?
- The keys no longer fit on one machine; how do you partition the map?
What is your process for approaching a logic-building problem with time complexity constraints?
Medium · Data Structures and Algorithms
Approach
- Restate the problem with a small example and pin the constraints: input size, value ranges, duplicates, negatives, sorted or not. The size sets the budget: around 10^5 elements rules out O(n^2) and calls for O(n log n) or O(n).
- Say the brute force and its cost, then find the repeated work in it. That waste names the tool: a hashmap for repeated lookups, sort plus two pointers for pair searches, a sliding window for contiguous ranges, prefix sums for range totals, binary search on a monotonic condition, dynamic programming for overlapping subproblems.
- Outline the chosen approach in a few lines before coding, including the invariant it maintains (for a sliding window, that the window always satisfies the constraint).
- Code it, then dry-run by hand on the example and on edge inputs: empty, one element, all equal, maximum size, and sums that overflow int (use long in Java).
- Finish with time and space complexity and the trade-off made, such as spending O(n) memory on a hash set to drop from O(n^2) to O(n).
Follow-up
- Your solution is correct but times out on the largest input; where do you look first?
- Can you do it with O(1) extra space, and what does that cost in time?
- Describe a problem where your first approach was wrong and how you noticed.
Can you walk through the implementation of a popular algorithm to solve a specific sorting or searching challe
Medium · Data Structures and Algorithms
Can you walk through the implementation of a popular algorithm to solve a specific sorting or searching challenge?
Approach
- Pick an algorithm you can write cleanly and justify it against the others: merge sort (O(n log n) worst case, stable, O(n) extra memory), quicksort (O(n log n) average, O(n^2) worst case with bad pivots, in place, not stable; randomise the pivot), heapsort (O(n log n) worst case, in place, not stable), insertion sort (near O(n) on nearly sorted input).
- Tie it to Java: Arrays.sort on primitives uses dual-pivot quicksort; on objects, and in Collections.sort, it uses TimSort, which is stable and fast on partly sorted data. Counting or radix sort beat comparison sorts for small bounded integer keys.
- For searching, write binary search with a stated invariant: lo and hi bound the answer, mid = lo + (hi - lo) / 2 avoids int overflow, and the loop condition matches how the bounds move. Show the lower-bound variant returning the first index >= target, since duplicates break the naive version.
- Extend it beyond arrays: binary search on the answer when a predicate is monotonic, such as the smallest capacity that ships all packages within a given number of days.
- Test empty arrays, one element, all duplicates, a target below or above every element, and sorted or reverse-sorted input for quicksort.
Follow-up
- Why does stability matter when you sort records by a second key?
- Sort a file too large for memory: how does an external merge sort work?
- Search a rotated sorted array in O(log n).
Can you explain static programs and their role in memory management?
Medium · CS Fundamentals and Database Management
Approach
- Interpret the question aloud: it most likely concerns static storage and static members. Lay out the memory regions: stack (call frames and locals, freed on return), heap (objects, reclaimed by the garbage collector in Java), and static storage (alive for the whole program, or in Java while the class stays loaded).
- Java: a static field belongs to the class, so all instances share one copy, initialised when the class is first initialised. In HotSpot since Java 8, class metadata lives in Metaspace (native memory) and static fields are held with the class's java.lang.Class object on the heap.
- Link it to garbage collection: static fields are GC roots while their class loader is reachable, so anything a static collection references is never collected. An unbounded static Map used as a cache is a classic leak; use a bounded cache or weak references.
- Threads: one static field is shared by every thread, so mutable static state needs synchronisation, atomics or ConcurrentHashMap; static final immutable constants are safe. In C or C++, static locals and globals sit in the data or BSS segment for the whole run.
- Costs: static methods cannot be overridden and static singletons make unit testing harder, which is why dependency injection often replaces them.
Follow-up
- A service's heap keeps growing until it fails; how do you confirm whether a static cache is the cause?
- Is lazy initialisation of a static singleton thread-safe? Show the holder-class idiom.
- What is the difference between a static nested class and an inner class in memory terms?
What are the differences between various types of database joins and how do they impact query performance?
Medium · CS Fundamentals and Database Management
Approach
- Cover what each logical join returns: INNER (matches only), LEFT/RIGHT OUTER (all rows from one side, NULLs where unmatched), FULL OUTER (all rows from both), CROSS (every pair), SELF (a table joined to itself, e.g. employee to manager), plus semi and anti joins written as EXISTS and NOT EXISTS.
- Name the traps: a WHERE filter on the right-hand table after a LEFT JOIN drops the NULL rows and silently makes it an inner join, so put that condition in ON; NOT IN against a column containing NULL returns nothing, so prefer NOT EXISTS.
- Then performance, where the optimizer picks the physical algorithm: nested loop is cheap with a small outer input and an index on the inner join key; hash join suits large unsorted equality joins but needs memory for the build side; merge join suits inputs already sorted on the key.
- Cardinality: a one-to-many join multiplies rows, so sums after it inflate; aggregate before joining or use EXISTS. Index foreign-key join columns, keep join key types identical so no cast blocks the index, and confirm the plan with EXPLAIN.
Follow-up
- Your LEFT JOIN returns fewer rows than the left table has; what happened?
- When does the database pick a hash join over a nested loop, and what makes a hash join spill to disk?
- Rewrite a NOT IN query so it behaves correctly when the subquery returns NULLs.
How do you optimize complex SQL queries for large-scale applications?
Medium · CS Fundamentals and Database Management
Approach
- Measure first: run EXPLAIN (EXPLAIN ANALYZE in PostgreSQL) and read the plan for full scans of large tables, row estimates far from actual counts, sorts or hashes spilling to disk, and nested loops over large inputs.
- Index for the query shape: a composite index with equality columns first and the range or sort column last, e.g. (customer_id, created_at) for WHERE customer_id = ? ORDER BY created_at; a covering index skips table lookups. Every index slows writes, so drop unused ones.
- Keep predicates index-friendly: created_at >= '2026-01-01' can use an index where YEAR(created_at) = 2026 cannot; the same applies to a LIKE pattern starting with a wildcard and to implicit type casts. Select only the needed columns rather than SELECT *.
- Restructure: replace OFFSET paging with keyset paging (WHERE id > :last_id LIMIT n), turn correlated subqueries into joins or EXISTS, aggregate before joining, replace ORM N+1 loops with one batched query, and refresh statistics when estimates are stale.
- At larger scale: partition big tables by date, precompute heavy aggregates in a summary or materialized table, send reads to replicas, and cache hot results, stating the staleness each one accepts.
Follow-up
- The query already uses an index and is still slow; what else in the plan do you check?
- Why does a query with OFFSET 1000000 get slower the deeper a user pages?
- How do you add an index to a large production table without blocking writes?
Explain the core principles of OOPs (Inheritance, Encapsulation, Polymorphism, Abstraction) with code examples
Medium · CS Fundamentals and Database Management
Explain the core principles of OOPs (Inheritance, Encapsulation, Polymorphism, Abstraction) with code examples.
Approach
- Give each principle a one-line definition and a small Java example from a single domain, such as payments, so the four examples build on each other rather than being four unrelated snippets.
- Encapsulation: private fields changed only through methods that enforce invariants, e.g. an Account whose balance moves only through deposit and withdraw, which reject negative amounts. Abstraction: an interface such as PaymentMethod with pay(amount) that hides how each method works.
- Inheritance: CardPayment extends a base class to reuse shared logic. Say when composition is better: behaviour that varies independently, or an is-a relationship that fails Liskov substitution, like Square extends Rectangle.
- Polymorphism: runtime polymorphism is overriding, resolved by dynamic dispatch on the object's actual type, so a List<PaymentMethod> calls each subclass's pay; compile-time polymorphism is overloading, resolved by argument types.
- Java specifics: abstract class (state, constructors) versus interface (multiple inheritance of type, default methods since Java 8); static and private methods are not overridden; @Override catches signature mistakes.
Follow-up
- When would you pick an interface with default methods over an abstract class?
- Show a design where inheritance caused trouble and refactor it to composition.
- How does the JVM decide which overridden method runs?
How do you manage infrastructure as code using Terraform, specifically regarding workspaces and backends?
Medium · DevOps and Cloud Infrastructure
Approach
- Backends hold state. Use a remote backend instead of a local terraform.tfstate, e.g. S3 with versioning and encryption, or Terraform Cloud. Terraform 1.10+ supports native S3 state locking (use_lockfile = true); DynamoDB-based locking is deprecated, so know it only as the older setup an interviewer may still ask about. Locking stops two applies corrupting state; versioning lets you recover a bad state file. Never commit state to Git, since it can contain secrets.
- Workspaces give one configuration several state files (terraform workspace new staging, then select), and terraform.workspace can drive resource names and sizes. They suit near-identical copies of one stack, such as short-lived test environments.
- State the trade-off: workspaces share one backend and usually one set of credentials, and nothing stops an apply to production while you believe you are in dev. The common alternative is a directory per environment calling shared modules, each with its own backend key and credentials. Pick one and justify it.
- Workflow: terraform init (with -backend-config per environment), fmt and validate, plan -out=tfplan, review, then apply that saved plan, ideally from CI. Moving state to a new backend uses terraform init -migrate-state.
- Failed apply: read the provider error; clear a stale lock with terraform force-unlock only after confirming no apply is running; find drift with plan -refresh-only; adopt hand-made resources with terraform import; rerun plan before retrying, since a partial apply leaves created resources in state.
Follow-up
- Someone changed a security group by hand in the console; how do you detect and reconcile the drift?
- How do you pass outputs from a network stack to an application stack?
- How do you keep secrets out of Terraform code and state?
How do you configure Docker containers for deployment in a production environment?
Medium · DevOps and Cloud Infrastructure
Approach
- Build small, reproducible images: a multi-stage Dockerfile that compiles in a JDK image and copies only the artifact into a slim JRE or distroless runtime, a base image pinned by version or digest, a .dockerignore, and layers ordered so dependency manifests are copied before source and stay cached.
- Harden it: run as a non-root USER, keep secrets out of the image and its layers (inject them at runtime from the environment or a secrets manager), use a read-only root filesystem where possible, and scan images in CI.
- Make it behave under an orchestrator: exec-form ENTRYPOINT so the app is PID 1 and gets SIGTERM for graceful shutdown, a health or readiness check, logs to stdout and stderr, and configuration through environment variables so the same image runs in staging and production.
- Set CPU and memory limits and fit the app to them: for Java, a container-aware JVM sized with -XX:MaxRAMPercentage so heap plus off-heap memory stays under the limit and the container is not OOM-killed.
- Tag images with the commit SHA, not only latest, so every deploy is traceable and a rollback means redeploying the previous tag.
Follow-up
- The container keeps being OOM-killed while the heap looks healthy; what do you check?
- How do you give the container a database password securely?
- Your image is very large; how do you shrink it?
What is the role of Jenkins in your CI/CD pipeline?
Medium · DevOps and Cloud Infrastructure
Approach
- Place Jenkins in the flow: a push or pull request triggers it through a webhook, and a Jenkinsfile in the repository defines the pipeline as code, so pipeline changes are reviewed like application code.
- Walk through the stages of a declarative pipeline: checkout; build and unit tests (mvn verify or gradle test for a Java service); static analysis; build a Docker image tagged with the commit SHA; push to a registry; deploy to staging; integration or smoke tests; an input step for production approval; deploy.
- Name the practices: build the artifact once and promote the same image through every environment; store credentials in Jenkins and bind them with withCredentials, never in the Jenkinsfile; run independent stages in parallel; use isolated or containerised agents so builds do not share state; archive test reports.
- Cover failure: a failed stage stops the pipeline, post blocks notify and clean up, and rollback redeploys the last good tag. Mention shared libraries for steps repeated across many repositories.
Follow-up
- A build passes locally but fails in Jenkins; how do you debug it?
- How would you run terraform plan and apply from this pipeline safely?
- The test stage keeps getting slower; how do you bring pipeline time down?
Describe the networking components of a cloud architecture, such as subnets, NAT gateways, and internet gatewa
Medium · DevOps and Cloud Infrastructure
Describe the networking components of a cloud architecture, such as subnets, NAT gateways, and internet gateways.
Approach
- Start with the VPC: a private address range (e.g. 10.0.0.0/16) split into subnets in each availability zone. A subnet is public only because its route table sends 0.0.0.0/0 to an internet gateway; private subnets have no such route.
- Internet gateway: lets resources with public IPs in public subnets reach and be reached from the internet. NAT gateway: lives in a public subnet and gives private instances outbound-only access (package downloads, external APIs) with no inbound connections; one per zone avoids a single-zone failure.
- Lay out a three-tier app: load balancer in public subnets, application servers and database in private subnets. Security groups (stateful, per instance) admit the database port only from the app's security group; network ACLs (stateless, per subnet) add a coarse layer and need rules in both directions.
- Answer the reported scenario 'How do you secure a database connection within a VPC?': private subnet, no public IP, security-group-to-security-group rules, TLS on the connection, credentials from a secrets manager, and admin access through a bastion or session manager rather than open SSH.
- Add cost and reach: VPC endpoints let private subnets reach storage services without NAT data charges; peering or a transit gateway joins VPCs, which requires non-overlapping CIDR ranges.
Follow-up
- An instance in a private subnet cannot reach the internet; how do you debug it step by step?
- What is the practical difference between a security group and a network ACL?
- How would you connect this VPC to an on-premises network?
Edge instances grow 400 MB per hour until the nightly restart
Hard · memory leak · heap analysis · metric cardinality
Edge API instances start at 700 MB resident and grow about 400 MB/hour; a nightly rolling restart has hidden it for weeks. Growth continues unchanged when request rate halves overnight, p99 degrades in the last hours before an instance is recycled, and heap used immediately after a forced full GC rises monotonically. The service holds no product state. Name the discriminating measurement that separates the plausible causes, give the most likely cause, and give the fix and how you would verify it.
Approach
- Separate resident memory from live heap first, because they fail differently. Resident size can grow from fragmentation, native buffers or thread stacks while the heap is flat; heap used after a full GC rising monotonically is the measurement that says objects are reachable and not being released. You already have it, so this is retention, not fragmentation, and that closes off half the candidate list.
- Use the rate's independence from traffic as the discriminator. Growth that continues at half the request rate rules out per-request objects that are merely slow to collect and points at a structure that grows with distinct values observed rather than with call volume. Write the candidates that have that property: a metrics registry keyed on a high-cardinality label, an unevicted cache, an interner, a per-key lock map.
- Take two heap snapshots an hour apart and diff by retained size, reading the dominator tree, not by allocation count or instance count. Expect one root holding a map with millions of entries, then follow the reference chain to the code that inserts and never removes. Allocation profilers point at churn, which is the wrong signal here.
- The candidate that fits this service is an observability label carrying an identifier, such as a request path recorded before templating so that /v1/resources/48213 becomes its own metric series. That grows with distinct ids seen, is independent of rate, and explains the late p99 degradation, since GC cost rises with the size of the live set.
Follow-up
- Post-GC heap is now flat but resident size still creeps. What are you looking at, and does it matter?
- How would you have detected this before an OOM, given the nightly restart masked the trend?
Suggested practice checklist
Each day covers one reported question group, ordered from the coding assessment material to the infrastructure topics and the project walkthrough, and each produces something you can reuse on the day of the interview.
Day 1 — Hashmaps and a repeatable problem-solving routine
- Write out your problem-solving sequence (constraints, brute force, repeated work, chosen structure, invariant, dry run, complexity) and apply it aloud to two array problems.
- Solve two-sum, group anagrams and longest substring without repeating characters in Java, timed, using HashMap and HashSet.
- Explain how a Java HashMap resizes and handles collisions, and when you would pick LinkedHashMap, TreeMap or ConcurrentHashMap.
Deliverable: A one-page solving checklist, three timed Java solutions with stated complexity, and a written HashMap internals summary.
Day 2 — Trees, graphs and heaps
- Implement BFS, iterative DFS and BST search in Java on an adjacency list, and note the complexity of each.
- Solve top-k frequent elements with a size-k min-heap and merge k sorted lists with a PriorityQueue.
- Write a priority job scheduler with a tie-breaking counter, then explain how you would change a job's priority.
Deliverable: Working Java traversals, two heap solutions and a scheduler class, each annotated with time and space complexity.
Day 3 — Sorting, searching and OOP in Java
- Write merge sort and a lower-bound binary search from scratch, and test both on duplicates and empty input.
- Write one connected Java example covering encapsulation, abstraction, inheritance and polymorphism, and explain where that polymorphism appears in a real system.
- Take an O(n^2) solution and optimize it for a large dataset, narrating each step aloud.
Deliverable: Tested sorting and search code, a single OOP example file you can explain in full, and a recorded optimization walkthrough.
Day 4 — Core Java memory, static members and threads
- Write a short explanation of stack, heap, Metaspace and static fields, and why a static collection can leak memory.
- Work the practice debugging case of an instance whose memory grows until a nightly restart, listing the heap-dump and GC-log steps you would take.
- Write a thread-safe lazy singleton with the holder idiom and a counter with AtomicInteger, and explain why a plain static int is unsafe.
Deliverable: A memory-model summary, a written debugging sequence for the memory-growth case, and two thread-safe code samples.
Day 5 — SQL: joins, tuning, normalization and ACID
- Write queries using INNER, LEFT, self and anti joins on a small orders schema, including the LEFT JOIN filter trap and the NOT IN NULL case.
- Run EXPLAIN on a slow query, add a composite index, rewrite an OFFSET query as keyset pagination, and compare the plans.
- Normalize a flat orders table to 3NF, then write a stock-reservation transaction that prevents a lost update and name its isolation level.
Deliverable: A join query sheet, before and after query plans, and a normalized schema with a concurrency-safe transaction.
Day 6 — Terraform, Docker and Jenkins
- Configure a remote backend with locking (S3 with use_lockfile on Terraform 1.10+) and create two workspaces for a small stack; write your answer on workspaces versus per-environment directories and your failed-deployment troubleshooting steps.
- Write a multi-stage Dockerfile for a Java service with a non-root user, health check and memory-aware JVM flags.
- Write a declarative Jenkinsfile that builds, tests, builds and pushes an image tagged with the commit SHA, and deploys with an approval step. If Jenkins is not on your resume, settle for listing its pipeline stages and what each one does.
Deliverable: A working Terraform backend setup with notes, a Dockerfile and a Jenkinsfile (or a written list of pipeline stages if Jenkins is not on your resume).
Day 7 — Git conflicts, cloud networking and the project walkthrough
- Create and resolve a deliberate merge conflict with zdiff3, and write out the steps you took, including how you would back out.
- Draw a VPC with public and private subnets in two zones, route tables, internet and NAT gateways and security groups, then answer how to secure a database connection inside it.
- Rehearse a full walkthrough of your strongest project, from architecture down to one query, one deployment step and one class design.
- Rehearse an outage or failed-deployment story from first symptom to follow-up fix, using the practice behavioral prompt on owning an outage.
Deliverable: A written conflict-resolution walkthrough, a labelled network diagram with a spoken database-security answer, a project walkthrough page and a rehearsed incident story.
Behavioural preparation
Candidates report that the technical interviews go deep into past projects, and the requirements they describe include explaining technical ideas to non-technical people. Two reported questions are also framed around your own practice. Prepare stories that show decisions you made yourself, the trade-off behind each, and how you checked the outcome.
- Walk through a project on your resume: what you built, the main design decision, and the alternative you rejected.
- Tell me about a deployment that failed and how you found and fixed the cause.
- Describe a time you explained a technical trade-off to a product manager or another non-engineer and how it changed the decision.
- Tell me about a performance bottleneck you noticed before it caused an incident and what you changed.
How do you handle database normalization and ensure ACID properties in your designs?
Medium · CS Fundamentals and Database Management
Approach
- Anchor the answer in one schema you actually designed, such as orders and payments: what you normalized, what you deliberately denormalized, and how transactions protected the writes. Abstract definitions alone do not answer 'in your designs'.
- Normalization: 1NF (atomic values, no repeating groups), 2NF (no dependency on part of a composite key), 3NF (no transitive dependency, e.g. customer_city stored on orders), BCNF. The gain is no update anomalies; the cost is more joins. Say where you denormalized for reads and what kept the copy correct.
- ACID with mechanisms: atomicity (commit or roll back as a unit, via the write-ahead log), consistency (primary key, foreign key, unique, check and NOT NULL constraints), isolation (READ COMMITTED, REPEATABLE READ, SERIALIZABLE and the anomalies each allows), durability (the commit reaches the log on disk before it is acknowledged).
- Give a concrete concurrency case: reserving stock or moving money inside one transaction, using SELECT ... FOR UPDATE or optimistic locking with a version column to stop a lost update, and say which isolation level it ran at.
- Close on the trade-off (stricter isolation means more blocking or serialization failures that must be retried) and how you verified it, such as a test that reproduced the race before the fix.
Follow-up
- Two requests decrement the same inventory row at once; what happens at READ COMMITTED?
- Where did you denormalize, and what kept the copies in sync?
- How do you keep data consistent when one operation spans two services, each with its own database?
Can you explain your workflow for resolving complex Git merge conflicts?
Medium · DevOps and Cloud Infrastructure
Approach
- Tell it as a real case: a long-lived branch that conflicted with main across several files, what was at risk, how you resolved it, and the habit you changed afterwards so it recurred less.
- Workflow: git fetch, then merge or rebase onto origin/main. For each conflicted file, view the common ancestor as well as both sides (git config merge.conflictStyle zdiff3), and use git log -p or git blame on the lines to learn why each side changed.
- Resolve by intent, not by picking a side: when both changes matter, combine them, and ask the other author when the intent is unclear. Build and run the tests before git add and continuing, because a clean textual merge can still break behaviour, such as new code calling a method the other branch renamed.
- Know the safety tools: git merge --abort or git rebase --abort to back out, git reflog to recover lost commits, git rerere to reuse repeated resolutions, and git push --force-with-lease rather than --force after rebasing a branch only you use.
- Prevention: smaller pull requests, rebasing on main frequently, and keeping formatting-only changes separate from logic changes.
Follow-up
- When do you merge and when do you rebase, and why not rebase a shared branch?
- A bad resolution reached main and broke the build; how do you undo it safely?
- How do you resolve a conflict in a lockfile or a generated file?
Narrate an outage you owned from page to postmortem
Hard · incident response · blast radius · postmortems
Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you cannot name what you measured, choose a different incident.
Approach
- Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
- Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
- Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
- Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.
Follow-up
- What would you do differently in the first five minutes, given the same dashboard and no more information?
- Which follow-up action did you deliberately not take, and why was dropping it the right call?