Skip to content

Commit caef071

Browse files
committed
docs: update act 1 scene 1
1 parent 9e884aa commit caef071

File tree

7 files changed

+118
-60
lines changed

7 files changed

+118
-60
lines changed

website/src/app/(v2)/learn/components/theatrical.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/src/components/v2/Header.tsx

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/src/content/learn/act-1/scene-1-a-radically-simpler-architecture.mdx

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,64 +18,63 @@ import messagePassing from "./scene-1/message-passing.png";
1818
import locationTransparency from "./scene-1/location-transparency.png";
1919
import horizontalScaling from "./scene-1/horizontal-scaling.png";
2020

21-
## How Backends Become Complex
22-
2321
<Narrative>
2422
The typical backend architecture follows a familiar pattern. A web server connects to a database. Traffic grows, so you add Redis for caching. You need async processing, so you add Kafka. You need coordination, so you add distributed locks.
2523
</Narrative>
2624

27-
Each layer solves a specific problem but introduces new ones. Cache invalidation bugs appear in production. Messages get lost in queues. Deadlocks take down the entire system. This results in coordinating transactions across services, debugging race conditions, and managing data consistency across multiple systems.
28-
29-
<Image src={oldArch} alt="Traditional backend architecture with separate layers for web server, cache, database, and message queue" />
30-
3125
## Every Solution Creates A Problem
3226

33-
As you add compnents to your architecutre to solve these problems, in turn you create new problems for yourself:
27+
As you add components to your architecture to solve problems as you scale, in turn you create new problems for yourself:
3428

3529
- **Caching**: brings cache invalidation bugs, stale data, and thundering herd problems.
3630
- **Message queues**: bring message ordering issues, exactly-once delivery problems, and dead letter queue monitoring.
31+
- **Pub/sub systems**: bring subscription management complexity, message replay challenges, and coordination overhead.
3732
- **Distributed locks**: bring deadlocks, lock timeouts, and split-brain scenarios.
3833
- **Multiple services**: bring distributed transactions, eventual consistency, and network partition handling.
3934

40-
These problems compound. A cache invalidation bug combined with a race condition can cause data corruption that's nearly impossible to reproduce or debug.
35+
Worse, these are bugs you can't unit test for. They're emergent behaviors that only appear under load when it matters most.
4136

42-
New features touch caching logic, queue handlers, database schemas, and service boundaries. Complexity grows exponentially with each addition.
37+
Yet it's accepted as _the way things must be_. Nobody got fired for adding Kafka, Redis, and RabbitMQ to the stack. The fact that each one brings its own failure modes is assumed to be the growing pains of any successful business.
4338

44-
Worse, these aren't bugs you can unit test for. They're emergent behaviors that only appear under load when it matters most.
39+
<Image src={oldArch} alt="Traditional backend architecture with separate layers for web server, cache, database, and message queue" />
4540

4641
<Separator />
4742

4843
## How We Got Here
4944

50-
The way you've designed your app is through an **age-old practice of "separating state and compute."** This solidified in the 1980s when client-server architecture emerged and everyone started running databases on dedicated machines.
45+
Looking back at the very first thing you did when starting your application: setting up a web server and a database. The way you've designed your app is through an **age-old practice of "separating state and compute."**
5146

52-
This came from the fact that computers were slow and had limited resources. Running application code and database operations on the same machine meant they'd fight over CPU and memory, making both perform poorly. Separating them protected databases from compute overhead. This tradeoff made sense when CPU and memory were severly limited.
47+
We've been doing it this way since the 1980s, when client-server architecture put databases on their own machines.
5348

54-
## 40 Years Later
49+
This came from the fact that computers were slow and had limited resources. Running application code and database operations on the same machine meant they'd fight over CPU and memory, making both perform poorly. Separating them protected databases from compute overhead.
5550

56-
Those constraints from fourty years ago no longer apply to today's servers. Modern CPUs are orders of magnitude faster, and memory is abundant and cheap. **Application bottlenecks has shifted from local compute to network latency and locks.**
51+
This tradeoff made sense when CPU and memory were severely limited, but the pattern outlived its purpose. As traffic grew, we added caching layers, message queues, and distributed locks — each solving a problem from the last without questioning the original assumption of how we got here.
5752

58-
A Postgres query over the network takes 1-10ms minimum. The same query on a local SQLite database (running in the same process as your application) takes 0.01-0.1ms. The network round trip and related locks is now the expensive part, not running the application and database on the same machine.
53+
## 40 Years Later
54+
55+
Those constraints from forty years ago no longer apply to today's servers. Modern CPUs are orders of magnitude faster, and memory is abundant and cheap. **Application bottlenecks have shifted from local compute to network latency and locks.**
5956

