Sign in

Ismael Celis

@ismaelcelis.com
890 followers 405 following 1.6K posts
PostsRepliesMedia
Ismael Celis @ismaelcelis.com · 28/09/2026
Built-in telemetry. You can provide your own notifier. The work continues!
100
Ismael Celis @ismaelcelis.com · 28/09/2026
Live inspection of the running system
100
Ismael Celis @ismaelcelis.com · 28/09/2026
DI injector built in. I want to minimise the number of concepts and names, if possible.
100
Ismael Celis @ismaelcelis.com · 28/09/2026
You can use it for simple config values, but also components that have a lifecycle, such as workers that run in threads or fibers (you can do this with Dry::Types too, I think)
100
Ismael Celis @ismaelcelis.com · 28/09/2026
This is heavily inspired by Dry::System, but with some features that I wanted for my apps: * An app declares the components it needs, separate from their implementation. * Component implementations can declare their dependencies statically (you can inspect the dependency tree without running)
100
Ismael Celis @ismaelcelis.com · 26/09/2026
The generator
100
Ismael Celis @ismaelcelis.com · 26/09/2026
I made good progress with an app generator for Sidereal, my framework for reactive, command-driven apps in Ruby. It generates an app with a default directory layout, a CLI, and an initial app that shows how you can send commands from anywhere. Reactive and multiplayer by default!
110
Ismael Celis @ismaelcelis.com · 25/09/2026
Causal reactivity: if a UI sends a command to the backend, it probably wants to update itself whenever the backend produces an event correlated to the initial command. Make this the default behaviour.
000
Ismael Celis @ismaelcelis.com · 24/09/2026
Give the robots abstractions to test your logic in Given, When, Then terms. Then both you and the robot have a common, high-level understanding of what the behaviour should be without noisy mechanics.
021
Ismael Celis @ismaelcelis.com · 23/09/2026
Event-sourced #Ruby app with Jev classifying comments in bulk. I had to lower concurrency to the minimum, and add an artificial sleep(), otherwise it's too fast to see 😆
000
Ismael Celis @ismaelcelis.com · 22/09/2026
I build new event-sourced apps and then remember that I get deterministic projection replays for free and it blows my mind every time. This will never get old.
000
Ismael Celis @ismaelcelis.com · 21/09/2026
In the Everything is a Command Architecture™️, automations just dispatch the commands that a human operator would have used. Build the app for humans, then automate where it makes sense.
110
Ismael Celis @ismaelcelis.com · 21/09/2026
This is Jev automating comment moderation.
120
Ismael Celis @ismaelcelis.com · 21/09/2026
It's truly incredible that I can, given the right building blocks, just model the flow of information in a system, attach a few reference wireframes, and have a robot one-shot the implementation. This is a toy (event-sourced) moderation app with a Jev automation.
110
Ismael Celis @ismaelcelis.com · 31/07/2026
Container types do it too! (covariance). Composing these three separate but compatible array types compiles into a single one.
000
Ismael Celis @ismaelcelis.com · 31/07/2026
Functions themselves can be composed. The subtyping system fuses them into a single bigger function, if their input and output types allow it.
100
Ismael Celis @ismaelcelis.com · 31/07/2026
Or just pipe an input type, to a callable, to an output type and voilà, turn any lambda into a typed function. Or use the transform helper, for ergonomics.
100
Ismael Celis @ismaelcelis.com · 31/07/2026
And here as a mixin, to turn your own classes into typed, composable functions.
100
Ismael Celis @ismaelcelis.com · 31/07/2026
More progress in my Ruby library for data structures, typing and coercions. This time making it easier to compose your own code into these pipelines.
100
Ismael Celis @ismaelcelis.com · 24/07/2026
So then your API handlers can just declare and work with Money objects.
000
Ismael Celis @ismaelcelis.com · 24/07/2026
You can register your own encoders. This is one that turns fields declared as Money values from a string wire value into internal Money instances (and back!).
100
Ismael Celis @ismaelcelis.com · 24/07/2026
Now, Steppe transparently uses the right Plumb codec (JSON, Forms, your own) depending on the request's content type. So you can just declare the internal types you want in your handlers, and the codec will coerce incoming wire values for you.
100
Ismael Celis @ismaelcelis.com · 24/07/2026
In Steppe, you define HTTP endpoints that support declarative payload schemas. Before, types in the schema needed to consider how to coerce from wire formats (JSON, forms, XML) into the Ruby types you want to use.
100
Ismael Celis @ismaelcelis.com · 15/07/2026
0183
Ismael Celis @ismaelcelis.com · 12/07/2026
Ruby app streaming server-rendered HTML table cells to 3 browser windows over SSE and Datastar. For no good reason.
141
Ismael Celis @ismaelcelis.com · 12/07/2026
Ah and also I didn't need to decide how to make this collaborative, which is also built-in.
000
Ismael Celis @ismaelcelis.com · 12/07/2026
Just define little typed functions, group them together in a workflow, and let the framework orchestrate it for you
100
Ismael Celis @ismaelcelis.com · 12/07/2026
This one shows how individual inputs/outputs unlock steps as they're provided in whatever order.
100
Ismael Celis @ismaelcelis.com · 12/07/2026
Preparing a talk at the London Ruby User Group tomorrow. This is a reactive, server-rendered dataflow workflow in Ruby. The DAG component shows how steps run as soon as their dependencies are satisfied by other steps, so concurrent execution is self-organising.
170
Ismael Celis @ismaelcelis.com · 11/07/2026
As part of this work, Sourced reactors are now just a simple interface without any dependencies or inheritance tree. So it's pretty easy to make your own code use Sourced as an async messaging backend.
000
Ismael Celis @ismaelcelis.com · 11/07/2026
Sourced's runtime will provide more sophisticated queueing, like partitioning commands queues by arbitrary attributes. For example if you want to make sure commands for the same domain entity are processed sequentially. And, of course, full event sourcing, projectors, workflows, too.
100
Ismael Celis @ismaelcelis.com · 11/07/2026
Nothing else changes in your code. This is a schedule configuration that dispatches commands on an interval. It works with the built-in file-system backed runtime, or Sourced's Sqlite one.
100
Ismael Celis @ismaelcelis.com · 11/07/2026
I'm improving the integration hooks in my Ruby toolkit for reactive apps, and it's looking good! I've also made my Sourced library work as a regular job queue as well as an event sourced backend. So now you can have mix both styles with a single backend.
130
Ismael Celis @ismaelcelis.com · 11/07/2026
Claude just one-shotted a Mermaid visitor for Plumb Ruby types 😆. Here, a composition of labeled Any values to represent a checkout flow.
000
Ismael Celis @ismaelcelis.com · 10/07/2026
Finally, you can conveniently turn a Codec into a decoder / encoder pair for your types. And document them via JSONSchema. Handy!
110
Ismael Celis @ismaelcelis.com · 10/07/2026
But there's more. Encoders can be grouped into "codecs". For example a JSON codec groups encoders that turn JSON types into Ruby types (and back). Codecs can also participate in compositions and are direction-aware. They will turn your types into deserializable / serializable types.
100
Ismael Celis @ismaelcelis.com · 10/07/2026
These encoders are themselves just Plumb types, so they can be composed with other types. But they also automatically compose in the right direction!
100
Ismael Celis @ismaelcelis.com · 10/07/2026
Then there's "encoders", which are two-way transformations. Here, turning HTML form booleans into Ruby booleans
100
Ismael Celis @ismaelcelis.com · 10/07/2026
There's also new Hash syntax unlocked by the subtyping system.
100
Ismael Celis @ismaelcelis.com · 10/07/2026
JSONSchemas are produced off a composition's "input type". For example this is a transformation from a String into an Integer. The schema represents the valid input you'd need to call this type. But of course you can also generate for the output type!
100
Ismael Celis @ismaelcelis.com · 10/07/2026
All type compositions produce JSONSchema (you can register visitors to represent your own custom types too).
100
Ismael Celis @ismaelcelis.com · 10/07/2026
It also applies to unions.
100
Ismael Celis @ismaelcelis.com · 10/07/2026
In fact the subtyping system is leveraged to reduce (safe, value-preserving) types to the subset that the composition actually makes possible. This applies to constraints too.
100
Ismael Celis @ismaelcelis.com · 10/07/2026
These checks are recursive, and support covariance.
100
Ismael Celis @ismaelcelis.com · 10/07/2026
Now, Plumb types/functions expose input and output types, and an entire "static" (ie boot time) subtyping system. So Plumb will tell you when your compositions are impossible.
100
Ismael Celis @ismaelcelis.com · 10/07/2026
Me in a room of Rails developers talking about reactive UIs.
130
Ismael Celis @ismaelcelis.com · 06/07/2026
Another example from work (could probably make better use of destructuring hashes)
010
Ismael Celis @ismaelcelis.com · 06/07/2026
I use it quite a lot for user-facing APIs.
110
Ismael Celis @ismaelcelis.com · 12/06/2026
This is kinda elegant. Here, even the Submit button is just another step that depends on the outputs of all the other steps, so it unlocks itself once all other steps have run. I also added indicators to show how you can partially complete inputs for different steps until requirements are met.
000
Ismael Celis @ismaelcelis.com · 11/06/2026
Each step defines expected inputs and outputs, so the workflow can also be checked statically for correctness.
000