cswork / libraryOpen practice workspace

Interview guides · Ascension Energy Group

Ascension Energy Group Software Engineer Interview Guide

Ascension Energy Group Software Engineer interview guide: three reported rounds, Spring Boot, microservices and Java questions, and a 7-day prep plan.

The 60-second brief

Ascension Energy Group works in the energy sector, and candidates describe the Software Engineer role as backend work on the services that process energy data and let internal systems talk to each other. The reported duties include building and tuning backend services, designing APIs, untangling legacy code, writing automated tests, joining code reviews, and owning code from development through deployment and production monitoring. Spring Boot and microservices are the stack named most often, and some reports mention moving legacy monolithic applications to microservices. Engineers are described as working with site leads, product managers, front-end engineers, data scientists, DevOps teams and other engineering groups.

This guide is for engineers with a Java and Spring Boot background, typically several years of professional backend experience, who are preparing for the Software Engineer loop at Ascension Energy Group. It covers the three reported rounds, the fifteen reported questions (framework and architecture, Java coding, and behavioral), the stack topics candidates list (REST APIs, relational databases and ORMs, cloud and containers as extras), and a seven-day plan that sequences them.

Candidates report three rounds over roughly 3-5 weeks: a recruiter screen, a Zoom technical evaluation centred on Spring Boot and microservices, and a final round with the hiring manager and a site lead, held virtually or on campus depending on the office.

Focus areas

  • Spring Boot
  • Microservices
  • Java

Overview

The reported loop is stack-first. Candidates describe a technical round built around Spring Boot and microservices, and the question list follows suit: how auto-configuration works, how a global exception handler is wired into a REST API, how configuration differs between dev, staging and prod, and where a monolith or a set of services is the better call. Being able to explain what the framework does at startup, and not just which annotations to write, is the preparation that matches these questions.

Five Java coding questions are reported: the first non-repeating character in a continuous data stream, merging two sorted lists of energy reading timestamps, a thread-safe singleton, the maximum subarray sum, and validating a string as JSON. They span a hash map plus ordering structure, two-pointer merging, concurrency and memory visibility, dynamic programming in its simplest form, and parsing with a stack or recursive descent. Practise each in plain Java with tests and a stated complexity.

Several reports also list wider scenarios: securing a new Spring Boot REST API, keeping data consistent across independent services, ingesting telemetry from thousands of remote sensors, and how services should behave when the primary database is down. They are open-ended by design in the reports, so rehearse starting with requirements (volume, ordering, loss tolerance, latency) and stating assumptions out loud before naming technologies.

The behavioral set is five prompts plus the slow database query story: pushing back on a product manager, learning a technology quickly, coping with vague or shifting requirements, mentoring or leading an initiative, and why this role fits your career. Candidates say these come up in the final round with the hiring manager and a site lead, and that communication matters even in technical rounds. Build five or six stories with real numbers and decisions, since one story can often serve two prompts.

The Ascension Energy Group interview process

Candidates describe three stages; the details below follow those reports and may vary by region and hiring team. The timeline is reported as roughly 3-5 weeks, although one account gives 2-4.

Recruiter Screen

Candidates describe a short phone or video call with a recruiter, reported as about 20 minutes, to line up your background with the role, explain the duties and check expectations on both sides. It is also reported as a good moment to ask high-level questions about the team and current initiatives. Treat it as a pitch plus logistics: a tight summary of your backend work and a few specific questions. The working arrangement (remote, hybrid or onsite) is reported to depend on the team and location, so ask about it here.

What to demonstrate

  • How well your background lines up with a Java, Spring Boot and microservices role
  • How clearly you summarise your recent backend work
  • Whether expectations such as location and working arrangement match

How to prepare

  • Write a short walk through your CV that leads with the services you built, the stack (Java, Spring Boot, REST, SQL) and the systems you owned in production.
  • Prepare a spoken answer to what you want from your next role, the same one you would give to the career-path question later in the loop.
  • List questions about the team, its current projects, how software relates to physical sites, and whether the role is remote, hybrid or onsite.

Technical Evaluation

Candidates report a first technical interview over Zoom that mixes a coding quiz with a technical discussion about Spring Boot and microservices. The panel is said to open with a short introduction to the company and the project before the technical part, and candidates describe the tone as a discussion rather than an interrogation. Topics candidates list include dependency injection, the application context and bean configuration, REST and asynchronous messaging between services, handling network partitions, transactions and query optimization against relational and NoSQL stores. The reported coding questions are the ones to practise for the coding portion: work them in Java, say the complexity and walk through edge cases aloud.

