Reducing Latency is like Risk mitigation

Introduction and Importance of Web App Performance

Kai Malcolm introduces himself as a full stack developer and live sound engineer. He begins the talk by emphasizing the importance of optimizing web apps beyond the marketing claims of Silicon Valley companies. The speaker highlights that the primary reasons for focusing on app performance are cost reduction, user satisfaction, and ease of scaling as the app grows.

The Relationship Between Latency and Performance

The speaker introduces an analogy between managing latency and workplace hazard management. This section covers strategies to reduce latency, such as eliminating latency through faster database queries, substituting latency by parallelizing tasks, and isolating latency using caching or background work. The notion of moving latency closer to the user through edge computing is also discussed, stressing cautious utilization.

Illustrating Optimization Scenarios

Kai Malcolm explains various scenarios with examples, such as caching and streaming data, using visuals like waterfalls to demonstrate the effectiveness of these strategies. He contrasts old and new methods, including the transition from server-first approaches to the use of content delivery networks (CDN) and explores the evolving concept of streaming in frameworks like React.

Streaming Data with React

This chapter covers how streaming in React allows server-side data fetching without sending JavaScript to the client, thus avoiding HTTP round trips. Malcolm demonstrates this with a simple React component example and explains the benefits and caveats, such as browser loading indicators and search engine crawler considerations.

Edge Computing and Streaming Benefits

The speaker illustrates how moving computation closer to the user can eliminate and substitute latency, providing examples on how server and streaming strategies can achieve this. The limitations and necessary conditions, like the database proximity to the edge and the avoidance of skeleton states on slow connections, are discussed.

React Frameworks and Their Performance Features

Kai delves into how different React frameworks support performance optimization, specifically touching on Astro and NextJS. He evaluates Astro's server-first logic, its minimalist JavaScript approach, and NextJS's server rendering, streaming, and incremental static regeneration. The discussion points out the challenges, such as deployment limitations with NextJS.

Principles of Performant Framework Design

The speaker describes factors that contribute to performant framework design, such as predictability, parallelization, isolated latency centers, and the ability to deploy anywhere to avoid vendor lock-in. Examples from frameworks like Astro illustrate these principles, contrasting with the sometimes ambiguous defaults in NextJS.

Strategies for Addressing Latency

Malcolm summarizes the methods to manage latency effectively: starting with eliminating latency, moving to substituting and isolating it through techniques like parallelization and caching, and finally considering moving processes closer to the user. He advises using loading states only as a last resort for poor connections.

Conclusion

In closing, Kai reiterates the latency management hierarchy and emphasizes the importance of starting with elimination tactics. He invites the audience to connect on various platforms and engages in a Q&A session.

Thank you for having me.

My name's Kai.

I'm a full stack developer and live sound engineer, as was mentioned.

I'm from Melbourne.

I'm here today to talk a little bit about, the most effective ways that we can optimize, web apps and seeing through the Silicon Valley marketing, of all these companies that claim they're gonna make your app faster.

But, we need to understand as developers actually how that works and why it works, if it does at all.

So before we dive too much into, how we can make our apps faster, let's talk about why we should care about performance at all.

So first and foremost is pretty simply money.

An app that uses less resources or uses resources on your server more efficiently.

Is it a cost reduction?

It's pretty much as simple as that.

Maybe the two don't correlate perfectly.

But a reduction in resource usage is a reduction in costs for the most part.

Next is user satisfaction.

In an age where yeah, it's changing a bit at the moment, but we talk about, how we are offloading, o offloading, fetching and, all this data stuff to the client.

Now the user's waiting for that.

They're sitting on their device and they're going, all right, great, the website's loaded, but I'm still sitting here looking at the spinner.

The data's still coming in from an API, whatever it may be.

That's a, it's a user experience that if we can avoid, we should be.

And of course for power users like ourselves and, other power users of our apps, it can be really great to have, just a really, working at the speed of thought kind of experience.

And as you scale your app, having a more performant app means that you can scale, more easily.

So this is gonna sound like a little bit of a stretch at first, but bear with me here.

I like to relate how we look at, managing latency, to workplace hazard manage management.

So if you've ever had the joy of sitting through an OH and S presentation, this probably looks probably eerily familiar.

But basically you've got your most effective kind of hazard, mitigations at the top.

And then you work your way down to basically just we accept the risk and we just protect ourselves from it.

And so I think this is a really great way, to look at approaching latency in software.

So we can implement this with a few little adaptations and stuff.

So eliminating the risk becomes eliminating the latency.

It's pretty straightforward.

Obviously if you make a database query twice as fast, it's twice as fast, no matter how complex your infrastructure is, a two times improvement.

If you can manage that, amazing, that's a two times improvement.

All the way down the line.

Yeah, that's a tangible and objective benefit no matter how your app is set up.

So next we can substitute the latency.

So this is about using that time while we're waiting for something to load more effectively.

So seeing what we can do while we wait.

Looking at the overhead of API calls, we're waiting for this one API query, can we batch it with another API query if you've got control over that, API endpoint, and parallelizing what we can as well.

Next we can look at isolating latency.

So this is a pretty well solved problem.

We're talking things like caching, background workers, things like that.

Essentially due to slow stuff or the user isn't waiting.

And then we've got moving latency, which is all the way down here.

Everything above this point is more effective.

That's how these hierarchies work.

But if we get down to this point, moving the latency.

Is doing things like edge compute, and moving your data closer to your app.

We are at a time a little bit where everybody and their dog wants to take your money for Edge Compute.

But a slow app that runs near your users is still slow.

It's probably just expensive now, that's not to say there isn't value there, it should be used with understanding of what you're doing and with, some amount of caution as well.

And at the bottom, analogous to PPE, is loading and skeleton states.

So this is like a last ditch effort.

Fairly objectively speaking, there are loading and skeleton states are a bad user experience if we can avoid them.

And we have the capability with modern technology to minimize them on good internet connections.

But they are still, of course, a necessary evil, for those bad connections like 4G or cellular in this room apparently.

So I wanna demonstrate a bunch of these scenarios with some little waterfalls.

So in my mind, like the sort of control or like the kind of base app, that, performance can be measured against as a totally unoptimized, fully server ended app running on a single server.

So it's just you request something from a server, it just goes and gets it from the database and sends it back to you.

We deviate from the warehouse of frontend devs a bit for a moment here, but just bear with me a second.

Obviously this waterfall's pretty simple.

And let's say the database query could be a little bit faster, we can optimize it, so of course, let's do that.

Now, obviously, probably not to scale, but point being, we could have the most complex infrastructure ever.

Imagine if, Google did something like this in by some miracle.

No matter how complex your app is and the infrastructure to run it is, that's still an improvement.

Everything below this point is gonna vary.

So those, three benefits we talk about at the start, would cost user satisfaction and scaling.

Eliminating latency is the only strategy that's gonna actually achieve all three of those.

Everything else works around latency in a way.

And so that it doesn't have all three of those benefits.

So let's say maybe we go and we, yeah.

Yes.

Eliminating latency.

Let's say we go and we chuck a case in front of that.

It sounds pretty simple.

And so now we're looking at this could apply to a CDN as well.

And this is great in the best case scenario where that data or that file has just been cached, we get the same file.

It's recent, nice and fast.

This is the ideal situation, when it comes to caching.

But the trade off, is that an ideal situation?

We do have to accept some amount of stale data, or try your best to actively keep the cache fresh, as your website grows as well.

Even though you might have your cache time to live set at, say, an hour, whatever it may be, you are gonna be, serving out more stale data by quantity, even though your expiry time or time to live is the same.

So speaking of CDN, this is pre React server components and this sort of going back to the server mindset, this was a very, common way, to build an app.

And let's be optimistic and assume we hit the cache and it's nice and fresh here.

So basically what would happen is we go, we hit a CDN, we get our HTML we have to go through our HTML and browser sees, oh, there's some JavaScript here.

It needs to go back to the CD and grab that JavaScript, bring that back, interpret a JavaScript, hydrate the page.

Oh.

Hang on, here's a little, React query hook in here.

We gotta go to the server.

