Someone in the Brainfart Studio Discord mentioned looking for a game dev job. Ever since that conversation started, I’ve been thinking about what I believe the bare minimum should be.

I want to be upfront: I’m not a game industry veteran. I’m a professional software developer by trade, and I’ve been part of the hiring process on that side of things. Now I’m building games and trying to build a studio. That combination gives me a unique perspective, though not necessarily the ‘industry standard’ one.

Take this as an informed opinion. The things I would be looking for if I were hiring a programmer. The goal is to put together a practical roadmap for programmers who are trying to figure out where to focus their time. Even if not the industry standard, I still believe all of these are practical and necessary for good programmers.

Core Programming Fundamentals

This covers the language and structural knowledge a developer needs before anything else.

Object-Oriented Programming

OOP is the foundation of most game codebases. The four core building blocks are classes, inheritance, interfaces, and composition. Each one has a specific job, and game development uses all of them.

Composition is the most naturally used in game development specifically. Most modern engines are built around it. A player isn’t one monolithic class. It’s a collection of focused components: movement, health, input, animation. Each component owns one thing or does one thing. Understanding composition well is worth prioritizing early.

Inheritance is the next logical step. It’s useful for sharing behavior across related types. This comes up regularly in enemy hierarchies, UI elements, and item systems. The key is understanding the parent-child relationship. The parent class defines the shared behavior. The child class inherits it and extends it where needed. Applying it outside of that use case creates structural problems as the codebase grows.

Interfaces are a more abstract concept. In my opinion, that means more time should be spent properly learning these. An interface defines a contract. Any class that implements it agrees to expose a specific set of methods or properties. Unrelated types get treated the same way by other systems, without those systems needing to know anything about the types themselves. That pattern keeps systems decoupled in the codebase.

Knowing when to reach for each tool is key. Each one solves a different problem. Recognizing which problem you are facing and which tool solves it is a skill that develops the more projects you create.

Data Structures

Games process data on every frame. Movement calculations, collision checks, AI decisions, audio triggers. The volume and frequency of that processing means the way data is stored and accessed has a direct impact on how the game performs. Choosing the right data structure for a given situation is part of writing efficient game code.

Arrays, lists, dictionaries, and queues each handle data differently. An array is fast to iterate but fixed in size. A list is flexible but slower to search. A dictionary gives fast lookups by key. A queue processes things in the order they arrived, which matters for things like input buffers or event queues.

A concrete example: storing every enemy in a scene. An array works if the count never changes. A list works if enemies spawn and die during play. A dictionary works if enemies need to be looked up by ID. In a small project the difference is negligible. In a scene with hundreds of enemies updating every frame, the wrong choice shows up as a performance problem that slows the game down.

The Game Loop

The game loop is one of the things that makes game development genuinely different from most other programming. Web apps respond to events. Mobile apps wait for user input. A game runs continuously, every single frame, reading input, updating state, and rendering the result regardless of whether the player did anything or not. That constant cycle is the heartbeat of the entire program.

Two concepts in the game loop are particularly worth focusing on:

The first is delta time. This is the amount of time that elapsed since the last frame. Without it, movement speed is tied to frame rate. A player running at 60fps moves twice as fast as one running at 30fps. Multiplying velocity by delta time keeps behavior consistent across any hardware.

The second is fixed update. Physics calculations require a consistent timestep to produce stable, predictable results. Fixed update runs on its own independent cycle at a set interval, decoupled from the frame rate entirely. A frame can run fast or slow and the physics timestep stays the same. That separation keeps physics stable regardless of what the rest of the game is doing.

Systems Thinking

Writing a script that works is one skill. Planning how that script fits into a larger codebase, what it depends on, what depends on it, and how it communicates with everything around it is a different skill entirely. In my experience, it’s the one that separates good programmers from great ones. And it doesn’t get practiced enough.

Designing Before Building

Before writing code for a new feature, a developer should be able to describe the system on paper first. What specifically does it own? Does it need anything from other systems? What does it produce or trigger when something changes?

I use a Technical Design Document for this. Before a single line of code gets written, I list every major system the game needs, what each one is responsible for, and how they connect. That process identifies missing pieces and dependency problems while they’re still easy to fix. Systems that aren’t planned tend to get bolted on later. They tend to directly reference other systems and classes, creating unintended dependencies.

If you want to go deeper on how I approach this, I covered the full process here: Technical Design Documents: System Planning for Programmers.

State Machines

A state machine is a structure that defines a set of states and the rules for moving between them. Only one state is active at a time. Transitions between states are explicit and controlled.

State machines show up constantly in game development. An enemy has states like patrolling, chasing, attacking, and dead. An animation controller has states like idle, running, jumping, and falling. A UI screen has states like idle, transitioning in, and transitioning out. The pattern applies almost everywhere a system needs to behave differently depending on what’s currently happening.

Knowing how to implement one from scratch is important. That implementation knowledge is what makes the built-in state machine tools in engines easier to use correctly, because the underlying logic they’re abstracting is understood.

Separation of Concerns

Each class should have one clearly defined job and own only the data that job requires. A player controller handles movement. A health component tracks hit points. A UI manager updates the display. Each system is responsible for its own piece and nothing else.