60-
**Combining compute and storage eliminates the biggest source of latency for modern applications**: network trips to the database and locsk.
57+
This is best demonstrated with a simple comparison between a real-world Postgres query over the network versus a SQLite query on the same machine: A Postgres query over the network takes 1-10ms over LAN. The same query on a local SQLite database running in the same process as your application takes 0.01-0.1ms, **roughly 100x faster**. (These benchmarks are heavily dependent on the workload, this is a conservative performance number for SQLite.)
6158

62-
Additionally, **it also eliminates entire categories of bugs**. No network means no network partitions. No shared state means no race conditions. No locks means no deadlocks.
59+
That 100x difference is not about switching to a marginally different database, it's about rethinking your architecture for modern computers by eliminating the centralized database completely in favor of databases colocated with your compute. **Combining compute and state removes the biggest sources of latency in modern applications.**
6360

6461
<Separator />
6562

66-
## The Actor Model: Combining Compute & Storage
63+
## The Actor Model: Combining Compute and State
64+
65+
Actors take the completely opposite approach to "separating compute and state:" they **merge state and compute together**.
6766

68-
Actors are the opposite of "separating compute and storage." **They put compute and storage in the same place** and load state into memory when awoken.
67+
Each actor's **state is isolated to itself** and cannot be read by any other actors. Instead, you communicate with actors over the network via actions.
6968

70-
Each actor's **state is isolated to itself** and cannot be read by any other programs. Instead, you communicate with actors over the network via actions.
69+
They're like mini-servers: they can accept and respond to network requests and even send network requests themselves. They remain running as a long-lived process with in-memory state until they decide to go to sleep.
7170

72-
They're like mini-servers: they can accept and respond to network requests and even send requests themselves without being prompted. They remain running as a long-lived process with in-memory state until they decide to go to sleep.
71+
In addition to performance and complexity benefits, this architecture **eliminates entire categories of bugs by design.** No network to the database means no network partitions. No shared state means no race conditions. No locks means no deadlocks.
7372

74-
<Image src={actorArch} alt="Actor architecture showing compute and storage combined in isolated actors" className="max-h-[500px]" />
73+
<Image src={actorArch} alt="Actor architecture showing compute and state combined in isolated actors" className="max-h-[500px]" />
7574

7675
## The 4 Properties That Eliminate Complexity
7776

78-
By combining compute and storage, actors present a few key properties that eliminate entire categories of problems.
77+
By combining compute and state, actors present a few key properties that eliminate entire categories of problems. These properties are the core of the design patterns that we'll discuss in further articles.
7978

8079
### Isolated State
8180