What to demonstrate

  • Working knowledge of Spring Boot: dependency injection, the application context and bean configuration
  • Microservice communication and data persistence topics candidates list, such as REST, messaging, transactions and query tuning
  • Code you can write and explain live over a video call, with stated time and space complexity

How to prepare

  • Explain Spring Boot auto-configuration aloud from the starter on the classpath to the conditional bean that gets registered, and outline how a global exception handler turns exceptions into a consistent error response.
  • Write the first non-repeating character, sorted-merge, singleton, maximum subarray and JSON validation solutions in plain Java without IDE help, each with edge-case tests.
  • Prepare your slow database query story with the plan before and after, since the query tuning prompt is reported alongside the framework questions.
  • Be ready to compare a monolith with microservices using concrete costs: network failure, data consistency, deployment overhead and team boundaries.

Final Round

The final stage is reported as conversations with the hiring manager and a site lead, either virtually or on campus depending on the office. Candidates say it mixes IT scenarios, behavioral questions and deeper architectural discussion, in a welcoming tone but with a technical bar that is still high. Example scenarios candidates list include designing a system to ingest telemetry from thousands of remote sensors, how services should react if the primary database goes down, and approaching a project whose business requirements are still changing. Prepare a bank of structured stories for the behavioral prompts and a repeatable method for open-ended design questions.

What to demonstrate

  • Behavioral stories on pushing back, learning quickly, ambiguity, mentoring or leading, and career direction
  • Architectural reasoning: system boundaries, database choice, scalability, fault tolerance and secure data flow
  • How you work with non-engineers such as a product manager or a site lead

How to prepare

  • Write STAR outlines for the five reported behavioral prompts, with one number or observable result in each.
  • Sketch a telemetry ingestion design: sensor gateway, message queue, consumer services, time-series or relational storage, idempotent writes, back-pressure and monitoring.
  • Rehearse the database-down scenario: timeouts, retries with backoff, circuit breaker, queued writes, degraded reads and what the user or operator sees.
  • Prepare questions for a hiring manager and a site lead about how the software interacts with physical facilities and what the team is working on now.

Where candidates lose points

Naming Spring Boot annotations without being able to say what happens at startup

Practise tracing it end to end: the starter on the classpath, conditional configuration classes, bean registration in the application context, and how a property or your own bean overrides a default. If you can explain why a bean appeared, you can answer auto-configuration and configuration-per-environment questions.

Answering monolith versus microservices with 'microservices scale better'

Give both sides with costs: network failure and partial outages, data consistency across services, deployment and monitoring overhead, versus a monolith's simpler transactions and releases. Say when you would split a service and when you would not.

Jumping to code on the streaming first non-repeating character or the JSON validation question without clarifying

Ask about input type and size, character set, whether the stream is unbounded, how often the answer is queried, and what counts as valid JSON. Then name the brute-force version, choose the data structure, state complexity and test edge cases such as empty input, all repeats and nested brackets.

Treating an open-ended design prompt, like telemetry from thousands of sensors, as a technology shopping list

Gather requirements first: message rate, ordering needs, tolerance for loss or duplicates, latency, retention and who consumes the data. Then draw the components, name failure modes, and say what you monitor.

Telling behavioral stories with no decision, no number and no result, especially the slow query story

For each story, write the situation in two sentences, your specific action, and a measured outcome, such as latency before and after. Rehearse them aloud until each fits one clear answer.

Questions & practice

Write an algorithm to find the first non-repeating character in a continuous data stream.

Medium · Coding and Problem Solving

Approach

  1. Clarify the contract first: after each incoming character, return the first character seen exactly once so far, or a sentinel such as null or '#' when there is none. Ask whether the alphabet is ASCII or full Unicode and whether case matters.
  2. Keep a count map (an int[256] for ASCII, a HashMap otherwise) plus a queue of candidates in arrival order. On each character, increment its count and enqueue it if the count is now 1; then pop from the front while the front's count is above 1. The front is the answer.
  3. State the complexity: every character is enqueued once and dequeued at most once, so the work is amortised O(1) per character and O(n) over the stream; space is O(k) for k distinct characters, bounded by the alphabet size.
  4. For worst-case O(1) per step, use a doubly linked list of non-repeating characters plus a map from character to node and a repeated set: remove the node on the second occurrence. In Java a LinkedHashSet does this, with iterator().next() giving the first element.
  5. Cover edge cases: a query before any unique character exists, a character that repeats a third time (it must not re-enter), and Unicode input, where a Java char is a UTF-16 unit, so supplementary characters need code points.

