cswork / libraryOpen practice workspace

Interview guides · Altium Limited

Altium Limited Software Engineer Interview Guide

Altium Limited Software Engineer interview guide: three reported rounds, C++/C# threading, UI layout, auth, and behavioral prep with a 7-day plan.

The 60-second brief

Altium Limited makes PCB design software. Its products named in candidate reports are Altium Designer, a desktop application, and Altium 365, a cloud-connected platform for collaborative work on electronics designs. The users are electrical engineers, designers and manufacturers. The Software Engineer role covers work on both sides: desktop concerns such as UI rendering, geometric algorithms and multi-threaded architecture, and cloud concerns such as services and infrastructure.

This guide is for candidates applying as a Software Engineer at Altium Limited, most likely with a C++ or C# background, since those are the languages the requirements list. It covers the three reported rounds, the technical and coding questions candidates describe (threading, .NET internals, authentication and authorization, UI layout, cloud cost, API lookups, class lifetimes, request headers, closures), the behavioral questions, and a seven-day plan.

Candidates report three rounds over roughly 3-5 weeks: Recruiter Screening, Technical Assessment (which may include a take-home assignment) and Leadership Review. The sequence is a candidate description, not a published process.

Focus areas

  • Altium Designer (AD) knowledge
  • Authentication vs Authorization
  • Multi-tenant system development

Overview

The role as candidates describe it sits between a desktop application and a cloud platform. The work named includes UI and rendering, geometric algorithms, multi-threaded architecture and cloud services. The required skills are C++ or C#, data structures and algorithms, and system-level programming. AWS or Azure experience, familiarity with PCB design software, and multi-threaded high-performance work are listed as extras. So your preparation should be deep in one language rather than wide across many.

The reported questions follow the same split. Several are about how things work underneath: synchronizing multi-threaded asynchronous code, how managed .NET threads relate to native OS threads, the order of constructors and destructors when an exception is thrown, and how closures capture state. Others are design and web questions: authentication versus authorization, a layout system with measure and layout passes, finding a category from an ID through an API, request headers, and cloud cost. Candidates also report multi-tenant system development, code review or static analysis, and time and memory constraints as topics. Be ready to justify your design choices and to state the Big O cost of whatever you write.

The Technical Assessment may include a take-home assignment, and it can lead into follow-up technical discussion. Be ready to defend your code line by line afterwards. Treat the take-home as the opening of a conversation: write code you can explain, name the edge cases (empty, very large and malformed input), and keep a short list of what you would change with more time.

The Leadership Review is reported as a cultural-fit and leadership stage. The behavioral questions candidates list are a career walk-through, management style with cross-functional teams, a tricky bug, what you know about the company's products, and where you see yourself in five years. Prepare real episodes with measured results, and read up on Altium Designer and Altium 365 before the first call.

The Altium Limited interview process

Candidates describe three stages, and each asks for different preparation: a concise career story and product summary for the screen, the take-home plus C++ or C# internals for the technical round, and measured stories for the Leadership Review.

Recruiter Screening

Candidates describe Recruiter Screening as the first gate, an initial check of fit and qualifications. Reports say remote-work and office expectations vary by team and region, so use this call to clarify them. Go in with a tight account of your background and a specific reason for wanting a role on desktop and cloud engineering tools.

