Rystad Energy · Software Engineer
Updated · 2026-10-08

Rystad Energy Software Engineer
Interview Guide

THE 60-SECOND BRIEF

Rystad Energy publishes energy research and data products.

This guide is scoped to a Software Engineer candidate at Rystad Energy.

Role-specific process: confirm stages and timing with your recruiter. The checkpoints below organize preparation.

PythonAPI DesignCoding Tests (Algorithms/DS practice)

9 min read

Browse Software Engineer questions

See the practice prompts

21Practice promptsAcross five skill areas
4With worked solutionsIncluded in the practice prompts

A data-focused software interview becomes much easier to prepare for when you can demonstrate what happens to a bad row, a repeated import and a failed function call. This guide uses an energy-asset fixture to work through those decisions in Python, then connects the code to an ingestion pipeline and an API. The exercises are original practice, not a transcript of a Rystad Energy interview.

Use the Software Engineer question bank for additional coding and design practice, then test your answers against the concrete contracts below.

Practice timings and scorecards here are editorial. Confirm the team, level, language, format and allowed tools with your recruiter. Existing compiled reports do not establish a universal current sequence.

01

Confirm the assessment

editorial

Preparation checkpoint; confirm whether this component is in your actual assessment.

What to demonstrate

  • Ask for the actual format, language, duration, tools and quantitative component.
  • If quantitative reasoning is confirmed, rehearse it under the supplied timing; this guide does not verify a fixed question count.

How to prepare

  • Ask for the actual format, language, duration, tools and quantitative component.
  • If quantitative reasoning is confirmed, rehearse it under the supplied timing; this guide does not verify a fixed question count.
PracHub Software Engineer practice ↗
02

Python, validation and OOP

editorial

Preparation checkpoint; confirm whether this component is in your actual assessment.

What to demonstrate

  • Run the JSON filter against threshold equality, bad units, invalid booleans and duplicate IDs.
  • Use a fake source through the OOP contract; test decorator return values, exceptions and metadata.

How to prepare

  • Run the JSON filter against threshold equality, bad units, invalid booleans and duplicate IDs.
  • Use a fake source through the OOP contract; test decorator return values, exceptions and metadata.
PracHub Software Engineer practice ↗
03

Ingestion and API case

editorial

Preparation checkpoint; confirm whether this component is in your actual assessment.

What to demonstrate

  • Work the authorized three-row ingestion fixture; preserve units, source, time and parser version.
  • Replay the import and define tenant authorization, a stable cursor, errors and observation dates.

How to prepare

  • Work the authorized three-row ingestion fixture; preserve units, source, time and parser version.
  • Replay the import and define tenant authorization, a stable cursor, errors and observation dates.
PracHub Software Engineer practice ↗
04

Project decisions and collaboration

editorial

Preparation checkpoint; confirm whether this component is in your actual assessment.

What to demonstrate

  • Prepare a project where you changed a technical decision after new evidence.
  • Explain your contribution, a disagreement and how you left a teammate owning their work.

How to prepare

  • Prepare a project where you changed a technical decision after new evidence.
  • Explain your contribution, a disagreement and how you left a teammate owning their work.
PracHub Software Engineer practice ↗
05

Behavioral evidence and reflection

editorial

Preparation checkpoint; confirm whether this component is in your actual assessment.

What to demonstrate

  • Rehearse an incident with observable impact and an honest account of missing measurements.
  • State what you would change next time without inventing a metric.

How to prepare

  • Rehearse an incident with observable impact and an honest account of missing measurements.
  • State what you would change next time without inventing a metric.
PracHub Software Engineer practice ↗

PracHub editorial advice for the preparation topics above.

01

Definitions without responsibilities

Use a source boundary and show substitutability instead of adding inheritance everywhere.

02

Invalid data silently accepted

Separate envelope failures, invalid rows and valid rows that miss the filter.

03

Scraping before confirming access

Choose an official API, licensed feed or agreed fixture; preserve provenance and stop on access challenges.

04

Managerial preparation mapped to database trivia

Prepare ownership, collaboration, disagreement and observable outcomes for a project discussion.

Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.

18 technical prompts4 include a worked solution

Separate source and filter responsibilities

mediumWorked solution
Python & Machine Coding

Use AssetSource and JsonSource to return a BatchResult without coupling the consumer to a source format.

Approach
  1. Separate source parsing from filtering.
  2. Define a substitutable read interface and a frozen result record.
  3. Run a fake source through the same consumer and explain composition versus inheritance.
Worked solution 25 min
Python
import json
import math