Follow-up

  • How would you change the solution if only the last N characters of the stream count (a sliding window)?
  • Several producer threads feed the stream concurrently. How do you keep the answer consistent?
  • The alphabet is unbounded, for example sensor ids instead of characters. What happens to memory and how would you bound it?

How would you merge two sorted lists of energy reading timestamps?

Medium · Coding and Problem Solving

Approach

  1. Use two pointers: compare the heads, append the smaller timestamp to the output, advance that pointer, and when one list runs out append the rest of the other. That is O(n + m) time; for arrays the output needs O(n + m) space, for linked lists you can relink nodes with O(1) extra space.
  2. Decide tie handling out loud: taking from the first list when values are equal (<=) keeps the merge stable, and you should ask whether equal timestamps are duplicates to drop or distinct readings from different meters to keep, possibly ordered by source id.
  3. Normalise before comparing: convert timestamps to a single representation such as epoch milliseconds or java.time.Instant in UTC, since mixed time zones or string formats make a lexical comparison wrong.
  4. Cover edge cases: either list empty or null, lists of very different lengths, and inputs that are not actually sorted (validate, or sort first and say it costs O(n log n)).
  5. Generalise: for k sorted lists use a min-heap (PriorityQueue) holding one head per list, giving O(N log k) for N total readings; for data larger than memory, stream from iterators or files and write the output incrementally (external merge).

Follow-up

  • How would you merge readings from k sensors instead of two?
  • Neither list fits in memory. How does the implementation change?
  • A reading arrives late, after later timestamps have already been merged and emitted. How do you handle it?

Implement a thread-safe singleton pattern in Java.

Medium · Coding and Problem Solving

Approach

  1. Start with the simplest correct option: a private constructor and a private static final instance created at class initialisation. The JVM runs class initialisation once under a lock, so this is thread-safe, but the instance is created as soon as the class is initialised.
  2. For lazy creation without locking, use the initialisation-on-demand holder: a private static nested Holder class with a static final INSTANCE field, returned from getInstance(). The holder class is initialised only on the first call, and the JVM guarantees that happens once.
  3. If asked for double-checked locking, the field must be volatile: without it, instruction reordering can let a second thread see a non-null reference to a partially constructed object. Check null, synchronise on the class, check null again, then assign; reading the field into a local variable cuts volatile reads. A plain synchronized getInstance() is also correct but takes the lock on every call.
  4. Mention the enum singleton (enum Singleton { INSTANCE; }): thread-safe, and safe against both serialisation and reflection attacks, at the cost of not being able to extend a class.
  5. Name what still breaks the other versions: reflection calling the private constructor (guard by throwing if the instance already exists), deserialisation creating a copy (add readResolve returning the instance), and separate class loaders each holding their own copy. In a Spring application, a default singleton-scoped bean is usually the better tool.

Follow-up

  • Why exactly is volatile required in double-checked locking, and what can go wrong without it?
  • How does serialisation break a singleton, and how do you prevent it?
  • How does Spring's singleton scope differ from the classic singleton pattern?

Given an array of integers, find the maximum subarray sum.

Medium · Coding and Problem Solving

Approach

  1. Name the brute force: try every start and extend the end while keeping a running sum, O(n^2) time and O(1) space (O(n^3) if each sum is recomputed).
  2. Give Kadane's algorithm: for each element x, cur = max(x, cur + x) and best = max(best, cur). The reasoning is that a negative running sum can only hurt any subarray that continues it, so restart at x. O(n) time, O(1) space.
  3. Handle all-negative input: initialise cur and best to the first element, not 0, so the answer is the largest single element. Ask whether an empty subarray is allowed, since that changes the answer for all-negative input to 0. Reject or define behaviour for an empty array.
  4. Watch overflow: with int inputs, accumulate in a long. To return the subarray itself, record a tentative start whenever cur restarts at x and save start and end whenever best improves.
  5. Mention the equivalent prefix-sum view: the answer is the maximum over j of prefix[j] minus the minimum prefix before j. A divide-and-conquer version runs in O(n log n) and is worth knowing but not the target.

