Interview guides · ALIQAN Services
ALIQAN Services Software Engineer Interview Guide
ALIQAN Services Software Engineer interview guide: two rounds, .NET, Angular, AWS, Boomi and ServiceNow questions, STAR stories and a 7-day plan.
The 60-second brief
Candidates describe ALIQAN Services as a company that delivers software for its clients through project-based work. The work named in reports includes .NET enterprise applications, cloud-based integrations and GIS mapping platforms, across ecosystems such as AWS, ServiceNow and Boomi. Reports also say hiring is often aimed at narrow specialisms, for example GIS development or ServiceNow implementation. In this role you turn a client's business requirements into working, maintainable code inside whichever of those platforms the project uses, and you work with project managers and other engineers on an agile cadence.
This guide is for engineers interviewing for a Software Engineer position at ALIQAN Services, whether the role leans toward .NET back-end work, Angular or GIS front-end work, or integration work on Boomi, ServiceNow or AWS. It covers the two reported rounds, the eight reported questions (stack knowledge, troubleshooting, design and delivery trade-offs), worked structures for the two experience stories, and a seven-day plan.
Candidates report two rounds over roughly 2-4 weeks: a Technical Screening, then Specialized Discussions about past work and what you could contribute to projects.
Focus areas
- Boomi Integration
- .NET
- ServiceNow HRSD
Overview
Reports describe the Software Engineer role as the full development lifecycle: turning client requirements into code, working in an agile team with project managers and other engineers, and shipping something that can grow as the client's needs change. The stack varies by project. Candidates mention .NET for back-end work, Angular for front-end work, GIS mapping and dashboards, and integration layers on Boomi, ServiceNow or AWS. The requirements candidates describe list a strong grasp of relational databases and Git as must-haves, with cloud certifications (AWS or Azure), CI/CD experience and familiarity with ServiceNow or Boomi as extras.
The reported questions are mostly of the form 'tell me how you did X' or 'how would you approach Y': a walk-through of scalable .NET work, Boomi data mapping and error handling, Angular components versus services, AWS for performance or reliability, a hard integration bug, a maintainable new module, a system bottleneck, and speed versus clean code. Candidates also report an emphasis on hands-on experience over theoretical trivia. That means your preparation is evidence gathering: for every technology on your resume, have one decision you made, one thing that went wrong, and one result you can state with a number or a concrete before-and-after.
Topics candidates list for the technical side are dependency injection and inversion of control, asynchronous programming in .NET, and security for web APIs and cloud-hosted services; on the front end, Angular lifecycle hooks, performance, mapping or data-visualisation libraries, and responsive design and cross-browser behaviour. Because the work is niche-specific, read the job description first and decide which of these apply to your role. Where you have not used a platform, say so plainly and describe the closest thing you have built, naming the concepts that carry over (for example, mapping, retries and dead-letter handling apply to any integration tool).
The ALIQAN Services interview process
Candidates describe two stages. The reported timeline is about 2-4 weeks end to end, and the exact order of questions within each stage is not published.
Technical Screening
The first stage is called Technical Screening, and candidates describe it as an initial check of your technical ability for the role. Because the role is tailored to a specialism, expect it to touch the technology named in the job description, whether that is .NET, Angular, Boomi, ServiceNow, AWS or GIS. Approach it by giving each answer a clear shape: what the technology does, why you chose it, how you used it, and the trade-off you accepted. Short, specific answers drawn from real projects serve better than a survey of features.
What to demonstrate
- Working knowledge of the technology your role is built around
- Ability to explain the reasoning behind a technical choice, not only the syntax
- Fit between your hands-on background and the requirements for the role
How to prepare
- Write a short spoken summary for each stack item on your resume (.NET, Angular, AWS, Boomi, ServiceNow or GIS) and attach one decision you personally made to it.
- Practise the reported stack questions aloud: Boomi data mapping and error handling, Angular components versus services, and AWS for performance or reliability.
- Refresh the .NET topics candidates list: dependency injection and inversion of control, asynchronous programming, and web API security.
- Revise relational database fundamentals (joins, indexes, transactions) and your Git branching and merge workflow, which the reported requirements name as must-haves.
Specialized Discussions
The second stage is called Specialized Discussions. Candidates describe in-depth conversations about your past work and how you could contribute to current projects. This is where your resume gets opened up, so any project you list can be explored in depth. Approach it by preparing two or three projects to architecture level: the problem, the design, your own contribution as opposed to the team's, the hardest issue and how it ended. Have a view ready on the reported design questions too: a maintainable new module, finding and fixing a bottleneck, and balancing delivery speed against clean code.
What to demonstrate
- Depth of your account of projects you have delivered
- Clarity about your own contribution within a team
- How you would apply your experience to a client project
How to prepare
- Pick two resume projects and write for each: architecture, your contribution, the hardest problem, the result.
- Rehearse the two experience prompts with a clear structure: the walk-through of scalable .NET work and the troubleshooting of a complex integration issue between two systems.
- Prepare a position on the design questions: how you structure a new module, your step-by-step bottleneck process, and how you trade speed for cleanliness.
- Write down two questions about the team's current technical challenges to ask at the end.
Where candidates lose points
Touring the .NET stack instead of answering the scalable-applications walk-through with decisions
Pick one system and give a decision, a trade-off and a measured result. Prepare the dependency injection, async and API security points so you can explain why you used them, not only that you did.
Describing Boomi data mapping while saying nothing about what happens when a document fails
Cover both halves: field mapping and type conversion, and the failure path (error routing, retries, alerting, a way to replay failed records). If you have not used Boomi, describe the same two halves for the integration tool you have used.
Defining Angular components and services as terms and stopping there
Say what each is for: a component owns a template and its view logic and has a lifecycle; a service holds shared logic or state and is injected, so several components can use one instance. Give a case where you moved logic out of a component into a service.
Claiming a performance or reliability improvement on AWS or a system bottleneck with no way of showing it
Walk through measure, locate, change, re-measure. Name the signal you used (metrics, logs, query plans), the one change you made, and the number that moved.
Saying 'we' throughout a project discussion so your own contribution is unclear
For each resume project, write the part you owned, the decision you made alone and the one you argued for. Be ready to explain the architecture and the hardest challenge in detail.
Questions & practice
Archive a resource graph without breaking live references or recursing
Medium · graph traversal · topological order · tenant isolation
Resources reference other resources within a tenant; for the largest tenant the reference table holds up to 2,000,000 nodes and 8,000,000 edges. Archiving a resource must archive everything reachable from it that nothing outside the set still references, refuse when a live external referrer exists, and terminate when references form cycles, which they legitimately do. Produce the archive order and the refusal list, targeting O(V+E). Say what stops the traversal crossing a tenant boundary, and why recursion is the wrong control structure at this size.
Approach
- Load the subgraph with the tenant predicate on both endpoints of the edge, not only on the side you started from. Scoping the left table alone is the classic cross-tenant leak: one mis-entered edge then pulls another tenant's resources into the traversal and, worse, into the archive.
- Traverse iteratively with an explicit stack. A 2,000,000-node graph can hold a chain deep enough to exhaust a native stack in the low tens of thousands of frames, and that failure is a process crash rather than an error you can return.
- Treat cycles as data rather than corruption: compute strongly connected components with Tarjan in O(V+E) using its own explicit stack, then condense. The condensation is a DAG, so a topological order over it gives the archive order, and every member of a component archives in one transaction because no order within a cycle is valid.
- Decide refusals with reverse edges. A candidate is archivable only if every in-edge originates inside the candidate set, so build the transpose or count in-degrees restricted to the visited set, and emit each blocked resource with the id of the external referrer, which is the only part of the answer an operator can act on.
Follow-up
- The graph is read in one query and the archive writes a minute later. What can change in between, and how do you make the write safe?
- The candidate set is 400,000 resources. Is that one transaction, and if not, what does a half-finished archive look like to a reader?
Canonicalise a request body into a stable idempotency fingerprint
Medium · parsing · canonicalisation · hashing
idempotency_key.request_fingerprint is a SHA-256 over the method, path and canonicalised body, and a retry whose fingerprint differs must be rejected with 422 rather than served the stored response. Write the canonicaliser. Bodies are JSON up to 256 KB nested at most 32 levels; clients vary key order, whitespace and unicode escaping, and some send 64-bit ids as JSON numbers. Produce a deterministic byte string such that semantically identical bodies match and any semantic difference does not. State your complexity and name two normalisations you refuse to perform.
Approach
- Parse once into a tree, then re-serialise under fixed rules: object keys sorted, array order preserved, one escaping convention, no insignificant whitespace. Parsing is O(n) and sorting keys is O(k log k) per object, so O(n log n) overall with O(depth) stack, and the 32-level cap is enforced during parsing because hostile nesting is how a canonicaliser becomes a stack overflow.
- Sort keys by their UTF-8 bytes and say why the obvious implementation is wrong in some runtimes: a default string comparison that orders by UTF-16 code units places surrogate pairs, meaning code points from U+10000 up, below U+E000 to U+FFFF, which is not UTF-8 byte order, so two services written in different languages disagree on the same document.
- Do not re-encode numbers through a double. IEEE-754 binary64 represents integers exactly only up to 2^53, so normalising a 19-digit id through a float changes it, and 1 against 1.0 cannot be reconciled without deciding whether they are the same value. Preserve the literal token, and require ids as strings at the API boundary if you want them comparable.
- Reject duplicate keys rather than picking one. JSON permits them and parsers disagree, most keeping the last, so any choice you make ties the fingerprint to a parser detail that the code handling the request does not necessarily share.
Follow-up
- A client sends the same logical request with an extra field your API ignores. Same key, different fingerprint, so you return 422. Is that the right answer?
- Where does the fingerprint get computed relative to request decompression and the body-size limit?
Identify the heaviest tenants in a five-minute window under memory pressure
Medium · top-k · heavy hitters · streaming
The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.
Approach
- Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
- Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
- State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
- Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.
Follow-up
- The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
- Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
Write the update path that detects a concurrent edit
Medium · optimistic concurrency · transactional outbox · audit history
resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.
Approach
- One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
- Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
- State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
- Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.
Follow-up
- A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
- Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?
How do you manage data mapping and error handling within a Boomi integration process?
Medium · Technical & Domain Proficiency
Approach
- Mapping: define the source and target profiles (XML, JSON, flat file or database) and map them in a Map shape. Use map functions for type and date conversion, a cross-reference table for code lookups such as status or country codes, and default values for target fields that are optional upstream.
- Validate before anything is written. A Decision or Business Rules shape checks required fields and formats, and documents that fail are sent down a separate path with the reason attached. Do not let them fail inside the connector call.
- Errors: wrap the connector calls in a Try/Catch shape so each failing document goes to the Catch path while the rest of the batch continues. Log the error message with a business key, then route the failed document to a store you can replay from, such as a database table, queue or disk folder. Use an Exception shape only when the whole run must fail.
- Retries: set a retry count on the Try/Catch only for transient faults such as timeouts and 5xx responses. Make the target write idempotent (an upsert on an external ID) so a retried document cannot create a duplicate. Do not retry validation failures.
- Edge cases to name: null versus empty fields, time zones in date conversions, repeating elements that need looping in the map, character encoding, and a partial batch where some documents succeed. For incremental pulls, keep a high-water mark in a persisted process property.
Follow-up
- The target system accepted half of a batch and then timed out. How do you reprocess without creating duplicates?
- How would you let a support team see and replay failed documents without opening the process in Boomi?
- Where do you keep code mappings that change often, and how do you deploy a change to them?
What are the key differences between Angular components and services, and when would you use each?
Medium · Technical & Domain Proficiency
Approach
- A component is a class marked with @Component that has a template and styles. It owns one piece of the view, gets its data through inputs and sends events through outputs, and it is created and destroyed with its DOM element, with lifecycle hooks such as ngOnInit, ngOnChanges and ngOnDestroy.
- A service is a class marked with @Injectable that Angular's dependency injection supplies. It holds logic and state that do not belong to any one view: HttpClient calls, caching, shared state in a BehaviorSubject or a signal, and logging.
- Scope matters. providedIn: 'root' gives one tree-shakable instance for the whole app. Listing the service in a component's providers array gives each component instance, and its children, their own copy. Providers on a lazy-loaded route get a child injector, and that is a common source of an accidental second instance.
- Rule I follow: components stay thin and deal only with presentation, and anything I would want to unit-test without rendering, or share between two components, goes into a service. Sibling components share state through a service instead of chaining @Output and @Input through their parent.
- Edge cases: subscriptions in a component must end on destroy (the async pipe or takeUntilDestroyed). A singleton service holding user state has to be reset on logout.
Follow-up
- Two sibling components must show the same live data. Where does the state live, and how do they subscribe to it?
- When would you deliberately provide a service at component level instead of root?
- How does OnPush change detection change the way a component should consume data from a service?
How have you utilized AWS services to improve the performance or reliability of a cloud-native application?
Medium · Technical & Domain Proficiency
Approach
- Start with the symptom and the metric: p95/p99 latency, error rate or availability from CloudWatch or your APM, and the value before you made the change. Without a baseline the story cannot show any improvement.
- Performance changes, each with its trade-off: CloudFront for static or cacheable responses, ElastiCache (Redis) in cache-aside mode for hot reads (staleness and invalidation become your problem), RDS read replicas for read-heavy queries (replication lag), and query or index fixes before any of these.
- Reliability changes: an Auto Scaling group behind an Application Load Balancer with health checks, RDS Multi-AZ for automatic failover (the standby takes no reads in a standard Multi-AZ instance deployment), and SQS between producer and consumer so a slow downstream does not fail the caller.
- With SQS, explain the delivery semantics. Standard queues deliver at least once, so consumers must be idempotent. A dead-letter queue with a maxReceiveCount stops a poison message from cycling forever, and the visibility timeout has to be longer than the processing time.
- Finish with the result in numbers and the cost: what latency or error rate moved to, what the change added to the bill, and what you would watch next.
Follow-up
- How did you invalidate the cache when the underlying data changed?
- What happens to messages in flight if a consumer instance dies halfway through processing?
- Which CloudWatch alarm would have caught the original problem earlier?
How do you approach designing a new module from scratch to ensure it remains maintainable?
Medium · Problem-Solving & Architecture
Approach
- Before writing code, pin down the module's job and its public interface: what callers pass in, what they get back and which errors they can receive. A small, stable interface is what keeps the code behind it cheap to change.
- Keep domain logic apart from I/O. Business rules go in plain classes, and the database, HTTP clients and queues sit behind interfaces, so tests can replace them and a storage change does not reach the rules.
- In .NET, wire this with constructor injection through IServiceCollection and choose lifetimes on purpose (Transient, Scoped, Singleton). Avoid captive dependencies: a Scoped DbContext injected into a Singleton outlives its request and is shared across threads. ASP.NET Core's scope validation catches this in Development.
- Tests at the boundary: unit tests for the rules, plus a few integration tests against a real database or a test container for the data access. Write down the main decisions (a short design record) so the next engineer knows why things are the way they are.
- Do not build abstractions in advance. I wait until a second or third real case before generalising, and I name the one place I expect the module to grow.
Follow-up
- The module now has three callers who want slightly different behaviour. How do you change it without breaking the other two?
- How do you version its interface if another team depends on it?
- What would you test against a real database rather than a mock, and why?
When faced with a system bottleneck, what is your step-by-step process for identification and resolution?
Medium · Problem-Solving & Architecture
Approach
- Define the symptom precisely first: which endpoint or job, which percentile (p95/p99 rather than the average), since when, and at what load. Then reproduce it, or find it in production metrics, before changing anything.
- Find where the time goes. Use tracing or APM spans across the request path, then check each resource for utilization, saturation and errors (CPU, memory, thread pool, DB connections, disk, network). In .NET, dotnet-counters shows thread-pool queue length and GC activity, and dotnet-trace or a profiler shows hot paths.
- Common causes to check in order: slow queries (read the execution plan, look for missing indexes and N+1 query patterns), connection-pool exhaustion, sync-over-async calls (.Result or .Wait()) that starve the thread pool, lock contention, and chatty calls to downstream services.
- Change one thing at a time and measure under the same load. Little's Law (L = λW) helps size pools and queues: concurrency needed equals arrival rate times time in the system.
- Fix by category (index, cache, batch, async I/O, scale out), confirm the percentile moved, then add an alert or a load test so the regression is caught next time.
Follow-up
- CPU is low but latency is high. What do you look at next?
- How do you tell whether the database or the application is the bottleneck when both look busy?
- How would you load-test the fix safely before it reaches production?
How do you balance the need for rapid feature delivery with the necessity of writing clean, scalable code?
Medium · Problem-Solving & Architecture
Approach
- Sort decisions by how hard they are to reverse. Public interfaces, database schemas and integration contracts with other systems get care up front because changing them later is expensive. Internal implementation behind a stable interface can ship simple and be improved later.
- Name what keeps speed safe: automated tests on the critical path, CI that runs them on every change, code review, and feature flags so unfinished work can ship turned off instead of sitting on a long-lived branch.
- When I take a shortcut, I write it down as a tracked ticket with the reason, an owner and the trigger for paying it back (for example, when a second client needs the feature). Debt that nobody records never gets repaid.
- Give a concrete example: what you deferred, why the deadline justified it, and when and how you paid it back. Include what it cost if it bit you.
- Avoid both extremes. Gold-plating code for scale nobody has asked for delays delivery, and skipping tests on code that handles money or data integrity only moves the cost to production.
Follow-up
- Tell me about a shortcut that came back to hurt you. What did it cost?
- How do you convince a project manager to schedule time for refactoring?
- When, if ever, is it acceptable to ship without tests?
Listing latency scales with page size, not with filters
Easy · n+1 · latency · query batching
The tenant listing endpoint reads resource filtered by tenant_id and status, ordered by updated_at DESC, and returns each row plus the owner's display name from app_user and the actor of that resource's latest resource_revision. p99 is 55 ms at 10 rows per page and 1.4 s at 200. Database telemetry shows 401 statements per request, each under 1 ms, and nothing in the slow-query log. Diagnose the cause and give the fix, stating the statement count per request and the p99 you expect afterwards.
Approach
- Read the counters before forming a theory. 401 statements for 200 rows is one driver query plus two per row, and sub-millisecond execution with an empty slow-query log rules out a bad plan. The time is round trips, which is why it is invisible in every per-query metric and scales with rows returned rather than with filter selectivity.
- Name the two per-row statements from their normalised text: a single-row app_user lookup by user_id, and a resource_revision lookup by resource_id ordered by version DESC LIMIT 1. Confirm by dropping those two response fields and watching the statement count fall to one. That locates the calls in the serialisation layer, not the repository.
- Check that the arithmetic accounts for the whole gap. Measure one round trip to the replica in isolation; 400 trips at roughly 3 ms of network plus 0.2 ms of execution is about 1.3 s on top of a 55 ms baseline, which matches. If the multiplication had fallen short, the N+1 would only be part of the story and you would keep looking.
- Batch both lookups. Collect owner_user_ids and resource_ids from the driver query, then issue WHERE tenant_id = $1 AND user_id = ANY($2) for the users, and PostgreSQL's SELECT DISTINCT ON (resource_id) ... WHERE resource_id = ANY($2) ORDER BY resource_id, version DESC for the latest revision, which the UNIQUE (resource_id, version) index serves directly. On an engine without DISTINCT ON, use a lateral join or a row_number window. Three statements per request at any page size.
Follow-up
- The page size is capped at 200 today. What breaks first if it is raised to 2,000, and is it still this bug?
- How do you stop the next N+1 from reaching production, given that no individual query is slow and the endpoint's tests pass?
Suggested practice checklist
Day 1 — Inventory your stack and map the loop
- List every technology from the role's job description and mark which you have used in delivered work: .NET, Angular, AWS, Boomi, ServiceNow, GIS.
- Pick the two projects you will anchor discussions on and write architecture, your contribution, hardest problem and result for each.
- Write which of the two rounds (Technical Screening, Specialized Discussions) your weakest area is most likely to come up in.
Deliverable: A stack inventory with used and unused marked, and a one-page summary of each of your two anchor projects.
Day 2 — Deepen .NET and tell the scalability story
- Explain dependency injection and inversion of control from memory, including service lifetimes and the bug a wrong lifetime causes.
- Explain async/await on an I/O path and the thread-pool problem of blocking on async code.
- List the security basics for a web API you can speak to (authentication, authorisation, input validation, secrets handling), then write and rehearse the walk-through of scalable .NET work.
Deliverable: A written outline of the .NET walk-through story and a half-page of spoken-answer notes on dependency injection, async and API security.
Day 3 — Integration: mapping, errors and the troubleshooting story
- Outline a Boomi-style integration process end to end: source, field mapping and type conversion, a failure branch, retries, alerting and replay of failed records.
- Write the story of the hardest integration bug you have fixed using the sequence reproduce, trace, localise, fix, prevent.
- Work through the practice question on turning a request body into a stable idempotency fingerprint, since retries in integrations cause duplicates.
Deliverable: A one-page integration outline with the failure path drawn, a written troubleshooting story and a solved idempotency exercise.
Day 4 — Angular components, services and GIS front ends
- Write a comparison of components and services with a real example of logic you moved from one to the other.
- List the lifecycle hooks you can explain and what you do in each, plus three performance techniques you have used (change detection strategy, trackBy, lazy loading, avoiding heavy work in templates).
- If your role involves GIS, write how you would handle a map library with many features on screen and how you would keep it responsive.
Deliverable: A written components-versus-services answer with an example, and a list of lifecycle hooks and performance techniques with one use case each.
Day 5 — AWS and the bottleneck method
- Write an account of one AWS change that improved performance or reliability, naming the problem, the service you used, the trade-off and the measured result.
- Write your step-by-step bottleneck process: reproduce, measure, locate, change one thing, re-measure.
- Work through the practice debugging question on listing latency that scales with page size, and note which of your process steps it exercised.
Deliverable: A written AWS story with a measured result, a bottleneck checklist and a solved debugging exercise.
Day 6 — Design for maintainability and delivery pressure
- Outline how you would design a new module: boundaries, interfaces, dependency direction, tests, configuration and logging, with one example from your own work.
- Write your answer to balancing rapid feature delivery with clean, scalable code: what you ship fast, what you refuse to skip, and how you record technical debt.
- Complete the practice behavioral exercise on arguing against a design, losing and committing anyway.
Deliverable: A one-page module design checklist, a written delivery-versus-quality answer with an example, and a finished behavioral practice answer.
Day 7 — Full dry run for both rounds
- Run a full spoken mock of the screening topics (your stack questions) followed by a project deep dive on your two anchor projects.
- Answer the .NET walk-through and the integration troubleshooting prompts aloud, then listen back and cut any sentence that names a technology without a decision.
- Write the two questions you will ask the team about their current technical challenges.
Deliverable: A completed mock with a list of the three weakest answers to fix, and two questions to ask the team.
Behavioural preparation
Candidates describe the second round as conversations about past work and your potential contribution to projects, and two of the reported questions are experience prompts. For this role the stories must show hands-on decisions on real systems: building something that held up under load, and diagnosing a failure that crossed a system boundary. Use situation, task, action and result, but spend most of the time on the action and the evidence.
- Can you walk me through your experience building scalable applications using the .NET framework?
- How do you balance the need for rapid feature delivery with the necessity of writing clean, scalable code?
- Tell me about a project on your resume where you owned a specific part end to end: the architecture, the hardest challenge and your own contribution.
- Describe a time you explained a technical trade-off to a non-technical colleague or a project manager and what they decided.
Can you walk me through your experience building scalable applications using the.NET framework?
Medium · Technical & Domain Proficiency
Approach
- Choose one or two real .NET systems and open with context only: what the application did, the data or traffic it handled, and your role, in two or three sentences. Spend the rest of the answer on decisions you made, not a tour of frameworks.
- Define 'scalable' with something you lived with: requests per second, table size, tenants, concurrent users or batch volume. If you have no hard figures, name the constraint you designed against rather than inventing a number.
- Name concrete .NET mechanisms and why: async/await on I/O paths to avoid thread starvation, dependency injection with correct lifetimes (scoped database context, shared HTTP clients through a factory), EF Core or SQL tuning (projection, no-tracking reads, avoiding N+1 queries, indexes, pagination), and caching with an invalidation rule.
- Include one thing that did not scale and how you found it (profiler, query plan, logs, load test), the change you made and the measured before and after. That evidence is what turns 'I built scalable apps' into a credible answer.
- Close with what you would change now. The mistakes that sink this answer are listing technologies without a decision, claiming the team's work as yours, and using 'scalable' with no measurement behind it.
Follow-up
- What happens if a request handler blocks on an async call with .Result or .Wait(), and how did you avoid it?
- How did you choose the lifetime of that service in dependency injection, and what bug does a wrong lifetime cause?
- What would break first if the load grew tenfold, and how would you find out before it did?
Describe a time you had to troubleshoot a complex integration issue between two disparate systems.
Medium · Technical & Domain Proficiency
Approach
- Set the scene briefly: the two systems, how they communicated (REST, SOAP, files, a queue, an integration platform), the symptom (missing or duplicated records, wrong values, intermittent timeouts) and who was affected.
- Narrate the debugging sequence: reproduce with one failing record, then follow it hop by hop using correlation ids, logs and captured payloads. Compare what system A sent with what system B received to localise the fault to the producer, the transformation, the transport or the consumer.
- Name the usual suspects you checked and ruled in or out: data types and date or time zone formats, null or empty handling, character encoding, expired credentials, rate limits and timeouts, retries that created duplicates, and ordering assumptions.
- Separate the fix from the prevention: the root-cause change, then what you added so it cannot recur silently (validation at the boundary, idempotent processing, alerting, a replay path for records that already failed, a contract test).
- State the result and your part in it, including how you worked with the owners of the other system. The story fails when it is 'I restarted it' or 'the other team's system was broken' with no evidence.
Follow-up
- How did you prove the fault was in the data mapping rather than in the transport?
- What did you do about the records that had already failed before the fix?
- How did you make sure the same class of failure would be caught earlier next time?
Argue against a design, lose, and commit anyway
Medium · disagreement · service boundaries · decision records
Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.
Approach
- State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
- Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
- Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
- Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
- What threshold on that alert would have proved you right, and did anyone ever look at it?
- If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?