Gotta fetch the thing from the API.

While we're doing that, we're gonna show a loading state.

This is excessively complex and a lot of layers on top of, almost the way the web was before this.

Thinking like the days when PHP was the norm.

And to an extent it still is.

WordPress is 40% off the web.

When PHP was the way to build web apps, that was normal to just fetch stuff on the server and send it when it's ready.

But this is a workaround to all that and it adds all these extra steps.

So yeah, we've got some stuff in here.

We've moved the latency by putting our HTML and our JavaScript on CDN, and we've got a loading site.

It's not ideal.

We can do better.

And of course if you have a cache miss, that probably needs no explanation really, except probably that's not the scale.

So you might have heard there's this thing going around React world in particular at the moment, but quite a few frameworks are called streaming.

It's a really great innovation and it means that we can fetch data on the server, without ever sending javaScript to do that to the client.

And avoiding HTTP round trips, shipping less JavaScript overall, is a bunch of great benefits that come with this.

But let's understand, what's going on under the hood.

So in, in traditional React, this is very simplified.

But let's just go with this for a second.

This was how it might work if you were fetching on the client side.

So HTML gets sent in, browser realizes one way or another probably after hydration, that it needs to fetch some content.

And then once it's got that content, it's gonna set it.

But all this is happening on the client side, and in a separate round trip to probably a different server with that, API as well.

So what streaming allows us to do is it's just gonna send this green part first.

It's actually gonna send an incomplete HTML document, and it's gonna have the placeholder just sitting in there as normal.

Then as it's sending that, it's gonna continue, it's gonna keep the HTTP request open, and it's just gonna wait.

On the server, it's going to a database, it's going to an API, and it's fetching, whatever you need to fetch.

And then once it's ready, it's gonna send it and it's gonna say, all right, just replace what we had there with the content that we've just fetched.

And in fact, it's gonna be in plain text.

Now, of course, this is like our last example.

This is simplified as well.

But I wanna stress how much closer it is to the real deal of actually what's going on.

So we've got our strategies in there as well.

But yeah, it's quite close to what's actually going on.

So I went into NextJS, I started up a new NextJS app and I wrote that top bit of code there.

And I called that in a server component.

I had streaming enabled.

It's just pretty simple.

It says hello from streaming.

After three seconds, I takes three seconds to resolve this promise.

And, I went into Dev tools, opened it up, and after three seconds, this little three lines of JavaScript, or of course it's minified.

So you know, this tiny amount of JavaScript gets appended, to my document that literally has the data.

I just fetched, hard coded in there because the service just serialized that sent it straight over to the client.

So consider, instead of pulling in React query, instead of using fetch.

Instead of having a client side cache for all of your data, you've got what formatted nicely is three lines of JavaScript or HTML slash JavaScript.

The only sort of caveat here, I is that the browser still display, displays the tab as loading.

So if you are in a browser tab, it's got that loading spinner on a tab icon still while this is happening.

But, considering the alternative is probably a small price to play.

The other interesting thing here is that because search engine crawlers don't some of 'em don't understand JavaScript, you basically just have to disable this whenever you think you're getting crawled.

And the way we do that, unfortunately, is just by taking a look at the user agent and having a guess.

Google and others crawlers are pretty open about the user agents and stuff, so we can take a guess when we're getting crawled.

But, as you can imagine, it seems like a pretty major feature, to just switch off when search engines come knocking.

And as well when you do get called and when Google is indexing you and, profiling you for your, your ranking.

You are just having to wait in this sec, in this case for three seconds, before you display the entire pages.

Going back to that kind of pre streaming.

So looking again at a waterfall here, we've eliminated some latency by we're not actually needing to go to a server anymore for serving this from the edge.

So we've moved that latency there.

We've eliminated some, because, we're not reaching out to an API server.

That whole thing is redundant.

We're talking to the database directly from the edge.

Of course, you need to make sure, that if you're doing that, that your database is close to the edge.

You've got replicas or whatever that may be, but that sounds like a backend problem.

You've got substituted latency there.

And of course, we still need those skeleton states, because on a slow connection, we still need to give the user something to look at.