Follow-up

  • Return the start and end indices of the best subarray, not just its sum.
  • How do you solve it when the array is circular? (Take the larger of the normal Kadane result and the total minus the minimum subarray, unless every element is negative.)
  • How does the approach change if the subarray length is capped at k?

Write a function to validate if a given string is a valid JSON format.

Medium · Coding and Problem Solving

Approach

  1. Clarify the scope: full RFC 8259 validation or only balanced brackets? In production I would call a parser such as Jackson's ObjectMapper.readTree and treat a JsonProcessingException as invalid; in the interview I would write a recursive-descent validator.
  2. Structure it as one function per grammar rule over an index into the string: parseValue skips whitespace and dispatches on the next character ('{' object, '[' array, '"' string, 't', 'f' or 'n' for the literals true, false and null, '-' or a digit for a number). An object is '{', then optional string ':' value pairs separated by commas, then '}'. After the top-level value, only whitespace may remain.
  3. Get strings and numbers right, since most bugs live there. Strings allow only the escapes " \ / \b \f \n \r \t and \u followed by exactly four hex digits, and raw control characters below 0x20 are invalid. Numbers are an optional '-', then '0' or a nonzero digit followed by digits, an optional '.' with at least one digit, and an optional e or E with an optional sign and at least one digit, so 01, +1, .5 and 1. are all invalid.
  4. State the complexity: a single pass, O(n) time; recursion depth equals the nesting depth, so deeply nested input can overflow the call stack. Fix that with a depth limit or an explicit stack.
  5. Test the edge cases: empty or whitespace-only input (invalid), trailing commas and single-quoted strings (invalid), bare top-level scalars such as 42 or "x" (valid under RFC 8259), and duplicate object keys (syntactically valid, though the RFC says names should be unique).

Follow-up

  • How would you report the position and reason of the first syntax error?
  • The JSON arrives in chunks over a socket. How do you validate it without buffering the whole payload?
  • How would you go further and check the document against a schema, for example required fields and types?

Denormalise tenant onto revisions and backfill it live

Hard · denormalisation · online migration · backfill

resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.

Approach

  1. Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
  2. Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
  3. Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
  4. Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.

Follow-up

  • The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
  • How do you verify the backfill actually finished, given rows are still being inserted while it runs?

Replace offset paging on the resource feed with keyset

Easy · keyset pagination · composite index · sort order

resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.

Approach

  1. Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
  2. Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
  3. Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
  4. Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.

Follow-up

  • The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
  • Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?

How does Spring Boot auto-configuration work under the hood?

Medium · Backend and Framework Knowledge

Approach

  1. Start at @SpringBootApplication: it combines @SpringBootConfiguration, @ComponentScan and @EnableAutoConfiguration, and the last one imports AutoConfigurationImportSelector, a deferred import selector that runs after your own configuration classes are processed.
  2. Explain candidate discovery: since Boot 2.7 the selector reads META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports from every jar on the classpath (older versions used spring.factories, which Boot 3 no longer reads for auto-configuration), so adding a starter dependency adds candidates.
  3. Explain the filtering: each auto-configuration class is guarded by conditions such as @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty and @ConditionalOnWebApplication, and binds its settings through @ConfigurationProperties classes (spring.datasource., server.).
  4. Say why your beans win: because auto-configuration is evaluated last, @ConditionalOnMissingBean makes it back off when you define your own DataSource or ObjectMapper. Ordering between auto-configurations uses @AutoConfiguration(before/after) or @AutoConfigureOrder.
  5. Show how you debug it: start with --debug (or debug=true) to print the condition evaluation report, or call the Actuator /actuator/conditions endpoint, and turn a class off with exclude on @SpringBootApplication or spring.autoconfigure.exclude.

Follow-up

  • How would you write your own starter with an auto-configuration that backs off when the application defines its own bean?
  • A bean you expected is missing at startup. Which steps do you take to find out which condition failed?
  • Why does declaring your own DataSource bean disable the auto-configured connection pool settings?

Explain the difference between monolithic and microservices architectures. What are the trade-offs?

Medium · Backend and Framework Knowledge

