I think ECS is a new enough architecture that the patterns are still very much folk lore.
I think a lot of the way I try and structure my Bevy apps comes down to trying to separate the rendering from my game logic. Its very easy to confuse the two responsibilities.
Coming from the web and Ruby I find the lack of automated testing and TDD to be foreign to me. So I've been trying to figure out patterns that make my games easier to test. Hoping to write about it soon.
> I think ECS is a new enough architecture that the patterns are still very much folk lore.
ECS is a pretty old idea, built on concepts that are even older. I was playing around with an ECS-like engine of my own in C over 10 years ago, based on blog posts and talks that are now 20-25 years old. Even the Wikipedia article for ECS can trace the origins back to the 1960s. (Though obviously it hasn't been applied to video games for quite that long.)
Nowadays I'd probably reach for Godot and Kotlin if I just wanted to build a game in an ergonomic language on a solid foundation. You could still apply ECS concepts there, as well.
Rust has testing in the standard library -- IMHO Bevy is far easier to test than most game engines because it's "just rust". You can test game logic by starting headless apps, proding the ECS, and making assertions on the results.
For acceptance tests I've dusted off cucumber (after ten years of not thinking about BDD), as I it works great with Bevy
Phlex is an amazing project. Makes frontend fun again. I've built my own UI library using DaisyUI and Tailwindcss that we use in all our Rails projects: https://github.com/inhouse-work/protos
It cannot be overstated how nice it is to move away from erb and into pure ruby. Private methods as partials is a game changer for refactoring your UI.
I don't know if I'm a fan but I've almost never had erb be the problem in a Rails app. The LLMs can work with it easily. Don't need to bring in extensions to work with major text editors. Sticking with ERB for now.
I found using partials between projects to be a headache. I wanted a way to standardize my UI for accessibility across all projects and not have to re-implement the same standards over and over. It's nice to be able to package the UI up into a gem and keep it maintained everywhere.
It's got a nice path for incremental adoption, so there is no need to go all-in unless you want to. Personally my Rails projects are all 95%+ ruby now.
Another nice knock off effect is that the LSP and integration with my IDE (neovim) is much nicer with Phlex components than it is with ERB.
I also think personally I don't like "magic" spaces like ERB where its hard for me to follow the code path. How does ERB manage multiline blocks with html in them? Hard to understand, harder to explain. To me it feels like there is less to worry about in Phlex where I'm thinking more in Ruby, less in the specifics of the view context.
Have you had much luck with slots? I have a project using View Components that leverages slots. I've tried migrating to Phlex, but the lack of explicit slot support has been a bit frustrating.
Slots are pretty easy in Phlex. Have a look at the README in the repo I linked for an idea. I just create `with_column` or `with_header` methods that take a block and store it.
As someone who's worked with Ruby for 12 years here are some insights:
Ruby makes message passing first class. That just changes how you think about programs. In exchange you give up passing functions so our anonymous functions are our blocks (actually just another object that can receive messages). So you don't `list(something)` you `something.list` and that lets you change what `.list` does depending on who `something` is very easily.
Ruby's defining feature is that the line between language author and program author is razer thin. You can completely change the language yourself by extending core classes. This is why we have `1.day` in Rails even though its not supported in the main language. DHH (author of Rails) could add that without consulting Matz (author of Ruby). So lots of stuff gets prioritized to make developers happy because its easy to add.
In Ruby the messages you receive are passed up the chain of ancestors. Your ancestors are a linked list of classes going back to a root class like `Object`. You can modify this linked list at will with mixins and inheritance with complete control (should I go before or after this other receiver).
Ruby's REPL and debugging experience is amazing. I often keep one tab with an `irb` or `rails console` open to sketch things while developing the code for it elsewhere. I'm also always inside a debugger to figure things out. When I'm in Rust or Python I'm met with a very different environment.
I get the debugging experience, as I've already found it pleasant and painless compared to the difficulty of setting debugging environments in other languages. But for the messages first approach and thinness, what's the real world benefit in practice?
You can intercept and modify messages on the fly. This helps a lot if you want to update legacy code, but don't want to break other classes that use the same call. It also allows for modularization more easily. I have a project that receives data from a bunch of sources, so I have a class for each source (this is simplified). To make it much easier to add a new one, which happens pretty regularly, the code that calls the processor checks all the classes in a specific folder for a certain function (`.respond_to?`) and then gets the right one for the data type. This means that to add a new source I have to change 0 lines of legacy code, just drop the new class file in the right place and it works.