So all of that said, I wanna go into a bit of a, a dive on how different React frameworks support this.

My wheelhouses React.

So I, can't speak too much, to other frameworks, but I know Svelte does support this.

I couldn't find any information on Vue, but I'm happy to be corrected on that front as well.

I actually wanna start with something that is more cross framework dough, which is Astro.

Astro is a really interesting thing because it's its target market is what the web was originally built for, which is text and content driven websites.

And it just brings modern tooling and stuff that we as developers have come to just accept as the norm through things like React, and it brings them back into the, back to the content driven websites, in a way that is still very, performant, for the user.

So by default, Astro just compiles to plain HTML and CSS, and JavaScript.

If you put it in there, it's server first.

It only ships JavaScript to the front end if you explicitly tell it to.

And it only if you use a framework like React, it's only gonna hydrate little Islands, they call them.

So if you have, for example, a little dropdown, maybe, you just want, oh, when I click on this, you're not using the new Popovers API that was talked about yesterday.

And you just wanna, make this work in React.

It's only gonna hydrate or bring in React for that tiny little bit of the page, which is a really great thing.

As far as React nodes, if you, as far as Reaction knows, even, if you were to do this twice in two separate cases, React has no idea that those two things, exist together.

As far as it knows, they're separate apps.

They're very clear about their target market.

They're host agnostic, which is a really important thing.

Means that you can deploy to static site hosting.

If you do use their server rendering you can deploy to still lots of serverless places.

And you can bring in any framework as well.

As I said.

The other thing that they say on their website, which I really love, is it should be impossible to build a slow website with Astro.

I'm sure I wasn't the only one to hear that and go, challenge accepted.

The point is there that they're doing everything they can to say, if the website's slow, that's on you.

NextJS So some context here.

There was an earlier version of this talk where I absolutely had a go at NextJS here.

I really bagged him out.

Recently, NextJS 15 was released about a month ago.

And there's been some improvements in this space.

That means this slide is a little less negative now.

For starters as has been the case for a while NextJS is server first.

As was talked about this morning, we're NextJS is encouraging that you, alright, let's not send everything to the client.

Let's just run stuff on the server.

Let's just, render this all fetch on a server where we can, and just send it over to the client ready to go.

They've also got streaming support.

However, it's worth noting that both of those things, technically speaking, are still in React Canary.

So they're not actually in React stable yet.

Maybe they're not production ready, maybe they are.

That's up for you to decide.

But Vercel and NextJS consider it stable enough that they're pinning stable NextJS to the canary version of React.

We also have incremental static regeneration, which is possibly the worst example of naming I've ever heard.

And the livestream [?] Say, which I'll come back to, but basically what's happening there is it's going all right, We're running build time almost again, and again.

So we're gonna fetch this page.

We're gonna get all the data we need for it on the server side, and then we're gonna cache the static HTML and just serve that out for say, five minutes.

After the five minutes is up, we're gonna do that fetch again.

We're gonna cache a newer version so you can have the benefits of static HTML, without the latency on each request.

I also really that NextJS is built on React standards.

That they've been quite big recently, not so much in the past, but recently they've been big on, making sure that Next users things that are coming straight from the React team, like server actions.

However, unfortunately it's really unreasonably difficult, to deploy NextJS anywhere that isn't Vercel or a NodeJS runtime.

So Vercel is their platform.

That's how they make the money to build Next.

But if you don't wanna use that, you are stuck with a long running node process, which isn't ideal for being able to deploy to serverless.

The double asterisk there and the asterisk on ISR as well is that there is a community effort called Open Next.

And they're working really hard.

They're, doing all this hard stuff, like going through undocumented APIs, looking through the NextJS source code, to actually bring these features that are implemented, closed source or proprietary for their cell, and bringing it to the masses so that you can now run NextJS with a subset that most of these, goodies on, CloudFlare workers, CloudFlare pages, AWS, and Netlify as well.

And the interesting story behind that Netlify Runtime is they developed that in-house and then open source it through Open Next.

So there is that much demand that Netlify as a company invested time in creating their own runtime for NextJS for their platform.