Approach

  1. Define both precisely: a monolith is one deployable unit, usually with one shared database and in-process calls; microservices are independently deployable services, each owning its data and talking over the network (REST or messaging).
  2. Give the monolith's real strengths before its weaknesses: local method calls, one ACID transaction across modules, a single place to debug and deploy. Its costs are coupled releases, scaling the whole application for one hot path, and long builds as the codebase grows.
  3. Give what microservices buy (independent deploys and scaling, fault isolation, team ownership per service) and what they cost: partial failure that needs timeouts, retries with backoff and circuit breakers; cross-service consistency handled with sagas or a transactional outbox instead of one transaction; distributed tracing and correlation ids; service discovery and one pipeline per service.
  4. State a decision rule: a small team or an unclear domain starts with a modular monolith with clean module boundaries; extract a service along a bounded context when it needs a separate release cadence, scaling profile or owner.
  5. For a legacy system, describe the strangler fig pattern: route one capability at a time to a new service behind a gateway or facade, move its data ownership with it, and retire the old code path once traffic has moved.

Follow-up

  • An operation updates data owned by two services. How do you keep them consistent without a distributed transaction?
  • How would you carve the first service out of a legacy monolith, and how do you split the shared database?
  • What should calling services do when a downstream service is slow or down?

How do you implement global exception handling in a Spring Boot REST API?

Medium · Backend and Framework Knowledge

Approach

  1. Use a single @RestControllerAdvice class (@ControllerAdvice plus @ResponseBody) with @ExceptionHandler methods, so every controller shares one error contract instead of try/catch blocks in each endpoint.
  2. Map exception types to status codes explicitly: a domain ResourceNotFoundException to 404, MethodArgumentNotValidException from @Valid on a @RequestBody to 400 with field errors taken from getBindingResult().getFieldErrors(), ConstraintViolationException from validated path or query parameters to 400, and a catch-all Exception handler to 500 with a generic message while the stack trace goes to the server log only.
  3. Return one consistent body. In Spring 6 / Boot 3 that can be ProblemDetail (RFC 7807, now RFC 9457: type, title, status, detail, instance), and extending ResponseEntityExceptionHandler covers Spring MVC's own exceptions such as HttpMessageNotReadableException for malformed JSON. Add a trace or correlation id so a client report can be matched to a log line.
  4. Know the resolution rule: Spring picks the handler whose declared exception type is closest in the hierarchy to the thrown one, so a specific handler beats the Exception catch-all. Lighter alternatives are @ResponseStatus on an exception class or throwing ResponseStatusException.
  5. Name the gap: exceptions thrown in servlet filters, including Spring Security, happen before the DispatcherServlet and never reach the advice. Handle those with an AuthenticationEntryPoint and AccessDeniedHandler, or a filter that writes the same error body.

Follow-up

  • Why does your advice not catch an authentication failure, and how do you return the same error format for it?
  • How would you test the handler, for example with @WebMvcTest and MockMvc?
  • How do you avoid leaking internal details such as SQL errors or stack traces in a 500 response?

How do you manage configuration properties across different environments (dev, staging, prod)?

Medium · Backend and Framework Knowledge

Approach

  1. Keep shared defaults in application.yml and per-environment overrides in application-dev.yml, application-staging.yml and application-prod.yml, selected with spring.profiles.active (for example the SPRING_PROFILES_ACTIVE environment variable). Build one artifact and promote it unchanged; only configuration differs between environments.
  2. Know the precedence: command-line arguments override Java system properties, which override OS environment variables, which override the application and profile-specific config files. Relaxed binding maps SPRING_DATASOURCE_URL onto spring.datasource.url, which is how containers inject values.
  3. Bind settings into typed @ConfigurationProperties classes annotated @Validated (with @NotNull, @Min and so on) instead of scattered @Value fields, so a missing or malformed production value fails at startup instead of on the first request.
  4. Keep secrets out of the repository: inject them from environment variables or a secret store (HashiCorp Vault, AWS Secrets Manager, Kubernetes Secrets), and for many services consider a central Spring Cloud Config server, with @RefreshScope beans when values must change without a restart.
  5. Limit @Profile-specific beans, because code that only runs in prod is code you never exercised in dev or staging; prefer differences expressed as property values.

Follow-up

  • How would you rotate a database password in production without redeploying the service?
  • How do you guarantee a required property missing in prod stops the application at startup?
  • Where do Kubernetes ConfigMaps and Secrets fit alongside Spring profiles?

