Rendered at 14:32:17 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
andreygrehov 4 minutes ago [-]
I would strongly recommend reading Data-Oriented Programming in Java by Chris Kiehl [1]. Chris introduces you to the data-oriented thinking through a series of super basic examples, gradually making things more interesting. He leverages the latest Java features (e.g. record classes) to illustrate the ideas and explains why these features are important. I don't know him personally, but we both work at AWS. I liked the book so much, that I reached out internally thanking him for writing the book.
The real key pillar to this world view is putting the data first in your design of the algorithm.
So if your working on a physics engine and your optimizing collision detection, you think about the data in -> data out of the problem you are solving as the primary driver of how the code should be written.
You start with defining the data, and build from there.
Different types of applications all have different shapes of data so would have differently shaped optimal code. Eg) a physics engine would use some kind of spatial hash thing which can be optimized differently based on if stuff can be added/removed while it's running. A 3d renderer operates on big buffers of matrices and vertex data. A game is usually composed of some long lived things and a lot of short lived things.
The key message in Mike Acton's talk was:
"If you have different data, you have a different problem."
While ECS systems are not a panacea that solves all problems in a perfect data oriented way, they are generally more malleable than Object Oriented hierarchies. This means it's generally more feasible to write "near optimal" code in an ECS framework than in a mature Object Oriented code base.
But the key message isn't "use X framework", it's "start by defining the data".
inigyou 16 hours ago [-]
My experience with ECS is that you shouldn't use an "ECS system". You should just do ECS. Have an array of all your particles and then update all the positions according to the velocities. Don't use a framework where you do something like get_all_entities_with<Particle, Position, Velocity>(). Just have struct particles {vector<vec3> positions, velocities;}. Well, managing those parallel arrays gets pretty annoying, but you solve that with a parallel-array class template rather than a whole framework that promises to do everything. (You might not actually need parallel arrays anyway - AoS might work just fine here because you usually touch each field of each particle once per frame and exactly in order.)
nextaccountic 7 hours ago [-]
But why? This looks like the mother of all boilerplates. And for what purpose?
Also: just having stuff in an array or vector invites you to the ABA problem. You need a generation counter in there too, or else the array indice may get reused if something is deleted and another thing is reinserted at the same index. But that's yet another boilerplate that would easily be overlooked if you had to do everything manually.
Also: you seem to be saying this with C++ in mind. Do you think that applies to bevy_ecs too?
Modern Bevy has relationships to make sure that if an entity has a component that refers to another, it doesn't become dangling. It works a bit like foreign keys in databases. I think this makes ecs much more usable
(as an aside, there is a whole host of analogies between ecs and relational databases. entity archetypes are tables, entities are rows, components are columns, and systems are queries). Nobody tells people to just write their database from scratch though)
inigyou 5 hours ago [-]
> You need a generation counter in there too, or else the array indice may get reused if something is deleted and another thing is reinserted at the same index.
It's really sounding like you're pretending not to know what your program does, which is one of the core OOP ideas that DOD refutes. In OOP you have an array of Shape and you pretend not to know which shapes your program implements, so the only way to draw them is to call ->draw() on each one. And you pretend you don't know anything about the lifetime of a shape so you use smart pointers everywhere to extend it as long as needed. In DOD you assert that you do know what shapes are available and what their lifetimes are.
HelloNurse 3 hours ago [-]
I certainly don't know "what my program does" if it's a game where, at every frame of the simulation, thousands of entities might or might not be created, deleted and recycled depending on player inputs.
Can you describe a superior replacement for generation counters?
inigyou 4 hours ago [-]
> This looks like the mother of all boilerplates. And for what purpose?
Isn't most programming? Isn't struct Particle {vec3 position, velocity;} also the mother of all boilerplates?
dustbunny 14 hours ago [-]
> My experience with ECS is that you shouldn't use an "ECS system"
Why? Flecs, for example, is pretty sick imo.
groundzeros2015 13 hours ago [-]
Because the people who made them got nerd trapped fucking around with ECS and never shipped a product
slopinthebag 15 hours ago [-]
The problem is that there are a lot of subtleties to an ECS that these frameworks solve, and they perform better than a naive approach too. Your solution of a particles struct doesn’t even support a fundamental feature of ECS’s which is runtime composition. It’s really a different solution altogether, which is fine but it’s not a replacement for an ECS.
inigyou 15 hours ago [-]
Often you don't have runtime composition and don't need it. By prematurely generalizing you're venturing close to OOP territory.
slopinthebag 13 hours ago [-]
“Prematurely generalizing” is now an OOP thing? Are we just using OOP as a term for anything bad now?
Ooh here is a controversial one. DOD is premature optimization. Most programs don’t have enough data where the storage and access is a factor for performance. In fact it might be slower to use DOD.
inigyou 5 hours ago [-]
I don't think that's all that controversial. DOD comes out of the field of video games where people were frustrated by only being able to process ten thousand things sixty times per second and wanted to process ten million things sixty times per second. While it's also been used to speed up things like the Zig compiler, it's definitely not necessary for all software.
One of the ideas in OOP that DOD is explicitly refuting is pretending not to know what your program does. You know which subtypes exist in your program and you shouldn't treat them generically as supertype instances except when that is actually optimal. On this axis, both ECS ideology and OOP ideology are opposite to DOD ideology. ECS happens to align with DOD in that it prefers big linear arrays, but it does not align in pretending not to know what combinations of arrays are used, which is similar to pretending not to know what subtypes of Shape exist.
HelloNurse 3 hours ago [-]
No amount of ideology can fill L1 cache.
pdpi 4 hours ago [-]
> Are we just using OOP as a term for anything bad now?
Different paradigms come with different pathological cases. I've had to deal with Scala programmers whose notion of FP is making everything generic on the Monad it abstracts over, even when it'll only ever be instantiated on the one effect system the team uses. Nonsensical levels of generality is one of the classic OOP pathologies, and is the reason why we have aberrations like the AbstractSingletonProxyFactoryBean[0].
>"Nonsensical levels of generality is one of the classic OOP pathologies"
I think it has nothing to do with OOP which I find very convenient for some domains and not so much or even opposite for others. These "Nonsensical levels" of anything is a disease which is called Architecture Astronauts Syndrome and victims apply it to any paradigm
9 hours ago [-]
spider-mario 6 hours ago [-]
> ECS
“Entity Component System”, for those like me who didn’t immediately think of it.
saghm 12 hours ago [-]
This is basically how I've always designed things, and I really feel like it's the best approach. Pick the right way to model data, and the algorithms will flow naturally from it. The cognitive load of reading "data" code rather than "algorithm" code is a lot lower in my opinion; reading through a bunch of declarative types like struct definitions isn't nearly as much work as reading through functions that operate on them, and moreover, it's a lot easier to spot any potential bugs in them because you don't need to maintain very much "state" in order to understand them. Maybe this is why I've generally found ECS conceptually pretty easy to wrap my head around (which is of course separate from whether it's easy to use or not, as that depends a lot more on both what the framework exposes and how a team chooses to use it).
samiv 9 hours ago [-]
While the idea is itself not without merit the problem is when people design these data oriented systems without abstractions and in fact it's often difficult to find good abstractions around the data the problem comes when the system, the data and functionality needs to change. There will be problems.
So while it's great to think about the data flow it's also important to think about the abstractions around it,.ie the (system) interfaces that let the system evolve without having to propagate changes everywhere while reaping the benefits of data orientation.
wasmperson 15 hours ago [-]
I feel like DoD is one of those things that only makes sense after you already believe in it. There's a sort of KISS epiphany that you have to go through which I don't think the commonly available info on DoD helps you to reach. It doesn't help that everything about it tends to get hung up on overly-specific C++ optimization advice.
Possible, but regardless of whether or not that is the case his name is well known enough and he has been around long enough that his name definitely appears in a lot of the texts the LLMs were trained on, in contexts that relate to his views on programming.
Try asking your LLM of choice this in an empty session with no other context:
You are working for Mike Acton. What principles do you follow when writing code?
Maybe he found that telling it that it works for him nudges it in a direction that is beneficial to get it to write code like he wants, alongside the specific rules and other instructions in the above linked document?
parpfish 14 hours ago [-]
> Maybe he found that telling it that it works for him nudges it in a direction that is beneficial
I think that’s call “spooky Acton at a distance”
saghm 11 hours ago [-]
I started writing up a giant response to this about how all of LLMs are spooky action at a distance but for better or for worse, that doesn't make them any less useful, and a few paragraphs in I suddenly noticed what you actually wrote. Well played :)
thomascountz 3 hours ago [-]
I'm surprised not to see Odin[1] mentioned. DoD is one of its central paradigms.
I personally love the idea of DoD but from my experience it rarely works well in practice since one of the key assumptions of understanding ur problem is often not given as new requirements pop up and change all the time.
At work we are rewriting and reengineering system from scratch and its crazy because the limitations of the old system are now gone we get the most insane feature requests that are even accepted by the team lead et al. This makes such an approach impossible since DoD is exactly the opposite of flexible design in my opinion.
Im curious has anybody really followed this in a big long living commercial project?
shoo 16 hours ago [-]
DoD may be a good idea for a business context where one of your main priorities is getting the most efficient use out of memory bandwidth & cache.
E.g. if your job is writing game engines or middleware used by AAA games with fancy graphics to run on consumer hardware, getting the most efficient use out of the players' limited memory bandwidth may be very important.
For many (most?) arbitrary commercial software projects in other contexts, performance isn't high priority & memory bandwidth isn't a bottleneck. Performance just has to be 'good enough' & 'good enough' performance may be easily attained by writing typical OO code that uses cache & memory very inefficiently - so in those cases DoD is an engineering trade off that solves a problem that doesn't need to be solved & may create new problems if introduced.
FpUser 6 minutes ago [-]
I have business oriented C++ backend where I use mostly OOP but also use DoD in few critical places that do a lot of calculations over big chunks of data. It is simpler than for games since I only need high average performance, not real time 60 or whatever FPS with no jitter.
hecreto 17 hours ago [-]
Based on your description the only prescriptive thing one can say is lean into composition and avoid inheritance. While there are some pitfalls with composition it is easier to unpack and restructure than inheritance is. Changing requirements will of course incur changing data structures etc. but hopefully this can be avoided a bit by using LUT/indexes and other things to minimize impact.
ButlerianJihad 15 hours ago [-]
Why do you lowercase "oriented" when "DOD" (and "OOD") is the usage in the slides?
nine_k 13 hours ago [-]
Autocorrect to Department of Defense?
ChicagoDave 8 hours ago [-]
Domains should own their data and some data objects can be split between domains.
Data first is fine for simple systems, but lead to chaos for complex systems.
remiminnebo 9 hours ago [-]
That's where my agents got it from!
PessimalDecimal 17 hours ago [-]
This seems like a particular branding on cache-aware data structures and algorithms. Is there more to it?
wasmperson 15 hours ago [-]
One good example of DoD which isn't CPU-cache-related is relational databases. When designing a CRUD application, the Data-Oriented way of doing it is to figure out what data you will be storing and how to organize that data to minimize access times, which is what you're doing when you design the DB schema.
An example of failing to follow DoD is the N+1 query problem: a programmer builds an abstraction that operates on individual DB rows, but "where there's one, there's more than one": you will inevitably be running that code in a loop so that you can process multiple rows. If instead the programmer had abstracted over groups of rows, then per-item query overheads suddenly become per-batch overheads.
inigyou 16 hours ago [-]
I consider it generally the ideology of anti-OOP. While OOP ideology teaches you to structure the program after the problem it solves, DOD ideology explicitly teaches you to throw all that away and think about what runs fastest on the computer. Maybe it should be called Computer-Oriented Programming or Hardware-Oriented Programming. The specifics very a lot but the top-level ideology of "fuck OOP" is consistent.
> While OOP ideology teaches you to structure the program after the problem it solves
I completely disagree with this characterization. OOP teaches you a synthetic set of concepts (go4) and then asks you to solve problems in terms of that.
And the reason why the canonical bird as a subclass of animal doesn’t work, is it’s extremely difficult to divide the world into strict categories (are you Aristotle). So the solution is to organize virtually rather than around natural traits.
The most natural way to solve a programming problem is a big list of instructions with if/else and goto. It’s very learnable, even for young children.
inigyou 5 hours ago [-]
OOP is not Singleton, it is "class Car extends Vehicle {Tyre tyres[4]}" etc
Rendello 11 hours ago [-]
> cache-aware data structures and algorithms
Maybe hardware- and access-aware more generally.
One of Mike Acton's other talks has a "Is Data-Oriented Design even a thing?" section, which goes over what he means when he refers to DOD:
I wish people weren’t so dogmatic about DOD. It’s applicable mainly when you have extremely large amounts of data which can be processed in parallel, which seems mostly the case with video games (eg. look at most DOD examples) and other niche cases. It’s called “Data-Oriented Design” but it really should be called “parallel-processing design” because the average DOD advocate will never advocate for a different OOP approach if it solves a problem where those techniques are more appropriate. Advocates tend to be quite dogmatic, ask them about RAII or modern C++\Rust for example and you’ll see what I mean.
And I say this as someone who basically sees programming as data and associated algorithms and always approaches problems by considering state or data first.
crabmusket 6 hours ago [-]
I think this is broadly true, but there's also a subset of DOD design advice (e.g. see Richard Fabian's book) that has a lot to say about relational database design. In our team it's provided us with some very practical motivation for more normalised table designs. Doing 1+n requests to the database due to poor implementation feels similar now to pointer chasing.
The connection is that ORMs convince you to have an object-oriented view of the world, which maps nicely to object classes. But highly normalised designs don't map as cleanly to classes and objects, so you need to approach with a different style of programming on the application side.
Instead of seeing a User instance, you start to see a more complex bundle of login methods, profile events, etc.
Quothling 14 hours ago [-]
I knew OOP had failed when we started getting programmers who solved memory issues with batching instead of thinking about why they were loading that much data into the memory to begin with. For the previous 20-30 years that didn't really matter. Code was the bottleneck and implicity and abstractions helped developers ship changes faster. It turned out that it didn't give us maintainable or safe code bases, and there is a sweet irony to be found in the world of banking. Where the JAVA systems meant to replace the old parallel paradigms are now being replaced with systems that are better links between the COBOL systems and the customers than JAVA ever was.
AI changes that. Especially because it appears that LLM's can't understand the OOP abstractions any better than your hardware can compute it.
That being said. OOP and DOD both have advantages and disadvantages. If you go back to what I said first it wasn't exactly a failing of the OOP paradigm. The biggest issue I have with OOP is actually that it's too easy to do things wrong with it. Which isn't helped by the multimillion dollar industry which thrives on teaching developers everything except core computer science. People know their DRY, SOLID, CLEAN, TDD, Agile and every design pattern in the world, but they don't know how the interface they've just implemented actually handles their data.
inigyou 19 hours ago [-]
Serious question. Does DOD mean anything more than array programming, in practice?
laladrik 19 hours ago [-]
Have a look at the talk Practical Data-Oriented Design by Andrew Kelly [1]. He covers the following techniques:
1. Indexes instead of pointers. This allows you to avoid alignment of 8 bytes in your structure for x86_64.
2. Storing booleans out-of-band. Booleans cause padding all the time.
3. Struct of Arrays. Based on your question I assume you're familiar with it.
4. Store sparse data in hash maps. I remember one time when it allowed to eliminate inheritance.
5. Encoding the data instead of OOP/polymorphism. I haven't got an occasion to use it. The idea is to add extra tags to avoid boolean properties.
Yes. Array programming happens to overlap heavily with DOD in modern hardware because of caching and SIMD, but if you were programming an Atari ST it wouldn't.
There are also cases where the optimal data format isn't array oriented because the memory access patterns for the problem in question just require something else.
You also have to think of hot vs cold data, which has nothing to do with arrays.
inigyou 18 hours ago [-]
Can I propose renaming it to Hardware Oriented Programming?
sirwhinesalot 7 hours ago [-]
Eh, I don't think that's a good renaming. Data-Oriented Design is in opposition to Domain-Driven Design (a software development approach that prioritizes modeling software to match a real-world business domain).
OOP tells you to structure your software as objects exchanging messages, and DDD tells you what those objects (or their classes rather) should be.
Similarly, Procedural programming tells you to structure your software as procedures, and DOD tells you what those procedures should operate on.
The focus on the data is the really important part. What is the actual data I'm operating on (without any fluff on top) and what do I need to transform it into? What subsets of that data need to be operated on at any given point in the program? That's the core of DOD.
Then, as a second step, comes the hardware. Now that I know what data I need to operate on, how do I lay it out to best take advantage of the hardware I'm targeting? If you rename the paradigm to "Hardware Oriented Programming", it shifts the focus from data modeling to code (IMO), which is the wrong frame of mind.
For example, virtual calls are slow compared to direct calls, because they screw up branch prediction and often can't be inlined. In HOP, you'd probably ban virtual calls entirely because virtual calls bad.
But in DOD, they honestly probably don't matter at all! Because if you did the data modeling as instructed, and then you laid out the data to best take advantage of the hardware, your virtual function is going to be operating on a pile of data in bulk, making the virtual call cost pure noise.
A lot of it just comes down to KISS and avoiding unnecessary overhead and indirection (like vtables, C++ STL containers etc.) so you're getting the most out of the hardware.
laladrik 18 hours ago [-]
It's fair to mention that DOD is not only getting the most out of the hardware. It also allows the busy work to be avoided. I caught myself a couple of times, when I wanted to make a set of types united by some interface. However, in reality what I could do (and I did eventually) is having several instances of single SOA (one per type) which were processed differently. The addition of a new "type" turned from a good hundred line patch to 20-30 lines
atoav 18 hours ago [-]
It means instead of building a street object, with car objects that have tire objects as its children and then running through the tree to rotate the wheels you just have an array pointing to exactly the appropriate data type to accomodate the type of wheel rotation you need.
Very often the answer is indeed arrays, but it can easily be something else, depending on the problem. Data driven design is not very complicated, it just means instead of thinking about abstraction you think about the shape the data needs to be in to accommodate the most common transformations you need to do with it.
RobRivera 19 hours ago [-]
Yes
deepnlp-contact 9 minutes ago [-]
[dead]
Veliladon 15 hours ago [-]
[dead]
asdaqopqkq 14 hours ago [-]
can we train more LLMs on this and also the codebase of handmade hero?
[1] https://www.manning.com/books/data-oriented-programming-in-j...
So if your working on a physics engine and your optimizing collision detection, you think about the data in -> data out of the problem you are solving as the primary driver of how the code should be written.
You start with defining the data, and build from there.
Different types of applications all have different shapes of data so would have differently shaped optimal code. Eg) a physics engine would use some kind of spatial hash thing which can be optimized differently based on if stuff can be added/removed while it's running. A 3d renderer operates on big buffers of matrices and vertex data. A game is usually composed of some long lived things and a lot of short lived things.
The key message in Mike Acton's talk was:
"If you have different data, you have a different problem."
While ECS systems are not a panacea that solves all problems in a perfect data oriented way, they are generally more malleable than Object Oriented hierarchies. This means it's generally more feasible to write "near optimal" code in an ECS framework than in a mature Object Oriented code base.
But the key message isn't "use X framework", it's "start by defining the data".
Also: just having stuff in an array or vector invites you to the ABA problem. You need a generation counter in there too, or else the array indice may get reused if something is deleted and another thing is reinserted at the same index. But that's yet another boilerplate that would easily be overlooked if you had to do everything manually.
Also: you seem to be saying this with C++ in mind. Do you think that applies to bevy_ecs too?
Modern Bevy has relationships to make sure that if an entity has a component that refers to another, it doesn't become dangling. It works a bit like foreign keys in databases. I think this makes ecs much more usable
(as an aside, there is a whole host of analogies between ecs and relational databases. entity archetypes are tables, entities are rows, components are columns, and systems are queries). Nobody tells people to just write their database from scratch though)
It's really sounding like you're pretending not to know what your program does, which is one of the core OOP ideas that DOD refutes. In OOP you have an array of Shape and you pretend not to know which shapes your program implements, so the only way to draw them is to call ->draw() on each one. And you pretend you don't know anything about the lifetime of a shape so you use smart pointers everywhere to extend it as long as needed. In DOD you assert that you do know what shapes are available and what their lifetimes are.
Can you describe a superior replacement for generation counters?
Isn't most programming? Isn't struct Particle {vec3 position, velocity;} also the mother of all boilerplates?
Why? Flecs, for example, is pretty sick imo.
Ooh here is a controversial one. DOD is premature optimization. Most programs don’t have enough data where the storage and access is a factor for performance. In fact it might be slower to use DOD.
One of the ideas in OOP that DOD is explicitly refuting is pretending not to know what your program does. You know which subtypes exist in your program and you shouldn't treat them generically as supertype instances except when that is actually optimal. On this axis, both ECS ideology and OOP ideology are opposite to DOD ideology. ECS happens to align with DOD in that it prefers big linear arrays, but it does not align in pretending not to know what combinations of arrays are used, which is similar to pretending not to know what subtypes of Shape exist.
Different paradigms come with different pathological cases. I've had to deal with Scala programmers whose notion of FP is making everything generic on the Monad it abstracts over, even when it'll only ever be instantiated on the one effect system the team uses. Nonsensical levels of generality is one of the classic OOP pathologies, and is the reason why we have aberrations like the AbstractSingletonProxyFactoryBean[0].
0. https://docs.spring.io/spring-framework/docs/current/javadoc...
I think it has nothing to do with OOP which I find very convenient for some domains and not so much or even opposite for others. These "Nonsensical levels" of anything is a disease which is called Architecture Astronauts Syndrome and victims apply it to any paradigm
“Entity Component System”, for those like me who didn’t immediately think of it.
So while it's great to think about the data flow it's also important to think about the abstractions around it,.ie the (system) interfaces that let the system evolve without having to propagate changes everywhere while reaping the benefits of data orientation.
AI generated skill?
Try asking your LLM of choice this in an empty session with no other context:
You are working for Mike Acton. What principles do you follow when writing code?
Maybe he found that telling it that it works for him nudges it in a direction that is beneficial to get it to write code like he wants, alongside the specific rules and other instructions in the above linked document?
I think that’s call “spooky Acton at a distance”
[1]: https://odin-lang.org/
At work we are rewriting and reengineering system from scratch and its crazy because the limitations of the old system are now gone we get the most insane feature requests that are even accepted by the team lead et al. This makes such an approach impossible since DoD is exactly the opposite of flexible design in my opinion.
Im curious has anybody really followed this in a big long living commercial project?
E.g. if your job is writing game engines or middleware used by AAA games with fancy graphics to run on consumer hardware, getting the most efficient use out of the players' limited memory bandwidth may be very important.
For many (most?) arbitrary commercial software projects in other contexts, performance isn't high priority & memory bandwidth isn't a bottleneck. Performance just has to be 'good enough' & 'good enough' performance may be easily attained by writing typical OO code that uses cache & memory very inefficiently - so in those cases DoD is an engineering trade off that solves a problem that doesn't need to be solved & may create new problems if introduced.
Data first is fine for simple systems, but lead to chaos for complex systems.
An example of failing to follow DoD is the N+1 query problem: a programmer builds an abstraction that operates on individual DB rows, but "where there's one, there's more than one": you will inevitably be running that code in a loop so that you can process multiple rows. If instead the programmer had abstracted over groups of rows, then per-item query overheads suddenly become per-batch overheads.
People posted a wide variety of specific ideas under my other comment: https://news.ycombinator.com/item?id=49061421
I completely disagree with this characterization. OOP teaches you a synthetic set of concepts (go4) and then asks you to solve problems in terms of that.
And the reason why the canonical bird as a subclass of animal doesn’t work, is it’s extremely difficult to divide the world into strict categories (are you Aristotle). So the solution is to organize virtually rather than around natural traits.
The most natural way to solve a programming problem is a big list of instructions with if/else and goto. It’s very learnable, even for young children.
Maybe hardware- and access-aware more generally.
One of Mike Acton's other talks has a "Is Data-Oriented Design even a thing?" section, which goes over what he means when he refers to DOD:
https://www.youtube.com/watch?v=rX0ItVEVjHc&t=741s
And I say this as someone who basically sees programming as data and associated algorithms and always approaches problems by considering state or data first.
The connection is that ORMs convince you to have an object-oriented view of the world, which maps nicely to object classes. But highly normalised designs don't map as cleanly to classes and objects, so you need to approach with a different style of programming on the application side.
Instead of seeing a User instance, you start to see a more complex bundle of login methods, profile events, etc.
AI changes that. Especially because it appears that LLM's can't understand the OOP abstractions any better than your hardware can compute it.
That being said. OOP and DOD both have advantages and disadvantages. If you go back to what I said first it wasn't exactly a failing of the OOP paradigm. The biggest issue I have with OOP is actually that it's too easy to do things wrong with it. Which isn't helped by the multimillion dollar industry which thrives on teaching developers everything except core computer science. People know their DRY, SOLID, CLEAN, TDD, Agile and every design pattern in the world, but they don't know how the interface they've just implemented actually handles their data.
1. Indexes instead of pointers. This allows you to avoid alignment of 8 bytes in your structure for x86_64.
2. Storing booleans out-of-band. Booleans cause padding all the time.
3. Struct of Arrays. Based on your question I assume you're familiar with it.
4. Store sparse data in hash maps. I remember one time when it allowed to eliminate inheritance.
5. Encoding the data instead of OOP/polymorphism. I haven't got an occasion to use it. The idea is to add extra tags to avoid boolean properties.
[1] - https://vimeo.com/649009599
- Andrew Kelley Practical Data Oriented Design (DoD) - https://youtu.be/IroPQ150F6c?si=F1Z0pLO2W5hbQgpM
- CppCon 2014: Mike Acton "Data-Oriented Design and C++" - https://youtu.be/rX0ItVEVjHc?si=jv4hhTSBh3XH--xQ
- Why You Shouldn’t Forget to Optimize the Data Layout - https://cedardb.com/blog/optimizing_data_layouts/
- Handles are the better pointers - https://floooh.github.io/2018/06/17/handles-vs-pointers.html
- Enum of Arrays - https://tigerbeetle.com/blog/2024-12-19-enum-of-arrays/
- Data oriented design book - https://www.dataorienteddesign.com/dodbook/
- Data-oriented design in practice - Stoyan Nikolov - https://youtu.be/_N5-JjogNXU?si=vhaxYcfE6tl11Sux
- Programming without Pointers - Andrew Kelley - https://www.hytradboi.com/2025/05c72e39-c07e-41bc-ac40-85e83...
- More Speed & Simplicity: Practical Data-Oriented Design in C++ - Vittorio Romeo - CppCon 2025 - https://youtu.be/SzjJfKHygaQ?si=jafavSl2YJWk4vIx
- Rust Handle - https://taintedcoders.com/rust/handles
There are also cases where the optimal data format isn't array oriented because the memory access patterns for the problem in question just require something else.
You also have to think of hot vs cold data, which has nothing to do with arrays.
OOP tells you to structure your software as objects exchanging messages, and DDD tells you what those objects (or their classes rather) should be.
Similarly, Procedural programming tells you to structure your software as procedures, and DOD tells you what those procedures should operate on.
The focus on the data is the really important part. What is the actual data I'm operating on (without any fluff on top) and what do I need to transform it into? What subsets of that data need to be operated on at any given point in the program? That's the core of DOD.
Then, as a second step, comes the hardware. Now that I know what data I need to operate on, how do I lay it out to best take advantage of the hardware I'm targeting? If you rename the paradigm to "Hardware Oriented Programming", it shifts the focus from data modeling to code (IMO), which is the wrong frame of mind.
For example, virtual calls are slow compared to direct calls, because they screw up branch prediction and often can't be inlined. In HOP, you'd probably ban virtual calls entirely because virtual calls bad.
But in DOD, they honestly probably don't matter at all! Because if you did the data modeling as instructed, and then you laid out the data to best take advantage of the hardware, your virtual function is going to be operating on a pile of data in bulk, making the virtual call cost pure noise.
Very often the answer is indeed arrays, but it can easily be something else, depending on the problem. Data driven design is not very complicated, it just means instead of thinking about abstraction you think about the shape the data needs to be in to accommodate the most common transformations you need to do with it.