1a14d2a8
extracted
4. Stephen Margheim - On the tasteful journey to Yippee - wroc_love.rb 2025.txt37320e0bcb10| Status | Model | Tokens (in/out) | Duration | Cost | Nodes/edges | Read set (nodes/edges) | Time |
|---|---|---|---|---|---|---|---|
| completed | claude-opus-4-7 |
433,833
/
17,204
146,126 cached · 16,202 write
|
286.9s | - | 26 / 50 | 145 / 2 | 2026-04-18 07:42 |
| failed | claude-opus-4-7 |
RubyLLM::BadRequestError: You have reached your specified API usage limits. You will regain access on 2... | 2026-04-17 16:18 | ||||
Okay, keep your hands up. Who converted
to SQite?
You're an
influencer. All right, so our next
speaker is Steven Mahim and his software
engineer uh which is now developing his
new framework and he will tell us a
little bit more about Yepy. Did I say
that correct? Yes. uh about EAP and the
philosophy that stands behind it. So,
Stephen, the stage is
[Applause]
yours. Thank you. Okay. Can you hear me
well
enough? All
right. So, as Luc said, I'm Stephen. You
can find me basically everywhere on the
internet at Fractalmind. Um and I blog
at fractalmind.github.com.
github.io. Nice to meet you all. Those
of you I met last year, nice to see you
again. Those of you I'm meeting for the
first time, nice to meet
you.
Um, you might be familiar with this is
uh a small selection of some of my open
source
projects. Plume is actually my my newest
project. If you're interested in
learning more, I'm introducing it to the
world next week at Ruby Kai. So, keep
your eyes out for that YouTube video.
But that is a uh a parser for SQLite's
dialect of SQL uh in Ruby.
Um, if you do know me, you likely know
me for the work I've done to make the
Ruby ecosystem and Rails the framework
the best way to build web applications
using the SQLite database engine.
And just because I have a captive
audience, I thought I would take the
opportunity to plug the course I had the
opportunity to make earlier this year
with Aaron Francis
um to sort of lay out everything that I
know and everything that I think about
how to build incredibly high leveraged
applications with Rails. Um, so that
course is called highleveraged rails.
You can find it at
highleverils.com. It is a paid course.
So this is an ad, but I have disclosed
that. So you cannot sue me
now. Um, I have been working for the
last I don't know three years or so with
Joel who spoke yesterday about one of
his open source projects literal. He
maintains a number of projects that you
also probably know about Flex or
Quickdraw uh and a number of
others. And we had the opportunity to
have a longer more in-depth conversation
about how we think about web development
on the podcast that he uh runs with his
co-host Colin. I don't know two years
ago or
so. And those conversations, the the
short one that we had and recorded and
the many longer ones
offline, led us to start to realize that
we shared a really deep similarity when
it comes to taste.
And taste is something that
is hard to define and hard to put your
finger on it and and hard to get a sense
of whether or not you share it with
someone without just talking a lot and
working together some and and seeing and
comparing code that you like and code
that you dislike and why do you like
this and why do you like it slightly
more than
that.
But as we
talked, we came to appreciate that we
had a really similar vision for what
really
beautiful,
powerful,
simple developer experiences could and
should look like. And we realized that
we were both interested in trying to
make as many of those things real as
possible.
So, we sort of started off on this
crazy
project without like a ton of planning
or direction to see like what would it
look like to try to build a new web
application framework from a blank sheet
of
paper and to do so in a way where the
very best most interesting ideas could
be completely separate from the
application framework and could just be
gems that anyone could use. You could
use them in a Rails app. You could use
them in a Hanami app. Um, but you could
also use them inside of a framework that
uh was designed to be philosophically
and and tastefully aligned with them.
And that is what led us to starting what
we call the the Yippi project, a project
to make web
development exciting
again.
And I'm going to be upfront with you,
all right, from the
outset that this is a teaser trailer.
I'm not going to be showing you a GitHub
URL at the end of this talk.
um because we don't have one. I mean, we
do have one, but it's private and half
of the code in there doesn't work with
the other half of the code because
everything is changing all the time. But
what I do want to do is I want to take
this
opportunity
to give you a taste of where we're at,
where we're going, and what
the vision is for what this thing is
about. And so in order to do
that uh I thought we would just break
things down into two parts. The first
part is pretty straightforward. I am
going to say a lot of things about a lot
of things. It's just going to be a bunch
of bullet points in slides and a and
some some code
samples. And then I'm going to do that
relatively quickly because I want to
save enough time for us to just talk.
There's like literally a thousand
different discreet interesting ideas
that Joel and I have talked about for a
long time and I don't know
like what you would be more interested
in talking about. So I'm just going to
throw a whole bunch of stuff at you and
then you respond back, hey, I'd like to
go a little bit deeper here or there or
there or all three of them. So I would
like to actually have a a longer Q&A
session. Um, and then of course we can
continue the conversations over the next
two
days. Now, as I
said, this project is probably best to
describe as
vaporware. It isn't like fully
vaporware. There's lots of code on my
machine and Joel's machine and in
different repositories,
but for the sake of setting
expectations, I'm going to say this is
vaporware. I I am presenting a
vision. Basically everything I'm going
to talk about today we have working code
experiments with in a few different
things. So um but at the very end I'll
give you some of our hairbrain ideas
that we haven't experimented with in
code but have talked about and thought
about. So we will get to fully fully
vaporware parts by the end of the talk.
So what are we going to talk about? Uh I
say we what am I going to talk
about? I want to walk through some of
the key elements of a web application
framework and just share with you what
we're thinking. So we're going to start
with persistent data. What are we doing
there? Then I'm going to talk about the
model layer. What are we thinking about
at the model layer? Then we're going to
move up to the controller layer, the
view layer. talk some about
testing and then hosting and
deployment and then finally
accessories. And now you have a very
clear sense of what the rest of the
talk's going to look like. We're going
to walk through bullet points. It's
exciting
stuff. All right. Persistent data.
This was one of the very first decisions
that Joel and I made because we we
shared a very similar thought from day
one, which was what if you just made a
web application framework that only
worked with one
database? Like why do we need
options?
Genuinely, like it's great that there
are web application frameworks that have
adapters for all of the different
databases. Like that's genuinely great,
but those things
exist. How am I going to out
compete? You know, you got to you got to
make some some cuts and less is more.
And what's interesting is if you make a
decision at the beginning, you say, you
know what, I'm going to build a
framework that only works with SQLite
that is only ever going to work with
SQLite. some interesting things start to
come up because now you can really
target the exact unique benefits of that
database. You don't have to take the
subset of features that are common to
all the databases and see what you can
do. It's like all right let's let's see
what juice can I squeeze out of just
SQLite. And when I say SQLite only, I I
mean it. There's going to be a SQLite
database for your primary data. There's
going to be a SQLite database for your
cache data. There's going to be a
different SQLite database for your job
queue. There's going to be a different
SQLite database for your message pub.
There'll
be full text search on top of any of
those databases that you want all that
set up. like there going to be a lot of
SQLite databases and only SQLite
databases and it's going to be beautiful
and
exciting. Now, one of the things that I
have been talking about for a
while, but is genuinely worth noting
like when you go allin on SQLite, it it
really does change the way in which you
think about how to architect a framework
and how to build apps on top of that
framework. It is genuinely true that
when you are using SQLite, N plus1's are
a feature and not a bug. And I'm going
to just take two minutes to try to
convince you of that
fact. What is philosophically,
mathematically the fastest possible read
that you could make from a relational
database? What are the characteristics
that you would need? Right? You would
need an index, right? So if you're
making an indexed read, way faster than
a non-index read. If all or the large
majority but definitely all if all of
your data is in the engine's in-memory
cache, right? The databases, no database
engine today is constantly reading and
writing from disk. They have a pool of
memory and they keep data around in
memory. So if you make an indexed read
where all of the data is already in
memory and there's no latency, no HTTP
latency, no interprocess latency, no
serialization or des
serialization,
then you have like the definitionally
fastest possible read in the world. So,
how do you make more reads like
that?
Well, the simpler your
queries, the easier it is to have
indexes that cover them all the time.
Where do we where do we hit queries
where the index falls down? It's like
well I kind of you know I need to join
these three tables in this particular
way with this one CTE and then I get the
data that I need and I've minimized HTTP
handshakes. So so I'm doing it and
that's good but I have a really
complicated query to cover with an
index or you have these complicated
queries that are less
common. So the they're not kept in the
hot pass. the database engine is like,
"Yeah, I see this every now and again.
I'm not going to like keep this data hot
and
fresh." If you just write a whole bunch
of really small
queries, select these columns from that
table and these columns from that table
and these columns from that table and
I'll merge them together in
Rubyand. It's way easier to have a much
larger number of the fastest possible
read queries in the world. Like you can
have many queries that execute in two
nanoseconds. And when there's no
serialization overhead, there's no
latency overhead. Like there's literally
there's literally no reason to not have
200 queries per request. And in fact,
you can way increase your index hit
rate, your cache hit rate, and that is
going to make your whole application
faster. So, how does that change how you
think about a web application framework?
There are many ways, and we'll talk
about some of them as we
go, but I really want to hit this point
hard. The fundamental reason that we are
going all in on SQLite is that it is the
only database engine in the world that
has wide adoption and extensive usage
and you can feel confident that it like
works where latency effectively does not
matter. Like there's just not really
much of a point in thinking about
it. And that really changes how you
think about building
applications. So we wanted to like
explore all of the ripples. So
persistent data equals
SQLite. Now it does come with some
interesting ripples. The very first most
important one that we realized like on
day one
was well if I want all of these really
awesome benefits all of these
performance
benefits there's really only one type of
deployment that makes
sense and that is single machine
deployments with persistent file
systems. If you're doing anything else
you're not going to reap all of these
awesome benefits. So we decided day
one like literally three years ago we're
having conversations like this is what
it's going to be and it has not changed
and it will not
change. We are building the best web
application framework in the
world for building single machine
deployments that exclusively use SQLite
for any and all persistent data. Now, if
you're not into
that, I'm confident that I can get you
into it over the next couple of years.
But also, there are a lot of great other
options. Use Hanami, use Rails, use
whatever tickles your fancy. But if you
accept these two
constraints, it is amazing how much
leverage is given to you.
And we're going to explore some of that.
But this is the original core defining
constraints. What if you had a framework
that only worked with one database and
you're interested in building apps that
you only deploy on one machine? How far
can you go?
And as this
project moves its way out of the
vaporware stage into the software stage,
I I'm going to be more and more excited
to show you how far you really can go. I
will just say now, I'm pretty confident
this is true, except for a couple people
over there. However far you're thinking
right now, like this is the limit. This
is the mathematical limit of how far I
can go. You're at least one order of
magnitude wrong. and I look forward to
building an app and proving it to you
and then laughing in your
face. All right, let's move on. Model
layer. What do we want to do differently
here?
Well, we want to have a really
object-oriented approach that very
cleanly slices the essential parts of a
model. There's a lot I really love about
Rails. And when you have a scenario
where your web resource, the way that
you represent the world, to the HTTP
response has a very direct mapping to
your model, the way that you represent
the entity to your application and that
entity has a very direct mapping to your
table. There are very few developer
experiences that can compete with Rails
in that scenario. If you have a oneto
one to one mapping between resource
model and
table that framework sings as soon as
you have a little bit of a gap right
you're like for the web page I really I
wanted to like have one form I say one
name but I kind of need to write to two
tables or I kind of want to represent
this entity to my system as one thing
but I I kind of need to read and write
from two tables.
Rails doesn't sync. There aren't really
strong conventions
there. And what we want to do is we want
to really cleanly slice and define these
types of objects. There is a resource. A
resource is an entity that you present
to your request response cycle. The
model is the entity that you present to
your application. The table is the
entity that understands the schema of a
particular persistent table on your
database. A table is the most common
type of a data source. Of course, you
have other data sources. You can build a
dynamic data source. That's what joins
are. Say like I'm selecting some data
from this data source. It's not mapped
to a single table. it's this particular
cartisian product or or scoped cartisian
product of these two or three
tables and then you can also have values
right and if you define these layers
really cleanly and really
well you can collapse them when they
need to be collapsed expand them when
they need to be expanded yes this is the
accordion we'll talk about it more in a
bit so we want to make it as easy as
possible to work with the model layer.
Whether you've got one model per table,
three models per
table, one model for three
tables, or some mix
thereof. Now, in order to do
this, this is like a remix of the
repository pattern, right? What's the
repository pattern in its simplest form?
It's to say let's separate the
responsibility of talking to a data
source from the responsibility of
presenting structured data to our
application. Let's make those two
different types of classes and let's
define a really clear contract between
those types of classes such that I can
get data from the database and hydrate a
model and I can make whatever ad hoc
queries I need against the data sources.
But I'm just going to be honest with
you. While I like the concept of the
repository pattern, I hate the names.
So I like these names better. But it's
the same basic idea and I'm uh we are
100% borrowing it with some some minor
tweaks.
The other defining characteristic of our
model layer is that we are embracing
query building as opposed to the sort of
active record style approach of
uh everything is driven through a model
which is itself really just a table in
in this parlance. Right? So what would
it look like to give you the full
expressive power of
SQL in Ruby from anywhere at any point
to talk to any data source to craft any
data source to craft any query however
you need it whenever you need it with as
much ease
as working with active record
today. Um this is one of the use cases
for having a SQL parser.
one of the reasons why uh we started
that
project. Um but in general it sort of
shifts the way that you think about
things to say like we're going to have
queries. Those queries we can have
relationships to tables or sources. They
have relationships to models like I go
to a table to get the data. I take the
data and I hydrate a model. So queries,
tables, and models are going to be
central, but they're going to be
different things. Um things that you can
collapse together such that you don't
need to see any of them, right? So we
can we can build a very active record-l
like interface where you don't need to
have three different files or call three
different classes, but behind the
scenes, that's what's there and you can
eject into those layers at whatever
point you need.
Um,
so code sample just to give you a little
bit of a concrete sense what I'm talking
about. What would a query builder look
like in in Ruby? Here we go. This is um
a
query I I use quite often um to do
basically tagging on top of SQLite.
Um, so here we
are getting our
artists, but we only want those artists
that have certain tags. And so inside of
this wear condition, we need like a a
nested select. How could we make that
easy to build? If you've ever done this
in
AL, then you you will know that this is
much simpler than AL. If you haven't,
uh, take my word for it. This is much
simpler than auto. But beyond just
having this fluent Ruby interface at the
very core, one of the things um we're
going to have behind it, I'm also really
excited
about is this. Now, this is going to
look a little bit crazy for a moment.
I'm going to give you a moment to like
study it. And it might be crazy. I I
admit that. But it's also beautiful and
profound.
This is
Ruby. This is a Ruby hash using basic
Ruby
primitives. You've got uh a constant
all. You've got a constant that maps to
some sort of object. Artist. This could
be a class. This could be a module. We
don't know at this point. We've got
nested hashes. We've got some basic
primitives. We've got like a boolean
true. Uh we've got a a method call on a
constant. We've got an array and some
primitives. Okay,
but really study th this is just SQL.
There's no extra
words. This is just taking SQL and
structuring it.
And what this does is provide the most
important missing feature that SQL has
which is
composability. You can take that hash
that is the value there to the exist
key. You can build that however
dynamically you want. You can store that
wherever you want in a constant in a
variable and you can compose it
incredibly easy, right? Like we know how
to build Ruby hashes. We understand the
composability that they
provide. And if you have a
tur fully expressive, right? Like every
single thing you can do in SQL you can
do in this way. Now you just have
composability and SQL plus composability
is radically powerful. There's like all
kinds of things that spin out of that.
So these
layers you're going to see like we like
layers but having these layers of
control
um are going they are the sort of
defining characteristic of our model
layer. So these are queries these are um
how we are thinking about uh doing
queries. That's the model layer. Let's
keep torrenting. All right,
controllers. What if you had a router
and a controller in a single
class? For those of you who have used
it, you would think I have a sense.
Okay, this is this is ROA. Yeah. Yeah,
exactly. ROA is a really really cool
idea.
I don't have the time to like get into
it fully, but it's the idea of a routing
tree. The core core difference here is
let's just use Rails as an example. In
Rails, you have your routes file and at
boot time, Rails goes and parses that
text file and says, "Okay, I'm going
to build a um they build a state
machine." So, there's a source text, a
parse step. That parse step presume
produces a state tree. That state tree
is stored in memory as a Ruby object and
then requests come in and they match
against that whole state tree to figure
out what controller do I initialize and
what action do I call. And what that
means is that your startup time, your
boot time has to eat the cost of
figuring out all of your routes.
And you need to have computed everything
that there is to know about every single
route in your entire application every
single time. And a routing tree just
says like we know how code works. Like
what if we just execute code
paths for our request to get to our
response
handler. So then like you don't have to
do anything at start time and it doesn't
matter if you have 10 routes or you have
10 million routes, you have constant
start time cost. right? There's no added
cost to having a million more
routes. Whereas, uh, in Rails, there are
like you just have more stuff to
parse. Um, this benefit is really at the
heart of the decision on top of just
like some taste preferences, but we're
going to talk more about this. One of
the the the central sort of goals we're
we're shooting for here is every app
regardless of size should start in
milliseconds like as like ideally
singledigit
milliseconds. All right, we deserve so
much better from our developer
experience. Like we we accept so much
lag everywhere in every tool and we
shouldn't and we should stop and we
should start kicking people who build
slow tools and make them build fast
tools. Fast is important so zero startup
cost. Now one of the things that the
we're really inspired by roa and this
idea of a routing tree but we're
building a completely different routing
tree. There's there's no code shared and
we really wanted to focus on having a
very flexible approach. What do I mean
by
that? What if you just want one
controller prep? This is what ROA is and
what ROA does, right? You have a
controller. It's just like your app
basically,
right? Um, cool. You can do that. What
if you want to go the complete opposite
direction? You want to go hanami. You
want one controller per action. You like
atomize everything.
Let me have a class per action. Cool.
Let's make that possible. What if you
want one controller per resource like
Rails does? Okay. You know, I'm going to
group actions together. I can take these
seven actions and put them in one class.
No need to have seven different classes
for that. I'll just do one file. Yeah.
All right. Let's make that
possible. What if you want one
controller for all of your member
actions for this resource, but a
different controller for all of your
collection actions for a resource? That
should also totally be possible. All of
these should be equally as easy to build
and to run and be able to mix and match
all of them however you need for
whatever part of your application they
are best appropriate
for. And what if you had some parts
where it's like we don't need to even
execute some code. Here's a freaking
hash. Here's the key. Call this right
like 01. you have parts of your app
where the routing is quite simple or you
don't have a lot of pre-logic or post
logic. Let's do that too. Let's do all
of it all the time
anywhere. How would you make that
work? Well, it could look like this. So,
I'm going to Here are the code
samples. All right. So, this is a
resource
controller. This is Rails style. So,
controllers have a handle method. And
this says, how do you handle this
request as it's coming
through? You have all of the HTTP verbs
and you say, all right, like if you're
at this point and you're a get request,
let's just call this method. So here at
the in the private methods here of this
class, we define an index method. That
index method would take the params and
the captures and whatever all it needed,
make some queries, do whatever it needs,
render a view, you know, response stuff.
um collection actions at the top, member
actions at the bottom, all in one
controller. What if we wanted to split
and we wanted to take our member actions
and put them in a separate controller?
What would that look like? It just look
like this, right? We're going to make a
new controller called a controller's
article. That
object, like every yippy
controller, as we also saw from Joel's
literal talk, is going to respond to two
proc. So you can just
say delegate that right? So if you have
the article ID segment, you match on
that. Uh I don't know what to do. Talk
to controllers article. It'll manage
that. You could
also do some hashbased routing. Say look
if you if you see at this point, right?
So we break the route down as you're
parsing. So this could be high or low.
In this case, we're at the the root
controller. Like look, if you just have
if your route is just
articles, you don't need to call and
process the Ruby of the handle method.
Just go straight to the articles
controller. Um, also here you see
inline responding, right? So if it's not
articles, if we're just on the root
route and it's a get request, here's
what you do. All right? So you don't you
can put a method call in there. You
could extract this out into a method you
call root or base or whatever. Or you
can just inline. This is obviously a
single line proc. You could have a
multi-line
proc. Um we love
callables. So those are controllers.
Those are some of the core ideas. Those
are the code samples. Got to move on.
View
layer. I mean I don't know what else to
say.
Flex is great if you haven't used Flex.
Um, genuinely Flex is a lot like
Tailwind in that you look at it. The
very first time I had this experience
with both of these projects, you look at
it and you're
like, "What is that? That's weird. I
don't like it." And then you try it,
you're like, you've seen this
meme and you try it for another week.
Oh, if you haven't. So, I'm just trying
to make the point. You really do need to
try it. You can't look at code and
really get an appreciation for whether
or not you're going to enjoy it. You
will enjoy it. That's kind of the
magical thing. You're Ruby programmers.
Believe me, you will like building views
in
Ruby. I promise you, because you like
Ruby. Just try it out. You'll
see. It really comes with the benefit of
being like highly testable. That's a
nice side effect. Testing is important.
We'll get to that in a bit. We're going
to fully embrace all of Flex. You're
going to have views. You're going to
have components. You're going to have
kits. If you haven't used kits in
Flex, you can make building views so
beautiful. These Ruby files are elegant
pieces
of code pros.
We're going to take advantage of
selective rendering. We're going to do
some really cool stuff. You don't have
to execute all of the code paths. Flex
is a really powerful
layer of abstraction, right? So you can
say, I just need in this context, we're
not going to use turbo, but let's
imagine like a turbo frame, right? I
just I just need this turbo frame. We're
going to replace the
content. Hit the controller. Hit the
view. I just need this flex can just
noop up every method call that is not
related to building that part of the
view. Just wait. I don't know. Ignore
it. Ignore it. Ignore it. Ignore it.
Ignore it. Oh, that's a turbo frame
we're looking for. Cool. Now I'll turn
on these methods actually doing
something. Render. Render. Render. And
cool. Done. Early return. Send it
back. Very fast. And you get tiny
payloads over the wire. Really cool
stuff.
is going to work with literal. Again, I
I don't know what to say. We took all of
our best ideas, we put them in gems. The
gems are great. You now know everything
you need to know about literal. Uh so,
every flex component, every flex view is
going to be yippy. Every yippy
component, every yippy view is a a flex
class that extends literal properties.
Um those two things work very well
together. Um
yeah, guess what?
You are going to have hundreds of
queries in your views and you're going
to love it because it's awesome. Do you
know the reason why you don't have
queries in your views? Do you know the
actual
reason? It's because if you put them in
your views, you can't really tell if
you're making 200 queries or two queries
in one request. And we're like, got to
be safe. You can't have 200. So, let's
put them all in one place so that if I
see 200 lines in this controller action,
I know I [ __ ]
up. But if it doesn't matter whether you
do 200 queries or two queries, if in
fact it might be better for you to do
200 queries, I promise you it is better
for your application architecture to put
your data fetching next to your data
use. In reality, it is better. The
reason we don't do it in Rails and we
don't do it with my SQL and Postgress is
because we can very easily destroy our
P50 performance just through
recklessness. It's like I've got 50
partials that I render in this view and
stuff sprinkles around and you just you
never really see the overview. You never
really see oops this is the straw
that'll break the camel's back. When you
don't have that problem, you can embrace
the benefits of putting data fetching
right next to data usage. And it is
better. Locality of behavior is a great
thing. And when you can, you should, and
we
will. Um, this is a a sort of simple
example. If you've used flex or you've
used literal, uh, this will look pretty
straightforward to you.
Um, when we have literal arrays, that'll
be a literal array of albums.
An album here is a model by the way, not
a table. This is an immutable data
repres
uh SQL making
methods. All right, let's talk about
testing. We're rubists. We like testing.
We're rubious, too. We like
testing. So, this is our goal.
You should be able to run an application
suite of 10,000
tests, browser tests, unit tests, system
tests in one second maximum. I mean, it
should be 100 milliseconds, but I'm
giving myself a little bit of wiggle
room.
Okay, this is what we deserve. Like,
we're adults. We're
professionals. Like, we deserve like
actually good software. Here's the
thing. Um, this is possible. Let's think
about what we would need to make it
possible. Like step number one,
absolutely you need to be running tests
on every single core of your machine. We
all have multi-core
machines. How many of us are running
single threaded tests? A lot of
us. So you absolutely need to be running
tests across your multi-core machine.
But importantly as well, you need to
saturate those
cores. like we don't I love my laptop. I
don't need to treat it like a baby. I
need to use it. Saturate your
computational
resources. Now, quick draw is the
testing framework that is again like
take all of the very best ideas, extract
them into completely isolated and
separate gems. This is just a completely
generic testing
framework with a testr runner. And the
defining characteristic of the testr
runner is this. It has a customuilt work
distribution system that's going to use
every single one of your cores as much
as possible. It's just going to hammer
your cores to run your tests as fast as
is computationally
possible. And if you like fully saturate
all of your cores on I've just got an
M1, you
know, guess what? You can run tens of
thousands of tests in less than a
second.
What else are we going to do to make
10,000 tests of all kinds uh run in less
than a
second? Yeah, we're going to take
advantage of
SQLite. In-memory SQLite databases are
incredibly powerful. This is why Rails
sets this as the default as well.
So if you have one in-memory database
per thread and there's multiple threads
that you're going to run per core
um your data fetching is microscond I
mean in in simple cases it's it's crazy
nanocond
but you need to manage this a
bit so let's think it through we should
make sure to use a single fixture
step right one time set up your fixtures
do it in the minimal number of SQL
queries like this needs to run in
milliseconds and now we have a database
that we use as a template and we clone
our in-memory databases right so if you
spin up 100 threads across 10 different
cores you you don't want to run 100
fixture
steps all right that's overhead
so I'm just trying to walk you through
some of the key sort of like thinking
here but run a lot of tests super super
fast defining characteristic of of
testing in yippy. So the final sort of
strategy we're embracing here to make
this true find and implement high
leverage shortcuts just to give you one
example. How do you test views today in
Rails fundamentally like what do you
have to do? You have some Ruby code that
you execute that produces a string. You
then run some more Ruby code to take
that string and produce a syntax tree.
And then you take your test code to
write assertions against that syntax
tree. What if the Ruby code when you're
writing tests produce the syntax tree
instead of the string? The browser needs
the string. The tests need the syntax
tree. So if I'm in test mode, I should
just have the Ruby code produce a syntax
tree. save a
step. And one of the high lever points
about flex is that because it gives you
this clean layer of
abstraction. You can in certain cases
make those methods noop, right? That's
selective rendering. In another case,
you can have those methods build a
completely different type of data
structure than they would in
production. It's very I wouldn't say
easy. It's not easy, but it is possible
to have flex
drive completely
different execution behaviors depending
on these contexts. Selective rendering,
no op production, build out a string,
testing, build out a syntax tree. So
testing super super super fast. That's
the number one goal. We want to run a
whole bunch of tests really really fast.
We're going to be doing various
different tricky and devious things to
make that happen.
I forgot I had that bullet point. Um,
another example, if you haven't seen
this project, this is a uh a project. I
believe it's written in Zigg. Um,
they're building a new headless browser.
It's only ever going to be a headless
browser. I like highly constrained
software, by the way. It runs super
fast, right? Integrate into it. Make a
highly opinionated choice. Make system
tests, browser tests run super
fast, right? So just look for leverage
and execute that leverage as much as
possible. Let's talk a little bit about
hosting and deployment. I hope that at
this point you're thinking this is an
odd slide to have when talking about a
web application
framework. But frameworks should in the
modern day take on more
responsibility in my opinion. You should
constrain certain things and use those
constraints to spend in other areas. So,
we want Yippi to spin up Digital Ocean
or Hezner machines for you. Yeah, give
me an API key. They've got APIs. You own
it. You pay for it, but I can write the
scripts to get the machine set up for
you. We also want to make deployment
easy to do and hard to [ __ ]
up. How do you [ __ ] up deployments?
conceptually like what's the mistake?
The mistake is you mix different types
of deployments because the primitives
that are available to us today are too
coarsely grained. We need fine grain
primitives. One type of deployment is
deploying your app. You've got Ruby
files. You make a change to the Ruby
files. You want Ruby to execute
differently in production. Version one,
version two. Version one runs this Ruby.
Version two runs that Ruby. That's an
application deployment. Those
are relatively well understood types of
deployments for us to do well, right?
But it's completely different from doing
an infrastructure deployment. I've got
version XYZ of this C utility I'm using
and now I want to upgrade it to version
XY1 to fix a security patch.
It's very easy to send both of those
types of deployments to production at
the same time and you shouldn't. You
should isolate your different types of
deployments. You should do an
infrastructure deployment when you need
to do infrastructure, not change any of
your Ruby. And then once that's done,
deploy a Ruby change. And you can do
them one after the other if you want,
but you should always isolate them. And
both of these types of deployments are
completely different from making a
schema change.
If you need to change your
database, make that change. Now, it is
often the case that you need to
orchestrate multiple of these types of
deployments to get to the end state that
you
want. But by isolating them, it's much
easier to do safe and sane
deployments. But what do you need? Well,
you need a deployment queue. You need a
way to ensure that one at a time start
wait till it's done successfully done
cool okay move on to the others but like
for me as a developer let me just say
make these
changes now do it right I don't want to
have to make you be the deployment Q
manager because let's be honest you and
I will [ __ ] it up at least once right
this is the kind of thing that software
should do so a framework should also
manage a deployment Thank
you. Here's another thing.
Um, YAML config
is this isn't recorded, right? This is
just Among Us. Uh, it's a scourge and a
cancer. Um, and we should burn it with
hot
fire. So, here's a promise I am making
to you about this vaporware that you
can't see the code of it. Uh, it's not
going to have YAML config.
Okay, for some of you that'll make you
sad. For those people, come talk to me
later. We can do some therapy. But there
should also be very minimal generated
configuration. Rails, what was like the
defining characteristic of Rails? What
made it spark 20 years ago? Convention
over configuration. Run rails new today.
Tell me how many configuration files you
have. Okay. Yes, we're going to embrace
Docker for infrastructure.
Docker is a really great tool for
managing your set of infrastructure
dependencies. Oh, I thought I had a
bullet point
there.
But you don't need to use Docker for
your
app infra deployments. Yes, Docker. App
deployments, no Docker. How do you get
really fast deployments? Don't build a
full Docker image from scratch every
time you change a character in a Ruby
file. Okay, that's how. So, we're not
going to do that because we also want
deployments to be submillisecond. Okay,
let's talk about
accessories. Web
frameworks should be way more
generous. We deserve batteries included
frameworks. What are some batteries? You
should have exception
monitoring. It is ridiculous to imagine
putting an application in production,
even a side project, a thing for just
like your mom and your cousins to plan
their Disney vacation. Whatever it is,
if it's on the internet, it should have
exception monitoring, including CSP
violations, right? Just like you should
be able to see what has happened and
gone wrong in your application. That
should be available to
you. You should have performance
monitoring. You should be able to see
like how is this application actually
performing? Where are bottlenecks? Where
should I go and look to make things
better? That should just be
there. You should be able to monitor
your background jobs. Again, like you
just need to be able to see things. You
can't make intelligent decisions without
information and the information has to
be available to you. Otherwise, it's
barely information. You should have
logs.
Why in God's green earth are we having
to pay for logs? Just just show them to
me on the screen. There doesn't have to
be a thousand features, but I should be
able to see my logs in like a usable
way. You should be able to inspect your
database. This is not rocket
science. We're going to build all this
into Yippi because it's what you
deserve. We're adults.
Now, if that doesn't get you excited,
um, you're Polish,
but eventually it'll start to bubble up.
You'll get some warmth. I I understand
there's a certain coldness. I get it. I
married a Russian. I understand the
culture. But at some point in your
heart, all right, it'll come. Now, I
want to talk just a little bit about the
philosophy here. Fundamentally, we are
building a framework from first
principles, right? Like blank sheet of
paper. What would it look like to make
the developer experience for rubists
amazing in 2025? What are the tools and
technologies available to us that we can
build on? I want to be very clear. I
love Rails. I work in a Rails app. I
will continue to work in Rails apps for
many years. Rails is great, but
fundamentally there is no way that it
can
change the fact that it came into
existence 20 years ago and the world was
very different 20 years ago. Yes, Rails
evolves very well. As open source
projects go, it evolves incredibly well.
But there are foundational decisions
that were made 20 years ago that they
can never change. They're too deep in
the very core of that framework. And
there are things we can do if we just
start from scratch and we just rethink
what's possible and what should be
there, right? Like deployment should
take less than a second. Test should
take less than a second. What do you
have to do on day one to make those
things
true obviously as well? Yippi is
grounded in an
aggressive love of simplicity.
We don't need the options that we think
we need. I'm just telling you, you don't
need five servers to run your web app.
You
don't. I will prove it to you.
But in the absence of proof, I'm the one
with the microphone. So, trust
me, you don't need different database
engines. There's so much that we don't
need. And with simplicity comes
leverage. If you apply simplicity
correctly, you get leverage. And if you
apply leverage correctly, you get value.
And that's what Yippi is all about.
Where can we find simplicity that gives
us leverage? How can we use the leverage
to provide value to us as professional
developers to make our lives not just
better, but more
enjoyable. This is what the accordion of
complexity is all about. give me
freedom when I have a situation where
everything can be
compressed where I only need one file
and it can be my router and my
controller and do my data fetching and
do my rendering all together because
it's really simple what I'm doing here
and I only need 30 lines. Let me put all
30 lines in one file. Don't make me have
five different files.
But in the case where I'm doing
something complicated, let me
name each layer. Let each layer have a
very clear relationship and contract to
the other layers, strong conventions,
and let me do that work. And I know for
a fact every single application that we
have built or will built will have
certain places where it's very simple
and you could do it in one file and
certain places where it's quite
complicated and it would be nice to have
10 different files. And you should be
able to mix and match the degree of
complexity that your application needs
in the parts of your application that
need
it as needed. This is what it means to
play the accordion. You expand in the
places that need to expand and you
collapse in the places that need to
collapse. And you aren't forced to
always expand or always
collapse because that's what the
framework told you and that's the only
way that the framework works. And when
you can expand and collapse as needed,
where needed, then you can start to make
music. And that is beautiful. So, couple
wild ideas. And I also do realize I
completely screwed my ability to do Q&A,
but we can talk outside. Um, just a few,
this is full vaporware, but
I want to share with you what does
Bootnap do?
Bootstrap takes all of your Ruby and it
processes it and it gets the actual Ruby
VM instructions and then it takes all
those VM instructions and it writes them
to
files and then it says whenever Ruby
needs to do stuff it's like don't
reinterpret you don't have to reparse
all this stuff just execute the VM
instructions. Now, for those of you who
might not know, one of the most sort of
famous taglines of SQLite,
SQLite, 30% faster than the file system.
So, very early on, we thought like it
would be kind of nice if we could have
bootstrap write to a SQLite database
instead of to the file system. It'll
just be a bit faster. And we started
exploring that. As we're we're as we're
doing that code, we're like, wait a
minute. How do you get really fast
deployments? You simplify things. Which
is faster? Sending a 100
files that total to 100 megabytes over
HTTP to another server or sending one
file that totals 100 megabytes over an
HTTP
server? Well, I don't know to be honest.
I haven't done this experiment, but I
think that there's a fair chance that
the one file and if it isn't faster, it
is certainly a lot easier to ensure that
you get a full clean successful
deployment, right? I don't have a
whoops, 50 files made it, but the other
50 didn't. So, what if we just like took
your whole
app, parsed it, got all the Ruby VM
instructions, threw them in a SQLite
database, and that's what you
deployed. I don't know. That's a wild
idea. We'll see if we build. I don't
know. Uh, here's another wild
idea. Well, there is another wild idea,
but it's in my speaker notes. So, we're
going to look at the speaker notes for a
moment. Oh, of course. Now I remember.
Um, what if you had purely declarative
migrations? What does that mean? Don't
tell me the migrations you need. Just
say this is my schema and let me make
some edits. No, this is my schema. And
then the framework migrated you from
schema A to schema B in the way that was
safest and fastest. Uh we already talked
about like you should be able to deploy
in less than a second. You should be
able to in fact here's the wild idea.
Run CI from your
laptop once everything is green then do
deployments. And all of that should take
less than one second.
That's a wild
idea. There are a whole bunch of other
wild ideas. Most of what Joel and I do
is talk about wild ideas and then every
now and again we actually do some typing
and make some software. But mostly it's
wild ideas.
So technically I spoke for 57 minutes
but are there any questions?
I guess we can take one.
I told you I would save time for
questions though. I'm a man of my word.
Well, we're one minute past the time,
but we'll make an exception. Okay. Thank
you for your presentation. It was
brilliant.
Um I have some notes. So, uh the SQL as
a hash representation was beautiful to
me. Um another one, the queries in the
views.
Um in my mind it was like uh if if the
database is so fast you can treat it as
a variable. Yes, that's a good way of
saying it. Um and uh my question is
about the routing trees because I'm not
familiar familiar with ROA. So uh the
cost is uh for me is like the cost is
moved from the boot time to execution
time, right? So uh if the request comes,
how do you find the controller that
should
um uh handle the the
request? Thank you. Yeah. So it is the
the the cost is moved from from boot
time to request time. Here is um the
core thought. Uh is it is Ruby
slow? And like what's the Yeah. Okay. I
mean that's a whole debate, but you know
what I mean. Um, simplicity and
leverage. Which is more
important to have every single
development experience, every single
production experience have to eat 5
seconds of boot time. Joel did a uh a
poll a while back, right? Like write an
assertion, assert true into your
application, run just that one test. How
long did it take? The average time was 5
seconds.
some 3 seconds, some 12 seconds, right?
That's an incredibly large startup time
for your app and for your tests. And
Ruby is fast enough and you don't have
to process everything, right? Like you
just hit the code pass, you start
finding your way through the tree pretty
quickly. Um, and then finally, like if
you really have hot
paths, you do direct hashbased routing.
this is 01, like this is super fast. So
you always have the hatch. If you have a
truly hot path, you're like, I want this
to respond in a five milliseconds,
right? Then you're optimizing
everything, then you're going to lean
into this hashbased routing. But in a
lot of other cases, really is everything
is fast enough. And the benefit of those
super fast startup times is what allows
for the possibility of tests plus
deployment in less than a second. And
that is
like we're not going to give up anything
for that.
All right, that sounds really great. Uh
just one disclaimer, Stephen. Uh,
regarding YAML, we were recorded. Oh, so
it's not between us, but you know, we
can always talk to with that. Just to
big YAML, I love you. Um, I use lots of
YAML configuration. Uh, big fan, you
know, screw TML. Um, so don't don't
cancel me. This is the part where nobody
is going to reach. I hope they
will. Okay, we'll just make marketing
around that. Uh, ladies and gentlemen,
Stephen Mark.
[Applause]