p99 jumped on one listing filter while p50 stayed flat

Medium · query planning · composite index · percentiles

After a release that added an owner_user_id filter to the resource listing, p99 rose from 90 ms to 1.9 s while p50 stayed at 40 ms. Traffic and row counts are unchanged. resource carries the index (tenant_id, status, updated_at DESC, resource_id DESC). The new query filters tenant_id and owner_user_id, orders by updated_at DESC, resource_id DESC, and takes 20 rows. On PostgreSQL, explain the shape of the regression, prove it from a query plan, and give the index you would add.

Approach

  1. Start from the shape. A flat p50 with a moved p99 means a subset of requests changed cost, not all of them, so the first job is naming the subset. Bucket the endpoint's latency by the tenant's row count; the natural hypothesis is that large tenants are a small share of requests and all of the tail.
  2. Get the plan for the new query on a large tenant with EXPLAIN (ANALYZE, BUFFERS). Expect an index scan over the tenant's range, a filter discarding most of it, then a Sort feeding the Limit, possibly reporting Sort Method: external merge Disk. Read actual rows on the scan node, not estimated.
  3. Explain why the existing index cannot serve it. A composite B-tree is seekable only as a left prefix, and with no equality predicate on status the scan cannot treat updated_at as an ordering, because rows in the tenant's range are ordered by status first. Everything matching must be read and sorted before LIMIT 20 can apply, so a tenant with 400,000 rows pays 400,000 rows to return 20.
  4. Add (tenant_id, owner_user_id, updated_at DESC, resource_id DESC). Equality on the first two columns leaves the index ordered by updated_at within that pair, so the plan becomes an index scan that stops after 20 rows with no Sort node. PostgreSQL can scan a B-tree backwards, so the DESC markers matter only if the two sort columns ever disagree in direction; keeping them explicit documents the order the keyset cursor depends on.

Follow-up

  • The endpoint paginates with OFFSET. What does page 500 cost with your index, and what does the keyset version cost?
  • How would you have caught this before release, given that a 10,000-row seed database produces the same plan shape at an unnoticeable cost?

Suggested practice checklist

The plan follows the three reported rounds and the stack candidates list: Spring Boot and microservices first, then the Java coding set, database stories, and a final day of behavioral and design rehearsal.

Day 1 — Map the loop and trace Spring Boot startup

  • Write the three reported stages (recruiter screen, Zoom technical evaluation, final round with hiring manager and site lead) and note which question types you would meet in each, marking your weakest.
  • Trace a Spring Boot application start: the starter dependency, auto-configuration classes with conditions, bean registration, and how your own bean or property overrides a default.
  • Say your explanation of auto-configuration aloud and fix the gaps you hear.

Deliverable: A one-page loop map plus a written outline of how auto-configuration works, spoken once.

Day 2 — Build exception handling and environment config

  • Build a small REST controller in Spring Boot with a @RestControllerAdvice that maps validation, not-found and unexpected exceptions to one consistent error body and status code.
  • Set up dev, staging and prod profiles with property files, environment variable overrides and externalised secrets, and note how each is selected at deploy time.
  • Outline how you would secure the API: authentication, authorisation, input validation and transport security.

Deliverable: A runnable project with the handler and three profiles, plus a spoken outline of both answers and the security outline.

Day 3 — Microservices trade-offs, consistency and failure

  • Write a one-page comparison of a monolith and microservices listing at least four concrete costs and the conditions under which you would split a service.
  • Outline consistency across services: idempotent requests, the outbox pattern, sagas and what you do when a call times out halfway.
  • Sketch what services do when the primary database goes down: timeouts, retries with backoff, a circuit breaker, queued writes and degraded reads.
  • Sketch a telemetry ingestion pipeline for thousands of sensors, naming the queue, consumers, storage and monitoring.

Deliverable: The comparison page and two diagrams, one for failure handling and one for telemetry ingestion.

Day 4 — Java coding set one: streams, merging, subarrays

  • Solve the first non-repeating character in a data stream with counts plus an ordered structure, and state per-character cost and memory.
  • Merge two sorted timestamp lists with two pointers, then handle duplicates and extend the idea to many lists with a heap.
  • Solve maximum subarray sum with the single-pass running maximum, including the all-negative case, and write tests for each.