And the last one's a little bit of a, personal opinion.

I find that a little bit unintuitive the way things change, just with looking at, oh, if I make this function async, suddenly my behavior changes in regards to server components and streaming and stuff.

Personally I prefer a more explicit opt-in, but that's just me.

So now that I've bagged out NextJS for five minutes, what does good framework design look like?

So why is it so much easier to make Astro go fast compared to Next?

I should mention that obviously this is apples and oranges.

An app that is best suited for Astro is definitely not an app that is best suited for NextJS.

But nonetheless, both have the potential to be what I call performant by design.

And so I've got a few principles which kind of, put them on the right path for that.

So my first factor is predictability.

Astro, for instance, won't render anything on demand unless you explicitly tell it to.

And this happens at multiple levels.

I talked about everything's at build time unless you tell it to, to do otherwise.

It's also once you do that, if you want things to render on the client instead of on the server, you gotta explicitly opt into that every time as well.

It's not necessarily the best default for everybody, but it is predictable.

Now.

What kind of happened with NextJS up until recently was Vercel tried to do a bunch of stuff "automagically".

And it ended up being a bit of an indeterminate mess.

For example, this snippet, was dynamic that rendered on demand 'cause you had a little cache notation there.

But if I, remove one parameter and this code, which still looks very standard until the latest version of NextJS would actually run at build time only, and then be built as static, which is crazy.

It's been fixed, but it was there for quite a while.

Yes, that's static.

So my next factor is parallelize every time with that word parallelization.

So modern web apps in reality do a lot of data fetching, so it's often ideal that we don't have to wait for all of it to finish before we give the users something to look at.

So the best case for any app is that you just make one round trip to an API or a database or something, and that gives you everything you need to render that page.

But the reality is you've got secondary data.

You've got authentication authorization that might be a separate call altogether.

You've got all, sorts of things that depend on each other.

So I try to put data fetching into one of three buckets.

And so the first is a, what I call the critical path.

And, this is a, a thing you can have an opinion on.

This is, totally subjective.

But for me, the critical path is usually authorization and authentication, so it's what do we have to do before we can give the user anything?

I want to make sure that the user is allowed to see this page based on the URL before I even send them a loading state to look at.

And, you've got different ways to make sure that's fast and everything.

But once that's done, great, we can send you a loading state.

Then you've got your primary data.

So this is like the main data that you need to make a certain page work.

So maybe you've got, for example, a support dashboard.

This might be fetching your sort of top level, support tickets.

So it's enough to begin to display the page, but not enough to necessarily complete it.

And then you've got your secondary data.

So there's anything you need to make the page interactive to, sprinkle in the little bits of goodiesness, that maybe you couldn't do in that primary data call.

So in, going back to our support dashboard example, that might be for example, like the top, top three replies, to the support tickets or anything like that's difficult to pull in, in that initial call.

So what we can do is once the critical path is done, we send a skeleton state.

Once the primary data's done, we stream that in.

And as each bit of parallelized secondary data is done, we stream each of those in.

And once they're finished, we've got a complete page.

This means that we give the user as much, to look at as we can.

And we get it ready for them as quick as we can instead of, for example, if that, secondary data was one after the other.

So my next principle is isolated latency centers.

So for example, Astro won't render client side like I said, unless you explicitly opt in on each component, it's isolating that latency for you unless you tell it to not do that.

I'm not saying NextJS should implement this, but it's a good example.

The Next equivalent, it's not really the same feature, but an example of isolating latency is what Next calls partial pre rendering.

So basically the diagram down the bottom, illustrates it pretty well.

It's gonna bring in everything it needs to render the app, besides little bits of data fetching.

And it's just gonna show a skeleton for only those states until everything else comes in.

However, the unfortunate truth at the moment, is that is currently considered experimental, although it's likely to come, hopefully in the not too distant future.

My other complaint with this, at least in its current state, is that it lacks predictability, because you've gotta opt in either for your entire app or at the page level.

So there's not that kind of granularity and predictability in what's gonna be partially pre rendered.

Astro does the same thing right away with their server defer directive, which is an attribute you gotta put on every single, element or component that you wanna do it for.