def select_assets(raw):
    data = json.loads(raw)
    if not isinstance(data, dict) or not isinstance(data.get("assets"), list):
        raise ValueError("assets must be a list")
    selected, rejected, seen = [], [], set()
    for i, row in enumerate(data["assets"]):
        if not isinstance(row, dict):
            rejected.append((i, "row must be an object"))
            continue
        asset_id, capacity = row.get("id"), row.get("capacity")
        valid_number = (type(capacity) in (int, float)
                        and math.isfinite(capacity) and capacity >= 0)
        if not isinstance(asset_id, str) or not asset_id.strip():
            rejected.append((i, "invalid id"))
        elif row.get("unit") != "MW" or not valid_number:
            rejected.append((i, "capacity must be finite nonnegative MW"))
        elif type(row.get("active")) is not bool:
            rejected.append((i, "active must be a boolean"))
        elif row["active"] and capacity >= 100 and asset_id not in seen:
            seen.add(asset_id)
            selected.append(asset_id)
    return selected, rejected

sample = '{"assets":[{"id":"wind-1","capacity":120,"unit":"MW","active":true},' \
         '{"id":"solar-1","capacity":80,"unit":"MW","active":true},' \
         '{"id":"bad-1","capacity":true,"unit":"MW","active":true}]}'
assert select_assets(sample) == (["wind-1"], [(2, "capacity must be finite nonnegative MW")])
assert select_assets('{"assets":[]}') == ([], [])
assert select_assets('{"assets":[{"id":"x","capacity":100,"unit":"MW","active":true}]}')[0] == ["x"]

from dataclasses import dataclass
from abc import ABC, abstractmethod

@dataclass(frozen=True)
class BatchResult:
    selected: tuple
    rejected: tuple

class AssetSource(ABC):
    @abstractmethod
    def read(self):
        raise NotImplementedError

class JsonSource(AssetSource):
    def __init__(self, raw):
        self._raw = raw
    def read(self):
        selected, rejected = select_assets(self._raw)
        return BatchResult(tuple(selected), tuple(rejected))

assert JsonSource('{"assets":[]}').read() == BatchResult((), ())

Scroll sideways to view long lines.

EXPECTED RESULTThe empty source returns BatchResult((), ()).
Follow-up
  • Which edge case would invalidate your contract?
  • What changes if the input or telemetry must be bounded?

Explain shallow copies with a nested fixture

medium
Python & Machine Coding

How does Python manage memory, and what is the difference between deep copy and shallow copy?

Approach
  1. Trace a nested list shared by two dictionaries before comparing shallow and deep copies.
  2. State which references each owner retains and when unreachable objects can be reclaimed.
  3. Do not treat the GIL as a general application-state lock.
Follow-up
  • What happens if two callers reach this at the same time?
  • Where could this allocate more than you expect?

Validate and filter an energy-asset payload

mediumWorked solution
Python & Machine Coding

Return unique active asset IDs with capacity at least 100 MW. Reject malformed JSON/envelopes at request level; return indexed reasons for invalid rows.

Approach
  1. Define the envelope and row contract before coding.
  2. Validate units, finite nonnegative numbers and exact booleans.
  3. Return indexed rejection reasons separately from filtered-out valid rows.
Worked solution 25 min
Python
import json
import math

def select_assets(raw):
    data = json.loads(raw)
    if not isinstance(data, dict) or not isinstance(data.get("assets"), list):
        raise ValueError("assets must be a list")
    selected, rejected, seen = [], [], set()
    for i, row in enumerate(data["assets"]):
        if not isinstance(row, dict):
            rejected.append((i, "row must be an object"))
            continue
        asset_id, capacity = row.get("id"), row.get("capacity")
        valid_number = (type(capacity) in (int, float)
                        and math.isfinite(capacity) and capacity >= 0)
        if not isinstance(asset_id, str) or not asset_id.strip():
            rejected.append((i, "invalid id"))
        elif row.get("unit") != "MW" or not valid_number:
            rejected.append((i, "capacity must be finite nonnegative MW"))
        elif type(row.get("active")) is not bool:
            rejected.append((i, "active must be a boolean"))
        elif row["active"] and capacity >= 100 and asset_id not in seen:
            seen.add(asset_id)
            selected.append(asset_id)
    return selected, rejected

sample = '{"assets":[{"id":"wind-1","capacity":120,"unit":"MW","active":true},' \
         '{"id":"solar-1","capacity":80,"unit":"MW","active":true},' \
         '{"id":"bad-1","capacity":true,"unit":"MW","active":true}]}'
assert select_assets(sample) == (["wind-1"], [(2, "capacity must be finite nonnegative MW")])
assert select_assets('{"assets":[]}') == ([], [])
assert select_assets('{"assets":[{"id":"x","capacity":100,"unit":"MW","active":true}]}')[0] == ["x"]