Deliverable: Three working Java solutions with edge-case tests and time and space complexity written beside each.

Day 5 — Java coding set two: concurrency and parsing

  • Write a thread-safe singleton three ways (enum, holder class, double-checked locking with volatile) and explain why each is safe.
  • Write a JSON validator as a recursive-descent parser covering objects, arrays, strings with escapes, numbers, literals and nesting, then compare it with using a library parser.
  • Take an O(N^2) loop you wrote recently and rewrite it with a hash map or sort, stating the new complexity.

Deliverable: Two Java implementations with tests (singleton under concurrent access, validator on valid and malformed inputs) and one before-and-after rewrite.

Day 6 — Database performance story and drills

  • Write your own slow-query story as a STAR outline with the symptom, the EXPLAIN finding, the fix, and before-and-after numbers.

Deliverable: One STAR outline for the slow database query prompt, backed by solved notes on the two practice questions.

Day 7 — Behavioral bank and a mock final round

  • Write STAR outlines for pushing back on a product manager, learning a technology fast, vague or changing requirements, and mentoring or leading an initiative.
  • Write and rehearse the answer to what you want from your next role and how Ascension Energy Group fits.
  • Run a mock final round: answer one behavioral prompt, then one open-ended design question (telemetry ingestion or database failure) out loud, stating assumptions first.
  • List three questions for the hiring manager and a site lead.

Deliverable: Five story outlines with a result in each, one recorded spoken answer per prompt, and a list of questions to ask.

Stories to prepare for the hiring manager and site lead

The reported behavioral prompts cover pushing back on a product manager, learning a technology quickly, vague or changing requirements, mentoring or leading an initiative, and why this role fits your career. Candidates say they come up in the final round with the hiring manager and a site lead, and that communication and handling feedback matter in the technical rounds too. The slow database query prompt is filed as a story here as well. Prepare structured STAR stories with a decision and a result in each.

Describe a time you had to optimize a slow-performing database query.

Medium · Backend and Framework Knowledge

Approach

  1. Open with the symptom in one or two sentences: which query, which endpoint or job it slowed, and the number that showed it was slow (p95 latency, timeout rate, database CPU). Without a before-number the story cannot show an improvement later.
  2. Walk the diagnosis in order: find the statement through the slow query log or pg_stat_statements, run EXPLAIN ANALYZE, and name the cause the plan showed. Typical causes are a sequential scan on a large table, a missing or wrong-order composite index, a function wrapped around an indexed column, a join returning far too many rows, or an ORM issuing one query per row (N+1).
  3. Explain the fix and why you chose it over alternatives: a composite index with equality columns first and the range or sort column after, a covering index, a rewritten predicate, a fetch join or batch size to remove N+1, keyset instead of offset paging, or caching only when stale data is acceptable. Name the cost, such as write overhead, and how you built it safely (CREATE INDEX CONCURRENTLY in PostgreSQL).
  4. Close with proof and a lesson: the plan after the change, latency before and after on production-like data, a check that writes and neighbouring queries did not regress, and what you now do by default, such as reviewing plans for new queries before release.

Follow-up

  • Why was the existing index not used, and how did you confirm the planner picked the new one?
  • What did the index cost on writes and storage, and how did you ship it without locking the table?
  • How would you find the next slow query before users notice it?

Tell me about a time you had to push back on a technical requirement from a product manager.

Medium · Behavioral and Leadership

Approach

  1. Choose a real disagreement about a technical requirement where you held a position: a deadline that skipped testing, a feature that would break an API contract, a design that would not scale. State the product manager's goal first, so the story shows you understood it before you pushed back.
  2. Argue from evidence, not preference: the failure mode, the estimated cost or risk (extra effort, latency, data-consistency exposure, support load), ideally backed by a measurement or a quick prototype.
  3. Show that you arrived with options rather than a flat no: a reduced first release, a different mechanism for the same goal, or a phased rollout behind a feature flag, with the trade-off of each laid out so the product manager could decide.
  4. Report the outcome honestly, including when you only partly won: what was decided, what you did with it, what happened afterwards and how the working relationship stood. The mistake that sinks this story is casting the product manager as uninformed, or ending with a refusal and no change.

Follow-up

  • What would you have done if the product manager had still insisted on the original plan?
  • How did you estimate the risk, and was the estimate right in hindsight?
  • How do you decide whether a requirement is worth a debate or just a note on the ticket?