And last one we have is deploy anywhere.

As I said earlier, portability and modularity and how we deploy our apps means that it's as flexible as possible and we can deploy close to our users.

So that also avoids vendor lock-in, which is bad for competition and performance as well, of course.

But yeah, deploying anywhere is a pretty crucial one for me and one that Next hasn't achieved at the moment.

Although they do seem to be working towards, they're contributing a bit to the Open Next effort.

Recently, in terms of, some other frameworks, Next is the only one that lags behind.

You've got, a couple of things that open next can do.

Static site hosting obviously has its limits, but only the usual limits that you'd see imposed on it.

But for the most part, the two I've compared it to Astro and Remix are doing, pretty well on that front.

Unfortunately, as many a person on, what used to be Twitter would tell you, it goes against Vercel's best interests commercially to make it easy to deploy NextJS anywhere, but they seem to be slowly coming to the realization that, oh, actually no, let's, make it deployable anywhere.

And if people want to come to us for the, better product than they can.

So you might have noticed there's a bit of a pattern here.

Deploying anywhere is how we move our latency.

Isolated latency centers mean that obviously we can isolate our latency parallelization.

Got it.

Is how we substitute our latency.

And predictability gives us the tools that we need, to be able to simplify, eliminating latency, because that's the one thing, eliminating latency is the one thing that a framework or a SaaS can never do for you.

So summing up, always start with eliminating the latency if you can.

If you can't, or if you've done it to the greatest extent that you can, go in, try substituting the latency.

So with that one, you wanna parallelize, you wanna look at reducing overhead, batching, things like that.

Then you can isolate caching, use with caution, but still a good tool, background workers, things like that.

Then you can move the latency if you're still, not satisfied with your results by that point.

Run the app closer to your users and make sure that your data is close to your app.

And loading and skeleton states as a last resort, for really long running tasks or for users with bad connections.

Thank you very much.

You can find me at these platforms, and I'm happy to take questions and would love to chat.

Reducing Latency is Like Risk Mitigation

Kai Malcolm
Web Directions 2024

Illustration of a speedometer with the needle pointing to the right, indicating high speed.

WHY DOES IT MATTER?

WHY DOES IT MATTER?

An illustration of a money bag with a dollar sign.

WHY DOES IT MATTER?

Slide showing a money bag icon, server icon, and angry face icon surrounding the text "WHY DOES IT MATTER?"

Hierarchy of Controls

Elimination: Physically remove the hazard

Substitution: Replace the hazard

Engineering Controls: Isolate people from the hazard

Administrative Controls: Change the way people work

PPE: Protect the worker with Personal Protective Equipment

Most effective | Least effective
An inverted triangular diagram titled 'Hierarchy of Controls' with layers: Elimination, Substitution, Engineering Controls, Administrative Controls, and PPE, showing decreasing effectiveness from top to bottom. A vertical gradient bar on the left indicates effectiveness from 'Most effective' (green) to 'Least effective' (red).
  • Eliminate the Risk ➔ Eliminate the Latency
  • Substitute the Risk ➔ Substitute the Latency
  • Engineering Controls ➔ Isolate the Latency
  • Admin Controls ➔ Move the Latency
  • PPE ➔ Loading & Skeleton States

Server Side Fetching

A visual flow diagram illustrating data transfer from Client to Database, passing through Server, Cache, and Edge, with bars representing the stages.

Cache Hit

Illustration of a network flow diagram showing a cache hit. The flow includes stages: Database, Server, Cache, Edge, and Client. An arrow labeled "Isolated Latency" highlights part of the flow.

CDN + API Server with Cache Hit

Diagram of a network request process showing interaction between client, edge, cache, server, and database with arrows indicating a cache hit scenario.

CDN + API Server with Cache Miss

Diagram illustrating the sequence of request handling in a CDN and API server environment with a cache miss. It shows the flow from the client through the edge, cache, server, and database, highlighting different stages with colored blocks and arrows.

Edge Streaming with Cache Miss