Scroll sideways to view long lines.

EXPECTED RESULTThe fixture selects wind-1 and rejects row 2 as invalid capacity.
Follow-up
  • Which edge case would invalidate your contract?
  • What changes if the input or telemetry must be bounded?

Choose threads or processes from the workload

medium
Python & Machine Coding

Explain the difference between multi-threading and multi-processing in Python, specifically in the context of CPU-bound versus I/O-bound tasks.

Approach
  1. Separate CPU computation from waiting on network or storage.
  2. Confirm the Python runtime and whether the relevant work releases its interpreter lock.
  3. Compare process isolation and serialization overhead with thread-shared state and synchronization.
Follow-up
  • What happens if two callers reach this at the same time?
  • Where could this allocate more than you expect?

Record calls without changing the function contract

mediumWorked solution
Python & Machine Coding

Instrument select_assets with a decorator that preserves its return, exception and metadata.

Approach
  1. Wrap calls with functools.wraps.
  2. Return the original result and re-raise the original exception.
  3. Discuss bounded telemetry and what happens if the sink fails.
Worked solution 25 min
Python
import json
import math

def select_assets(raw):
    data = json.loads(raw)
    if not isinstance(data, dict) or not isinstance(data.get("assets"), list):
        raise ValueError("assets must be a list")
    selected, rejected, seen = [], [], set()
    for i, row in enumerate(data["assets"]):
        if not isinstance(row, dict):
            rejected.append((i, "row must be an object"))
            continue
        asset_id, capacity = row.get("id"), row.get("capacity")
        valid_number = (type(capacity) in (int, float)
                        and math.isfinite(capacity) and capacity >= 0)
        if not isinstance(asset_id, str) or not asset_id.strip():
            rejected.append((i, "invalid id"))
        elif row.get("unit") != "MW" or not valid_number:
            rejected.append((i, "capacity must be finite nonnegative MW"))
        elif type(row.get("active")) is not bool:
            rejected.append((i, "active must be a boolean"))
        elif row["active"] and capacity >= 100 and asset_id not in seen:
            seen.add(asset_id)
            selected.append(asset_id)
    return selected, rejected

sample = '{"assets":[{"id":"wind-1","capacity":120,"unit":"MW","active":true},' \
         '{"id":"solar-1","capacity":80,"unit":"MW","active":true},' \
         '{"id":"bad-1","capacity":true,"unit":"MW","active":true}]}'
assert select_assets(sample) == (["wind-1"], [(2, "capacity must be finite nonnegative MW")])
assert select_assets('{"assets":[]}') == ([], [])
assert select_assets('{"assets":[{"id":"x","capacity":100,"unit":"MW","active":true}]}')[0] == ["x"]

from functools import wraps

def record_calls(events):
    def decorate(fn):
        @wraps(fn)
        def wrapped(*args, **kwargs):
            try:
                result = fn(*args, **kwargs)
            except Exception:
                events.append((fn.__name__, "failed"))
                raise
            events.append((fn.__name__, "ok"))
            return result
        return wrapped
    return decorate

events = []
instrumented = record_calls(events)(select_assets)
assert instrumented('{"assets":[]}') == ([], [])
assert instrumented.__name__ == "select_assets"
try:
    instrumented('not json')
except json.JSONDecodeError:
    pass
assert events == [("select_assets", "ok"), ("select_assets", "failed")]

Scroll sideways to view long lines.

EXPECTED RESULTThe event list records one success and one JSON failure; metadata remains select_assets.
Follow-up
  • Which edge case would invalidate your contract?
  • What changes if the input or telemetry must be bounded?

Optimize a specified matrix computation

medium
Quantitative & Analytical Reasoning

How would you optimize an algorithm that processes large matrices of energy production forecasts to minimize time complexity?

Approach
  1. Define dimensions and the mathematical operation before naming a complexity.
  2. Measure the baseline and identify redundant work, sparsity or batching opportunities.
  3. Check numerical agreement, memory use and conversion costs after optimization.
Follow-up
  • How does this change if the input no longer fits in memory?
  • What is the worst case, and how likely is it on real data?

Editorial rehearsal plan. Adapt it to the confirmed team, level and assessment.

Small steps. Visible outcomes.0 / 7 completed
ONE WEEK · YOUR PACE

Prepare, practise & reflect

One practical outcome each day. Spend longer where you need it.

0 / 7 done
01Confirm the role
  • Ask for the actual format, language, duration, tools and quantitative component.
  • If quantitative reasoning is confirmed, rehearse it under the supplied timing; this guide does not verify a fixed question count.

Deliverable: Written format and stack questions