Describe a situation where you had to quickly learn a new technology to complete a project.

Medium · Behavioral and Leadership

Approach

  1. Choose a case with a real deadline and a technology that was new to you (a framework, a messaging system, a database or a deployment tool). State what had to ship and by when, and keep the example recent, ideally backend work.
  2. Describe the learning method concretely: reading the official reference rather than only tutorials, a throwaway spike to test your understanding, reading logs or library source when behaviour surprised you, and asking someone experienced to review your design instead of asking them for every answer.
  3. Show how you limited risk while learning: you shipped a thin slice first, wrote tests around the unfamiliar parts, kept the new technology behind a clear interface or flag, and had the work reviewed.
  4. Be honest about the result and the gaps: what shipped, what you got wrong first and how you found it, and what you would still not claim expertise in. The mistake is claiming mastery after a few days, or a story made only of courses watched with no delivered outcome.

Follow-up

  • What was the first thing that went wrong, and how did you find it?
  • How did you decide what to learn deeply and what to take as given for the deadline?
  • How did you check your understanding was correct before the change reached production?

How do you handle situations where the project requirements are vague or constantly changing?

Medium · Behavioral and Leadership

Approach

  1. Start with your method in a sentence or two, then back it with one example: clarify the goal behind the request, write down assumptions and open questions, and get them confirmed by whoever owns the answer.
  2. Cut the work to the smallest slice that is useful and testable now, and show the feedback loop: an early demo, a regular check-in with the product manager or stakeholders, and decisions recorded in a ticket or short design note.
  3. Describe technical protection against churn: isolate what is likely to change behind an interface or configuration, version an API instead of changing it in place, keep schema migrations additive, use feature flags, and keep tests strong enough that changes stay cheap.
  4. Show how you handled a change when it arrived: you re-estimated, said what it cost in time or scope, and wrote down why direction moved. The mistake is answering that you simply adapt, with no mechanism, or waiting for a perfect specification before starting.

Follow-up

  • Which assumption turned out wrong, and what did it cost?
  • How do you stop constant change from eroding the schedule?
  • How do you decide when to stop asking questions and start building?

Tell me about a time you mentored a junior team member or led a technical initiative.

Medium · Behavioral and Leadership

Approach

  1. Pick one branch and commit to it: mentoring one specific person with one specific gap, or a technical initiative you led, such as moving a module out of a monolith, introducing a testing or CI practice, or a query-performance clean-up. Say who was involved and what your role was.
  2. For mentoring, say what the person struggled with and how you taught: pairing, review comments that explain the reasoning, tasks of increasing size, letting them make and fix small mistakes, and handing over ownership step by step.
  3. For an initiative, show planning and influence: the goal and how success would be measured, the steps, how you won over teammates and the product manager, how you sequenced rollout so production stayed safe, and how you reported progress.
  4. Finish with a result that belongs to others, not just you: the junior engineer now merging independent changes or owning a component, or the initiative's measurable outcome. The mistake is a story where you did all the work yourself, or a claim of being a natural mentor with no specific event.

Follow-up

  • What did the person do that surprised you, and how did you change your approach?
  • What would you do if they stopped asking for help but kept getting stuck?
  • How did you handle a teammate who opposed the initiative?

What are you looking for in your next role, and how does Ascension Energy Group fit into your career path?

Medium · Behavioral and Leadership

Approach

  1. This is a structured pitch rather than a STAR story. Use past, present, future: what you have built and enjoyed (backend services, Java, Spring Boot, microservices), what you want more of, and why this role provides it.
  2. Make the next step concrete and tied to the role as candidates describe it: owning services from development through deployment and monitoring, working on APIs and legacy-to-microservices migration, and working with site leads and product managers. Say what you want to learn, not only the title you want.
  3. For why Ascension Energy Group, use facts you gathered from the recruiter call and public information, such as the energy domain, telemetry and IoT data, and software that sits close to physical operations. Avoid flattery and avoid claims about company values you cannot support. Ask about what you are unsure of.
  4. Keep it consistent with your CV and your other answers, and avoid framing it as escape from a current job. The mistake is a generic line about growth and challenge that would fit any employer.

Follow-up

  • Where do you want to be technically in a few years?
  • What would make you leave a role like this?
  • What would you want to learn first if you joined the team?