Diagram illustrating the process of edge streaming with a cache miss, showing interactions between client, edge, cache, and database levels.
<!DOCTYPE html>
<html>
  <head>
    <!-- stuff -->
  </head>
  <body>
    <div id="content">
      <!-- my amazing placeholder -->
    </div>
    <script>
      fetch('someurl.com').then(setTheContent)
    </script>
  </body>
</html>
<!DOCTYPE html>
<html>
   <head>
      <!-- stuff -->
   </head>
   <body>
      <div id="content">
         <!-- my amazing placeholder -->
      </div>
      <script>
         document.getElementById('content').innerHtml = 'wow!';
      </script>
   </body>
</html>
Slide with sample HTML code and annotations. Arrows point to comments labeled "Skeleton State" and "Substituted & Eliminated Latency".
const res = await new Promise<string>((resolve) => {
    setTimeout(() => {
        resolve('Hello from Streaming!');
    }, 3000);
});
return res;
<script> self.__next_f.push([1,"7:\"Hello from Streaming!\" "]) </script>

Edge Streaming with Cache Miss

Diagram illustrating latency changes through different stages in a network path including Database, Cache, Edge, and Client, with arrows indicating Eliminated Latency, Moved Latency, and Substituted Latency.

Astro

SSG-first

Astro defaults to output plain old HTML and CSS. No JS unless you explicitly include it.

Host agnostic

Astro adopts the adapter pattern to allow you to deploy to any machine or managed service.

Server-first

Astro only ships JS if you must have frontend interactivity, and only ships it for those “islands”.

Any Framework

Use React, Vue, Svelte, Alpine, Solid, Preact, or a combination of the above.

Specific Market

Astro does content-driven websites and they do it well. There’s no pretending to be something else.

Hard to break

Their website says it should be “impossible to build a slow website with Astro.”

Eliminate Latency, Good defaults, Move Latency

Logo of Astro with stylized text and decorative arrows pointing to various headings on the slide.

What does good framework design look like?

  • Predictability

export default async function SomePage() {
  const response = await fetch('https://example.com', {
    cache: 'no-store',
  });
  const posts = await response.json();
  return (
    <p>{JSON.stringify(posts)}</p>
  );
}

What does good framework design look like?

  • Predictability
  • Parallelisation
Diagram illustrating a framework design with a highlighted critical path leading to a primary step, which then branches into two secondary steps.

What does good framework design look like?

  • Predictability
  • Parallelisation
Illustration of a workflow diagram with labeled paths: "Critical Path" connects to "Primary," which splits into two "Secondary" paths. An arrow labeled "Send a skeleton" points to the "Critical Path."

What does good framework design look like?

  • Predictability
  • Parallelisation
  • Isolated latency centres

What does good framework design look like?

  • Predictability
  • Parallelisation
  • Isolated latency centres
Screenshot of a presentation slide showing a section titled "Partial Prerendering" with a note about it being experimental, accompanied by a graphic illustrating a web page layout with components labeled "Navbar," "Cart," "Product Information," and more, emphasizing static and dynamic elements.

What does good framework design look like?

  • Predictability
  • Parallelisation
  • Isolated latency centres
  • Deploy anywhere

What does good framework design look like?

  • Predictability
  • Parallelisation
  • Isolated latency centres
  • Deploy anywhere
A table comparing different hosting solutions: Cloudflare Pages, Vercel, Static Site Hosting, Netlify, and NodeJS Server, with checkmarks indicating feature support across various platforms.

What does good framework design look like?

  • Predictability
  • Parallelisation
  • Isolated latency centres
  • Deploy anywhere

Eliminate the Latency

Substitute the Latency

Isolate the Latency

Move the Latency

Loading & Skeleton States

THANK YOU

🦋 @kaimalcolm.com

📷 kai.malcolm

🌐 kaimalcolm.com

  • fetch API
  • loading and skeleton states
  • CDN
  • React query
  • JavaScript
  • Promise
  • streaming
  • NextJS
  • search engine crawlers
  • HTML
  • incremental static regeneration
  • server actions
  • async
  • React standards
  • component
  • authentication
  • skeleton state
  • partial pre rendering
  • server defer directive
  • deploy