02Payload validation
  • Define the envelope and row contract before coding.
  • Validate units, finite nonnegative numbers and exact booleans.
  • Return indexed rejection reasons separately from filtered-out valid rows.

Deliverable: Runnable filter and edge cases

Practice prompt ↗Worked solution ↗
03Objects and substitution
  • Separate source parsing from filtering.
  • Define a substitutable read interface and a frozen result record.
  • Run a fake source through the same consumer and explain composition versus inheritance.

Deliverable: Fake source through one caller

Practice prompt ↗Worked solution ↗
04Decorator contract
  • Wrap calls with functools.wraps.
  • Return the original result and re-raise the original exception.
  • Discuss bounded telemetry and what happens if the sink fails.

Deliverable: Success, failure and metadata checks

Practice prompt ↗Worked solution ↗
05Authorized ingestion
  • Work the authorized three-row ingestion fixture; preserve units, source, time and parser version.
  • Replay the import and define tenant authorization, a stable cursor, errors and observation dates.

Deliverable: Three-row staging and replay trace

Practice prompt ↗Practice prompt ↗Worked solution ↗
06API contract
  • Define authorization before reading data.
  • Specify ordering, snapshot behavior and concurrent updates between pages.

Deliverable: Tenant, units, cursor and error specification

Practice prompt ↗
07Combined mock
  • Run the ingestion case under the editorial timing.
  • State the assumption you changed and why.

Deliverable: 45-minute case plus project reflection

Expand any day for tasks and deliverables. Your progress is saved on this device.

Prepare evidence of your own decisions, collaboration and learning. These are editorial prompts.

Unblock an engineer without taking the keyboard

easy
mentoringleasesat-least-once

A teammate has spent two days on a job handler that occasionally writes duplicate rows. They are certain the queue is delivering twice by mistake. You suspect a lease expiring under a slow handler, so the job is running concurrently with itself. Describe how you have unblocked someone in this position: what you asked before offering a hypothesis, what you showed them rather than told them, and what you left them owning. Then say what you would do if their theory turned out to be the right one.

Approach
  1. Ask before diagnosing, and ask for things answerable from data they already have: the attempt count on the job rows that produced duplicates, the handler's observed duration against its lease expiry, and whether the duplicate rows share a natural key that a unique constraint could have caught.
  2. Teach the shape rather than the answer. A lease cannot distinguish a dead worker from a slow one, so a handler that outruns its lease is running twice by design, and deploys deliver the other half by killing handlers mid-run on every rollout. Both of their candidate theories produce identical duplicate rows, which is why the evidence has to come from timings rather than from argument.
  3. Hand over a checklist they execute: a natural key on every write the handler performs so the second copy collides rather than appends, the record of intent written before any external effect, a lease heartbeat while running, and the metric that shows it working.
  4. Keep ownership with them deliberately. Pair on the first write, then step back; if you finish it yourself you have closed one ticket and left the same person stuck on the next redelivery.
Follow-up
  • How would you distinguish a genuine double-delivery from a lease expiry using only the data already stored?
  • Their handler calls an external endpoint before recording that it did. What do you tell them to change first?

Argue against a design, lose, and commit anyway

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

Narrate an outage you owned from page to postmortem

hard
incident responseblast radiuspostmortems

Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you lack exact numbers, explain the observable outcome honestly.

Approach
  1. Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
  2. Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
  3. Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
  4. Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.
Follow-up
  • What would you do differently in the first five minutes, given the same dashboard and no more information?
  • Which follow-up action did you deliberately not take, and why was dropping it the right call?
  • 01

    A teammate has spent two days on a job handler that occasionally writes duplicate rows. They are certain the queue is delivering twice by mistake. You suspect a lease expiring under a slow handler, so the job is running concurrently with itself. Describe how you have unblocked someone in this position: what you asked before offering a hypothesis, what you showed them rather than told them, and what you left them owning. Then say what you would do if their theory turned out to be the right one.

  • 02

    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.

  • 03

    Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you lack exact numbers, explain the observable outcome honestly.

PracHub preparation framework ↗
Does an OOP answer need inheritance?

Only when it provides a useful substitutable interface. Composition can be simpler. In the example, a source contract is useful because the format can change independently of filtering; separate subclasses for every field would add little value.

PracHub preparation framework ↗
Should I rehearse scraping a live site?

Use a supplied or authorized fixture. Discuss official APIs, licensed feeds and agreed access mechanisms before choosing scraping. Layout drift, units and replay are better practice problems than bypassing access controls.

PracHub preparation framework ↗
What should I say if I do not know the team’s stack?

Ask. Explain the parts of your solution that are language-independent, then prepare the stack named in the role description. Do not present this guide’s Python examples as proof that every Rystad team interviews in Python.

PracHub preparation framework ↗
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.