The mechanism that connects them is event-driven architecture. Rather than systems holding direct references to each other, they communicate by firing events. When the health component registers a hit, it fires a health changed event and carries the new value as a payload. The UI manager has subscribed to that event and updates the health bar in response. The player controller has no reference to the UI. The UI has no reference to the damage calculation logic.

Event-driven architecture is the pattern that makes this scalable. A new system that needs to respond to a health change subscribes to the existing event. The systems that already fire and consume that event need no changes. The same event can have one listener or ten. The firing system has no knowledge of any of them.

Reusability is the long-term result. A health component that owns its own data and communicates only through events carries over to the next project without changes. The same applies to an input manager, an audio manager, a save system. Building systems this way from the start means each project inherits working pieces from the last one.

Version Control

Version control is used across the industry. Every developer on a team needs a basic working knowledge of it. But a programmer specifically should understand how to use it properly, not just functionally.

I have a series on version control strategies if you would like to go more in depth on this topic.

Branching Strategy

A good branching strategy keeps work separated intentionally. The most common structure has three levels:

  • A main branch that only ever holds shipped, production-ready code
  • A development branch that holds everything currently integrated and in progress
  • Feature branches where all active work actually happens

Every system gets its own branch. A save system is a branch. A refactor of the player controller is a branch. A bug fix is a branch. Work stays isolated until it’s finished, reviewed, and ready to merge. That separation means a broken feature can’t contaminate the rest of the project, and multiple people can work in parallel without stepping on each other.

Commit Strategy

Each commit should represent one logical unit of work. One new component. One bug fix. One refactor of a single system. Keeping commits scoped this way means any single change can be identified, reviewed, or rolled back in isolation without affecting unrelated work bundled into the same snapshot.

Commit messages document what changed and why. A prefix categorizes the type of change: feat for new functionality, fix for bug fixes, refactor for restructured code that doesn’t change behavior. After the prefix comes a specific description of what actually changed.

A commit that says fix stuff provides no information about what was broken, what was changed, or where to look. A commit that says fix - clamp player velocity before collision check to prevent tunneling identifies the problem, the solution, and the location. Six months into a project, that specificity is what makes a commit history useful for diagnosing problems and understanding past decisions.

Pull Requests

A pull request is the review step before a branch merges. It produces a full diff of every file and every line that’s about to change, before anything is finalized. On a team, a second developer reviews it. Solo, you review it yourself. That review step identifies problems that are easy to miss while writing code.

Writing code and reviewing code use different parts of the brain. During development, the focus is on making something work. During a pull request review, the focus shifts to evaluating whether it was built correctly. That shift in perspective is where developers start noticing patterns in code, recognizing where structure broke down, and making better architectural decisions.

Engine Knowledge

Honestly, I debated including this section. Engine knowledge is important. But in my opinion, strong OOP fundamentals and systems thinking will carry a developer further. Engines are built on those principles and can be learned easier. That said, walking into a job interview with zero engine knowledge is a harder sell. So here’s my opinion on what is worth knowing.

Unity is used for indie, mobile, and mid-scale projects. It uses C# and has a large asset ecosystem and extensive documentation. The component-based architecture mentioned earlier is the core design philosophy. It’s a great engine for a developer coming from an OOP background.

Unreal is the standard in AAA development and anything requiring high-fidelity visuals. It uses C++ for performance-critical code and Blueprint (a visual scripting system) for gameplay logic. The learning curve is steeper, but the rendering capabilities and built-in tooling are significantly more powerful.

Godot is open source and lightweight. It uses GDScript, which is a Python-like language designed specifically for the engine (though C# support does exist). It’s grown quickly in the indie space, particularly after Unity’s pricing changes in 2023.

What to Know Across All Three Engines

Four areas come up on almost every project regardless of engine.

Physics covers how collisions are detected. Animation covers how states are structured in an animation controller and how code drives transitions between them. Audio covers how sounds are triggered, managed, and routed at a basic level. Scene and asset management covers how scenes load and unload, how prefabs (or equivalent structures) work, and how assets are referenced without creating unnecessary dependencies.

These concepts transfer across engines. Unity calls it a Rigidbody. Unreal calls it a Physics Body. Godot calls it a RigidBody3D. The implementation details differ. The underlying problem each one is solving is the same. Learning these systems in one engine lowers the learning time when switching to another.

A Shipped Project

A finished, playable project gives the systems thinking and architecture work somewhere concrete to point to. The project demonstrates scope awareness and the ability to resolve problems that only appear when all systems are running together. The ability to walk through why a system was structured a specific way, what the tradeoffs were, and what would be approached differently demonstrates that the architectural decisions were intentional.

Game jams are worth considering specifically for this. The hard deadline forces scope decisions that open-ended projects allow developers to defer indefinitely. It’s a finished project with a clear set of decisions that can be explained and defended.

Conclusion

The skills covered here sit underneath every programming role in game development. Object-oriented programming provides the structure the codebase is built on. Systems thinking determines how that structure is planned and organized before code is written. Version control keeps the work tracked, recoverable, and collaborative.

These are all the skills I personally would look for when hiring a programmer. Role-specific work builds on top of this foundation. Combat systems, tools development, AI, shaders, and networking all require these fundamentals to be in place first. The specifics of any given role are built on top of these.

If you’re working toward a first game dev job, this is where I recommend focusing. Pick one area, learn it intentionally, and move to the next. Each one compounds on the others.

Categories: Tutorials

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *