August 4, 2025
Fatty Structs: Casey Muratori’s Approach to Data Structure Design
Understanding the philosophy behind dense, practical data structures in performance-critical programming

By Kristiyan Ivanov
5 min read
If you've spent any time in the game development community or followed discussions about performance programming, you've likely encountered the name Casey Muratori. Known for his work on game engines, his "Handmade Hero" project, and his outspoken criticism of modern programming practices, especially OOP, Muratori has consistently advocated for a more direct, performance-oriented approach to software development.
Now, here's something interesting I discovered while researching this article: the term "fat struct" appears to have been coined by Muratori himself. In discussions on the Handmade Network forums about entity systems, Casey directly uses this terminology when explaining his approach to data structure design, describing how a "fat struct" approach can be more effective than discriminated unions for game entity systems. This perfectly encapsulates one of the core principles underlying his programming philosophy: the idea that data structures should be dense, practical, and designed around actual usage patterns rather than abstract theoretical principles.
What Are Fatty Structs?
In the context of Muratori's programming philosophy, "fatty structs" refer to data structures that:
- Pack related data together rather than spreading it across multiple objects or classes (as opposed to the OOP paradigm)
- Prioritize cache efficiency by grouping frequently-accessed data
- Avoid unnecessary indirection that comes from object-oriented design patterns
- Include all necessary context within a single structure to minimize dependencies
Think of them as the antithesis of the "lean" objects promoted by clean code methodologies — instead of many small, specialized objects, you have fewer, more substantial structures that contain everything needed for a particular operation.
The Problem with Traditional OOP Structures
To understand why Muratori advocates for this approach, consider how traditional object-oriented programming typically structures data:
// Traditional OOP approach
class Shape {
public:
virtual double getArea() = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() override { return 3.14159 * radius * radius; }
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() override { return width * height; }
};// Traditional OOP approach
class Shape {
public:
virtual double getArea() = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() override { return 3.14159 * radius * radius; }
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() override { return width * height; }
};This design follows clean code principles: each class has a single responsibility, they're polymorphic, and they hide their internal details. However, when you need to process thousands of shapes in a game engine running at 60 FPS, this structure creates several problems:
- Memory fragmentation: Each object is allocated separately
- Cache misses: Related data is scattered across memory
- Virtual function overhead: Each method call requires indirection
- Poor compiler optimization: The compiler can't see through the abstraction
The Fatty Struct Alternative
Muratori's approach would flatten this into something more like:
enum ShapeType {
SHAPE_CIRCLE = 0,
SHAPE_RECTANGLE,
SHAPE_TRIANGLE,
// ... more types
};
struct Shape {
ShapeType type;
// Union or just pack all possible data together
union {
struct { float radius; } circle;
struct { float width, height; } rectangle;
struct { float base, height; } triangle;
};
// Maybe include additional commonly-needed data
float cached_area;
int material_id;
float last_update_time;
// ... other frequently accessed properties
};
float GetArea(Shape* shape) {
switch(shape->type) {
case SHAPE_CIRCLE:
return 3.14159f * shape->circle.radius * shape->circle.radius;
case SHAPE_RECTANGLE:
return shape->rectangle.width * shape->rectangle.height;
case SHAPE_TRIANGLE:
return 0.5f * shape->triangle.base * shape->triangle.height;
}
return 0.0f;
};enum ShapeType {
SHAPE_CIRCLE = 0,
SHAPE_RECTANGLE,
SHAPE_TRIANGLE,
// ... more types
};
struct Shape {
ShapeType type;
// Union or just pack all possible data together
union {
struct { float radius; } circle;
struct { float width, height; } rectangle;
struct { float base, height; } triangle;
};
// Maybe include additional commonly-needed data
float cached_area;
int material_id;
float last_update_time;
// ... other frequently accessed properties
};
float GetArea(Shape* shape) {
switch(shape->type) {
case SHAPE_CIRCLE:
return 3.14159f * shape->circle.radius * shape->circle.radius;
case SHAPE_RECTANGLE:
return shape->rectangle.width * shape->rectangle.height;
case SHAPE_TRIANGLE:
return 0.5f * shape->triangle.base * shape->triangle.height;
}
return 0.0f;
};This "fatty" structure might seem less elegant from a clean code perspective, but it offers significant advantages:
- Cache efficiency: All shapes are the same size and can be stored in a contiguous array
- No indirection: The compiler can optimize the switch statement effectively
- Predictable memory layout: Easy to reason about memory usage and allocation
- Flexible data inclusion: Can easily add fields that multiple shape types need
Real-World Application: The Banking Pattern
One of Muratori's documented techniques that exemplifies the fatty struct concept is what he calls "banking" data in editor code. As he explains in his blog posts about The Witness editor development:
struct ListerFilter {
// Instead of separate objects, pack all filter state together
bool update_automatically;
EntityType include_types[MAX_ENTITY_TYPES];
int include_type_count;
// Banking pattern - store multiple configurations
ListerFilter saved_configurations[8];
int current_config;
// Additional context that might be needed
float last_update_time;
int viewing_id_count;
EntityID viewing_ids[MAX_VIEWING_IDS];
};struct ListerFilter {
// Instead of separate objects, pack all filter state together
bool update_automatically;
EntityType include_types[MAX_ENTITY_TYPES];
int include_type_count;
// Banking pattern - store multiple configurations
ListerFilter saved_configurations[8];
int current_config;
// Additional context that might be needed
float last_update_time;
int viewing_id_count;
EntityID viewing_ids[MAX_VIEWING_IDS];
};This structure violates several clean code principles — it's not focused on a single responsibility, it exposes its internals, and it's quite large. But in practice, it's incredibly efficient because:
- All related state is in one place
- Switching between configurations is just a memory copy
- Cache locality is maximized
- No dynamic allocation needed
When to Use Fatty Structs
Muratori's approach isn't universally applicable — it's particularly valuable when:
Performance is Critical In game engines, audio processing, or other real-time systems where every microsecond matters, the cache efficiency and reduced indirection of fatty structs can make a significant difference.
Data Access Patterns are Known When you understand how data will be accessed together, you can design structures that optimize for those patterns rather than abstract theoretical use cases.
Iteration is Common If you frequently process collections of similar objects, having them in a uniform, contiguous layout dramatically improves performance.
Allocation Overhead Matters Dynamic allocation and deallocation can be costly and unpredictable. Fatty structs often enable simpler memory management strategies.
The Philosophical Divide
The fatty struct approach represents a fundamental philosophical difference about software development priorities:
Clean Code Philosophy: Code should be easy to understand, modify, and extend. Performance can be optimized later if needed.
Muratori Philosophy: Code should directly reflect what the computer actually does. Abstractions that hide this reality often create more problems than they solve.
This isn't to say that one approach is universally correct — they optimize for different constraints. Clean code principles work well for business applications where developer productivity and maintainability are paramount. Fatty structs excel in performance-critical domains where the cost of abstraction is prohibitive.
Practical Implementation Tips
If you're interested in applying fatty struct principles:
- Start with your actual data access patterns: Don't design in the abstract — look at how your code actually uses data.
- Measure before and after: The performance benefits should be measurable and significant.
- Consider the trade-offs: You're trading some maintainability for performance — make sure it's worth it.
- Use unions judiciously: They can save space but make the code harder to debug.
- Document your layout decisions: Future maintainers will need to understand why you chose this approach.
Conclusion
Fatty structs represent more than just a data structure design choice — they embody a different way of thinking about the relationship between code and the underlying hardware. While Casey Muratori's approach may seem radical compared to mainstream software development practices, it offers valuable insights for anyone working in performance-critical domains.
The key insight is that sometimes the "cleaner" solution according to software engineering principles isn't actually cleaner when you consider the full system — including the CPU, memory hierarchy, and compiler optimizations. By designing data structures that work with these systems rather than abstracting them away, fatty structs can deliver significant performance improvements where they matter most.
Whether you adopt this approach wholesale or simply use it to inform your design decisions, understanding the principles behind fatty structs provides valuable perspective on the trade-offs inherent in all software design choices.
The next time you're designing a data structure, ask yourself: am I optimizing for the abstract ideal, or for the reality of how this code will actually run?
Further Reading:
- Casey Muratori's blog at caseymuratori.com
- "Semantic Compression" and other programming philosophy articles
- The Handmade Hero project for practical examples of these principles in action