Unmasking the ECS Design Pattern: A Re-invention of Existing Concepts?
Posted on July 20th, 2025 #programming
The Entity Component System (ECS) design pattern is popular with game developers. It’s celebrated as a modern data-oriented paradigm, standing in stark contrast to traditional Object-Oriented Programming (OOP). But what if ECS isn’t an entirely novel invention? What if it’s actually a clever re-packaging of long-standing programming concepts, driven by the unique needs of game development?
The ECS Design Pattern#
Let’s begin with a quick refresher on what ECS is, conceptually:
- Entity
- It represents a “thing” in a game (e.g. a player, a tree, a bullet). Think of it as an empty container.
- Component
- This is pure data. It describes a specific attribute of an entity (e.g.
Position{ x, y, z }
orHealth{ value }
). An entity gains capabilities by having components attached to it. - System
- This is pure logic. It processes entities with a specific set of components and performs operations on that component data. For example, a “movement system” might process all entities that have both a
Position
and aVelocity
component.
Design Patterns as Missing Language Features#
A design pattern is a general, reusable solution to a common problem that occurs within a given context in software design. There have been many books published about design patterns, one of the most famous being the “Gang of Four” book.
One of the criticisms of design patterns is that many of them appear to be workarounds for missing language features. Peter Norvig demonstrated that 16 of the 23 classic design patterns in the “Gang of Four” book are either simplified or eliminated by more powerful language constructs. This is further reinforced by various academic papers like "On the Interaction of Object‑Oriented Design Patterns and Programming Languages" by Gerald Baumgartner, Konstantin Läufer, and Vincent F. Russo.
This makes me wonder: What underlying language features is the ECS design pattern effectively a workaround or implementation of?
Mixins#
Mixins are an object-oriented concept that allows a programmer to inject some code into a class. Instead of reusing code through inheritance, you “mix in” the code into the class instead.
Mixins were introduced by the Flavors programming language, an early object-oriented extension to Lisp. Its creator, Howard Cannon, was inspired by a local Ice Cream Parlor shop which offered basic flavors of ice cream (vanilla, chocolate, etc.) and blended them into a combination of extra items (nuts, chocolate chips, fudge, etc.). These extra items were called “mix-ins".
Bringing this back to ECS: The Entity is the ice cream and the Component is the “mix-in".
The idea is you take your ice cream (the entity) and mix-in the nuts and chocolate chips (the components).
More technically, you take a Player
entity and mix-in a Position
and Health
components.
Reflection#
Reflection is the ability for a programming language to examine and modify its own structure and behavior at runtime. In object-oriented programming languages, reflection might allow the inspection and modification of classes, interfaces, fields, and methods.
In languages with mixins, reflection could allow the addition or removal of mixins from objects at runtime. Reflection could also be used to check which mixins an object has before passing them off to a function that operates on them.
Connecting the Features#
Suppose we have a multi-paradigm programming language that has objects, free functions, and supports both mixins and reflection. In such a hypothetical language, we could reproduce the ECS pattern using native features:
- Entity
- This is an “empty” object (e.g. a
Player
object). - Component
- This is an object with state (your “mix-ins,” like a
Position
orHealth
object). - System
- This is a free-function that uses reflection to check if the entity has the required mixins (components) and then operates on them.
To reproduce ECS:
- Instantiate an empty object (the Entity).
- Instantiate the stateful objects (the Components).
- Use reflection to mix-in the components into the entity object.
- Pass the entity object to free functions that can operate on objects possessing the mixins (the System).
This is, in effect, ECS but represented entirely by native language features.
Mixins Aren’t Strictly Required#
It’s important to clarify that mixins are not strictly required to achieve this. You might prefer not to use mixins if, depending on the programming language, their members occupy the same namespace as the entity object and risk naming conflicts with other component members or existing object fields. In this case, you could rely solely on reflection:
To reproduce ECS without mixins:
- Instantiate an empty object (the Entity).
- Instantiate the stateful objects (the Components).
- Use reflection to inject the components as distinct fields onto the entity object.
- Pass the entity object to free functions that can operate on objects possessing the fields (the System).
The “Rediscovery” by Game Developers#
If mixins and reflection have existed for decades, why is ECS often presented as an innovation, popularized by game developers?
In my opinion, the answer lies in the constraints of statically typed languages, which dominate the game industry. While incredibly powerful for performance, languages like C++ inherently lack the built-in runtime dynamism, flexible reflection, and mixin capabilities found in languages like Ruby or Lua.
Thus, ECS emerged as a pragmatic and highly effective design pattern to mimic these dynamic, compositional capabilities within a more rigid, compiled environment. It’s an architectural solution that allows C++ programmers to achieve the same kind of runtime flexibility that is often a native feature in other languages, while simultaneously offering superior control over data layout for performance.
Closing Thoughts#
I sometimes see “ECS frameworks” being announced for programming languages that already possess the inherent dynamism and reflective capabilities that ECS simulates. Hopefully, this exploration of ECS offers a new perspective. It’s a testament to clever design and engineering that a pattern can effectively bring the benefits of one paradigm into the ecosystem of another, proving that sometimes, the “new” is simply a brilliant re-invention of the familiar.