Finding a new role should be an exciting milestone. But for many engineers with a few years of industry experience under their belt, the standard tech interview process often feels disconnected from the actual job.
Across the industry, the standard playbook often requires candidates to spend their free time brushing up on abstract algorithms and data structures. While foundational computer science is important, these standard algorithmic problems pulled straight from popular interviewing platforms rarely reflect the daily reality of building complex, scalable software.
There was a time when Nordeus followed this standard playbook, too. We asked the typical algorithmic questions you might see at other major tech companies. But over time, we realized this approach had a major flaw: it forced professionals to spend weeks preparing for a highly specific type of test, rather than allowing them to showcase their practical, everyday engineering skills.
We realized we needed to change course to better respect our candidates' time and experience. We wanted our interviews to closely mirror what our people actually do on the job. Today, we draw a clear distinction in how we interview based on experience:
- For junior engineers starting their careers, we use smaller, focused problem-solving tasks to understand their foundational logic. But,
- For experienced engineers, whether you have some experience or are a seasoned senior, we drop you right into the codebase of a real-world game feature.
We stripped out the obscure internet puzzles to bring your interview preparation time down to an absolute minimum. But one rule has remained constant since day one: we don't do "gotchas". We have always firmly believed that interviews should never rely on hidden traps, trick questions, or obscure trivia to evaluate a candidate.
At Nordeus, we want to see your craft, not your weekend algorithm grinding and test preparation.
Our Philosophy: Simulating a Day on the Job
Our goal is to remove the barrier of "interview prep" entirely. This session is designed to reflect your daily work, requiring no special preparation. If you are an experienced engineer who knows how to design clean architecture, write maintainable code, and collaborate with a team, you are already fully prepared.
We aim to understand your thought processes, communication style, and ability to engineer quality software collaboratively, rather than seeking perfection.
Instead of a test, think of our two technical rounds as collaborative work sessions. We want to drop a real problem into your IDE, roll up our sleeves, and see how we work together to build something great.
What the Technical Round Actually Looks Like
Our technical loop consists of two 90-minute sessions. These can take place on the same day or be split across different days, depending on your schedule and preference. For both rounds, you can choose your preferred language (Java, C#, or Python), and you are welcome to use a Nordeus-provided laptop with VS Code or bring your own.
Here is exactly what to expect in each:
Round 1: Hands-On Coding & Code Review
This round is all about the micro-level of software craftsmanship: object-oriented design, clean code, and team collaboration.
- Part 1: Hands-On Coding. You'll be given a video game feature (or a portion of one) and asked to code its core functionality. We'll break it down into stages so you can build upon it. We are looking for separation of concerns, extensibility, and how you handle edge cases. While we expect production-quality code, we won't compile it, so small syntax mistakes won’t derail your interview.
- Part 2: Code Review. We will analyze a small code snippet together, similar to one found in a teammate's pull request. We want to see how you review code for correctness, readability, and design, and how constructively you point out improvements.
Round 2: System Design (Tailored to Your Domain)
This round zooms out to the macro-level. We want to see how you break down an ambiguous problem, map out a comprehensive architecture, and justify the technical trade-offs of your design.
Crucially, we won't ask you generic internet questions like "Design Instagram," and we tailor this round entirely to your domain. You don't need to spend weeks studying database sharding and load balancers if you are a frontend engineer!
Instead, we will give you a high-level, somewhat vague video game feature e.g., "Live Leaderboards" and ask you to take the lead on designing it for your side of the stack.
- If you are a Backend Engineer: We will focus on backend architecture. We want to hear how you handle millions of concurrent players, manage database loads, design your APIs, and resolve network bottlenecks.
- If you are a Frontend/Client Engineer: We will focus on client-side architecture. We want to hear how you manage complex application state, handle rendering performance, structure your components, and efficiently sync data with the backend over flaky mobile networks.
This round is dedicated to macro-level architecture and high-level design. We will map out the system, dive deep into component decisions, and embrace the trade-offs, all with zero coding required.
The Collaboration Factor
We want to know what it would feel like to brainstorm, build, and solve problems together as a team. Your interviewers are your potential future teammates, and they are there to help you solve the problem.
Tips for Success:
- Don't be shy: The interviewers are there to help you solve the problem. Share your ideas, even if you're not totally sure. If you stay quiet, we can't help you out. We love to see you work with us, not just tackle everything on your own.
- Understand the problem first: Ask clarifying questions to fully define the requirements, use cases, and constraints before you write a single line of code or draw a single box on the whiteboard.
- Embrace the Trade-offs (There is no "One Right Answer"): We don’t have a secret answer key. Software architecture is all about trade-offs. We don't care which valid approach you take; we just want to discuss why you chose it and its limitations.
Specifically for Round 1 (Hands-On Coding):
- Prioritize Clean Logic: Think aloud as you propose a modular, object-oriented, and testable design. Focus on readability and maintainability rather than prematurely optimizing for scale.
- Model the data: Talk through your data structures, interactions between components, and why you chose them.
- Catch the Edge Cases: We highly value candidates who proactively identify failure states and think about how to handle them gracefully.
Specifically for Round 2 (System Design):
- Start Simple, Then Deep Dive: Begin by designing a simple, high-level system as a baseline. Once that is agreed upon, dive deep into component-level decisions, explaining your APIs and data models.
- How your system handles the real world: We will want to discuss Availability (how does it handle failures?), Scalability (how does it handle traffic spikes?), and Extensibility (how easy is it to add new features later?).
Let’s Look at a Real Example: The Daily Quest System
To give you a concrete idea of what to expect, let’s walk through a real example: building the core logic for a Daily Quest System.
Part 1: Hands-On Coding
Interviewer: Design a system where players complete tasks to earn rewards (e.g., "Score 5 goals", "Train 3 different players"). The system should listen for game events, update active quests, and grant rewards when completed.
We aren't looking for an entire game engine from scratch. We are looking for a separation of concerns. An experienced approach separates the system into decoupled domains. For example, you might structure your core contracts like this:
public enum GameEventType
{
GoalScored,
MatchWon,
PlayerTrained
}
public class GameEvent {
public GameEventType Type { get; }
// A flexible payload to carry additional context about the event
public Dictionary<string, object> Payload { get; }
...
}
// Define the rules of a quest
public interface IQuestCondition {
bool IsMatch(GameEvent gameEvent);
int CalculateProgressDiff(GameEvent gameEvent);
}
// Define a common contract for giving items, coins, or XP
public interface IReward {
void Grant(Player player);
}
By establishing these contracts, your main Quest class becomes incredibly clean and completely agnostic to what the quest is or what it gives the player.
public class Quest {
public string Id { get; }
public int TargetProgress { get; }
public int CurrentProgress { get; private set; }
private readonly IQuestCondition _condition;
private readonly IReward _reward;
public Quest(string id, int target, IQuestCondition condition, IReward reward) {
Id = id;
TargetProgress = target;
_condition = condition;
_reward = reward;
}
// In a production system, ProcessEvent would need synchronization — either via lock, Interlocked, or by ensuring quests are processed on a single-threaded event loop.
public void ProcessEvent(GameEvent gameEvent, Player player) {
if (_condition.IsMatch(gameEvent)) {
CurrentProgress += _condition.CalculateProgressDiff(gameEvent);
if (CurrentProgress >= TargetProgress) {
_reward.Grant(player);
}
}
}
}
⚠️ Watch out for the completion guard. As written, if a player keeps scoring goals after completing a "Score 5 goals" quest, the reward keeps being granted on every subsequent event.
public class Quest {
public bool IsCompleted { get; private set; }
...
// In a production system, ProcessEvent would need synchronization — either via lock, Interlocked, or by ensuring quests are processed on a single-threaded event loop.
public void ProcessEvent(GameEvent gameEvent, Player player) {
if (IsCompleted) return;
if (_condition.IsMatch(gameEvent)) {
CurrentProgress += _condition.CalculateProgressDiff(gameEvent);
if (CurrentProgress >= TargetProgress) {
IsCompleted = true;
_reward.Grant(player);
}
}
}
}
With this setup, implementing a "Score Goals" condition is incredibly clean:
public class ScoreGoalsCondition : IQuestCondition {
public bool IsMatch(GameEvent gameEvent) {
// We only care about events where a goal was scored
return gameEvent.Type == GameEventType.GoalScored;
}
public int CalculateProgressDiff(GameEvent gameEvent) {
// Every time this event happens, increase progress by 1
return 1;
}
}
Once the basic system is outlined, we will introduce a new requirement to see how your design adapts.
Interviewer: "The game designers want a new quest: 'Win 3 matches in a row.' If the player loses a match, their progress must reset to 0."
This requirement exposes a limitation in CalculateProgressDiff. A reset isn't expressible as a simple delta. A first instinct might be to pass currentProgress into the method so the condition can return -currentProgress to cancel it out, but that creates an implicit contract between the condition and the caller's addition logic. If the call site ever changes, the reset silently breaks. This is a natural moment to evolve the interface. Instead of returning a delta and leaving the arithmetic to the caller, the method takes ownership of the full calculation:
// The contract is extended to take ownership of the full calculation and support progress resets
public interface IQuestCondition {
bool IsMatch(GameEvent gameEvent);
int CalculateNewProgress(GameEvent gameEvent, int currentProgress);
}
public class WinStreakCondition : IQuestCondition {
public bool IsMatch(GameEvent gameEvent) {
return gameEvent.Type == GameEventType.MatchWon || gameEvent.Type == GameEventType.MatchLost;
}
public int CalculateNewProgress(GameEvent gameEvent, int currentProgress) {
if (gameEvent.Type == GameEventType.MatchLost) return 0; // reset
return currentProgress + 1; // It's a win, add 1 to the streak
}
}
The Quest.ProcessEvent simplifies to a straight assignment:
public class Quest {
...
public void ProcessEvent(GameEvent gameEvent, Player player) {
if (IsCompleted) return;
if (_condition.IsMatch(gameEvent)) {
CurrentProgress = _condition.CalculateNewProgress(gameEvent, CurrentProgress);
if (CurrentProgress >= TargetProgress) {
IsCompleted = true;
_reward.Grant(player);
}
}
}
}
You have just demonstrated the Open/Closed Principle in action, and a natural API evolution driven by a real business requirement. Notice that ScoreGoalsCondition required only a mechanical update, renaming the method and passing currentProgress through, with no logic change. WinStreakCondition slots in cleanly without touching the Quest engine beyond replacing one line. This is the practical reality of OCP — you won't always predict every future requirement upfront, but when your abstraction reaches its limit, the seams should be obvious and the refactor intentional.
Unit Testing and Edge Cases
Since we don't compile the code, we won't obsess over minor syntax errors. However, we do care deeply about your testing mindset and attention to detail.
We want to hear you identify the "happy paths," but more importantly, the edge cases and failure states that often cause issues in live-ops environments. For example:
- Idempotency: What happens if a network retry fires the exact same "GoalScored" event twice?
- Overflow: What if a quest requires 5 goals, the player has 4, and a special event grants 3 goals at once?
Once we discuss what to test, we expect you to implement a few of those tests. We highly value clear, structured code, and we love seeing candidates use the AAA methodology (Arrange, Act, Assert) to keep their tests readable and focused.
Let's look at an edge case - Over-completion Exploit. What happens if a player has already completed the quest, but the client sends another valid "GoalScored" event? A naive implementation might just keep evaluating the progress and granting the reward over and over again.
An experienced engineer anticipates this and writes a test to guarantee the reward is only granted exactly once, no matter how many extra events come in:
[Test]
public void ProcessEvent_WhenQuestAlreadyCompleted_DoesNotGrantRewardAgain() {
// ARRANGE: Set up a quest that requires only 1 point
var mockCondition = new Mock<IQuestCondition>();
mockCondition.Setup(c => c.IsMatch(It.IsAny<GameEvent>())).Returns(true);
mockCondition
.Setup(c => c.CalculateNewProgress(It.IsAny<GameEvent>(), It.IsAny<int>()))
.Returns((GameEvent e, int current) => current + 1);
var mockReward = new Mock<IReward>();
var quest = new Quest("score_goal", 1, mockCondition.Object, mockReward.Object);
var player = new Player();
var gameEvent = new GameEvent(GameEventType.GoalScored);
// ACT: Fire the event TWICE (e.g., player scored 2 goals, but quest only needed 1)
quest.ProcessEvent(gameEvent, player);
quest.ProcessEvent(gameEvent, player);
// ASSERT: Reward granted exactly once, no matter how many extra events arrive
mockReward.Verify(r => r.Grant(player), Times.Once);
}
Part 2: Code Review
In the second part of the session, we shift gears. We will show you a code snippet—imagine it is a pull request from a newer teammate who was tasked with adding a new feature to the Quest System.
The Feature Request: "We are introducing a 'Premium Subscription'. If a player is a Premium, they should receive 2x progress on all daily quests. Furthermore, when they complete a quest, they get an additional bonus of 50 Gems on top of the normal reward."
Here is the code they submitted in their PR:
public void UpdateQuestProgress(Player player, Quest quest, GameEvent gameEvent) {
int progressToAdd = 1;
if (gameEvent.Type == "GoalScored" && quest.Type == "ScoreGoals") {
if (player.IsPremium) progressToAdd = 2;
quest.Progress += progressToAdd;
if (quest.Progress >= 5) {
player.Coins += 100;
if (player.IsPremium) player.Gems += 50;
}
}
else if (gameEvent.Type == "MatchWon" && quest.Type == "WinMatches") {
if (player.IsPremium) progressToAdd = 2;
quest.Progress += progressToAdd;
if (quest.Progress >= 3) {
player.Coins += 150;
if (player.IsPremium) player.Gems += 50;
}
}
}
What we look for: We aren't looking for you to just point out syntax tweaks. We expect an experienced engineer to look at the architectural implications of merging this PR and gently but firmly flag several major issues:
- Not Extensible (Violates Open/Closed Principle): The
if / elseif chain based on event strings is a nightmare for a live-ops game. Every time a game designer invents a new quest type (e.g., "Train a Player"), an engineer has to open this exact file, modify the core logic, and recompile. An experienced engineer would suggest relying on theIQuestConditionstrategy pattern we built in Phase 1 instead of hardcoding conditions here. - Poor Separation of Concerns: This single method attempts to handle everything: event matching, calculating progress modifiers, updating state, and distributing different types of currency. Directly mutating the
player.Coinsandplayer.Gemsinside a quest processing method means this code now has to know about the player's currency model. That responsibility belongs toIReward.Grant(), which is exactly what it was designed for. - Code Duplication: The Premium multiplier
if (player.IsPremium) { progressToAdd = 2; }and the bonus reward logic are copy-pasted for every quest type. If the product manager decides next week that Premium users should actually get 3x progress, the developer has to hunt down and change it in dozens of places. An experienced engineer might suggest using a Decorator Pattern to wrap the baseIQuestConditionorIRewardinterfaces with the Premium modifiers. - Magic Numbers and Strings: Values like 5, 100, 3, 150, and 50 are hard-coded into the logic. These targets and rewards should be data-driven (e.g., passed in via the
Questconfiguration object from a database or JSON). Hardcoding them makes the game impossible to tune via live-ops without a full app update. quest.Progressis public mutable: Directly settingquest.Progressfrom outside the class breaks the encapsulation — theQuestshould own its own state transitions- No completion guard: if a player keeps scoring goals after completing a quest, the reward keeps being granted on every subsequent event.
Join Our Team
At Nordeus, we value your engineering craftsmanship, your ability to write maintainable code, and your collaborative spirit over rote memorization. We want to hire great software engineers, not great test-takers.
If you are an experienced engineer who wants to tackle complex, real-world architectural challenges alongside a supportive team without spending your weekends grinding algorithms, we would love to meet you!
You can learn more about the entire interview process in this video: Acing the interview game at Nordeus.
Check out our open engineering roles on our Careers page and come build games with us.