Solving the distributed schema problem with @effect/schema by Jess Martin (Effect Days 2024)
By Effect | TypeScript at Scale
Summary
Topics Covered
- Move Database to Frontend
- Client-Server Multiplies Schemas
- Single Schema Powers Stack
- Schema Enables Cross-App Sync
Full Transcript
(audience applauds) All right.
Thank you, Anjana.
Thank you for raising the energy level in the room to about where I can meet it.
OK, so today, guys, we're going to be talking about beyond apps, solving the schema problem with effect schema.
So as Anjana was saying, my name is Jess Martin.
I just want to say, if you can tell from my accent, I'm not actually from here.
I'm from the states.
But I'm very much enjoying your country, for those of you who are here.
This is me yesterday at St. Anton.
The weather is very nice, some very deep powder there.
And apparently, it's dumping there today.
So let's see.
So a little bit about me.
I've been building web and mobile apps for almost two decades now.
I know that's hard to believe.
Not any hair is gray yet, I don't think, maybe in my beard.
But I've seen a lot of technologies come and go over the years.
And the last couple of years, I've been focusing on thinking about, what is the shape of software to come?
And that's really what we're going to be focusing on today, is how might software be built in the next few years that departs from the ways that we've done it in the past?
And how might that affect the experience that users have using that software?
So as Anjana mentioned, I'm working on DXOS, which is a platform that helps you build this different shape of software that we're going to talk about.
You can find out more on that at DXOS.org.
And if you want to find out more about my philosophy of computing, you can go to my website at jessmart.in.
It is very dry.
OK.
So this talk is divided into three parts.
I'm going to go pretty quick, so I'm going to try and give you a roadmap up front.
The first part is going to be the more philosophical musings on a new shape of software.
So that'll have to put on your thinking caps for a minute.
In part two, we'll actually get to some Effect-specific stuff, and unpack how this new shape of software collides with the Effect Schema package.
And then in part three, hopefully we'll have time to talk about beyond apps and what software might be like in the future.
And we'll have some demos along the way and have some fun.
OK.
So what if software had a different shape?
Something I've been thinking about for a while.
But let me put it to you in a scenario.
You go into work on Monday morning, and your boss says, you're on a new project.
You're going to be building a new web app or a mobile app.
It's a pretty standard SaaS app, something you've built before, that kind of stuff.
He gives you a little description of the project.
I'm not going to give you one.
I'll let you fill in the blank based on your workplace, wherever you work.
Imagine the decisions that are currently going through your mind.
I imagine you're thinking, I'll probably use this language.
Or maybe I'll use this front-end framework.
Or we're already using this tool internally.
Obviously, I'll have to use that tool when I build this new project, because I can't just choose whatever I want.
Or maybe you get to explore, and you're thinking about this new hotness platform or library.
You're like, oh, maybe this is the project where I can bring in the new thing.
So things that are going through your mind, TypeScript, React, maybe Effect.
But I bet of all the things that are going through your mind, it probably looks something like this in terms of shape.
You probably have a front-end that the user is going to interact with.
And whether that's a web app or a mobile app, whether you're building it in iOS or React, you've got that front-end.
And then you've got some database where you're storing the data for the user, whether you were thinking of Postgres or SQLite or some fancy or Amazon-hosted thing.
And in order to communicate with that database, you've got to put a back-end in front of it.
Because who writes to the database with their front-end?
That's insane.
No one writes SQL anymore.
It's not PHP anymore.
So you need a back-end.
And where back-end is going to wrap that database with some form of ORM.
And you've got another language over here.
And you've got the choice of JavaScript everywhere.
Or Rails is still around, guys.
And then you've got an API layer to disintermediate the front-end and the back-end.
Did anybody... raise your hand if you had a different shape in mind than this.
Sort of a couple hands.
I would love to talk to you afterwards to hear what you had in mind.
OK.
So what we're talking about when we're talking about shape is this weaselly word architecture.
And it's one of those words that's really hard to define.
Everybody has a different thing that they call architecture.
Front-end developers now want to be called front-end architects because it makes them feel special.
But I love this definition.
Architecture is the stuff that's hard to change.
And when you adopt this shape, you have made some things very hard to change.
You are going down this path.
And like a railroad, this path will make certain things very easy to do.
And it will make other things prohibitively difficult.
And I know that you're thinking of some of those things that have been difficult in your world in this client server.
I'll just name a couple offline support, real-time collaboration, all of these things that, whoo, to get that, I have to add whole new systems on top of my existing thing.
Well, what if we made one change, just one change to this diagram?
And let's just see what happens.
Since we can do that, we haven't written the software yet, we can just drag boxes around, right?
Let's drag this box, the database box, and we're going to put it over here in the front end.
Just see what it would look like something like this.
Just a front end and a database, right?
Well, if we do that, what else happens?
Let's see.
So the front end can just write to the database.
I know I teased PHP earlier.
But there's no server here.
There's just a client.
And there's a database.
And they can write directly to each other.
So there's no need for a back end to wrap the database.
And so they don't have a back end.
They don't also need an API layer.
And so I just have these two things.
That sounds kind of nice, actually.
Less boxes is usually good when we're defining an architecture.
But that kind of starts to feel like this.
Didn't we have this 30 years ago?
Well yeah sure.
We had a computer with a hard drive that was running application software that saved to and read from the hard drive.
But we don't want that.
The internet is here now.
We actually want our software to be connected to other software.
So maybe an architecture, maybe a shape like this might work.
So we could have a bunch of front ends that have the same database that's maybe synchronized or shared across them.
But each front end is writing to its own database.
I mean, this kind of makes sense.
I mean, at the very least, I would like my devices to share their data.
I mean, I'm sitting here looking at my slides on my phone, which are synchronized with the slides on my... that seems like a nice thing.
And I also might want to share some stuff with other people's devices as well.
So still, this is a different kind of shape.
Looks more like peer to peer, something like that.
We'll get into the networking stuff about this later.
But like, postpone those thoughts of WebRTC and WebSockets for a moment while we consider this.
Quick show of hands.
Who has heard the term local first software?
Two hands up from some folks.
That's great.
A lot of people.
That is awesome.
So yes, I am roughly describing something that meets the seven ideals of local first software.
If you want to talk more about local first software, I am not the expert.
You should be talking to Johannes in the back of the room who gave the talk earlier.
He has this wonderful podcast, localfirst.fm.
You should go check it out.
And it's a great listen.
All right, so that's local first.
So now you guys actually want to see some software.
Like, let's actually switch to demo mode.
Let's see.
All right.
All right, cool.
OK, so this right here...
oh, I got to speak in this.
This right here is a very simple piece of software that has the shape that I just described.
You can actually go try it out for yourself at hello.dxos.org.
If you want to pull it up on your phone, go for it.
Give it a try.
This replicates the experience that you would have of going to an event and wanting to meet people and get their contact information.
And so I'll daringly put my email there and pick my favorite emoji.
I'm tired.
Haven't had enough coffee.
Pick a color and hit Submit.
And it gives me a little name tag.
Not a whole lot to it, right?
Now, there's some things that you might notice if you've built software before that there was-- I didn't set up an account just now.
There was no, like, when I put in my email, go to your email.
Click the magic sign in link.
And then come back.
And now you have an account somewhere on a server.
That's because all this data is served, like I showed earlier, is saved locally on this machine.
And I can kind of demonstrate that by closing it and coming back to it.
And a few seconds will pass.
And hopefully it'll do it back up.
And maybe... and boom.
There we are.
Same data is there.
I can even... dare I turn the Wi-Fi off?
Oh.
Whoa.
Whoa.
What's he doing?
Oh my gosh.
This guy's a maniac.
All right.
Submit.
All right.
Whoa.
OK. Let's go turn it back on.
I just made an edit while offline.
Who does that?
You can't do that with most software?
All right.
So that's kind of neat.
But like we talked about, you actually want to share with other people, too.
So I'm going to bring up another device, so to speak.
This is another browser.
So just treat these as two separate devices.
On the right, you've got Chrome.
And on the left, you have Arc.
And let's see.
Let me go to this little Invite button.
Invite... not all of you yet.
Invite 1.
I'm going to copy that over here.
And I'm really tempting the conference Wi-Fi right now.
And connecting.
Dot dot dot.
Will we be in luck?
Let's see.
Drum roll.
Woo.
All right. I'm going to kick it one time and see if it works.
All right.
Invite.
Come on, baby.
Let's see.
Invite 1.
One more time.
Two invites.
I appreciated the Drum Roll on Drum Roll.
Somebody's giving it up.
Here we go.
Come on now.
Second time's the charm.
All right.
749271.
Can barely type.
Here we go.
All right. OK.
And there's Jeff Martinsss.
What?
What's going on here?
All right.
So there's no servers that this is getting backed up to.
Live demo.
Not live demo.
I promise this is real.
This is real.
email.com.
All right.
Having trouble typing.
Let me give an emoji to this guy.
He's slightly crying because it didn't work right away.
And I'll give it a different color.
All right.
And hit Submit.
And boom.
That was kind of fast.
All right. So now you can actually synchronize on both sides.
I can come over here and make some edits.
And hit Save.
And it automatically changes on both sides.
OK.
I could go on to demonstrate turning off the Wi-Fi again, making edits on both sides, bringing it back up.
But that takes time to do the PTP networking dance.
And I've got other stuff to show you guys today.
So trust me that it works.
Dot dot dot.
Thank you, sir.
Thank you.
All right.
I'm not sure if I'm supposed to drink that now or later.
Or-- [INAUDIBLE] Yes indeed.
Appreciate it, buddy.
OK.
So I need to explain a couple of things about what's going on there.
So that demo was powered by the DXOS.
And there's a lot of magic happening there behind the scenes.
I'm just going to give you... this talk is not about DXOS.
It's really about how we're using Effect Schema.
So I'm just going to give you the only intro you need in order to understand the rest of the talk.
There's basically three things that make up DXOS.
We have a data layer, which is our in-memory or our database... sorry, on the client database called Echo.
We're going to talk a lot more about that in a minute, because we're going to use that with Effect Schema.
We also have an identity layer, because when you don't have OAuth and all of that other stuff to deal with, you have to solve for identity somehow.
And so I saw you back there nodding your head.
And then you have to deal with networking.
So we do some peer-to-peer networking.
We call that mesh.
I won't talk about Halo or Mesh anymore today, but I will talk some more about Echo.
All right.
Now we're in part two.
Whoo!
We made it through one third of the talk.
OK.
So part two, now that I want to focus on one specific aspect of local first software that has a radically different shape when you move to this peer-to-peer system over client server, and that's schema.
What is schema?
I know that probably the first thing that went through your mind when you heard that was something about databases.
You probably thought about your database schema in Postgres or your SQL database that defines the shape of the data in the database.
But really schemas are everywhere in software.
Function signatures are schema.
These are the type of things you can pass to this function, and you have to pass them or the function fails.
There is API layers are schema.
In fact, they're explicitly.
This endpoint accepts these fields in these types.
Do not send me anything else, or I will 200 your butt.
Types themselves in our systems are schemas.
All of these things are schemas.
Even protocols like HTTP and so forth define schemas.
We find that basically computing is absolutely littered with schema, and we don't do a great job of handling that.
Let's go back to our architecture.
What we're going to do is we're going to look at schema and client server, and then talk about schema in this new shape of software.
Where is the schema here?
I've already hinted at it.
You've got, starting on that side, you've got in your database, you're defining your schema somehow, probably in the SQL, but maybe you're one of those crazy people who still believes in the NoSQL movement and likes your schema's implicit.
But you've got a schema somehow defining the shape of your data.
You also have another schema at the ORM layer.
People don't often think about this, but think about the time that you had to do a validation that wasn't a basic type in your database.
Maybe you had to validate an email address, or you had to validate the formatting of something.
You probably wondered where to write that.
Do I go write a stored procedure and store it in my database and then have my database tell me when it violates that?
Or do I just write it in the ORM layer?
Or do I do both?
Or how do I have my ORM layer ask Postgres about the stored procedure to find out if it's valid before I try to do the save and then it throws an accept?
You end up with a separate set of validations, usually, in your ORM layer.
That's a bunch of custom code that's not quite supported by your database or isn't just quite ergonomic.
And then over here in the API layer, you usually transform your back end into some API.
These aren't even the same thing anymore.
And we brag about that.
We're like, look, they're decoupled.
You can independently iterate on them.
I can make changes to the back end without having to change the API layer.
Except if you want to do any real work, you have to do both of them.
And then we have, on the front end, now that we have rich web apps on the front end, we have state management systems that have schema again.
Here's how we're going to store the data in memory in JavaScript.
We have TypeScript.
You guys know a few things about types way more than I do about them.
But we have to take care of that.
All of that is schema.
That's a lot of schema.
If you just consider making one change to a system, I just want to add one field to the front end.
One field, right?
Your boss says, oh, we just need middle name in addition to first name and last name.
How long can it take?
Imagine all of the changes that you have to go through in order to do that.
You've got to update your state management system, update the types, add a new field to your API, add a new field to the database, do a SQL migration, update the ORM.
There's changes in seven layers to just add another field to an existing object.
This is crazy.
OK.
Well, in this world, we still need schema.
It's not that we're throwing it out.
We still need to define what the shape of data is at rest.
We'd still like to have nice compile time type checking.
That would be wonderful.
And we still need database validations, like data validations.
If I need to know is this thing valid before I try to stick it in the database, I need the database to tell me, nope.
Not going to accept that.
Well, we think we can do all of this with a single schema.
One schema for the whole stack, all the way from the data to the front end.
The schema of the database is the schema of the API, is the schema of the front end.
Whole thing.
How do we do that magic?
With effect schema, of course.
All right.
So what's about to happen is I'm going to go into 2x mode.
You thought I was speaking fast before?
I am about to go very fast, because I'm going to give you a very brief overview of effect schema.
How many people have actually written s.struct?
Oh sweet.
I can go very quickly.
All right.
Great. Most of you are familiar with the basics of effect schema.
I'm still going to go through it.
Here's 2x.
All right.
So we need... effect schema, in my mind, does three things for the people who are not familiar with it.
It allows you to define the shape of data, define a schema, perform some validations against that data, and then do the all-important encode-decode step of transforming unknown data into that particular type that you've just defined, or the object that you've just defined.
So defining a schema looks like this.
I'm only showing you this, because I'm going to show it to you again on a slide seven or so slides from now.
And I want you to see how it's different.
So here's the basics.
s.struct allows me to define a schema object.
It has a title.
It has a done.
This is a task item.
You can even define fancy schemas, which is pretty cool.
So that custom code that I was talking about that often your database may or may not support some or all of, you can pipe that item or that field to a set of functions that will validate that it meets those requirements.
So you can make it required.
You can set a min-length for strings.
There's a whole bundle of these that are included with effect schema, which is really nice.
Every time I've needed to write one so far, for the most part, there's already been one there.
And obviously, we could add more.
And then the reason you want that fancy schema is so you can do validation.
So now I've written a bad task at the top, where that done field, that is not a Boolean.
Did I finish the talk slides?
I don't know.
I guess we'll find out in a few minutes.
It's a maybe.
That's not a Boolean.
And so is this thing a task?
If you call this isTask, it returns false.
But you can do a stronger thing, where you can assert that a thing is the right shape.
And if you do an assertion, it throws a parse error.
And parse errors are actually really cool.
I'm going to talk about those in a second, because they give you some really nice information about exactly what went wrong while you're parsing it.
All right.
I don't have to cover encode and decode because in 45 minutes, Anna's going to talk about that, which is awesome.
So that's coming up.
And let's see.
The other nice thing is that your types can be inferred from the schema.
So if you haven't heard that... if you're not familiar with that... sorry, Effect Schema, it's
that... sorry, Effect Schema, it's nice to be able to get the same compile time type checking that you have for validating data.
It's a really nice library.
OK.
In 2x mode.
Now I'm going to show you how we're using Effect Schema at DXOS.
Caution.
These are unreleased APIs.
This is not in production yet at DXOS, but it is something that we're testing internally and actively debating the shape of the API.
In fact, we were changing it last night on the train.
So if you like this API, or you want to have some feedback on it, feel free to talk to me or my three colleagues who I'll introduce in a little bit, and give us feedback.
Because we're going to implement this API in the next few weeks.
Or lock it down.
So this would be sample code from our Hello app earlier.
Remember our Hello app had a name, an email, your favorite color, and hex code, and an emoji.
So you've got a contact object that has those objects, strings, and you've got this extra import of DXOS echo schema.
And the only thing that's different here is this extra pipe, this magical line, e.echo object.
What the heck is that?
Notice that we pass in two things.
One is a name for this type so that we can recognize it and describe it or whatever.
And then the last thing is a version number.
Because eventually, you might want to change your schemas.
I'm not going to talk about that yet.
And so that's nice.
The other thing that you get here is we have our own schema, or we have our own function for deriving the types.
Why do we do that?
Why don't we just use s.schema2?
And the reason is that there are echo-specific fields.
I don't know if anybody was paying attention in Tim's talk when he talked about persistence.
But I was.
That's cool.
That's a cool library.
And he mentioned specifically that they have to add additional fields to the schema.
You need the database ID.
And that's one of the things that they add on top using their persistence library.
Same for here.
There's some database-specific stuff that you want to add.
That needs to be part of your types.
Because we're using one schema the whole way through.
You may not need that on the front end on your client server app.
But when you're using one schema, you do.
All right.
Let's go to validation.
Talk about this.
We talked about we want to do data at rest.
We want type checking.
And we want really nice validation.
And so here's a... this is actually... OK.
Pause on validation.
We'll come back.
That's the next slide.
This is the echo part.
Remember I talked about how we have a database in DXOS that's running on the client.
This is where you actually take an object and insert it into the database.
So that space.db.add is inserting an object into the database.
And you'll notice that I'm passing in that contact type.
That's the contact type that I got from the previous slide that we inferred from the schema object.
And then I can pass in these fields.
And when I do this, this performs the validation at that point.
And if it is valid, inserts it in the database.
If it doesn't, we'll talk about what happens in a second.
But this also triggers replication to all connected devices.
So this is that mesh piece where what you guys saw happening between those two devices in the Hello app was happening because this object was added in.
And then if you want to change the object remember that one change that you needed to add a field or that kind of stuff?
If you want to mutate the object, you just do this.
There isn't anything else.
You just set the email directly.
That updates the database.
That replicates it to all devices.
That updates it in memory.
That's it.
It's just one line.
Just mutate the object directly.
Remember this diagram from earlier.
So it's replicating that database across all the different clients.
The really cool thing about that object that you got back from the database, that space.db.add,
is it's a reactive object that any time any client makes any change to that object, it updates in memory using a signals library.
We can talk more about signals later.
But the nice thing is here's a really simple contact card that uses contact.name.
This thing will re-render magically, automatically, every time the name field changes on contact.
And so you get that kind of reactivity for free, which plays really nicely with React.
Makes it really easy to write these kind of applications.
Notice there's no subscriptions here.
It'll automatically re-render.
We talked about validations a minute ago.
This gives you really nice validation.
So you can write this email regex and pipe it in.
And I'm basically showing you this pipe.
If you're familiar with... this isn't anything surprising.
This isn't different at all from effect schema.
But then we take advantage of everything about the effect schema library for the purposes of validation.
So if you actually do a if you set contact email to something that's not an email, it'll throw a parse error.
And this is what I was talking about.
You have these really nice errors.
I don't know if anybody remembers ActiveRecord, but I'm old enough to remember ActiveRecord from Rails.
And these kinds of error messages make building UIs and building applications really nice.
Here's how the thing was wrong.
And you can translate that directly into a message to the user.
OK.
Woo!
OK.
We got one schema, data at rest, type checking, and database validations, all from effect schema.
And then building that on top of DXOS's Echo database, it also replicates all across all your devices.
You survived the first two parts of the talk.
We're now to part three, Beyond Apps.
So far, we talked about a new shape of software.
We talked about how we use Effect Schema to solve for the distributed schema problem.
But now I want to talk about Beyond Apps.
The thing is, once you have schema everywhere in your system, and it's the same schema, it enables all kinds of new and interesting things.
So let me show you another demo.
Let's see.
Actually, before I get there...
I'll do the demo in a second here...
one thing that we do...
who's familiar with Effect Schema's ability to serialize schemas to JSON schema?
So Effect Schema can actually take your schema and serialize it out to disk.
So what we do is we take that schema and we store the schema itself in the database.
Now, you might be thinking, why would you do that?
You literally have the library or whatever...
you exported const s.struct.
You have it in your code base.
Why would you need to store it in the database?
Let's find out.
Let's see.
All right.
OK.
Can you give me some volume?
There we go.
So we will come back over here and So I need to introduce another application.
So we had Hello, which is hello.dxos.org.
This is Composer.
So I'll tell you more about Composer in a second.
But the important thing you need to know about Composer is it's basically like a crossover between Notion and Air Table.
But it's fully customizable and it's open source.
And it's built on top of DXOS, so it's real time collaborative and all the magic.
And it is infused with schema.
In other words, it knows all about the schemas you have in the database.
And one way I'll demonstrate that is coming over here.
We're going to go to our Effect Days thing.
So Composer has this really nice thing called a table.
And the table actually can look up schemas in the database.
So you see this dxos.org.contact schema.
And then contacts.
Close here.
Whoa.
What just happened?
This application on the left let me open a new window here.
Not a new incognito window.
Whoop.
Boop.
Boop.
There we go.
All right.
So on the left, we have composer.dxos.org and hello.
I'm sorry, hello.dxos.org on the other side.
Those two applications are synchronizing the same database across applications.
So you have multiple apps reading from the same database.
The Composer code base knows nothing.
There's no code that defines a contact schema inside the Composer database.
Instead, it saw an object in the database, looked up the object schema from the database, and then was able to instantiate a table.
And so you get things like this, where I can come in and make a change, fix my last name, and it does it magically right both ways.
I can even add new records.
Whoops.
Not that one.
Here we go.
Johannes Schickling.
Johannes, what's your favorite emoji?
Mind blown.
Let's see if we can do it.
Sorry, we're already on mind blown.
And we're going to do a random color.
Six random digits.
Boom.
There's Johannes over there.
For some reason, I didn't pick up his name.
Johannes.
Boom.
Whoa.
OK.
So the reality of what's going on here is that my previous little diagram was a little bit of a lie.
The lie was that there's a front end that has a database in it.
When it's really multiple applications reading from the same database.
Fun.
So there's some magic that you get from this when you have the schema everywhere in your application.
And I could show you some really cool things that DXOS does, or that Composer does for you.
But I think the coolest one I'll jump straight to the closer.
Here we go.
Why does it keep doing that?
All right.
We'll open this little AI folder.
What is that?
All right.
Let's see.
OK.
So somebody might have actually read the effect blog and read this post.
But I've got this little AI thing that I can show you.
So over here, we've got these are AI prompts.
Who's used Chat GPT?
Please, every hand go up.
Most hands.
So when you're prompting an LLM, you have to tell it what you want it to return to you.
So you've got this little prompt here.
List all companies mentioned in the text below.
Now, we can actually add the schema into the contact, into what we're asking the LLM to return.
So this gives us the ability to say, hey, LLM, this is the shape of the data that I would like you to return.
And so we can go over here.
I think I need to do I need to recall one thing.
Creat table.
New schema.
dxos.org.contact.
OK cool.
Boop.
Close.
All right, when you go over here.
Boop.
All right, so we're asking, list all the people from mentioned in the text below.
And can you have it conform to this dxos.org.contact?
Let's see if this works.
So we'll go over to the summary of the trip.
This was a blog post posted on the effect blog.
I'm going to select the whole thing, come over here, and say, slash extract.
All right, hopefully you see a little processing thing right here.
Please.
This is reliant on us hitting the OpenAI API.
Let's see.
Actually check and make sure the function server's still running.
Boop.
Two seconds.
Two seconds.
Up up up.
At first, you don't succeed.
Today's been the demo day of do it twice.
Let's see.
Slash extract.
Ah, you're right.
That one is.
Thank you.
This is why you should always code with a large group of people watching you.
Slash extract.
There we go.
We do need some type.
I think I need some Effect.
All right, well, I'm going to give it up at the end here.
I think the point is that I wanted to make is that when you have schema encoded through your entire application, you can do lots of magical things.
Composer does some great stuff here.
If you'd actually like to see this AI demo working, I have three compadres sitting over here.
Let me switch over to my last slides, and we'll close this sucker out.
All right.
So one thing I didn't talk about was schema migration.
That's a hard problem.
We're working on it.
If it's something you're interested in learning how to change schemas across a decentralized or distributed group of applications, talk to me.
Johannes is also working on that one.
It's an interesting one.
Schema distribution is also not as challenging.
I'm not as easy as I made it sound.
How do you serialize arbitrary code for validations?
We don't do that yet.
In fact, schema doesn't do that yet.
But the point of the talk, hopefully, is a different shape of software makes some hard things easy.
You can go from many schemas down to a single schema.
So I hope you guys rethink the shape of software the next time you're approaching one of your new projects.
If you're interested in working on this or seeing more demos, come help us build.
Rich, Yaroslav, Dima, can you guys wave your hands?
All three of those guys are also working on DXOS.
So feel free to grab any one of us, ask us questions.
We have loved working with a Effect Schema and hoping to do more.
So thanks, guys.
(audience applauds)
Loading video analysis...