Problem: Most games are built in with a game loop ticking frames through many gameobjects/actors. Many actors share some functionality like physics collision, but there is also a lot of unique functionality on the same objects.
Naive solution: Use inheritance to build out your core functionality and override functionality as needed.
-This leads to a mess of interacting parts a massive type tree with many permutations of functionality.
-The deep type hierarchies blow out cache locality as the natural way is to tick through all functionality for a single object as you loop through all objects
ECS: Break functionality apart into discrete systems. Use composition to build the permutations of functionality.
-Its easier to to manage what you intended and also easier to experiment with functionality through composition than inheritance.
-The natural way to loop through functionality is one system at a time. You loop over your entities many times but because you can run the same system code in a tight loop its really speeds things up.
-Systems provide singleton like functionality to store global state in a private way. In a heavy actor model we have to rely on static fields, which can be ok, but now you have a large cumbersome hierarchy of functionality with global access to state.
tl;dr
-Composition over inheritance is good in all walks of coding.
-Many discrete tight loops of systems is usually better than one loop of complex logic.