Scaling AI for Customer Support at Markprompt with Effect
#2: Scaling AI for Customer Support at Markprompt with Effect
Join us as we talk with Michael Fester from Markprompt about scaling AI-powered customer support with Effect, building reliable and high-performance infrastructure, and enhancing developer productivity in a fast-evolving AI landscape.
Join us as we talk with Michael Fester from Markprompt about scaling AI-powered customer support with Effect, building reliable and high-performance infrastructure, and enhancing developer productivity in a fast-evolving AI landscape.
Effect is an ecosystem of tools to build production-grade software in TypeScript.
Song: Dosi & Aisake - Cruising [NCS Release]
Music provided by NoCopyrightSounds
Free Download/Stream: http://ncs.io/Cruising
Watch: http://ncs.lnk.to/CruisingAT/youtube
- (00:00) - Welcome & guest introduction
- (01:54) - Michael’s journey: from academia to AI & Customer Support
- (03:49) - What is Markprompt? Overview & use cases
- (07:45) - Markprompt’s system architecture
- (10:22) - Challenges of running AI-powered support systems
- (13:20) - Improving reliability with Effect
- (16:41) - Technical architecture breakdown
- (19:51) - The public API server setup
- (23:50) - Ingestion engine
- (26:29) - Onboarding engineers to Effect
- (30:51) - Migrating the codebase to Effect
- (35:19) - Effect in production: the power of schema
- (39:02) - Migrating to Effect: challenges & key takeaways
- (41:45) - Effect brings out the best in us engineers
- (45:34) - The Future of AI infrastructure
- (50:18) - Closing remarks & thanks
Transcript
01:00:00
As an engineer, you want to build
01:00:01
features, you want to
01:00:01
build like real robust systems.
01:00:04
You want to spend ideally
01:00:05
all your time doing that.
01:00:06
You don't want to spend your time
01:00:08
fighting the language,
01:00:09
building something that, you
01:00:10
know, the language was not meant for.
01:00:12
To give you an example,
01:00:13
like concurrency in TypeScript.
01:00:14
You want a way to build a concurrent
01:00:17
system where you're
01:00:18
thinking about what is
01:00:19
the system actually doing rather than
01:00:20
like setting up all the
01:00:22
boilerplate in order to
01:00:23
have that system in place.
01:00:25
So I would say Effect really
01:00:27
gives you the right premise.
01:00:28
So now that we've completed this
01:00:30
migration, kind of feel
01:00:31
like this is the end game feels
01:00:33
like more stable and at least if we
01:00:35
continue building our
01:00:36
code base on TypeScript with
01:00:38
Effect, it doesn't feel like we're going
01:00:39
to spend many more like
01:00:41
cycles, like refactoring
01:00:43
the code.
01:00:47
Welcome to Cause & Effect, a podcast
01:00:49
about the TypeScript
01:00:50
library and ecosystem called
01:00:51
Effect, helping engineers to build
01:00:54
production-ready software.
01:00:56
I'm your host, Johannes Schickling, and I've
01:00:58
been building with
01:00:58
Effect for over four years.
01:01:00
With this podcast, I want to help others
01:01:02
understand the powers and
01:01:04
benefits of using Effect.
01:01:06
In this episode, I'm talking to Michael
01:01:08
Fester, co-founder of
01:01:10
Markprompt, an AI-based customer
01:01:12
support platform.
01:01:13
In this conversation, we explore how
01:01:16
Markprompt uses Effect
01:01:17
across its entire architecture,
01:01:20
including API and RPC endpoints,
01:01:22
long-running workflows, and their
01:01:24
customer-facing product.
01:01:25
Let's get into it.
01:01:28
Hey, welcome Michael.
01:01:29
So nice to have you on the
01:01:30
Cause & Effect Podcast.
01:01:31
How are you doing?
01:01:32
I'm good.
01:01:33
Thank you.
01:01:33
I'm really happy to be here.
01:01:35
It's been, I guess it's been a while that
01:01:36
we're trying to
01:01:37
organize this, but I'm very
01:01:39
happy that we're doing it now.
01:01:41
Yeah.
01:01:41
I mean, you're super, super busy.
01:01:44
You're based in San
01:01:45
Francisco building an AI company.
01:01:48
So I'm really, really happy that you took
01:01:50
the time to share
01:01:51
your story about Effect.
01:01:53
Maybe you can share a little bit about
01:01:56
yourself, your
01:01:56
background and what are you doing?
01:01:59
Yeah, sure.
01:02:00
So I have a background.
01:02:02
I started in academia.
01:02:03
Actually, I was doing
01:02:04
research in mathematics.
01:02:05
Then I started getting into AI and I
01:02:07
built my first AI
01:02:08
company in Europe, specialized
01:02:10
on device voice recognition.
01:02:12
And we got acquired by Sonos.
01:02:14
And since then I moved here to San
01:02:17
Francisco for my next
01:02:19
venture, which is we're building
01:02:22
AI infrastructure for customer support.
01:02:25
So this is all like posts,
01:02:27
chat GPT, GPT 4 coming out.
01:02:30
And we saw this, this incredible
01:02:31
opportunity to start
01:02:32
working in this space.
01:02:35
That is awesome.
01:02:36
And I highly encourage everyone who is
01:02:40
like interested in AI,
01:02:41
particularly is looking
01:02:43
for a better customer support solution to
01:02:46
check out Markprompt.
01:02:48
Can you share about a few companies who
01:02:50
are already using Markprompt?
01:02:51
Yeah, sure.
01:02:52
So we've been working very closely with
01:02:55
some companies very
01:02:56
early on in the process.
01:02:57
Actually, that's really interesting.
01:02:58
Vercel started using us as one of our
01:03:00
first customers,
01:03:01
actually ElevenLabs, Replicate.
01:03:03
Things are just moving so fast.
01:03:05
So our approach here is not so much of a,
01:03:08
you know, we have a
01:03:09
vision of how things will
01:03:10
end up, right?
01:03:11
It's very much like a learning process,
01:03:13
working very, very
01:03:14
closely with customers, hand in
01:03:16
hand getting their feedback,
01:03:17
understanding together, how
01:03:19
this thing is evolving, how
01:03:21
we can improve, you know, the behavior of
01:03:23
the LLMs or how we can
01:03:24
extend the use cases.
01:03:26
So this has been a very different way, I
01:03:28
would say, of building
01:03:29
a company from the past,
01:03:30
because it's so new to
01:03:32
everyone in the space.
01:03:33
And we're just like scratching the
01:03:35
surface of the
01:03:35
opportunities of building these systems
01:03:38
for you know, down the line.
01:03:40
That's awesome.
01:03:41
So before we go into the technical parts,
01:03:44
maybe you can share
01:03:45
through the lens of one
01:03:47
of your typical customers, what
01:03:49
Markpompt does and which
01:03:51
problems it solves for some
01:03:53
of those customers.
01:03:54
Sure.
01:03:55
So there's multiple facets of this
01:03:57
running a support
01:03:58
operation is it's not a one size
01:04:01
fits all.
01:04:02
Every company has their own ways of, you
01:04:04
know, the nature of
01:04:05
customer issues, the way that
01:04:07
they engage with customers,
01:04:09
is it like direct contact?
01:04:11
Is it, you know, is it mostly self serve?
01:04:14
Technical challenges are very different
01:04:16
from one company to the other.
01:04:18
So we offer really an
01:04:20
end to end solution.
01:04:21
So there's multiple
01:04:23
components in our system.
01:04:24
It all starts with understanding your
01:04:26
system of record, which is
01:04:28
things like your underlying
01:04:30
customer knowledge, your, you know, your
01:04:33
documentation, your CRM, the API is that
01:04:37
you use for automating
01:04:39
parts of your support.
01:04:41
And we connect all that together.
01:04:43
And then we build applications that are
01:04:45
either customer facing
01:04:46
or agent facing so and use
01:04:49
internally for the company.
01:04:51
So it can be, you know, everything from,
01:04:52
you know, your contact form powered by AI
01:04:55
to your chatbot to your
01:04:58
Slack and Discord bots, right?
01:05:00
This is customer
01:05:01
facing email order replies.
01:05:04
So you send an email and the AI agent
01:05:08
will start, you know,
01:05:09
trying to resolve the issue
01:05:11
for you.
01:05:12
And it might be possible
01:05:13
that it resolves it fully.
01:05:16
And if not, it's going to ask you for all
01:05:17
the questions that the team then needs to
01:05:19
use once you know, the ticket
01:05:21
hits the inbox internally.
01:05:23
And then there's the all the features
01:05:25
which are internal
01:05:26
facing for the support
01:05:28
team to be able to streamline their work.
01:05:31
Because the truth is that customer
01:05:32
support before AI involved
01:05:36
a lot of essentially like
01:05:38
robotic work, just answering the same
01:05:41
questions over and over again.
01:05:42
This is not super engaging
01:05:44
and exciting just to have
01:05:45
to answer the same
01:05:46
question over and over again.
01:05:47
So if you can start like taking that
01:05:49
burden off of your
01:05:50
shoulders and help you, you know,
01:05:52
work on more like high leverage strategic
01:05:56
questions, then, you
01:05:58
know, we can really help
01:05:59
streamline the support operation.
01:06:00
what happens when you've put
01:06:01
an AI or both like customer
01:06:03
facing and agent facing, then you can
01:06:06
start, you know,
01:06:07
extracting a lot of intelligence,
01:06:09
a lot of knowledge, because you're really
01:06:10
at the nerve center
01:06:11
in a company, you are,
01:06:13
you know, the interface between your
01:06:15
customers and your company, right?
01:06:18
So you have so much like knowledge that
01:06:20
often just gets siloed
01:06:21
within the support team,
01:06:23
right?
01:06:23
It's it's actually very rare that support
01:06:25
team reports to product, right?
01:06:28
And I think that's a
01:06:29
that's really a problem.
01:06:30
But if we can start rooting
01:06:32
out all this insight and
01:06:33
knowledge, we can start
01:06:35
having a really, really good
01:06:36
understanding on, you know,
01:06:37
product shortcomings, where
01:06:39
the engineering team might,
01:06:40
you know, put some effort, right?
01:06:43
Because this is where your customers have
01:06:44
been experiencing a
01:06:46
lot of friction, right?
01:06:47
So we can start generating these
01:06:49
reporting and all sorts of
01:06:51
other insights that can help
01:06:53
you as a company
01:06:54
streamline your operations,
01:06:56
things like spotting knowledge
01:06:57
gaps, right, helping you write, you know,
01:06:59
keeping your your
01:07:00
documentation up to date,
01:07:02
these are all things that
01:07:02
we can start also working on.
01:07:05
So this concept of
01:07:05
agent is very versatile.
01:07:08
The agents can solve issues, they can,
01:07:10
you know, answer emails,
01:07:11
but they can also start
01:07:12
being tasked of gathering
01:07:14
insights and producing new
01:07:15
knowledge and keeping your
01:07:16
system of record,
01:07:17
always up to date.
01:07:18
So there's really a lot of
01:07:19
things that need to happen.
01:07:20
You cannot approach the
01:07:22
problem just from one
01:07:23
angle, you really want to tie
01:07:24
the whole thing together, because this is
01:07:26
where you start creating
01:07:27
these positive feedback
01:07:28
loops, so called data flywheels, right,
01:07:31
that enable you to
01:07:32
start deploying the solution
01:07:33
and trust it that it's
01:07:34
operating really well.
01:07:36
So yeah, there's,
01:07:37
there's a lot of things to it.
01:07:38
That sounds fascinating, and also quite
01:07:41
complex in terms of the
01:07:42
different surface areas, etc.
01:07:45
So maybe we can go
01:07:46
slightly more technical.
01:07:47
And before we go into all of the Effect's
01:07:49
specifics, can you more from like an
01:07:52
engineering perspective
01:07:53
provide an overview over your system?
01:07:56
And which different high level components
01:07:59
it's composed out of?
01:08:01
Sure.
01:08:02
So this is a, you know, apart from the
01:08:04
machine learning things,
01:08:06
the LLM things, this is a
01:08:08
classical, you know, setup.
01:08:09
there's some servers, both
01:08:11
like short, like serverless instances for
01:08:13
API endpoints, then
01:08:14
there's some long running
01:08:16
servers to do things like, you know, pre
01:08:18
processing data, post
01:08:21
processing, interactions.
01:08:23
So yeah, there's a there's a bunch of
01:08:25
servers here, everything
01:08:25
is in node, everything is
01:08:27
or TypeScript.
01:08:29
The front end is a React application.
01:08:32
So both we have a dashboard, which is a V
01:08:35
duplication, there is
01:08:37
some integrations
01:08:38
inside of Salesforce,
01:08:39
Zendesk, Slack, and so on.
01:08:41
So it's a classical full
01:08:43
stack, React TypeScript application.
01:08:47
So you've built quite an expansive system
01:08:49
here and building all
01:08:51
of that in TypeScript
01:08:52
I think TypeScript is sort of notorious
01:08:54
for only scratching the
01:08:55
surface and only covering
01:08:57
the happy path.
01:08:58
There's a lot of things that can go wrong
01:09:00
in all of those systems, integrating with
01:09:03
different systems.
01:09:04
All of those things I think have led you
01:09:06
to take a more serious
01:09:08
look at Effect and ultimately
01:09:10
adopting it.
01:09:11
So can you share that journey of going
01:09:14
from a non-Effect code
01:09:16
base to converting step
01:09:18
by step to Effect?
01:09:20
Sure, so obviously there's the common
01:09:22
complexity increase that
01:09:24
happens when you're building
01:09:25
a product and putting it into production.
01:09:27
So as you add features and so on, then
01:09:29
you hit sort of a ceiling
01:09:31
and then you start assessing
01:09:33
the situation and then you might start
01:09:35
refactoring and so on.
01:09:36
And then next phase, you hit some other
01:09:39
limitations and then you
01:09:41
reassess again the situation.
01:09:44
So that's just the common thing of
01:09:45
working on an
01:09:46
increasingly growing code base.
01:09:49
But then there's LLMs and these are
01:09:51
non-deterministic systems and it poses
01:09:54
some completely new kinds
01:09:56
of challenges.
01:09:56
How do you monitor the behavior of the
01:09:58
system, for instance?
01:10:00
How do you deal with readable streams
01:10:02
instead of just sending over a payload?
01:10:05
This has the potential to making your
01:10:07
code base more complex
01:10:08
if you need to stream some
01:10:10
tokens down to the customer, but you
01:10:12
actually also need to
01:10:13
store these chunks on your own
01:10:16
for post-processing.
01:10:17
How do you deal with that?
01:10:18
TypeScript doesn't give you
01:10:19
some great answers to this.
01:10:22
How do you deal with reliability?
01:10:24
OpenAI and Anthropic and so on, they offer
01:10:27
a service which is in high demand.
01:10:31
We are in a period right
01:10:33
now with scarce resources.
01:10:35
So these APIs, they go down and they
01:10:37
actually go down more
01:10:38
than you would think.
01:10:40
And so how do you build fallback
01:10:42
mechanisms so that, say that
01:10:44
OpenAI is down as your main
01:10:46
provider, your customers cannot be down.
01:10:48
If OpenAI is down,
01:10:49
that's not their problem.
01:10:50
That's our problem.
01:10:52
So how do you build a
01:10:53
fallback system to Anthropic?
01:10:55
What happens a few weeks ago when OpenAI
01:10:58
was down for, I think,
01:11:00
five hours and then the
01:11:01
entire industry shifts towards Anthropic
01:11:04
that just has this
01:11:05
spike that they're unable
01:11:07
to deal with?
01:11:08
So they are experiencing outage or just
01:11:11
very degraded performance.
01:11:13
There's outage and then there's actually
01:11:15
performance, the
01:11:16
speed of tokens and so on.
01:11:19
All that, there's no good answer, no
01:11:22
clear best practice in
01:11:25
vanilla TypeScript to deal
01:11:27
with these things.
01:11:28
And then there's the fact that we are a
01:11:29
startup working in a
01:11:31
fast-paced environment where
01:11:33
things change all the time.
01:11:36
And we want to be efficient and we want
01:11:37
to be able to not deal
01:11:39
with all the complexity
01:11:41
and all the problems and down times and
01:11:42
reliability, but we need to
01:11:44
be effective in shipping new
01:11:46
features to constantly
01:11:47
meet up with the demand.
01:11:49
the zero to one of
01:11:50
building a company here.
01:11:51
So there's multiple factors that really
01:11:54
made us think hard about
01:11:55
what would be the foundation
01:11:58
for us to be able to operate really fast
01:12:00
with a small set of
01:12:02
resources from a startup and
01:12:04
be a highly reliable solution because we
01:12:07
are catering to larger
01:12:08
companies, enterprise
01:12:10
companies.
01:12:10
So reliability is just really key.
01:12:13
Obviously, I can talk a lot about the
01:12:15
whole telemetry part of
01:12:18
things, making sure everything
01:12:20
is properly monitored and that we can
01:12:21
actually spot when things
01:12:24
go wrong before our customers
01:12:25
even see it and then we can take action.
01:12:28
So there's a bunch of things here.
01:12:29
And Effect just came at a very good
01:12:31
moment where we still had
01:12:33
a sufficiently small code
01:12:36
base that we could actually start
01:12:38
seriously thinking about
01:12:39
completely converting it to
01:12:40
Effect.
01:12:41
So the timing was really good.
01:12:43
So you've mentioned in regards to
01:12:45
adopting Effect for the
01:12:46
sake of improving reliability,
01:12:49
one specific use case was that Markbrom
01:12:52
is using LLMs such as
01:12:54
the products provided by
01:12:56
OpenAI.
01:12:56
And if they go down, you need to fall
01:12:58
back to another system.
01:13:00
And in case that system also goes down,
01:13:02
fall back to yet another one.
01:13:04
All of that is helping to improve the
01:13:07
reliability of Markprompt.
01:13:10
Which other challenges did you face
01:13:12
similar to that one in
01:13:14
regards to reliability and
01:13:15
how did you improve
01:13:16
reliability through Effect?
01:13:19
I mean, there's lots of examples where
01:13:21
Effect has helped us
01:13:23
consolidate our system.
01:13:25
One example is one day we woke up and we
01:13:28
realized that we were under
01:13:29
attack by a malicious hacker
01:13:32
who was interacting with a public facing
01:13:35
deployment of Markprompt and
01:13:37
sending, I think, 20 million
01:13:40
requests per hour.
01:13:43
And this was across the globe, so it was
01:13:46
kind of hard to deal with.
01:13:47
So Ethan Niser, he spent the summer with
01:13:50
us and he's been working
01:13:53
on a lot of Effect-related
01:13:54
things and on his first day,
01:13:56
this attack started happening.
01:13:58
And so he built this great rate limiting
01:14:02
system in the middleware
01:14:03
that is capable of looking
01:14:04
at multiple facets of a payload.
01:14:08
And the interesting thing here is that
01:14:09
you cannot just rely
01:14:11
on IP and fingerprinting
01:14:12
and so on.
01:14:13
You actually also want to start looking
01:14:14
at the LLM payload itself.
01:14:16
What kinds of messages is there, pattern
01:14:18
that we can spot here in
01:14:20
the nature of the requests?
01:14:22
And so it's a multi-tiered rate limiting
01:14:24
system that is very easy
01:14:25
to automatically trigger
01:14:27
and also manually trigger.
01:14:28
So this is an example also where building
01:14:30
with composability has just allowed us to
01:14:34
build a fairly sophisticated
01:14:35
system here, which just works.
01:14:38
We are now very
01:14:39
confident that it just works.
01:14:40
And by the way, one of the reasons that
01:14:42
we're confident that it
01:14:43
works is that it's very
01:14:44
easy to test because the whole dependency
01:14:46
injection story is
01:14:47
just such a natural thing
01:14:48
to do.
01:14:49
You just operate a provider and you can
01:14:51
very easily simulate a
01:14:53
situation that is hard to
01:14:55
reproduce with other means.
01:14:56
So dependency injection, I would say, is
01:14:59
a huge factor to the
01:15:00
reliability because we
01:15:01
can restart testing all sorts of
01:15:05
different combinations of
01:15:06
non-happy paths and building
01:15:08
that with just change or service that you
01:15:12
provide is just very,
01:15:13
very easy with Effect.
01:15:14
Telemetry is obviously a thing.
01:15:16
So I think that the whole testing story
01:15:19
is not just about one thing.
01:15:22
You want to do unit tests.
01:15:23
You want to do integration tests with
01:15:25
dependency injection.
01:15:26
You also really want to have a full
01:15:28
understanding of what's going
01:15:29
on when you are in production.
01:15:31
And so being able to have good traces,
01:15:33
good logging of every
01:15:35
component of your system
01:15:36
is non-trivial.
01:15:38
And so we rely heavily on telemetry and
01:15:41
Effect allows us to just
01:15:43
add spans everywhere in
01:15:45
a very, very simple way.
01:15:46
And it just naturally fits
01:15:48
into the rest of the system.
01:15:50
And this really has allowed us to have a
01:15:51
very, very good
01:15:52
understanding of failure scenarios
01:15:55
before our customers even see them.
01:15:57
So these are just general patterns that
01:15:59
are very natural to do
01:16:01
in Effect that we don't
01:16:02
even think about because they actually
01:16:05
don't incur any sort of
01:16:07
additional thinking on our
01:16:08
part.
01:16:09
It's just adding a line
01:16:11
and then it just works.
01:16:12
Yeah, this makes a lot of sense.
01:16:14
I can totally see how reliability is
01:16:17
almost sort of like a emerging factor,
01:16:20
like a second order effect that comes
01:16:22
when you have a composable system,
01:16:25
and when you have observability, when you
01:16:28
can look into what's going on,
01:16:30
when you can eat your
01:16:32
vegetables, then you're healthy.
01:16:34
And so
01:16:35
Building a reliable system is going the
01:16:37
extra mile on all of those things.
01:16:39
This makes a lot of sense.
01:16:41
Given that you built
01:16:42
quite an expansive system,
01:16:44
maybe we can try to break it down a
01:16:46
little bit more with a technical lens on.
01:16:49
So I'm sure that you have
01:16:51
a client side application.
01:16:53
You've mentioned that it's like a single
01:16:54
page app built with Vite,
01:16:57
but more on the backend side.
01:16:59
Can you describe to me what are the
01:17:02
different subcomponents of your system?
01:17:04
Yeah, so in a very simple way, the main
01:17:07
subcomponents here,
01:17:09
there's the application server,
01:17:11
which is essentially an RPC
01:17:12
server that the client
01:17:14
application is speaking with.
01:17:15
Then we have a public API.
01:17:18
So Markprompt is built as
01:17:19
Stripe for customer support,
01:17:21
in the sense that every part of the
01:17:23
system can be
01:17:24
interacted with via our APIs
01:17:26
so that you can actually build deeper
01:17:28
product experiences
01:17:29
inside of your core product.
01:17:32
So we have a public facing API server.
01:17:34
Then we have the ingestion engine, which
01:17:37
takes in all your systems data.
01:17:40
So this can be public facing like a
01:17:41
website that we would
01:17:42
scrape on a regular basis.
01:17:44
It could be GitHub repository or
01:17:47
historical tickets from
01:17:49
Zendesk and Jira and Confluence
01:17:51
and databases from
01:17:53
Salesforce and whatnot.
01:17:55
So we do a bunch of things here.
01:17:57
We import data.
01:17:58
We chunk it up.
01:17:58
We monitor the whole structure of
01:18:02
documents through the AST
01:18:03
and then we build
01:18:04
embeddings for multi-levels.
01:18:06
And then we store them
01:18:07
in a vector database.
01:18:08
And then we have all the
01:18:09
application related servers,
01:18:11
so things like Discord or
01:18:13
Slack or Zendesk and so on,
01:18:15
which are more like simple servers here.
01:18:18
Got it.
01:18:18
Yeah, that makes a lot of sense to break
01:18:20
it up into those different pieces.
01:18:22
I'd actually love to learn a
01:18:24
little bit more about each,
01:18:25
maybe starting with
01:18:26
the application server.
01:18:28
So you mentioned that you're
01:18:29
using Effect RPC in that regard.
01:18:31
What did you use before
01:18:33
you were using Effect?
01:18:34
And what are the qualities of using
01:18:37
Effect for an
01:18:39
application for an RPC server here?
01:18:41
Yeah, sure.
01:18:43
I mean, this before was
01:18:44
just like simple API handlers.
01:18:46
So you would have two
01:18:48
completely decoupled systems.
01:18:51
And now pretty much all the
01:18:53
endpoints are in full RPC.
01:18:56
It starts with a schema, actually.
01:18:58
We define the input schema, the output
01:19:01
schema, and the error schema
01:19:03
for each of these endpoints.
01:19:05
And then it trickles down all the way to
01:19:07
the client application.
01:19:08
So we have this.
01:19:09
Ethan actually wrote this and I hope he's
01:19:10
going to publish it.
01:19:11
Sometimes soon, essentially React query,
01:19:13
but wrapped around with Effect
01:19:15
so that we have the full schema from the
01:19:17
backend all the way to
01:19:18
the client side code.
01:19:19
And we have nothing more to deal with.
01:19:21
So the whole thing is completely
01:19:22
streamlined in terms of the
01:19:24
communication between the client
01:19:26
and the server.
01:19:28
That makes a lot of sense.
01:19:29
And so before you
01:19:30
didn't have any schema layer,
01:19:32
anything to express the kind of data
01:19:35
you're sending across?
01:19:37
No, we didn't actually.
01:19:39
Got it.
01:19:39
Well, I suppose that through the process
01:19:42
of just modeling the schema,
01:19:44
I'm sure that there were a couple of code
01:19:46
paths that you didn't
01:19:47
even account for before.
01:19:49
So the--
01:19:50
Absolutely.
01:19:51
The public API server where you're
01:19:55
exposing an open API spec, I suppose,
01:19:59
how similar or different is that to
01:20:01
your application server?
01:20:02
Can you describe the public
01:20:03
API server setup a bit more?
01:20:06
Yeah, sure.
01:20:06
So this is more of a
01:20:08
standard HTTP server.
01:20:10
This is actually using Effect HTTP.
01:20:12
So again, we have the full--
01:20:13
Everything starts with the Effect runtime
01:20:17
taking over directly
01:20:18
at the request level.
01:20:19
So again, we can model
01:20:20
everything very nicely via schemas.
01:20:23
And one really nice thing of having
01:20:26
everything defined by schemas is
01:20:28
that we can, for instance, automatically
01:20:30
generate the open API spec.
01:20:32
So this is something that we now have
01:20:34
every single endpoint is
01:20:35
automatically documented
01:20:37
just via the schema.
01:20:39
Got it.
01:20:39
How much overlap does your
01:20:41
internal application server,
01:20:43
that RPC server with
01:20:45
the public API server,
01:20:47
does it have a lot of overlap?
01:20:49
Do you reuse a lot of code internally?
01:20:51
Or are those rather
01:20:52
fulfilling different purposes?
01:20:54
They're fulfilling
01:20:55
completely different purposes, yes.
01:20:58
So yeah, I mean, these things are
01:21:00
completely separate.
01:21:02
So to give you one very important
01:21:04
differentiator is that on the RPC server,
01:21:07
we provide permissions.
01:21:10
So you want to make sure that a given
01:21:12
user can perform an action
01:21:14
that they are allowed to.
01:21:16
And so we have a very nice system that
01:21:19
provides the level of
01:21:21
permissions of a user.
01:21:23
This is completely transparent.
01:21:25
We actually used RLS before,
01:21:28
role level security to
01:21:29
enforce these policies.
01:21:31
But there was a real performance impact.
01:21:33
It was just slowing down the crazy,
01:21:34
especially when you're dealing with
01:21:36
hundreds of thousands of data points.
01:21:39
So this had a real impact.
01:21:41
So we got rid of that.
01:21:42
And actually, everything
01:21:43
now is modeled in code.
01:21:44
So the permission, which I guess was
01:21:46
always how things have
01:21:48
been done without RLS.
01:21:52
And so this is, again, an example where
01:21:54
Effect at the type level,
01:21:56
you can immediately spot whether this is
01:21:58
a user that is allowed to
01:22:00
perform a single action,
01:22:02
because that service
01:22:04
requires these sets of permissions.
01:22:06
And so this is very,
01:22:07
very nice to model as well.
01:22:09
That sounds very interesting.
01:22:10
So you've built yourself an internal DSL
01:22:13
to model those permission rules.
01:22:16
Can you explain a little bit more how
01:22:18
that's implemented and how
01:22:20
that can be imagined from
01:22:22
like a type level
01:22:23
perspective or from an API perspective?
01:22:26
Yeah.
01:22:27
I mean, so every user comes
01:22:28
with a set of permissions, right?
01:22:30
And these permissions
01:22:30
can be modeled via types.
01:22:33
And services can declare what level of
01:22:38
permission they required in
01:22:40
order to perform an action.
01:22:42
And so we immediately know if within an
01:22:45
RPC endpoint, we're using a service,
01:22:47
but that the provided
01:22:48
permissions don't match.
01:22:52
Well, then we can flag it.
01:22:53
And so we can guarantee that there's no
01:22:55
code being shipped where
01:22:56
there's an inconsistency here.
01:22:59
I love it.
01:22:59
That sounds very elegant.
01:23:00
I would love to see some code for that.
01:23:03
Could you imagine that there could be a
01:23:05
more commonly reusable package for that?
01:23:08
Or is that only something that makes
01:23:10
sense within your code base?
01:23:13
Good question.
01:23:15
Obviously, we have been just crunching
01:23:18
through this code and
01:23:19
getting into production.
01:23:20
So we haven't thought too much about how
01:23:23
this would extend to a
01:23:24
more generic use case.
01:23:27
But I guess this could be
01:23:29
a very interesting topic.
01:23:31
Right.
01:23:31
Well, anyone in the audience is happily
01:23:34
invited to try to
01:23:35
replicate this by themselves.
01:23:37
Maybe that's even something that we
01:23:39
could, as part of the Effect org, also
01:23:42
look into providing.
01:23:43
To hear you describing
01:23:44
this use case in this DSL,
01:23:46
that makes a lot of
01:23:47
sense for a lot of use cases.
01:23:49
So moving then to the ingestion engine,
01:23:53
can you describe a little bit more of how
01:23:56
that looks like from a
01:23:57
lifecycle perspective?
01:23:59
So you've mentioned the ingestion engine
01:24:01
is mostly meant to bring in data from
01:24:03
various data sources,
01:24:05
whether it's
01:24:05
Salesforce, whether it's Notion.
01:24:07
Walk me through how that works and how
01:24:10
does that work in regards to Effect?
01:24:13
Do you use Effect streams for that?
01:24:15
Yeah.
01:24:15
How does that work?
01:24:17
So it's still pretty bare bones here.
01:24:19
This is something that we have been
01:24:20
discussing also with the
01:24:21
Effect team and the Effect cluster.
01:24:23
This is where we will probably be going.
01:24:26
So now we have our own system, which is
01:24:28
basically just some
01:24:31
long running node servers
01:24:32
that are able to go and fetch the data.
01:24:36
So that could be make a large request to
01:24:39
an SQL database or starting a scraper
01:24:42
that visits a website and when it's done
01:24:45
and purged all the pages,
01:24:48
it is then indexed in a
01:24:49
temporary cache on our side.
01:24:52
And then it goes into the post processing
01:24:53
pipeline, where again, we obviously we
01:24:56
parallelize all this
01:24:58
because every page
01:24:59
needs some post processing.
01:25:01
So we chunk it up and we try to get a
01:25:05
sense of the structure
01:25:06
of the document, the AST,
01:25:08
and then we build these embeddings for
01:25:10
each levels in the AST and
01:25:12
then we store it in a database.
01:25:15
And so the main challenge here is that
01:25:17
these are long running
01:25:18
tasks which can time out,
01:25:20
they can fail and whatnot.
01:25:24
And so now we rely on external systems to
01:25:26
do the whole workflow orchestration,
01:25:28
but this is something where we are going
01:25:30
to be investing some
01:25:30
effort as well because
01:25:32
Effect is the perfect solution here
01:25:35
because we have the full
01:25:37
sets of tools for dealing with
01:25:40
these kinds of situations.
01:25:42
Yeah, I'm really looking forward for
01:25:45
Effect Cluster and Effect
01:25:46
Workflows to mature further.
01:25:49
I think there's more and more use cases,
01:25:52
particularly when it
01:25:53
comes to AI applications
01:25:55
where you want to express
01:25:56
things as long running workflows.
01:25:59
And I think this is where Effect is
01:26:02
really like such a perfect foundation.
01:26:05
So yeah, I think we're going to see some
01:26:08
very interesting updates
01:26:09
throughout the year here.
01:26:11
So stay tuned for that.
01:26:13
Maybe changing gears slightly.
01:26:15
Initially, you've built all of the
01:26:17
system, I think mostly by yourself.
01:26:19
This is where you started
01:26:21
adopting Effect by yourself.
01:26:23
and then later you onboarded other
01:26:25
engineers who didn't have
01:26:27
prior Effect experience.
01:26:29
Can you share a bit more about that
01:26:31
story, how you onboarded
01:26:33
other engineers who didn't
01:26:34
have Effect experience to become
01:26:36
successful working on your code base?
01:26:39
I mean, this is such
01:26:39
an interesting question.
01:26:40
Because you would think that Effect is
01:26:44
mainly for a TypeScript audience.
01:26:47
Web developers who are proficient now in
01:26:50
TypeScript and realize they
01:26:52
need to go to the next level
01:26:54
in terms of building complex systems.
01:26:56
My take here is actually that having a
01:27:00
code base which is already
01:27:01
fully Effect will actually
01:27:04
lower the barrier to entry for people who
01:27:07
have less experience with TypeScript.
01:27:10
So we've seen that with Elliot, our
01:27:13
founding engineer who has
01:27:14
joined a few months ago,
01:27:15
with very little TypeScript experience,
01:27:17
was able to very quickly take
01:27:20
it up and then be productive.
01:27:22
We've run some trials with some
01:27:23
candidates in the last few weeks with,
01:27:26
again, little experience in
01:27:27
TypeScript.
01:27:28
The thing is that we are hiring a lot of
01:27:30
machine learning engineers.
01:27:32
And so they have typically already a lot
01:27:35
of experience with more
01:27:36
of the machine learning
01:27:37
sets of tools like Python
01:27:39
and very little on TypeScript.
01:27:41
But what we have seen is onboarding them
01:27:44
on our code base, they
01:27:45
were actually able to be
01:27:46
very productive, very early on.
01:27:48
And the great thing is that there's not
01:27:50
much room for bad
01:27:51
practice or the kinds of things
01:27:53
that you need to learn when you get
01:27:55
started with a new language.
01:27:56
So what I think is that actually
01:27:58
Effect will
01:27:59
allow them to be proficient and
01:28:02
build very reliable
01:28:03
code within a few weeks.
01:28:05
If it had not been an Effect and more
01:28:07
like vanilla TypeScript,
01:28:08
yes, sure, there's fewer
01:28:09
things to learn.
01:28:10
It's kind of easy and so on.
01:28:12
But you would build up this understanding
01:28:15
and intuition for the
01:28:16
language over the course of
01:28:17
years.
01:28:18
Right.
01:28:18
And then within years, you'll be able to
01:28:19
build very complex and
01:28:21
reliable systems because you
01:28:23
have been proficient in the language.
01:28:26
So Effect gives you these guardrails that
01:28:29
with the whole thing is
01:28:31
in Effect, sure, you need
01:28:32
to understand the
01:28:33
whole catalog of things.
01:28:35
And this you just take up
01:28:36
as you have new use cases.
01:28:37
But if you have a really good foundation,
01:28:39
like the full RPC
01:28:41
server, you basically just
01:28:43
you start writing schema, right?
01:28:45
You write a schema and then
01:28:46
you fit it into the system.
01:28:48
And then once it's there, it's done.
01:28:51
You don't need to change it.
01:28:52
You haven't done anything bad
01:28:54
or anything that will break.
01:28:56
Right.
01:28:56
So my take on this is that actually it
01:28:59
might actually have the
01:29:00
opposite effect of what
01:29:01
we would expect, which is to be able to
01:29:03
onboard non TypeScript
01:29:06
engineers much, much faster.
01:29:09
Yeah, that definitely sounds
01:29:11
a little bit counterintuitive.
01:29:12
And the first time I heard about this
01:29:15
anecdote, and you shared
01:29:16
this privately with me before,
01:29:18
and that person you've just mentioned,
01:29:21
Elliot, gave a brilliant talk at the last
01:29:23
Effect Meetup in San Francisco where he
01:29:26
shared his first hand
01:29:28
experience learning Effect.
01:29:30
And yeah, I think that is a
01:29:32
little bit counterintuitive.
01:29:33
But when you think about it more, it
01:29:35
makes a lot of sense because most
01:29:37
experienced TypeScript
01:29:39
engineers over years, they had to first
01:29:42
learn how to do anything
01:29:44
with TypeScript at all and then
01:29:46
later learn to ignore like those 80 paths
01:29:49
out of 100 how you could do
01:29:52
something with TypeScript,
01:29:53
but that will lead you
01:29:54
down a dangerous road.
01:29:56
Whereas Effect constrains you a little
01:29:58
bit more and puts you on a
01:30:01
path to success in a way.
01:30:03
That's also like it has been my
01:30:04
experience for I think for me
01:30:06
coming more from TypeScript,
01:30:09
I had to unlearn a lot of things how I
01:30:12
used to do things in TypeScript before.
01:30:14
But this is where for me, a good
01:30:17
foundation to pick up Effect
01:30:19
was actually dabbling in other
01:30:21
languages such as Swift and Rust, where I
01:30:24
already primed my mind more on more saner
01:30:29
engineering practices and learning the
01:30:31
right habits and
01:30:33
embracing the right abstractions.
01:30:35
And so I can totally see how someone who
01:30:38
doesn't have prior TypeScript experience
01:30:40
maybe experience in other languages,
01:30:42
which have more appropriate primitives
01:30:45
can be up and running with Effect in no
01:30:48
time. That is amazing to hear.
01:30:50
So can you walk me through a little bit
01:30:53
more through the process
01:30:54
how you went tactically
01:30:56
about migrating the code base as it was
01:31:00
before Effect one by one
01:31:02
step at a time to migrating
01:31:03
it to Effect. Rome wasn't conquered in a
01:31:06
day, so wasn't your code
01:31:08
base. So how did you go about it?
01:31:10
We had some great help. And this is where
01:31:12
I think the Effect community
01:31:13
is such a wonderful community
01:31:15
of people who are nice and helpful. You
01:31:19
kicked it off with us. So we've been
01:31:22
this for years. And immediately I saw the
01:31:25
importance of this, but
01:31:27
never found the time to
01:31:30
actually learn how to do it in practice.
01:31:32
I think this is also
01:31:33
maybe related to the lack of
01:31:34
documentation and concrete examples back
01:31:36
in the day. How do you
01:31:37
actually convert an API
01:31:38
endpoint to Effect? How do you just do
01:31:41
it? So I think this little
01:31:43
catalyzer was really important,
01:31:45
which allowed us, our team, to then get a
01:31:49
sense of where we could
01:31:50
start fitting it in. It was still
01:31:52
like very, very new. This was earlier
01:31:53
last year. And then we had the pleasure
01:31:57
of having Ethan with
01:31:59
us over the summer. And he wrote a lot of
01:32:01
very important code for both
01:32:03
the backend side of things,
01:32:05
schema, the database, the React query
01:32:08
part, which put us in a
01:32:10
really good foundation.
01:32:11
We had most of it basically sketched out.
01:32:14
And then in September, we decided to
01:32:18
do a big, big push and just get
01:32:20
everything migrated. So this was like a
01:32:23
bit slow in the beginning.
01:32:24
And then very, very quickly, within a few
01:32:27
weeks, we had the
01:32:28
whole thing migrated over.
01:32:32
Good. That makes a lot of sense. And
01:32:34
happy to have acted as
01:32:36
a little catalyst here,
01:32:38
but you've picked up everything so
01:32:40
quickly. So I think this was rather a
01:32:43
little bit of giving you
01:32:45
more courage to go this way. And I think
01:32:48
you figured out quickly
01:32:50
how to work with everything,
01:32:53
and you migrated everything in no time. I
01:32:57
would say, honestly,
01:32:59
the important thing is the
01:33:00
catalyzer here. Once you have the starter
01:33:03
blocks, then it's quite easy to
01:33:05
generalize. It's more
01:33:07
like where do you start? And the
01:33:09
documentation obviously is much better
01:33:10
now. So I think this is
01:33:11
a great thing. But I think this little
01:33:14
initial nudge is
01:33:17
what made the difference here.
01:33:19
If you think about your system as sort of
01:33:22
like a tree of different subsystems,
01:33:27
you can go about converting an existing
01:33:29
system to Effect in different ways.
01:33:31
Either you go first for
01:33:33
the leaves, and then you go up one step
01:33:36
at a time until your entire
01:33:37
program, your entire system is
01:33:39
refactored with Effect. But you can also
01:33:41
isolate specific sub nodes
01:33:44
and from the outside call the
01:33:47
effect and inside of it call other
01:33:50
things that are not yet Effect with
01:33:52
Effect.runPromise or
01:33:54
effect dot promise to run some effects,
01:33:57
some non-effect code yet.
01:34:00
Did you find any particular
01:34:02
topology adoption strategy for you to be
01:34:06
working well? So did you go first
01:34:08
all for the leaves? Or did you go
01:34:11
wherever you wanted to
01:34:12
refactor something anyway? How did you
01:34:14
go about the decision process of when to
01:34:17
refactor what with Effect?
01:34:19
Well, yeah, it was the leaves in
01:34:20
the beginning. And then it was the top
01:34:24
down afterwards, because
01:34:26
we just realized that this
01:34:27
was what we needed. And we just needed as
01:34:29
quickly as possible. So
01:34:31
then there was a combined push
01:34:34
from the whole team to just get the
01:34:36
migration done as quickly as
01:34:37
possible. So the RPC was written
01:34:40
really like top down, right? Because,
01:34:43
well top down in the
01:34:44
sense that one route would be
01:34:47
migrated to RPC, Effect RPC, then another
01:34:49
one and so on. But for
01:34:52
sure, this is a nice thing with
01:34:53
Effect is that you can do it on the
01:34:55
leave to start with and you're not
01:34:57
touching anything else
01:34:58
of your code base. But yeah, you
01:35:00
definitely feel the urge to
01:35:01
get the whole thing end to end.
01:35:05
The runtime should be initiated at the
01:35:08
very beginning of the
01:35:10
thing, right? So that you have
01:35:11
the full happy path, because you get to
01:35:14
love the happy path here, right?
01:35:16
Nice. So if I remember
01:35:18
correctly, I think it's now been pretty
01:35:20
much a year since you've been using
01:35:22
Effect in production.
01:35:24
looking back, what were some
01:35:26
of the things that were
01:35:27
like unexpectedly good with
01:35:30
Effect and also some things that were
01:35:32
more challenging that you thought or
01:35:33
unexpected in some ways?
01:35:36
I didn't expect the Schema package
01:35:39
to be, you know, I didn't have any
01:35:42
expectations that this
01:35:43
was really a core thing. In fact, because
01:35:45
this is really wonderful, how you can
01:35:49
control both like the
01:35:50
decoding part, the encoding part, how you
01:35:53
can pipe everything
01:35:54
together. This is one of the sort of,
01:36:00
it shifts away how you think about a
01:36:01
problem. Because usually,
01:36:03
now when I think of a problem,
01:36:05
I start thinking about what is the
01:36:06
schema, right? It is very
01:36:07
different from what is the actual
01:36:09
application code. You redesign your
01:36:11
schema, you think, well, how should it
01:36:13
transform the data here?
01:36:16
And then you work your way up to getting
01:36:18
it all the way in your
01:36:21
code. So I think that schema was
01:36:23
a very, very positive,
01:36:24
unexpected thing that came out of this.
01:36:27
To give you an example,
01:36:29
I always wanted to, again, in the quest
01:36:32
for extreme lining and being
01:36:34
as efficient as possible as a
01:36:36
startup, things like documentation, I
01:36:39
care a lot about documentation. And the
01:36:41
more we can put that
01:36:42
on like autopilot, the better, right? So
01:36:44
the ability to take
01:36:46
your public API endpoints,
01:36:47
and the other schemas generate an open
01:36:49
API spec is exactly the kind of things
01:36:52
that you want, right?
01:36:54
So this was a very, very nice discovery.
01:36:57
Right. That makes a lot of
01:36:59
sense. And I think that goes
01:37:00
hand in hand with what I've also found in
01:37:04
my development practices over the years,
01:37:06
is that I always started to think more
01:37:10
about the types of a system.
01:37:13
That's like typically what I
01:37:13
start out with. I just like described the
01:37:16
types. But then I've also
01:37:18
found myself thinking about,
01:37:20
actually, this data of this particular
01:37:22
type doesn't just live inside of this
01:37:25
boundary of the system,
01:37:26
but it needs to move across network
01:37:29
boundaries, across threat boundaries,
01:37:31
etc. And now you're,
01:37:33
you need to serialize, deserialize that
01:37:35
variable. And this is where
01:37:38
schemas come in. So I ended up
01:37:40
doing exactly the same as you. Like I
01:37:43
start thinking about like,
01:37:44
okay, what data am I dealing
01:37:46
with here? And I modeled it as schemas
01:37:49
and derived the types from it.
01:37:51
And well, we see this here as
01:37:53
like a big revelation turns out in other
01:37:56
programming languages, that's what you
01:37:58
always do. When you have
01:37:59
like in Rust, etc. When you have like a
01:38:02
struct definition, you can
01:38:03
derive schemas from it. But
01:38:06
I think for TypeScript, this is really
01:38:08
for most engineers, that's
01:38:10
something novel. And I think
01:38:12
this is a pattern that should be embraced
01:38:14
much more since I think
01:38:16
default is you just ignore
01:38:18
the existence of a schema and you JSON
01:38:21
stringify everything. And then you're
01:38:23
surprised what happens
01:38:25
to your classes or to your dates. So
01:38:27
yeah, that schema is such
01:38:29
an integral part of Effect,
01:38:32
I think is no coincidence. And I'm very
01:38:34
happy that it gets so
01:38:36
much attention for detail.
01:38:38
Yeah, I mean, I would say it's not only
01:38:41
the fact that you have like
01:38:42
decoding and encoding and so
01:38:44
on, it's the fact that it's so much in
01:38:48
harmony with the rest of
01:38:49
the whole like Effect way of
01:38:51
doing things, right? So it just feels so,
01:38:55
you know, an integral
01:38:57
part of effect itself.
01:38:59
Yeah, I fully agree. So in regards to the
01:39:04
more challenging aspects,
01:39:06
what were some of the challenges that you
01:39:08
found working in an Effect code base?
01:39:11
Sure. I would say it can give you a sense
01:39:14
of, you know
01:39:17
maybe a
01:39:17
false sense of security.
01:39:19
Because you think that everything
01:39:20
is dealt with at the
01:39:21
type level and you get no
01:39:23
flagged of compile time errors and so on.
01:39:26
And this is partly
01:39:28
true. But I think it's very,
01:39:30
very important. It's very important to
01:39:33
understand that this is not
01:39:34
the case. And it gives you
01:39:36
really good like foundation, but you're
01:39:39
not exempt of or die or catch all that
01:39:44
was put at the end of
01:39:46
your pipes. And so all the other errors
01:39:48
in between are, you know, just you don't
01:39:52
see them. And we had
01:39:52
an example where we had this
01:39:55
bug in our code base, and it
01:39:56
was quite hard to figure out
01:39:59
where it went wrong. So I think this is
01:40:01
something that, you know,
01:40:02
Effect is a very, very powerful
01:40:03
tool. And it brings out the best in us,
01:40:08
you know, engineers, I think.
01:40:10
But it also needs to be
01:40:12
dealt with knowing that, you know,
01:40:15
it's not like a safety guard
01:40:17
or anything, you can mess up
01:40:19
and you can do things that are very hard
01:40:21
to then investigate because
01:40:23
you've built up this, this
01:40:24
sense of everything is just working
01:40:26
smoothly. So this is one thing I think
01:40:28
that this is as important
01:40:30
to keep in mind when you're building, you
01:40:32
know, on top of Effect. I
01:40:34
think another thing that it gets,
01:40:35
a little bit hard to track is,
01:40:38
dependency
01:40:39
injection is just wonderful,
01:40:42
in Effect. And I think that's, that's, I
01:40:43
would mention this probably
01:40:44
as the second most important,
01:40:46
discovery or delight of
01:40:48
working with Effect. But,
01:40:50
you know, where do you put your
01:40:53
dependency? Where is it in your code? How
01:40:55
do you like chase the
01:40:56
dependency, you know, the service
01:40:58
that you provided, that is now affecting
01:41:01
a completely different
01:41:02
part of your code base,
01:41:03
that might be, you
01:41:05
know, some some tooling
01:41:06
to build inside of the
01:41:07
IDE itself, to be able to make it easy to
01:41:10
spot the connection, right.
01:41:12
But now we still like in this
01:41:13
very barebone situation where it's kind
01:41:15
of hard to understand
01:41:17
exactly, you know, the hierarchy of
01:41:20
your dependencies, right, and where they
01:41:23
are actually being used
01:41:24
in your code. So it can be
01:41:26
a little bit hard to navigate sometimes.
01:41:28
Right. Yeah, I found that
01:41:29
myself as well in the project
01:41:31
I'm working on. And it is actually
01:41:34
something that we're thinking
01:41:35
about, that we could possibly
01:41:37
enhance through a IDE integration, etc.
01:41:41
So stay, stay tuned for
01:41:44
for that. You said something
01:41:46
interesting just before, where you said
01:41:48
effect brings out the
01:41:50
best in us engineers. Can you
01:41:53
elaborate on that? What do you mean by that
01:41:55
Well, as an engineer,
01:41:58
you want to build features,
01:41:59
you want to build like real robust
01:42:00
systems, you want to spend
01:42:03
ideally all your time doing
01:42:05
that. You don't want to spend your time
01:42:08
fighting the language, fighting, you
01:42:10
know, building something
01:42:11
that you know, the language was not meant
01:42:13
for, to give you an
01:42:14
example, like concurrency and
01:42:15
TypeScript, right, these are things that
01:42:17
you know, you want a way to,
01:42:21
you know, build a concurrent
01:42:22
system, where you're thinking about what
01:42:25
is the system actually doing
01:42:26
rather than like setting up
01:42:28
all the boilerplate in order to have that
01:42:30
system in place. So I would
01:42:34
say Effect really gives you
01:42:36
the right premise. So one thing I'm
01:42:40
thinking about is, now that we've
01:42:42
completed this migration,
01:42:44
the full migration, it kind of feels like
01:42:46
this is the end game. I know
01:42:48
this might sound a bit like
01:42:50
naive or unrealistic, but it kind of
01:42:54
feels like more stable.
01:42:56
And at least if we continue
01:42:58
building our code base on TypeScript with
01:43:00
Effect, it doesn't feel
01:43:02
like we're going to spend
01:43:04
many more like cycles, like refactoring
01:43:06
the code, right, it feels
01:43:09
like this sort of timeless,
01:43:12
like code base now now now it's, it's,
01:43:14
it's there. Effect is
01:43:16
going to evolve, our code base is
01:43:17
going to evolve. It feels like this is
01:43:19
going to be like adjustments
01:43:20
and not like a full rewrite.
01:43:22
And this is what I mean by, you know, it
01:43:24
feels like kind of like
01:43:25
building a house with some really
01:43:27
good like walls and floor and ceiling and
01:43:29
so on. It's just there now.
01:43:32
And this is what we are all
01:43:34
I guess, striving for as engineers is to
01:43:36
have these, you know, the
01:43:38
right tools, right, the right
01:43:39
foundations, instead of having to fight,
01:43:42
you know, all these like small things
01:43:44
that degrade over time,
01:43:46
and then need to revisit them. And you
01:43:48
realize how silly you were
01:43:51
writing this code, you know,
01:43:52
last year kind of thing, it feels like
01:43:54
more harmonious and stable
01:43:57
Right. I love that. And
01:43:59
I think that is also something that has
01:44:02
really brought me to Effect.
01:44:04
And to flip it on its head,
01:44:06
something that I experienced so many
01:44:10
times and have observed so many other
01:44:13
teams as sort of like a
01:44:15
malfunction of engineering teams and of
01:44:18
projects that engineers are
01:44:20
afraid to make changes in a
01:44:22
project. And I've seen this time and time
01:44:25
and time again. And when
01:44:27
you're afraid to make changes to a
01:44:29
project, particularly if the code was
01:44:31
written by someone else, maybe
01:44:32
the person is no longer there.
01:44:35
That's terrible. Like if you're, if
01:44:36
you're afraid to change something, you
01:44:38
might not change it and
01:44:39
you might not improve it. Or if you
01:44:41
change it, you don't know
01:44:42
about the consequences, etc.
01:44:45
And effect allows you to, gives you the
01:44:48
confidence to change something and you
01:44:51
know what's going to
01:44:52
happen. It even makes it fun to do so.
01:44:54
And I think that's
01:44:55
sort of like a twin to the
01:45:00
aspect of composability, almost like the
01:45:03
other side of the same
01:45:04
coin, where if you can compose
01:45:07
things together, you can decompose things
01:45:10
that makes, that's, those
01:45:11
are just aspects of changing
01:45:13
something and changing not in a big bang
01:45:17
way, but just moving things
01:45:19
around almost feels like a,
01:45:21
like a dance. We're getting a little bit
01:45:23
too, too like pie in the sky
01:45:26
here, but that's really like
01:45:27
my reality as well, like working with
01:45:29
Effect and one of the things
01:45:31
that I like so much about it.
01:45:33
And another thing here may be looking a
01:45:35
little bit more into the
01:45:36
future, but we're increasingly
01:45:37
living in the future where we get, where
01:45:40
we do a lot of our code
01:45:42
development in an AI-assisted way.
01:45:45
Now it's maybe no longer just us, us as
01:45:48
engineers doing performing
01:45:50
changes on our code, but it's now
01:45:52
AI systems performing changes on our
01:45:54
code. And this is where the same
01:45:56
underlying dynamic is even more
01:45:58
severe. If there is a system, if there is
01:46:02
some actor here who's
01:46:04
changing our code, who is maybe
01:46:07
even less competent or less knowledgeable
01:46:10
about this, then maybe even
01:46:12
more scary to make changes
01:46:14
to a system. And if the material here,
01:46:18
the house that we're
01:46:18
building that you've mentioned,
01:46:20
is where it's inviting to have changes be
01:46:24
made, that sets an even better foundation
01:46:27
for those AI-assisted,
01:46:30
for this AI-assisted future. So that's
01:46:33
something I love thinking about.
01:46:35
Yeah, I agree. I think the guardrails
01:46:41
here are so important,
01:46:42
right? You want to make sure that,
01:46:44
whatever the LLM generates has as little
01:46:49
unintended side effects as possible,
01:46:52
because it could hallucinate some code.
01:46:55
So if you can make sure that
01:46:57
the blast radius is as tight,
01:46:59
small as possible, then I think you have
01:47:01
a really good foundation. Obviously, I
01:47:03
don't know where this
01:47:04
is heading. Maybe we're going to be
01:47:05
completely wrong and that we're not even
01:47:07
going to be thinking
01:47:07
about code in the same way in the future.
01:47:12
But it feels like it's a
01:47:13
really good way of ensuring
01:47:16
that whatever an LLM generates stays
01:47:20
within the intended radius.
01:47:25
Exactly. And I think just to round this
01:47:28
part out, Effect gives
01:47:30
you a middle ground between
01:47:34
a natural language like English that
01:47:37
might be more intuitive to
01:47:40
describe what a system should do,
01:47:41
but maybe less concise, as opposed to a
01:47:45
type system or code,
01:47:47
which can be more concise,
01:47:49
but maybe less intuitive. And I feel
01:47:51
Effect is the best of
01:47:52
both worlds, where you can,
01:47:54
through the help of a type system, and
01:47:56
here in this case,
01:47:58
TypeScript provides a marvelous type
01:48:00
system that allows you to give you the
01:48:02
correctness benefits that you wouldn't
01:48:04
get from English, yet
01:48:06
it gives you that more intuitive way to
01:48:09
express and compose things. I feel like
01:48:13
Effect is converging
01:48:14
towards something that's the best of both
01:48:16
worlds here, that is
01:48:18
actually a wonderful substrate
01:48:20
for AIs to also compose
01:48:22
systems out of.
01:48:24
Wish that the latest versions of LLMs
01:48:26
have even better
01:48:27
understanding of the latest developments
01:48:28
in Effect. But I think you mentioned that
01:48:31
you have a nice hack, which is to
01:48:33
clone the Effect repository in cursor and
01:48:36
then have it look at
01:48:38
that while you're coding.
01:48:40
Yeah, so that's actually a shout out to
01:48:42
our friend Tim
01:48:44
Suchanek. He taught me about
01:48:48
this little trick, and I've since applied
01:48:50
it many times also for other projects.
01:48:52
I think we're currently in this uncanny
01:48:54
valley, where LLMs always
01:48:57
lag behind on the latest and
01:48:59
greatest of actual information. The
01:49:02
Effect docs, for example, in
01:49:04
particular, they have really
01:49:06
advanced a lot, got a lot better over the
01:49:09
last couple of months. And
01:49:10
those LLMs that we're using
01:49:13
on a daily basis have not fully caught up
01:49:15
to that level yet, and
01:49:17
are also sometimes trained
01:49:19
on old code snippets, different syntax,
01:49:22
etc. And so when you ask
01:49:24
them, you might get an outdated
01:49:25
response. So what Tim is doing, and since
01:49:29
he's taught me about
01:49:31
this as well, is basically
01:49:33
check out the Effect code base on your
01:49:36
computer locally, open
01:49:38
Cursor in it, and then
01:49:40
Cursor uses the full Effect repo with
01:49:43
all the examples, tests,
01:49:44
etc. as the context. And then
01:49:46
inside of that, you use the Cursor chat
01:49:49
to ask your question, and
01:49:51
you're going to get a really
01:49:52
useful result. So I've been using this a
01:49:55
bunch myself, but we're
01:49:57
also working on some exciting
01:49:59
improvements to the Effect docs, etc.,
01:50:02
where we're trying to
01:50:04
combine the latest and greatest
01:50:07
documentation with some of our own AI
01:50:11
developments as well. So stay tuned for
01:50:13
that. You won't need
01:50:14
those little tricks for too much longer.
01:50:18
So Michael,
01:50:19
thank you so much for taking
01:50:21
out so much of your busy day to share
01:50:24
about your experience and journey with
01:50:26
Effect. Really greatly
01:50:28
appreciate that you share all of that
01:50:31
with the entire audience
01:50:32
here. Before rounding this out,
01:50:35
is there anything else you would like to
01:50:37
share with the folks
01:50:38
listening? I'm sure that you're
01:50:42
very busy building a lot of things, but
01:50:44
you're also interested in
01:50:46
bringing on more engineers.
01:50:49
Anyone in particular who
01:50:51
should reach out to you?
01:50:53
Yeah, so we are constantly hiring and we
01:50:57
are obviously, it's a full TypeScript
01:51:01
Effect code base, but we are also very
01:51:04
much looking for
01:51:05
engineers with an ML background.
01:51:07
And we would warmly welcome them even if
01:51:11
they don't have
01:51:12
specific TypeScript experience.
01:51:14
So yeah, if you're interested in working
01:51:18
on such a code base, which I
01:51:20
think is really, really nice
01:51:22
and working on some really interesting
01:51:24
LLM related topics,
01:51:27
then absolutely reach out.
01:51:30
Yeah, I couldn't give you a warmer
01:51:33
recommendation to work at
01:51:35
Markprompt. Michael, Marie,
01:51:38
Elliot, all the folks working at
01:51:40
Markprompt are A plus humans. If I
01:51:43
wouldn't be doing all of the
01:51:45
things I'm doing, I'd strongly consider
01:51:47
working with you all as
01:51:49
well. So whoever gets a chance
01:51:50
to work with you, I think you're making a
01:51:53
very smart decision. So
01:51:55
now moving back my focus to
01:51:57
you, Michael, thank you so much again for
01:52:00
taking time, sharing all of your
01:52:03
experience and all of
01:52:05
those anecdotes with us. I greatly
01:52:06
appreciate it. Well, thank
01:52:08
you, Johannes. Always a pleasure
01:52:09
to speak with you. Awesome.
01:52:12
Thank you so much. Thank you.