I'd love to see an example where an use of it has made the code more readable and easier to maintain by the way
I don't have any specific example for you at this time, but I will point you to some people who can expound on that subject. There's the points raised in http://www.dataorienteddesign.com/dodmain/, and the ones Mike Acton raises in his CppCon talk and "typical C++ bullshit" slides. I'll summarize what I took from both of those:
- having your data declared in a simple, straightforward way instead of hiding it behind layers and layers of encapsulation and abstraction makes it easier to reason about what state the program has and what subsystem owns it - and I'd add that in a more data-oriented programming style, you might segregate your data by how often it is mutated, which can make it easier to understand where state changes happen
- implementing functionality by composing transforms of collections of data allows you to more quickly find where a bug is actually happening because your state mutations occur in specific places meaning that you can quickly find where a corner case was missed or where an unintended side effect is happening
- existence-based processing can cut down on the cyclomatic complexity of the code by reducing the number of checks for validity you need to do - and in my experience, checks for validity (ie. null pointers and such) negatively affect code readability in a huge way
- concurrency becomes easier to implement and debug when you build your program by transforming sets of homogeneous data instead of structuring your program around transforming individual pieces of data in isolation
To me, the essence of being "readable" means that I can understand what a program is trying to accomplish and how it goes about accomplishing that with a minimum of effort. A program that is "easy to maintain" is one that is easy to debug and easy to change in such a way that I don't introduce new bugs in the process. My impression of data-oriented design is that while it might appear that these data-oriented systems take more effort to change, I think ultimately how long it takes me to make a change is more important than how much code I need to touch. Those two things are related - "effort" here means the time to make the change plus the time to debug it - but given the choice between making a larger number of small changes that I have high confidence won't break anything and a smaller number of changes that are more likely to break something elsewhere in ways that I need to think extra hard about (eg. if I fix a bug in a base class that breaks subclasses), I'll take the former.
My experience with my own code (which I've recently started writing in a more data-oriented way even for gameplay code) is that I tend to add new features by adding new subsystems instead of modifying big swathes of existing code to add the features to all the different classes that already exist as tends to happen with the (object-oriented) projects I've worked on and still work on at work. Most of the true object-orientation in my own code occurs at the feature subsystem level, not at the individual data object level. This not only means that it's easier to fix bugs in specific features (because the code and data for those features is all in the same place), but my confidence in the correctness of the existing codebase gets continually higher as I add new things. I already spent time to make it work, and I'm not changing it, after all.
It might be interesting to take a reasonably complex program and implement it in "typical OOP" and "typical DOD" and compare the two to see how easy it is to do different maintenance and implementation tasks in each system.
edit: And I think it's a misconception that data-oriented programming = focus on cache performance and avoiding branching. Those are things that can come out of taking a data-oriented approach, but I don't think you need to go all the way to using bitmasks instead of conditional branching for your code to be "data-oriented". Data orientation to me just implies that your code is built around how your data is laid out and your data is laid out according to what you actually need to accomplish instead of trying to model the "real world" with abstractions in code.