@@ -85,15 +84,17 @@ This eliminates race conditions (can't happen when only one process touches the
8584

8685
Debugging becomes straightforward: the actor's state is the single source of truth. There's no need to reconstruct state from multiple systems or reason about eventual consistency across caches, databases, and message queues.
8786

88-
As your app grows, new features affect a limited number of actors which have a limited scope. Changes don't ripple through shared state or risk breaking unrelated parts of your system.
87+
As your app grows, new features affect a limited number of actors which have a limited scope. Changes don't ripple through shared state across services or risk breaking unrelated parts of your system.
8988

9089
<Image src={isolatedState} alt="Diagram showing actors with isolated state that cannot be accessed by other processes" className="max-h-[500px]" />
9190

9291
### Message-Based Communication
9392

9493
Actors **talk through actions and events**, not direct state access. This makes it easier to scale actors since they can scale horizontally across multiple machines and still communicate efficiently.
9594

96-
**Actors frequently talk to each other** to build larger systems that scale well. We'll be talking a lot about patterns like this in this course.
95+
Messages sent to actors are **automatically queued and processed sequentially**. This almost always eliminates the need for external message queues since backpressure, ordering, and delivery are handled by the actor runtime itself.
96+
97+
Crucially, **actors frequently talk to each other** to build larger systems that scale well. We'll be talking a lot about patterns like this in this course.
9798

9899
<Image src={messagePassing} alt="Diagram showing actors communicating through messages and events" className="max-h-[500px]" />
99100

@@ -117,13 +118,47 @@ Load spreads naturally since actors are small, lightweight units. No complex sha
117118

118119
## Putting It All Together: A Radically Simpler Architecture
119120

120-
When you switch to actors, your architecture no longer needs:
121+
When you build your backend with actors, the four properties listed remove the need for:
121122

122-
- **Redis/Memcached**: Caching is built-in (state lives with compute).
123-
- **Kafka/RabbitMQ/SQS**: Events and async messaging are built-in.
124-
- **Consul/etcd/ZooKeeper**: No distributed coordination needed.
123+
- **Redis/Memcached**: Caching is built-in (state already lives in-memory with compute).
124+
- **Kafka/RabbitMQ/SQS**: Message queueing, events, and async messaging are built-in to the actor runtime.
125+
- **NATS/Redis Streams**: Pub/sub is built-in to actors through message passing and events.
126+
- **Consul/etcd/ZooKeeper**: No distributed coordination needed, actors encapsulate their own state and the runtime handles discovery and routing automatically.
125127
- **Istio/Linkerd**: Actors handle routing and discovery automatically.
126128
- **Database sharding**: Actors distribute themselves automatically. No shard keys, no rebalancing logic, no cross-shard queries.
127129

128-
New features are new actors or modifications to individual actors, not changes that cascade through your entire system.
130+
## If Actors Are So Great, Why Aren't They Everywhere?
131+
132+
If you've reached this point and are unfamiliar with the actor model, you're probably asking this exact question. It all sounds a little _too_ rosy.
133+
134+
The truth is that actors _are_ used widely — just not visibly. Large enterprises with engineers who've spent years wrestling with traditional architectures have long since adopted them. The pattern has proven itself at massive scale:
135+
136+
- WhatsApp (notoriously acquired for $19B running Erlang/OTP with only 35 engineers)
137+
- Discord
138+
- LinkedIn
139+
- X
140+
- Pinterest
141+
- PayPal
142+
- FoundationDB (powering Apple, Snowflake, DataDog)
143+
144+
So why hasn't the actor model spread to smaller teams and mainstream development?
145+
146+
This mirrors TypeScript's trajectory. It started as a niche tool for large codebases — most developers dismissed it as unnecessary overhead with poor tooling. But as more developers felt the pain of loose typing at scale, adoption grew. Today, TypeScript is a non-negotiable for many teams because of that collective suffering.
147+
148+
Actors are on the same trajectory. The pain of distributed systems complexity is becoming impossible to ignore.
149+
150+
Other ecosystems have had mature actor frameworks for years — Erlang has OTP, Java has Akka, C# has Microsoft Orleans. But TypeScript has been the missing piece until recently with:
151+
152+
- **Rivet Actors**: Open-source actor infrastructure for TypeScript
153+
- **Cloudflare Durable Objects**: Leverages Cloudflare's existing network & JavaScript runtime
154+
155+
<Separator />
156+
157+
## What To Expect From Act I
158+
159+
This act will cover common design patterns based on the 4 principles listed above for building modern applications with actors.
160+
161+
We'll also discuss common anti-patterns when designing with actors and situations where actors may not make sense.
162+
163+
See the [table of contents](/learn) for a full list of content.
129164

website/src/content/learn/index.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,22 @@ import { Icon, faFeather } from "@rivet-gg/icons";
197197
Coming Soon
198198
</p>
199199
</div>
200+
201+
{/* Scene 10 - Coming Soon */}
202+
<div className="block relative p-6 border border-[#44403c] rounded-sm bg-[#292524]/20 opacity-50 cursor-not-allowed">
203+
<div className="flex justify-between items-baseline mb-2">
204+
<span className="font-serif italic text-[#78716c] text-lg">
205+
Scene 10
206+
</span>
207+
<span className="text-[#57534e]"><Icon icon={faFeather} className="w-4 h-4" /></span>
208+
</div>
209+
<h3 className="font-display text-3xl md:text-4xl text-[#a8a29e] mb-1 m-0">
210+
The Limits of Actors
211+
</h3>
212+
<p className="font-serif text-[#78716c] text-base md:text-lg italic m-0">
213+
Coming Soon
214+
</p>
215+
</div>
200216
</div>
201217

202218
{/* Act II - To Be Announced */}

website/src/sitemap/mod.ts

Lines changed: 0 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/src/styles/main.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,36 @@
310310
font-family: "Courier Prime", monospace;
311311
}
312312

313+
/* Drop cap for narrative text */
314+
.narrative-drop-cap::first-letter {
315+
font-size: 3rem !important;
316+
font-family: "Cinzel", serif !important;
317+
margin-right: 0.5rem !important;
318+
float: left !important;
319+
color: #d4b483 !important;
320+
line-height: 0.8 !important;
321+
font-weight: 400 !important;
322+
-webkit-font-smoothing: antialiased;
323+
-moz-osx-font-smoothing: grayscale;
324+
/* Firefox-specific fixes */
325+
display: block;
326+
padding-top: 0.1rem;
327+
}
328+
329+
/* Firefox-specific fallback */
330+
@-moz-document url-prefix() {
331+
.narrative-drop-cap::first-letter {
332+
font-size: 3rem !important;
333+
line-height: 0.75 !important;
334+
}
335+
}
336+
337+
@media (min-width: 768px) {
338+
.narrative-drop-cap::first-letter {
339+
font-size: 3rem !important;
340+
}
341+
}
342+
313343
/* Subtle noise texture overlay */
314344
.texture-overlay {
315345
position: fixed;

website/tailwind.config.ts

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)