What to demonstrate

  • Candidate fit, as candidates describe this first stage
  • Qualifications against the listed requirements (C++ or C#, algorithms, system-level programming)

How to prepare

  • Rehearse the resume walk-through as a story with a reason behind each move, ending on why this role.
  • Read about Altium Designer and Altium 365 so you can say what each is and which part of the stack you would want to work on.
  • Write down your language strengths (C++, C#, or JavaScript) and one project for each where you handled threading, memory or performance.
  • Clarify location and work-arrangement expectations, and ask whether a take-home is part of the loop and who the later rounds involve.

Technical Assessment

Candidates describe the Technical Assessment as an in-depth technical evaluation that may include a take-home assignment, with the code used as the basis for follow-up discussion. The technical and coding question types candidates report are the material to practise for this stage: threading and synchronization, .NET versus native threads, C++ object lifetimes with exceptions, authentication versus authorization, UI layout passes, cloud cost, API lookups, request headers, closures, and an algorithm under memory and time limits. Approach it by talking through your reasoning aloud and stating complexity and edge cases without being asked.

What to demonstrate

  • Working code that handles empty, large and malformed input
  • Command of your main language: memory management, threading and performance trade-offs
  • Complexity analysis and the reasoning behind the data structure you chose
  • Ability to explain and defend a take-home solution

How to prepare

  • For threading, write a one-page sheet: a race, a deadlock, and which primitive (lock, atomic, semaphore, concurrent queue, async/await continuation) fixes each, in C++ or C#.
  • Trace constructor and destructor order in a three-level C++ hierarchy when a base constructor throws, and check with a compiler.
  • Solve one problem where the input does not fit in memory (streaming, external sort, or a bounded heap) and state time and space cost.
  • If you receive a take-home, keep a notes file of design choices and a list of improvements, so you can defend every line.

Leadership Review

Candidates describe Leadership Review as the final assessment, centred on cultural fit and leadership qualities. The behavioral questions candidates list are good practice for this stage: management style and cross-functional work, a hard bug, what you know about the products, and your five-year direction. Approach it with three or four stories you can adapt, each with a decision you personally made and a result you measured.

What to demonstrate

  • Leadership qualities and cultural fit, as candidates describe the stage
  • Experience working with product managers, domain experts and other engineering teams
  • Motivation for the company and knowledge of its products
  • Communication of technical ideas to non-technical colleagues

How to prepare

  • Write each story as situation (two sentences), your decision, the evidence you used, and the result with a number you measured.
  • Prepare one cross-functional story where a requirement and a technical constraint conflicted and you resolved it with data.
  • Pick a bug story with a real root cause, how you isolated it, and the check that now prevents it.
  • Write a five-year answer tied to concrete skills (for example performance engineering or architecture) rather than a job title.

Where candidates lose points

Describing threading in general terms without naming the primitive or the failure

For each concurrency topic, practise a concrete case: the race, the lock ordering that deadlocks, and the fix. In C#, be able to say how a managed thread maps onto a native OS thread. In C++, know when to use a mutex, an atomic or a concurrent queue.

Guessing the output of a class hierarchy with constructors, destructors and exceptions

Learn the rules: base constructs before derived, destruction runs in reverse, a throwing constructor means that object's destructor never runs while fully built members and bases are still destroyed, and virtual calls in constructors do not dispatch to the derived class. Compile small examples and compare to your prediction.

Submitting a take-home you cannot explain line by line

Keep a notes file of every design choice, the alternatives you rejected, the edge cases covered and what you would change with more time. Reread your own code the day before the discussion.

Treating authentication and authorization as one idea, or defining them only by textbook

Give each a definition, a flow, and a failure mode: a token that proves who you are versus a check on what you may do. Add how this plays out in a multi-tenant system, where every query and cache key needs a tenant check.

Giving behavioral answers with no measured result

For every story, choose a number you actually recorded, such as latency, memory, build time or defect count, and say how you measured it. If the result is not a number, state the observable change and the evidence for it.

Questions & practice

How do you synchronize multi-threaded asynchronous algorithms?

Medium · Technical Competency and System Design

Approach

  1. Start by cutting shared mutable state: split the input so each task owns its slice, pass immutable data in, and combine results at a single join point (Task.WhenAll in C#, std::future::get in C++).
  2. Where state must be shared, keep critical sections short. In C# you cannot await inside a lock statement (compile error), so use SemaphoreSlim(1,1) with WaitAsync. In C++ use std::mutex with std::lock_guard, or std::scoped_lock when you need several mutexes.
  3. For producer-consumer pipelines, use a bounded queue (System.Threading.Channels, BlockingCollection, or a mutex plus condition variable). A full queue gives backpressure. Always wait on a condition variable with a predicate loop, because spurious wakeups happen.
  4. Use atomics (Interlocked.Increment, std::atomic) for counters and flags instead of a lock, and say which memory ordering you rely on. Only go lock-free where profiling shows the lock is the bottleneck.
  5. Deadlock rules: take locks in one global order, never hold a lock while awaiting or calling unknown code, and avoid sync-over-async (.Result or .Wait()), which can deadlock on a UI thread's SynchronizationContext. Propagate a CancellationToken so stopped work releases its resources.

Follow-up

  • Why does calling .Result on a Task from a UI thread hang, and what are the two ways to fix it?
  • How would you find a deadlock or race in a running process: thread dumps, ThreadSanitizer, stress tests?
  • When would you choose a lock-free structure over a mutex, and what does it cost you?

What are the correlation principles between managed.NET threads and native OS threads?

Medium · Technical Competency and System Design

Approach

  1. A managed System.Threading.Thread is the CLR's object for a thread. On .NET Core and .NET 5+ each one maps 1:1 to an OS thread. The documentation still makes no permanent guarantee, because older CLR hosts such as SQL Server could schedule managed threads on fibers.
  2. ManagedThreadId is not the OS thread id. Getting the native id needs a platform call such as GetCurrentThreadId on Windows. When native code calls into managed code on its own thread, the runtime creates a Thread object for it on first entry.
  3. A Task is not a thread. Tasks run as work items on reused thread-pool threads, and after an await a continuation can resume on a different OS thread. So [ThreadStatic] values can appear to vanish, while AsyncLocal<T> flows with the ExecutionContext.
  4. Lifetime: the process exits when all foreground threads finish, and background threads (including every pool thread) are stopped. UI threads in WinForms and WPF must be STA, which matters for COM interop.
  5. GC interaction: the runtime suspends managed threads at safe points. A thread blocked inside a P/Invoke call is in preemptive mode and can keep running during a GC until it returns to managed code, which is why pinned buffers matter.

Follow-up

  • What happens to thread-local data across an await, and how does AsyncLocal<T> differ from [ThreadStatic]?
  • How does the thread pool react when work items block, and what symptoms does pool starvation produce?
  • Why must a buffer passed to native code be pinned, or allocated outside the GC heap?

Solve an algorithmic problem involving memory and time constraints (e.g., processing large datasets).

Medium · Coding and Algorithmic Logic

Approach

  1. Turn the limits into a budget first. With n around 10^8, only O(n) or O(n log n) work fits. Memory decides whether a hash map of every key fits: estimate the bytes per entry (key, value and container overhead) times the number of distinct keys.
  2. Prefer a single streaming pass with buffered, chunked reads. For example, top-k uses a min-heap of size k: O(n log k) time and O(k) memory, instead of sorting everything. If your example involves geometry, such as checking overlap or clearance among millions of PCB primitives, use a spatial index (an R-tree or a uniform grid) instead of O(n^2) pairwise tests, and state the memory the index itself costs.
  3. When the data cannot fit in memory: external sort (sort chunks into runs on disk, then a k-way merge with a heap, O(n log n) overall), or hash partitioning (write each record to file hash(key) mod p, then process each partition in memory).
  4. Use compact representations: a bitset for presence (10^9 bits is about 125 MB), a counting array when the key range is small, struct arrays instead of object lists in C# to avoid per-object overhead, and vector::reserve in C++ to avoid repeated reallocation.
  5. If exact answers cost too much, name approximate structures and their error: a Bloom filter (false positives, never false negatives), Count-Min sketch (overestimates frequencies), HyperLogLog (distinct counts with a small relative error).

Follow-up

  • What changes if the input is a stream you can read only once?
  • How would you parallelise this across cores, and where do the partial results get merged?
  • How would you check the output is correct on an input too large to inspect by hand?

Explain the behavior of a class hierarchy involving constructors, destructors, and exception handling.

Medium · Coding and Algorithmic Logic

Approach

  1. Construction order: virtual bases first, then direct bases in declaration order, then members in the order they are declared in the class (not the order of the initializer list), then the constructor body. Destruction runs in exactly the reverse order.
  2. If a constructor throws, that object's destructor does not run. Only the base subobjects and members already fully constructed are destroyed, in reverse order. So a raw new in the constructor body leaks if a later step throws. Hold resources in RAII members such as std::unique_ptr.
  3. Destructors are implicitly noexcept since C++11. An exception escaping a destructor calls std::terminate, and so does one thrown during stack unwinding, so catch and log inside the destructor.
  4. Deleting a derived object through a base pointer needs a virtual destructor, otherwise the behaviour is undefined. Inside a constructor or destructor, virtual calls dispatch to the class currently being built or torn down, not to the derived override.
  5. Walk through a printed example aloud: Base ctor, Member ctor, then a throw in the Derived body; then name the exact output. If the language is C#, note that cleanup is deterministic only through IDisposable and using, and that finalizers run at the GC's discretion.

Follow-up

  • A member's constructor throws halfway through the initializer list: which destructors run, and in what order?
  • Why is calling a virtual function from a base-class constructor a bug waiting to happen?
  • How would you make a class that owns two raw resources exception-safe without writing try/catch?

Describe how request headers work in a web development context.

Medium · Coding and Algorithmic Logic

Approach

  1. Describe the structure: an HTTP/1.1 request is a request line, then header fields as case-insensitive name: value pairs, a blank line and an optional body. HTTP/2 sends the same fields as lowercase names compressed with HPACK (HTTP/3 uses QPACK).
  2. Group the common headers by job. Routing: Host, required in HTTP/1.1. Body: Content-Type and Content-Length. Negotiation: Accept, Accept-Encoding, Accept-Language. Identity: Authorization (for example a Bearer token) and Cookie. Caching: If-None-Match with an ETag, or If-Modified-Since, which lets the server answer 304 Not Modified.
  3. CORS: the browser adds Origin. A request with a non-safelisted header or content type (application/json, a custom header) triggers an OPTIONS preflight with Access-Control-Request-Method and Access-Control-Request-Headers, and the server must answer with matching Access-Control-Allow-* headers. Browsers forbid scripts from setting some headers, such as Host and Cookie.
  4. Server side: headers are untrusted input. Trust X-Forwarded-For only when it was set by your own proxy. Servers enforce size limits (431 Request Header Fields Too Large). If a response varies by a request header, send Vary so caches key on it.
  5. Keep secrets out of URLs, which end up in logs and browser history. Send them in Authorization, and pass a correlation id header through every downstream call for tracing.

Follow-up

  • Why does a fetch with a JSON body trigger a preflight when a plain form post does not?
  • How does a CDN decide whether two requests with different headers can share one cached response?
  • How would you carry a request id across a chain of services and into the logs?

Explain closures in JavaScript or other language-specific fundamentals.

Medium · Coding and Algorithmic Logic

Approach

  1. Definition: a closure is a function together with the lexical environment where it was created, so an inner function keeps access to outer variables after the outer function returns. It captures the variable binding, not a copy of the value.
  2. Show the classic loop: with var, for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) prints 3, 3, 3, because var is function-scoped and every callback shares one i. With let, each iteration gets a fresh binding and it prints 0, 1, 2.
  3. Give the uses: private state (a counter factory returning increment and get), the module pattern, memoisation caches, partial application and event handlers. In React, a stale closure is an effect or callback that captured an old state value.
  4. Memory cost: a closure keeps its captured environment reachable, so a long-lived listener or timer holding a large object leaks it until the listener is removed.
  5. Compare with other languages. C# lambdas capture variables through a compiler-generated class; since C# 5 foreach gets a fresh variable per iteration but a for loop still shares one. C++ lambdas capture explicitly with [=] or [&], and capturing by reference dangles if the lambda outlives the scope.

Follow-up

  • Write a counter factory and explain where the count is stored between calls.
  • What is a stale closure in a React effect, and what are two ways to fix it?
  • How does the loop example behave in C# with for compared with foreach?

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

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

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

Can you explain the difference between authentication and authorization?

Medium · Technical Competency and System Design

Approach

  1. Authentication establishes who the caller is (password plus MFA, client certificate, OpenID Connect sign-in) and produces a principal, session or token. Authorization decides whether that principal may perform a specific action on a specific resource.
  2. Map them to HTTP: a missing or invalid credential is 401 Unauthorized with a WWW-Authenticate header. An authenticated caller without permission gets 403 Forbidden, or 404 when you do not want to reveal that the resource exists.
  3. Keep the protocols straight: OAuth 2.0 is delegated authorization (access tokens carrying scopes), and OpenID Connect adds authentication on top of it with an ID token. Never accept an ID token as an API access token.
  4. Authorization models: role-based (RBAC), attribute-based (ABAC) and per-resource ownership or ACLs. In a multi-tenant service, every check must also confirm the resource's tenant matches the caller's tenant, and that check runs on the server, not only in the UI.
  5. Token handling: verify the JWT signature, exp, iss and aud on every request. Keep access tokens short-lived with refresh tokens, and state the trade-off: a self-contained JWT cannot be revoked before expiry without a denylist or an introspection call.

Follow-up

  • How would you revoke a user's access immediately when your API trusts self-contained JWTs?
  • Where would you centralise authorization checks across dozens of endpoints without missing one?
  • How do you stop a caller in one tenant from reading another tenant's record by changing an id in the URL?

How would you design the layout system for a complex UI, including measure and layout passes?

Medium · Technical Competency and System Design

Approach

  1. Model the UI as a tree of elements with two passes. Measure goes top-down: each parent passes an available size to its children, and each child returns a desired size. Arrange goes top-down again: the parent gives each child its final rectangle. This is the shape of WPF's Measure/Arrange and Android's onMeasure/onLayout. For a desktop design tool such as Altium Designer, picture dockable panels and property grids around a large design canvas: the canvas is drawn by its own rendering path rather than the layout tree, so the layout tree only gives it a rectangle, and resizing a panel must stay responsive.
  2. Each container owns its own algorithm. A stack sums children on one axis. A dock fills the remaining space. A grid measures fixed and auto rows and columns first, then shares the remainder among star-sized tracks. Margins, padding and min/max clamps apply in both passes, and a scrolling axis passes infinite available size.
  3. Invalidation: setting a property marks the element measure-dirty or arrange-dirty. Measure-dirty propagates to ancestors whose size can depend on it. Batch all invalidations and run one layout pass per frame before rendering. Cache the last constraint and desired size so an unchanged subtree skips re-measuring.
  4. Performance traps: nested auto-sized containers can measure children repeatedly and go quadratic. Text measurement is expensive, so cache glyph and line metrics. Long lists need virtualization: only realise the items intersecting the viewport.
  5. Guard against cycles where a layout result changes a size that triggers another layout (scrollbars appearing, for example). Cap the iterations per frame and report it. Keep layout on the UI thread, or make the tree immutable if you move it off.

Follow-up

  • How do you lay out text whose height depends on the width it is given?
  • How would you virtualize 100,000 rows of variable height and keep the scrollbar accurate?
  • What stops a layout loop where showing a scrollbar shrinks the width and removes the need for the scrollbar?

How do you optimize cloud infrastructure costs?

Medium · Technical Competency and System Design

Approach

  1. Measure before cutting: tag resources by service, environment and tenant, then use the provider's cost reports (AWS Cost Explorer, Azure Cost Management) to rank line items. The bill is usually dominated by compute, managed databases, storage and data transfer.
  2. Right-size compute from utilisation (p95 CPU and memory), not from peak guesses. Autoscale on load, and shut down or scale to zero dev and test environments outside working hours.
  3. Match the pricing model to the workload: reserved instances or savings plans for the steady baseline, spot capacity for interruptible batch work with checkpointing, and on-demand for the unpredictable remainder.
  4. Storage and network: lifecycle rules that move cold data to cheaper tiers (S3 Standard-IA or Glacier, Azure Cool or Archive), deleting orphaned disks and snapshots, a CDN to cut egress, and keeping chatty services in the same zone, since AWS charges for cross-AZ transfer.
  5. State the trade-offs: redundancy across zones costs money but buys availability, and engineering hours spent saving small amounts are also a cost. Close with budgets and anomaly alerts so a regression is caught within days, not at month end.

Follow-up

  • The bill doubled overnight: what do you check first, and in what order?
  • How much reserved capacity would you commit to, and how do you avoid overbuying?
  • How would you split the cost of shared infrastructure fairly across tenants?

Find a category from an ID using an API.

Medium · Coding and Algorithmic Logic

Approach

  1. Clarify the contract first: which endpoint takes the id, the response shape, whether the category sits on the item directly or must be resolved through a categoryId and a parent chain, and what to return for an unknown id (a 404 mapped to null or a typed not-found result, not an exception for a normal case).
  2. Write the call as async with a timeout. Check the status code before parsing the JSON, and treat a missing or null category field as its own case. Retry only transient failures (5xx, and 429 honouring Retry-After) with exponential backoff and jitter. Never retry other 4xx responses.
  3. If categories form a tree and you need the top-level one, walk the parent ids iteratively with a visited set to detect cycles. Memoise id-to-category in a dictionary so each node is fetched once. One lookup then costs O(depth) calls cold and O(1) when cached.
  4. For many ids: deduplicate them, use a batch endpoint if one exists, and otherwise cap concurrency (SemaphoreSlim, or a promise pool in JavaScript) so you stay under the rate limit.
  5. Test against a mocked HTTP client: found, not found, malformed id, missing field, timeout, 429 then success, and a parent cycle.

Follow-up

  • How would you resolve 10,000 ids without tripping the API's rate limit?
  • How long can cached categories be trusted, and how would you invalidate them?
  • What should the function return if the API is down: an error, a stale cached value, or nothing?

Exports duplicate a row range about once a week

Hard · leases · clock skew · idempotency

Roughly once a week an export writes a file containing a duplicated range of rows. The affected job_run rows show attempt = 1, status = succeeded, one started_at, and a lease_owner naming a different host from the one whose logs show the job starting. Leases last 30 seconds and are heartbeated every 10 from inside the handler; lease_expires_at is computed on the worker and compared against the database's now(). Find the mechanism, and give a fix that holds even if you cannot fix the clocks.

Approach

  1. Start from the fact that eliminates the obvious answer. attempt = 1 means no retry was recorded, so this is not a re-run after failure; two workers ran the same row concurrently and the takeover path never touched the counter. lease_owner naming a host other than the one that started the job is the same statement from the other side.
  2. Enumerate the mechanisms that cause a premature takeover, then find the signal that separates them. Either the lease genuinely expired because the heartbeat did not fire, which is what happens when the heartbeat runs on the handler's own thread and the handler makes a long blocking call, or it only appeared expired because two clocks disagree, since lease_expires_at is written from the worker's clock and evaluated against the database's. The discriminator is the distribution: incidents clustered on the longest exports indict the heartbeat, incidents clustered on one host indict skew. Measure both, and measure each host's offset against the database directly.
  3. Read the reclaim query precisely. In PostgreSQL now() is transaction start time, not statement time, so a reclaimer holding a long transaction compares against an older timestamp than expected; clock_timestamp() is the statement-time function. This is worth ruling in or out before you redesign anything, because it changes which rows look expired.
  4. Remove the second clock rather than trying to synchronise it. Issue and extend the lease in the database, with lease_expires_at = now() + interval '30 seconds' in both the claim and the heartbeat, so exactly one clock is ever compared and worker skew stops mattering to this predicate.

Follow-up

  • The displaced worker has already streamed half the file to object storage. What makes that side effect safe to repeat?
  • You now count takeovers. What alert fires on that counter, and at what threshold?

Suggested practice checklist

The plan follows the three reported rounds and the questions candidates list: language internals and concurrency first, then algorithms, then web, security and design, then debugging and the take-home, then behavioral stories. Practice-library exercises on tenants and concurrent edits are added where they match the reported multi-tenant topic.

Day 1 — Map the loop and pick your core language

  • Write out the three reported rounds (Recruiter Screening, Technical Assessment, Leadership Review) and, under each, the question types you expect to practise.
  • Choose C++ or C# as your main language for the technical round and list five internals you can explain without notes: memory model, threading, lifetime, exceptions, performance.
  • Read about Altium Designer and Altium 365 and write a three-sentence summary of each in your own words.

Deliverable: A one-page loop map, a five-item internals checklist for your language, and the two product summaries.

Day 2 — Threads, synchronization and the .NET to OS mapping

  • Write a minimal race condition and a minimal deadlock in your language, then fix each with the right primitive and note the cost of the fix.
  • Write a half-page explanation of how a managed .NET thread relates to a native OS thread and what async/await does to thread use.
  • Explain aloud how you would synchronize several asynchronous algorithms that share state, in under two minutes.

Deliverable: Two runnable examples (race and deadlock, each with its fix) and a half-page note on the managed-native thread relationship.

Day 3 — Object lifetimes and algorithms under memory and time limits

  • Build a three-level C++ class hierarchy with constructors, destructors and a throwing constructor; predict the output order, then compile and check.
  • Solve one large-data problem two ways, in-memory and streaming or bounded-heap, and write time and space complexity for both.
  • Write the edge cases for each: empty input, one element, duplicates, malformed records.

Deliverable: A hierarchy program with a written prediction and its actual output, plus two solutions with complexity and an edge-case list.

Day 4 — Web fundamentals, API lookup and language basics

  • Write a short note on request headers: what Authorization, Content-Type, Accept, Cache-Control and a tenant or correlation header do, and who sets each.
  • Design the API call that finds a category from an ID: request shape, error cases for a missing ID, caching, and retry behavior.
  • Write three closure examples in JavaScript (loop capture, counter factory, stale value) and explain what each captures.

Deliverable: A headers cheat sheet, a short API contract with errors, and three annotated closure examples.

Day 5 — Authentication, authorization, multi-tenancy and layout design

  • Write a comparison of authentication and authorization, with a token flow and a per-tenant authorization check on every query.
  • Outline a UI layout system with a measure pass and an arrange pass: tree, size constraints, invalidation, and what runs on which thread.
  • Do the practice-library exercises on a per-tenant cap under concurrent creates and on detecting a concurrent edit.

Deliverable: A one-page security note, a layout-system diagram with both passes, and your solutions to the two tenant exercises.

Day 6 — Cloud cost, debugging, code review and defending code

  • List five cloud cost levers (right-sizing, storage tiers, autoscaling, egress, idle resources) with how you would measure each before changing it.
  • Work through the practice-library debugging exercise on duplicated export rows, writing your hypothesis list and the order you would test it in.
  • Take a solution you wrote earlier and review it as if it were a take-home: list its edge cases, its weak points and what you would change.
  • Run a static analyser on that solution (clang-tidy, cppcheck or Roslyn analyzers), triage what it reports, then review an unfamiliar snippet for bugs and performance problems.

Deliverable: A cloud-cost checklist with metrics, a debugging log for the exercise, a self-review, and a triaged static-analysis report.

Day 7 — Leadership Review rehearsal

  • Write the resume walk-through, the cross-functional and management-style story, and the five-year answer, each as situation, decision, evidence and result.
  • Write the tricky-bug story: symptom, isolation, root cause and prevention.
  • Prepare your product-knowledge answer and two questions to ask the team.
  • Say all five behavioral answers aloud, recorded, and cut anything that has no decision or number.

Deliverable: Four written stories, a product-knowledge answer, two team questions and one recorded run-through.

Stories for the leadership round

The Leadership Review is reported as a cultural-fit and leadership stage, and the reported questions ask for your history, your cross-functional work, a hard bug, your knowledge of the products and your five-year direction. For a Software Engineer in a desktop and cloud product, the strongest material shows decisions you owned, evidence you used and results you measured.

Walk me through your resume and describe your career path.

Medium · Behavioral and Career Alignment

Approach

  1. Build the answer as a spine of three or four moves in order. For each, give the reason you took the step and one thing you built or owned, so the story reads as decisions rather than a list of employers.
  2. Point the thread toward this role: C++ or C#, threading and performance, desktop or cloud work. Choose the projects that show those, and skip ones that do not.
  3. Attach one measured result to your strongest entry, such as latency, memory use or build time before and after. Use a figure you actually recorded, and say how you measured it.
  4. Explain gaps, short stints and changes of direction in a single honest sentence, then move on.
  5. Finish with why this role is the next step, naming a specific part of the products or the stack.

Follow-up

  • Which project on your resume was hardest, and what exactly was your part in it?
  • Why are you looking to leave your current role?
  • If you could redo one career decision, which would it be?

What are your management style and your experience working with cross-functional teams?

Medium · Behavioral and Career Alignment

Approach

  1. If you have managed people, describe the style through one episode: how you set direction, gave feedback and handled a struggling report. If you have not, describe leadership by influence: a tech-lead stretch, code reviews, mentoring, or owning a design.
  2. For cross-functional work, pick a story with a product manager, a domain expert or another engineering team. State the conflict plainly, for example a requirement that clashed with a performance or integration constraint.
  3. Show how you resolved it with evidence: a prototype, a measurement, or a written trade-off that both sides could read. Then give the outcome.
  4. Show that you translate technical concepts for non-technical colleagues, with one concrete example of how you explained a constraint.
  5. The mistake that sinks this answer is a philosophy with no episode, or credit-taking for a team result. Say what you did, and name what others did.

Follow-up

  • Tell me about a disagreement with a product manager and how it ended.
  • How do you give feedback on a code review that is going to be unwelcome?
  • How do you decide between shipping on time and fixing a known technical issue?

Tell me about a time you solved a particularly tricky bug.

Medium · Behavioral and Career Alignment

Approach

  1. Choose a bug with a real technical shape in this stack: a race condition, a deadlock, a memory leak, a lifetime or destructor problem, or a rendering or performance regression. Avoid one that was solved by a lucky guess.
  2. Tell it as a debugging sequence: symptom, how you reproduced it, how you narrowed it (logging, a debugger, a crash dump, a profiler, a thread or address sanitizer, or bisecting commits), the hypothesis you tested, and the confirmed root cause.
  3. State what the fix was and why it fixes the cause rather than hiding the symptom. Then say how you verified it, ideally with a regression test or a measured before-and-after.
  4. End with prevention: a test, an assertion, a static-analysis rule, a code-review checklist item, or a design change. The weakest version of this story stops at 'and then I fixed it'.

Follow-up

  • How did you know you had found the root cause and not a symptom?
  • What check now stops this class of bug from coming back?
  • What would you instrument earlier if it happened again?

What do you already know about Altium Limited and its products?

Medium · Behavioral and Career Alignment

Approach

  1. Cover three layers in order: what the products are (Altium Designer for PCB design on the desktop, Altium 365 as the cloud-connected platform for collaboration), who uses them (electrical engineers, designers and manufacturers), and where the engineering problems sit (rendering, geometry, multi-threading, cloud services).
  2. Be exact about your own exposure. Say what you have used, what you only read about, and what you would want to learn. Do not claim hands-on experience with the software if you have none.
  3. Link at least one of those problem areas to something you have built, such as a layout or rendering engine, a concurrent pipeline or a cloud service, in one sentence.
  4. Finish with a specific question you want answered about the team's work. A weak answer recites a homepage and ends there.

Follow-up

  • Which part of the product would you most like to work on, and why?
  • What do you think makes software for PCB design technically hard?
  • What would you want to ask the team about their current engineering challenges?

Where do you see yourself in five years?

Medium · Behavioral and Career Alignment

Approach

  1. Anchor the answer in skills and scope, not titles: for example deeper expertise in performance and systems programming, ownership of a component or service, or mentoring other engineers.
  2. Connect the direction to what this role offers, a mix of desktop applications and cloud-connected systems. Say which of the two you want to grow in.
  3. Name two or three milestones along the way, such as what you want to have shipped or owned by year two and year four.
  4. Keep it honest and flexible. The mistake is a goal that implies leaving soon, or a vague line about growing with the company that tells the listener nothing.

Follow-up

  • What would you need from your team and manager to get there?
  • Which skill are you building right now to move in that direction?
  • If that path turned out to be closed, what would you do instead?