As a Software Engineer at Service Stream, you serve as a critical technical bridge between complex infrastructure requirements and scalable digital solutions. Whether you are focused on Power Platform development, data integration, or digital support engineering, your work directly enables the operational efficiency of large-scale utility and transport networks. You are not just writing code; you are building the systems that allow Service Stream to maintain its reputation as a leader in essential network services.
The role demands a balance of technical rigor and pragmatic problem-solving. Because Service Stream operates within high-stakes environments—such as energy, water, and transport infrastructure—your software solutions must be robust, reliable, and capable of handling significant data volume. You will often collaborate across multidisciplinary teams, translating business needs into technical architecture that supports thousands of field staff and corporate users.
Initial Screening
reportedHalf of this call is the part candidates treat as small talk: start date, notice period, work authorisation and its timing, location and time zone, on-call, and the number. Those are what kill offers late, after several engineers have each spent a day. Surfacing a hard constraint now costs you nothing and occasionally buys you something, since a loop compressed to fit a competing deadline can usually only be arranged if it is asked for early. The common failure is deflecting the compensation question twice, then discovering at offer stage that the band never reached your number.
What to demonstrate
- Whether your hard constraints are compatible with the role before a loop gets booked: earliest start, notice period, what authorisation you hold and when it needs action, days on site, willingness to carry a pager
- Whether you give a compensation range with something behind it, such as current total compensation or a competing timeline, rather than leaving the band untested
- Whether your stated timeline is real, since a competing deadline raised now is something scheduling can sometimes work around and the same deadline raised at offer stage usually is not
How to prepare
- Write each constraint down in one line before the call and state them as facts rather than negotiating them live under a question you were not expecting
- Set your range from two or three current data points for that level and location, and name the structure you are quoting in, so the number is comparable to the one they are holding
- If another process is running, say where it stands and by when, and ask directly whether this loop can be scheduled inside that window
Technical Discussions
reportedMost of the time lost in this format is not lost to thinking. It goes to a standard-library call you half-remember, an off-by-one in a loop bound, and a debugging loop that mutates code at random until something passes. When output is wrong, stop re-reading the whole function: take the smallest input that reproduces it and walk the state through by hand, printing intermediates if the environment allows. Guessing at a fix without a failing case you understand is how a five-minute bug becomes twenty, and the clock does not pause while you do it.
What to demonstrate
- Whether you reach the right structure without a detour, and can write it from memory rather than only recall that one exists
- Whether overflow is considered where the language has fixed-width integers, since a signed 32-bit value stops at 2,147,483,647 and then wraps in Java, is undefined behaviour in C++, and does not arise in Python, whose integers grow instead
- Whether recursion depth is treated as a constraint on large inputs, given that CPython's default limit is 1000 frames and a deep recursion can exhaust the stack in any language where an iterative version would not
- Whether a failing case is isolated and explained before any edit is made to the code
How to prepare
- From an empty file and with no references open, implement the pieces you lean on most: a heap push and pop, an iterative DFS with an explicit stack, and a binary search whose midpoint is written lo + (hi - lo) / 2, which avoids the overflow that (lo + hi) / 2 can hit in a fixed-width integer type
- Time yourself on the ten library calls you look up most, such as sorting with a custom comparator, splitting and joining strings, and finding the next key at or above a value in an ordered map, until the lookup is gone
- Take a solution you know is broken and, before touching it, write one sentence naming the input, the expected value and the actual value. Repeat until you do it without deciding to.
Practical Assessments
reportedThe same problem is scored by two different mechanisms depending on the format, and preparing for one does not cover the other. With a person watching, partial progress is visible and a hint is a correction you can absorb; silence is the expensive failure, because nobody can read a half-written function. With an automated grader there is no partial credit for what you were about to do, nobody to ask, and the worked examples in the prompt are the entire specification. Read them as a contract, down to whether an empty result should be an empty list or no output at all.
What to demonstrate
- In a live session, whether your commentary tracks what your hands are doing, and whether a hint redirects you or gets defended against
- In an automated one, whether you cover the cases the examples do not show, since the hidden cases are where the score moves
- Whether you manage the clock on purpose: abandoning an approach that is not converging while there is still time to write something simpler that finishes
How to prepare
- Have someone hand you a problem and feed you one deliberately wrong hint. Practise testing it against a concrete case instead of accepting or rejecting it on authority.
- Do one timed run a week in a plain browser editor with autocomplete, linting and your own snippets switched off, which is closer to what these environments give you
- For the automated format, write the harness before the solution: a main that feeds the worked examples plus an empty and a single-element case and prints expected against actual, so a wrong submission is caught by you first
Final Decision-Making
reportedWhere the day includes a partner from product, design or data, that conversation is weighted like the technical ones and prepared for least. They are deciding one thing: whether having you in the room makes their decisions cheaper. That means options with costs attached, not implementation detail and not "it depends". An estimate someone can plan against — a range, the assumption that would push it to the high end, and what you would drop to hit the low one — is worth more than a confident single number, which everyone present already knows is wrong.
What to demonstrate
- Whether an estimate comes as a range with the assumption most likely to break it, and states what a specific scope cut would actually buy
- Whether a technical constraint is handed over as a choice with consequences on their side, rather than as a verdict they have no standing to argue with
- Whether you establish what decision is on the table before proposing anything
- Whether risk is raised while it can still change the plan, with the trigger that would confirm it, instead of reported afterwards as a slip
How to prepare
- Take a project that shipped late and write the two-sentence warning you could have given three weeks earlier, naming what you would have needed decided at that point
- Rehearse one estimate out loud until it arrives in three parts: the range, the single assumption that would blow it, and the smallest thing you would cut to protect the date
- Rewrite an objection you have actually made — the "we can't do that" version — as two options with their costs, so the choice ends up with the person who owns it
PracHub editorial advice for the preparation topics above.
Treating the manifest as the authorisation boundary.
Checking entitlement when the manifest is requested feels natural, because that is where the session visibly begins. But the manifest is a cacheable document and the segment URLs inside it carry no identity, so anyone who obtains it plays until the delivery token expires, and a subscriber who cancels mid-title finishes the title. Authorisation has to sit at the licence request, the only thing on the path that is per-user and uncacheable by construction.
Assuming one encrypted copy of the ladder serves every DRM system.
Common encryption (ISO/IEC 23001-7) defines several protection schemes, and two matter in practice: AES-CTR full-sample ('cenc') and AES-CBC pattern encryption ('cbcs'). One of the three widely deployed DRM systems accepts only cbcs, and older client versions of the other two accept only cenc, so a single-copy strategy is correct only if every device class you still support has reached cbcs. Discovering late that one legacy class has not means a second packaging run, a second set of objects in every cache, and roughly double the storage for the affected catalogue.
Going silent while thinking
Narrate the candidates and why you are discarding them, even in fragments: sorting first would make this a two-pointer scan, but it destroys the original indices, which the output needs. From the other side of the table, a candidate thinking hard and a candidate stuck are indistinguishable until one of them speaks.
Treating a network call as though it were a local function call
A remote call can be slow, fail, or return after you stopped waiting, so name the timeout, the retry policy, and what the caller sees while the dependency is down. A call with no timeout turns one slow dependency into an exhausted thread or connection pool in every service upstream of it.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Detect overlapping availability windows including perpetual null end dates
availability_window holds (id, content_version_id, territory_code, licence_type, starts_at, ends_at NULL, max_resolution_cap NULL, status). Two active rows sharing (content_version_id, territory_code, licence_type) whose intervals intersect are a data bug, because serve-time resolution then picks one arbitrarily. ends_at NULL means perpetual. Intervals are half-open: a window ending at exactly the instant the next begins is not an overlap. Given up to 5 x 10^6 active rows, return every conflicting pair grouped by triple. Target O(n log n), and say what you do when one triple holds thousands of rows.
Approach
- Group by the triple with a hash map, O(n) expected. Groups are small in practice (single digits per content version and territory), so the sort cost is per group rather than over all 5 x 10^6 rows.
- Within a group sort by (starts_at asc, ends_at asc) and sweep with a running maximum end. Row r conflicts if r.starts_at < running_max_end, strictly less because the intervals are half-open. O(m log m) per group, O(n log n) overall.
- Represent NULL ends_at as a positive-infinity sentinel in the comparator, not as a coalesce to a far-future literal that a later row can legitimately exceed. One perpetual row conflicts with every later row in its triple, so report it once as a dominating row rather than enumerating m-1 pairs.
- Watch the output size, which is the real complexity risk: k conflicting pairs is O(m^2) within a pathological group, so the honest bound is O(n log n + k) and the API should stream or cap results rather than materialise them.
- Apply the same half-open convention to the serve-time range predicate, otherwise the audit and the runtime disagree about back-to-back windows and the audit becomes noise people learn to ignore.
Follow-up
- Two otherwise identical windows differ only in max_resolution_cap. Is that still a conflict, and how does the serve path choose deterministically?
- Run the check as a pre-commit guard on a single insert rather than as a batch audit. What does the existing index (content_version_id, territory_code, starts_at, ends_at) give you, and what is the query's cost?
Choose ladder rungs against a measured bandwidth distribution
You have 60 candidate encoding rungs sorted ascending by bitrate, and an audience bandwidth histogram of 1,000 buckets measured from delivered throughput. A viewer plays the highest rung whose bitrate is at most 80% of their sustained throughput, and cannot play at all if no rung qualifies. Choose exactly 12 rungs, always including the lowest candidate, maximising total delivered bitrate across the audience. Return the chosen rungs and the objective value, state the complexity, and say why a log-spaced ladder is not automatically the answer.
Approach
- Precompute for each candidate i the bucket at which viewers can first afford it, threshold_i = bitrate_i / 0.8, by binary search over the bucket edges: O(n log m). Build a prefix sum over bucket counts so any viewer-count range is O(1).
- Formulate the contribution: with the selected set sorted, if rung i is selected and j is the next selected rung, every viewer whose threshold falls in [threshold_i, threshold_j) plays rung i, contributing count * bitrate_i. Call it value(i, j), computable in O(1) from the prefix sums.
- Run the DP over (last selected rung, rungs used): f[j][t] = max over i < j of f[i][t-1] + value(i, j), with the lowest rung forced selected and a sentinel j = n+1 closing the final interval. O(n^2 k) = 60^2 * 12, about 43,000 transitions. The point is that the ladder is measured, not that the algorithm is clever.
- Say when the structure buys more: if value satisfies the concave quadrangle inequality, the inner maximisation admits divide-and-conquer optimisation at O(n k log n). That condition depends on the histogram's shape and must be tested on the real distribution rather than assumed.
- Name what the objective leaves out, because the interview point is the modelling: storage and cache footprint scale with rung count, device decode limits make some rungs unusable by whole client classes, and delivered bitrate is a crude proxy for perceived quality. Log spacing is a prior about a smooth unimodal distribution, and measured throughput is usually bimodal, which is exactly where the prior loses.
Worked solution 40 min
- Write threshold_i and the prefix-sum lookup, and confirm the 80% headroom appears in exactly one place.
- Derive value(i, j) and check it by hand on a three-rung example against a two-bucket histogram.
- Write the DP recurrence with the forced lowest rung and the closing sentinel, and count the transitions at n = 60, k = 12.
- Compare the DP's chosen rungs against a log-spaced ladder on a deliberately bimodal histogram and quantify the difference.
- List the constraints the objective omits and say where each would enter the formulation.
Follow-up
- Add a per-device decode cap so one client class cannot use rungs above 1080p. Does the DP still decompose, and along which dimension?
- Throughput varies during playback and the player switches rungs. What does that do to the highest-affordable-rung model, and what would you measure instead?
- Storage and cache footprint become a hard budget rather than a fixed rung count. Which dimension of the DP state changes?
Parse a live media playlist for seek offsets and deltas
You are given two successive snapshots of an HLS media playlist as text: #EXT-X-TARGETDURATION, #EXT-X-MEDIA-SEQUENCE, then repeated #EXTINF:, lines each followed by a segment URI, with #EXT-X-DISCONTINUITY markers possible between entries. A four-hour DVR window at two-second segments is 7,200 entries. Build a structure that answers which segment contains playback position P in better than linear time, and compute which segments were added and removed between the two snapshots. State the complexity of both the build and each query.
Approach
- Single-pass parse into an array of (media_sequence_number, duration_ms, uri, discontinuity_flag), converting each #EXTINF decimal-seconds value into integer milliseconds at parse time. Accumulating 7,200 floating-point seconds drifts, and every comparison downstream wants exact integers.
- Build prefix sums of duration_ms alongside the array, O(n) time and O(n) space, and answer a position query by binary search over the prefix array: O(log n), about 13 probes at 7,200 entries.
- Reset the accumulator at each #EXT-X-DISCONTINUITY and keep a per-discontinuity base. A discontinuity is a timeline break, so a prefix sum that runs straight through one maps every position after it to the wrong segment. The query then returns (discontinuity index, offset within it) rather than a single scalar.
- Diff by media sequence number, not by URI or by position. The first entry's number is #EXT-X-MEDIA-SEQUENCE and each subsequent entry is one greater, so added = numbers in B above A's maximum and removed = numbers in A below B's first. That is O(added + removed) and survives URI reuse across a discontinuity.
- Tie it back to delivery: every viewer refetches this document about once per target duration, so the delta is the thing you would actually transmit, and it is the reason a 7,200-entry playlist is a bandwidth problem rather than a parsing problem.
Follow-up
- Add #EXT-X-BYTERANGE so several segments share one resource. What changes in the structure, and what does a seek now have to return?
- The playlist is served as a delta against a client-held version. What must the server retain, for how long, and what happens to a client that has fallen further behind than that?
Put segment duration where alignment cannot drift
content_version(id, title_id, duration_ms, encoder_build_id, packaging_status) and rendition(id, content_version_id, stream_kind ENUM('video','audio','subtitle'), width, height, frame_rate_milli_fps, segment_duration_ms, gop_size_frames, status). Every video rendition of a content version must have identical segment boundaries, and each segment must begin at an IDR frame, so gop_size_frames has to divide the segment length in frames. Both facts currently live per rendition and nothing stops a re-encoded rung from disagreeing. Restructure so the database rejects drift, and say what breaks at a frame rate of 24000/1001 with a 2,000 ms segment.
Approach
- Identify the functional dependency. For video rows,
segment_duration_msis determined bycontent_version_id, not byrendition_id. A dependency stored at the wrong grain is exactly what lets two rows disagree, and the invariant is presently a sentence in a comment rather than anything the database can refuse. - Move the authoritative value to the parent as
content_version.segment_frames. Ask whether the child copy earns its keep: the manifest build already loads the content_version row to resolve the URL, so the join costs nothing and the denormalised copy buys no read. If it is kept anyway for write-path convenience, guard it with a composite foreign key —UNIQUE (id, segment_duration_ms)on the parent andFOREIGN KEY (content_version_id, segment_duration_ms) REFERENCES content_version(id, segment_duration_ms)on the child. Under the default MATCH SIMPLE semantics a NULL in either child column satisfies the constraint, which is the escape hatch that lets audio and subtitle rows carry no video segment length. - Fix the representation before writing any CHECK. 24000/1001 is 23.976023976… and is not representable in integer thousandths, so
frame_rate_milli_fps = 23976is already a different number from the real rate and every derived value drifts from it. Store the exact rational asframe_rate_num INTEGER, frame_rate_den INTEGER— 24000 and 1001 — and store the segment length in frames rather than milliseconds. - With frames and an exact rational, both invariants become integer CHECKs that cannot round:
CHECK (segment_frames % gop_size_frames = 0)for IDR alignment, and the millisecond duration becomes derived —segment_frames * 1000 * den / num— rather than a second stored copy to keep in sync. - Work the stated case: at 24000/1001, 2,000 ms is 48000/1001 frames, about 47.952, so no segment of that target length contains a whole number of frames and no integer GOP size divides it. 96 frames is exactly 4.004 s and admits any GOP size dividing 96. The encoder has to round a millisecond target to whole frames, and that rounding is where two rungs encoded at different times end up on different boundaries.
- Accept what the schema still cannot catch. Rate control can relocate an IDR without changing any stored column, so add a post-packaging QC step that compares segment counts and first-sample timestamps across the video renditions of a version and fails the publish on a mismatch, and pin
encoder_build_idper rendition so a rung re-encoded on a newer build is a visible difference rather than an inference.
Follow-up
- One rung fails QC and is re-encoded alone. What do you verify before republishing the manifest, and what is the rollback if boundaries moved?
- Audio renditions legitimately use a different segment length from video. Which of your constraints still applies and which has to be weakened?
- How do you migrate the existing millisecond column to frames without a window in which neither constraint holds?
Add a dedupe constraint to a live plays table
qualified_play holds about two billion rows, takes continuous inserts, and was created without the dedupe_key column the design calls for. Add the column, backfill it deterministically from session_id and content_version_id, and make it unique and NOT NULL without taking the table offline. Duplicate plays already exist and money was paid against them, and corrections are written as reversing rows that share the same logical key. Give the ordered plan, name the lock each step takes, and say what the uniqueness is actually on.
Approach
- Deploy the write path before the backfill. Ship the code that populates
dedupe_keyon new rows first so the backfill covers a finite set — everything older than that deploy — instead of chasing a table that keeps growing new null rows behind it. - Add the column as nullable with no default. A nullable column with no default has never rewritten the table; since PostgreSQL 11 a constant default does not either, but a volatile default still does. The statement takes ACCESS EXCLUSIVE briefly, and the real hazard is the queue: the ALTER waits behind a long analytics scan of this table, and every query arriving after it waits behind the ALTER. Take it with
SET lock_timeout = '2s'and retry, so a failure to get the lock is a no-op rather than a stall of the read path. - Backfill in bounded batches over the primary key range, committing per batch. Each UPDATE writes a new row version, so the backfill produces roughly a table's worth of dead tuples before autovacuum reclaims them; throttle to the rate autovacuum can keep up with and watch the dead-tuple count rather than wall-clock progress. Never hold one long transaction over the whole backfill — it pins the transaction horizon and blocks cleanup everywhere.
- Decide what the uniqueness is on before building anything. A reversing row carries the same logical key as the row it reverses, so a plain unique on
dedupe_keywould make corrections impossible. Either make the index partial — unique ondedupe_keyWHERE status IN ('pending','counted')— or fold a revision into the stored key. Say which, and say what a re-count after a reversal then looks like under your choice. - Resolve the pre-existing duplicates with a window function rather than a DELETE:
ROW_NUMBER() OVER (PARTITION BY dedupe_key ORDER BY qualified_at, id)identifies every row past the first, and each gets a reversing row. Deleting them would erase rows a payout already ran against; the ledger has to show the correction, not lose the evidence. - Build with
CREATE UNIQUE INDEX CONCURRENTLY, which takes SHARE UPDATE EXCLUSIVE and does not block reads or writes, at the cost of two table passes and waiting on open transactions. It fails if any duplicate remains, and a failed build leaves an index withindisvalid = falsethat must be dropped before retrying. Promote it withALTER TABLE ... ADD CONSTRAINT ... UNIQUE USING INDEX, a brief ACCESS EXCLUSIVE that does not rebuild. For NOT NULL, addCHECK (dedupe_key IS NOT NULL) NOT VALID, runVALIDATE CONSTRAINTunder SHARE UPDATE EXCLUSIVE, thenSET NOT NULL, which from PostgreSQL 12 uses the validated check instead of scanning the table again.
Worked solution 40 min
- Build a scaled copy with a known duplicate population and a writer process inserting continuously throughout.
- Hold a long SELECT open, then run the ADD COLUMN with
lock_timeoutset, and observe what queues behind what. - Backfill in batches, recording dead tuples and table size after each batch.
- Enumerate duplicates with ROW_NUMBER and write reversing rows for every row past the first — leaving one duplicate in place deliberately.
- Run CREATE UNIQUE INDEX CONCURRENTLY, observe the failure, and inspect
pg_index.indisvalidfor the leftover. - Drop the invalid index, reverse the last duplicate, rebuild, promote to a constraint, and set NOT NULL through the NOT VALID check path.
Follow-up
- The backfill is half complete when a bug in the key derivation is found. What is the recovery, and does it need another full pass?
- A duplicate surfaces after the constraint is live and the period is already closed. Which table changes and which does not?
- How do you prove the key is deterministic enough that a client regenerating it after a crash produces the identical string?
Describe a situation where you had to choose between a "quick fix" and…
Describe a situation where you had to choose between a "quick fix" and a "long-term architectural solution."
Approach
- Name the read and write paths separately; they rarely have the same bottleneck.
- State the consistency you need, and where you are willing to be stale.
- Fix the scope first: who calls this, how often, and what they do when it fails.
Follow-up
- What would you drop to keep the system up under load?
- What breaks first when traffic grows ten times?
How do you approach testing to ensure that your code doesn't break exi…
How do you approach testing to ensure that your code doesn't break existing legacy integrations?
Approach
- Name the failure you are designing for, then the recovery path.
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the read and write paths separately; they rarely have the same bottleneck.
Follow-up
- What would you drop to keep the system up under load?
- How does this behave when that dependency is down for an hour?
How have you utilized Power Automate or Power Apps to solve a specific…
How have you utilized Power Automate or Power Apps to solve a specific business bottleneck?
Approach
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
- Work from the requirement backwards to the design.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
How do you ensure the security and integrity of data when building low…
How do you ensure the security and integrity of data when building low-code or custom solutions?
Approach
- Clarify what is being asked and what a complete answer contains.
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Give playback start a failure contract the player can act on
A television app calls POST /playback/sessions to start a title and today receives HTTP 403 with a free-text message for every refusal. The refusals are not alike: the account has no entitlement, no active availability_window covers this content_version in the caller's territory, the plan's concurrent-stream limit is already met, the device supports only software-level DRM for a version that requires hardware, and the backend is shedding load. Define the response body, the status code carrying each class, and the exact action the player takes for each.
Approach
- Partition by what the player does, not by where the refusal came from. Five behaviours fall out: terminal-and-explain (no rights in this territory), terminal-and-upsell (no entitlement), recoverable-by-user-action (out of streams, and the user can stop one), recoverable-by-a-different-request (the device cannot decrypt this packaging, but can play another), and retry-later (shed). If two classes produce the same player behaviour, you have over-split and the client cannot use the distinction anyway.
- Map classes onto codes the SDK's generic HTTP layer already handles: 401 for a missing or expired credential, which the player refreshes and retries once; 403 with distinct stable codes for entitlement versus territory; 409 for the concurrency limit, because the conflict is with existing state the user can change; 422 when the requested content_version cannot satisfy the capabilities the client declared; 503 with Retry-After for shed. Never merge 401 and 403 — they have opposite retry semantics, and merging them produces a client that either loops on an unfixable refusal or drops a fixable one.
- Shape the body as RFC 9457 problem details with a stable machine-readable code as the switch value, plus class-specific data. The concurrency refusal must carry the active sessions (device label, title, started_at); 'you are out of streams' with no list is an unactionable dead end whose only exit is a support ticket. 451 is worth raising for the territory case and worth rejecting: it is defined for legal demands to block a resource, not for a licence that has not been bought, and a stable code under 403 says the same thing without misusing a status.
- State what a refusal must not contain. The territory refusal should not enumerate the territories and dates where the title is available, and the entitlement refusal should not vary its prose by account state, or the endpoint becomes an oracle for someone probing it. Keep the discriminating detail in the code the client switches on, not in text the client renders verbatim.
- Define the default branch: an unrecognised code is terminal, shows a generic error, and does not retry. That single rule is what makes adding a sixth code next quarter a non-breaking change for every client already in the field.
Worked solution 20 min
- List the five refusal classes in a column and write beside each the exact UI the player shows and whether it retries, before assigning any status code.
- Assign a status and a stable code per class, then re-read the retry column: only the shed class may retry without user action.
- Write the concurrency refusal body in full, including the active-session list, and check a user could pick a stream to stop from it alone.
- Write the player's switch and confirm the default branch is terminal rather than a retry.
Follow-up
- The session starts, then the subscription lapses ten minutes in. Which code does the client see, from which endpoint, and at what moment?
- Two devices in one household hit the limit at the same instant. What does each receive, and does the answer depend on where the limit is enforced?
Licence p99 collapses at live start while dependency looks healthy
At a scheduled live start, licence issuance goes from 300 to 3,000 requests per second, p99 rises from 40 ms to 9 s, 12% of requests time out and playback fails with decrypt errors. One entitlement-store read replica sits at 400 ms, up from 5 ms, with zero errors and a green health check. The licence path opens a transaction, locks the account's entitlement-snapshot row, calls the entitlement service, writes a license_grant row and commits. Fleet connection pools total 800. Give the ordered checklist and the two changes you would make.
Approach
- Separate queueing from service time first. Break the request into time waiting for a pool connection and time executing. If pool wait dominates, the pool is the queue and the cause sits upstream of the database: a long hold, not a slow statement.
- Do the concurrency arithmetic. Required concurrency is arrival rate times hold time. At 3,000 per second with a 5 ms hold that is 15 connections; with the entitlement call at 400 ms inside the transaction the hold is roughly 405 ms, so required concurrency is about 1,215 against 800 available. Demand exceeds capacity, the queue grows without bound for the duration of the spike, and p99 becomes queueing delay rather than work.
- Measure lock wait per row before assuming which lock matters. Fan-in on the account's entitlement-snapshot row is one account's own sessions, so it is small and is not the ceiling here. State the conditional you are ruling out: had the lock been on a per-channel key row, every session on the channel would serialise on it and the ceiling would be one over the hold time, about 2.5 acquisitions per second at 400 ms, which would collapse far harder than observed.
- Explain the green dashboard so it stops being reassuring. A degraded replica that still answers has no error rate, and availability or p50 indicators hide it. The signals that show it are the dependency's p99 by replica and the caller's own pool-wait metric; a health check that the replica passes tells you nothing about hold time.
- Change one: never hold a lock or a pooled connection across a network call. Resolve entitlement before opening the transaction, so the transaction contains only local writes and the hold drops back to a couple of milliseconds and required concurrency to single digits.
- Change two: bound the blast radius with a per-dependency timeout well below the client's, a concurrency limiter or circuit breaker on the entitlement call, and no unbounded retries, since retrying into a saturated dependency is what turns a slow replica into an outage. Name the degradation you choose: refusing a licence stops playback, so failing open would delete the only enforcement point in the system, and the correct behaviour is to shed load and fail closed with a retryable error.
Follow-up
- Key rotation on a live channel makes every session ask for a licence in the same second. How do you spread that without weakening enforcement?
- What should a client do on a licence timeout, and how do you stop its retries becoming the second wave?
- Would caching licences fix the load, and exactly what would it cost you?
For a candidate senior enough that the loop turns on design and judgement rather than on whether the coding round gets finished. Five days build one system properly and then stress it; coding gets a single maintenance day, on the assumption that the risk at this level is an unexamined tradeoff rather than a missed algorithm.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Numbers before diagrams
- Build your own reference card of the figures you will re-derive all week: bytes for a realistic record, requests per second implied by a given daily active count, and the storage that a year at a given write rate produces. Derive each one rather than copying it, because the derivation is what survives a follow-up.
- Turn one product statement into capacity requirements. From ten million daily users at four writes and forty reads each, state the peak-to-average factor you are assuming and why, then produce peak write QPS, peak read QPS and a year of storage.
- Write the two numbers whose order of magnitude changes the design, the read-to-write ratio and the working-set size against memory per node, and state the threshold at which each one flips your answer.
Deliverable: A one-page numbers card and one worked capacity estimate with every assumption written down.
Practice prompt ↗Practice prompt ↗Worked solution ↗02One system, from requirements to schema
- Spend the first ten minutes producing only functional requirements, non-functional targets with numbers attached, a p99 latency, a durability expectation, a consistency requirement, and an explicit out-of-scope list.
- Define the interface before the boxes: the three or four endpoints, their parameters, what each returns, and which of them are idempotent.
- Write the data model, then write the single access pattern that justifies it, and state what the schema would have to become if the dominant access pattern were the other one.
Deliverable: One design carried to endpoint-and-schema depth, with non-functional targets expressed as numbers and a written out-of-scope list.
Practice prompt ↗Practice prompt ↗03The consistency you are actually buying
- Write out what a client sees under asynchronous replication when its write commits on the leader and its next read is served by a lagging follower, then write the two fixes, pinning that session's reads to the leader for a bounded window or carrying a version token the replica must reach, and the cost of each.
- Work the quorum arithmetic on paper for N of three with W and R of two, and separate what R + W > N does guarantee, that any read set intersects any write set, from what it does not: on its own it is not linearizability, and a sloppy quorum that accepts writes on nodes outside the preference list breaks even the intersection.
- Take two storage choices with different defaults, a single-leader relational store committing synchronously and a quorum-replicated store that converges eventually, and write the specific product behaviour that would be wrong under each, rather than a general statement about which is stronger.
Deliverable: A page separating what quorum overlap guarantees from what it does not, with one concrete product misbehaviour attached to each gap.
Practice prompt ↗Practice prompt ↗04Failure is the design
- For one write path, work through the case where the client times out after the server has already committed, then design the idempotency key: who generates it, how long it is retained, and what the duplicate request returns.
- Express the retry policy as parameters rather than as a word: maximum attempts, base delay, backoff factor, jitter, and which error classes are retried at all. Then state why retrying a non-idempotent write without a key is a correctness bug and not merely waste.
- Compute the fan-out effect on tail latency. If a request waits on ten backends and each independently exceeds its p99 one percent of the time, the chance at least one is slow is 1 - 0.99^10, about ten percent. Then write why independence is the optimistic assumption and what correlates them in practice.
- Name the backpressure mechanism for one queue or one dependency in the design, a bounded queue with shedding or a concurrency limit, and write what the caller is told when it engages.
Deliverable: One write path with an idempotency design, a parameterised retry policy, and a written tail-latency calculation with its assumption named.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Scaling the hot path
- Choose cache-aside or write-through for one read path and write the staleness window each produces, then name the invalidation event and what the system does when that event is lost.
- Design against the stampede: either coalesce requests so only one recomputes a missing key, or refresh early with jittered expiry, and write why identical TTLs on keys populated in the same moment produce a synchronised expiry and a thundering herd.
- Shard one table by a key you choose, then answer the two questions that break the choice: which queries now require a scatter-gather, and what happens to the distribution when one tenant is a hundred times larger than the median.
- Write the cost of adding a node under plain modulo placement, where nearly every key moves, against consistent hashing, where roughly one key in n+1 moves, and state what virtual nodes are for.
Deliverable: A caching and sharding decision for one path, each with its failure mode and its rebalancing cost written beside it.
Practice prompt ↗Practice prompt ↗06Keep the coding hand in, at the bar that applies to you
- Solve one medium problem in thirty minutes, then spend twenty more making it production-shaped: named invariants, validation at the boundary, and errors that distinguish a caller mistake from an internal fault.
- Write the tests you would require of a colleague's version of that function: one for empty input, one for the boundary, and one for the case the implementation is most likely to get wrong.
- Read a piece of your own code from six months ago and write the change you would ask for, phrased as you would actually phrase it in review.
Deliverable: One problem hardened to review standard, with its test list and one written review comment.
Practice prompt ↗Practice prompt ↗07Defend it while being interrupted
- Run a forty-five-minute design mock with an interviewer briefed to change a requirement halfway, a tenfold traffic increase or a new strict consistency requirement, and to push on one number you estimated.
- Rehearse the two sentences a senior loop is listening for: naming the tradeoff you are choosing against and why, and saying what you would measure to learn that the choice was wrong.
- Prepare the design you regret: a real decision, the constraint that produced it, what it cost, and what you changed afterwards.
Deliverable: Mock notes recording how the design changed under the new requirement, plus a written account of one regretted decision.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
When the requirements were thin, the interesting part is how you fenced the problem off: the assumption you wrote down, who you got to confirm it, the narrow version you shipped first so the rest stayed cheap to change. Guessing and being right is luck. Guessing in writing, where someone could correct you, is method.
Tell me about a time you had to explain a technical issue to a non-tec…
Tell me about a time you had to explain a technical issue to a non-technical manager.
Approach
- Pick a story where you made the decision, not one where you watched it.
- Close with what you would do differently, concretely.
- State the situation in two sentences and spend the rest on the reasoning.
Follow-up
- What would you do differently if you ran that again?
- How did you know your change caused the improvement?
Describe a time you received constructive feedback on your code and ho…
Describe a time you received constructive feedback on your code and how you implemented it.
Approach
- Close with what you would do differently, concretely.
- Pick a story where you made the decision, not one where you watched it.
- Give the blast radius: what could have broken, and what you measured.
Follow-up
- What would you do differently if you ran that again?
- What did you decide not to do, and why?
How do you handle conflicting requirements from non-technical stakehol…
How do you handle conflicting requirements from non-technical stakeholders?
Approach
- Give the blast radius: what could have broken, and what you measured.
- Close with what you would do differently, concretely.
- Name the disagreement and how you resolved it with evidence.
Follow-up
- What would you do differently if you ran that again?
- What did you decide not to do, and why?
- 01
Tell me about a time you had to explain a technical issue to a non-technical manager.
- 02
Describe a time you received constructive feedback on your code and how you implemented it.
- 03
How do you handle conflicting requirements from non-technical stakeholders?
Is this an official Service Stream interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Service Stream. Rounds and questions reflect what candidates have reported, not a process Service Stream has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How long does the hiring process usually take?
The timeline varies, but candidates typically move through the stages within a few weeks. Being responsive and clear about your availability can help keep the process moving efficiently.
PracHub interview research ↗What is the company culture like for engineers?
The environment is collaborative and focused on reliability. You will work with teams that value practical solutions that help keep critical infrastructure running smoothly.
PracHub interview research ↗Should I expect a coding test?
Depending on the specific team, you may be asked to complete a practical exercise or a technical case study. Focus on clean, documented code and clear logic rather than just finding the "fastest" solution.
PracHub interview research ↗How much remote work is available?
Service Stream maintains a balance between operational needs and flexibility. Be sure to discuss specific location or hybrid expectations during your initial recruiter screen.
PracHub interview research ↗Sources & methodology 3 sources ↗
No official company page is cited. Rounds and questions come from candidate reports and PracHub editorial material; each source shows the date it was read.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
PracHub page · Accessed 2026-09-22 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
PracHub page · Accessed 2026-09-22 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
PracHub page · Accessed 2026-09-22