Introduction to Cloud Code Drama
A recent discussion arose around Cloud Code's performance issues, specifically flickering in terminals and frame rate constraints. This sparked a wider conversation about how React, terminal rendering, and frame budgets interplay in complex terminal UIs.
Why React in Terminal UIs?
- React changes from traditional DOM manipulation by prioritizing a virtual DOM to detect and batch updates.
- This approach simplifies development by reducing bugs and allowing easier collaboration across large teams.
- However, applying React's virtual DOM diffing in terminals, which rely on text appending, leads to performance costs. Understanding these concepts in depth can be aided by exploring Understanding React Server Components: A Game Changer for Web Development.
Terminal Rendering Complexities
- Terminals use a standard scroll buffer that operates append-only, making backward updates expensive.
- Modifying earlier lines requires sending complex ANSI escape sequences, increasing data size and slowing rendering.
- Alternative modes like 'alt buffer' allow full control but sacrifice features like scrollback and native text selection. For more on UI abstractions and rendering paradigms, see Understanding Headless, Boneless, and Skinless UI in Modern Development.
Cloud Code's Rendering Pipeline
- Cloud Code operates within a tight 16ms frame budget, with limited time available to render terminal updates.
- Due to React's rendering model and terminal constraints, Cloud Code experiences garbage collection pauses and flickering.
- The team had to balance performance with developer productivity and maintainability.
Trade-offs and Engineering Philosophy
- Cloud Code's choice to use React stems from developer familiarity and predictable component isolation.
- Facebook's (Meta's) approach of rewriting runtimes and compilers rather than replacing core technologies reflects in Cloud Code's evolution.
- The team continuously refines rendering, including forking 'Ink' and integrating native parts to improve performance.
- This iterative refinement aligns with themes discussed in Exploring Monorepos, Node.js Ecosystem, and Modern Development Practices.
Comparison with Alternatives
- Other tools like Open Code use the alt buffer mode for better responsiveness but lose scrollback history.
- Codeex uses Rust and libraries like Ratatouille but still faces similar constraints.
- Workarounds like 'Claude Chill' try to reduce updates but introduce other UI compromises.
Challenges in Terminal UI Development
- Terminals lack modern windowing features such as resize events, making responsive design difficult.
- Copy-paste, selection, and multi-window management require complex custom implementations.
- Maintaining 60fps responsiveness and keeping user experience smooth remains a significant engineering challenge.
Future Perspectives
- The trend is moving towards full graphical UIs (e.g., Conductor for Cloud Code) that overcome terminal limitations.
- Terminal UIs continue to push boundaries but are fundamentally constrained due to their legacy design.
- For insight into modern UI feature advancements, consider Exciting New Features from React Labs: View Transitions and Activity Components.
Conclusion
- Cloud Code balances engineering trade-offs between performance and developer productivity with React.
- Understanding terminal internals and React's model clarifies why performance issues exist and how they are being addressed.
- The community should recognize these complexities before harshly criticizing terminal-based React apps like Cloud Code.
Oh boy, there's a new React drama. And this one is funny because it's not really about React. It's about Claude
Code terminals and frame rates. And as is the case with all good dramas, it started on Twitter when Port Taric, who
works on Claude Code Anthropic, shared some details about how they were working to fix the flickering problem that
existed in Claude Code. This post goes really into detail on how they're thinking about the rendering in Claude
Code and has some interesting details that a lot of people had opinions on. in particular when they started talking
about the render budget saying that they have 16 milliseconds of frame budget to get something out. So they only have 5
milliseconds left over from the React scene to actually get the antsy written in the terminal. People have opinions on
this. We apparently live in the clown universe where simple TUIs are driven by React and take 11 milliseconds to lay
out a few boxes in monospace text and where a TUI triggers garbage collection too often in its rendering pipeline and
where it flickers if it misses its frame budget. Ah, oh boy. There's a lot to talk about here. If you haven't already
seen the Primogen video on this topic, I highly recommend it. It inspired me to go a bit deeper here. Why am I the
JavaScript guy here to talk about these things? Well, I think I'm falling somewhere between these perspectives. On
one hand, I think the performance characteristics of Cloud Code are unacceptable at best. It's pretty absurd
how poorly it performs in many cases, and I understand why there is so much push back for that. On the other,
there's a lot of nuance around how terminal rendering works that's full of surprises. I don't think many of you
already know. I know that I've been surprised. I've done deeper dives myself. I've always been a nerd about
CLIs and terminals. I've been using GNU screen since I was 14 years old. So, I've been through the loop with weird
CLI [ __ ] and TUI stuff. I also have a handful of friends and people in my life that work on cloud code. So, I have
some unique perspective from there as well. I also obviously was an investor in bun and a good friend of the bun team
which is a key DAP inside of cloud code that helps it perform at all. That said, cloud code still isn't open source. So
most of what I'll be talking about here is me doing my best to reverse engineer how it works and explain my
understanding of it. Once again, Enthropic it would be much easier to defend you guys if you just open source
cloud code, but they haven't. So, I'm going to do my best regardless because as much as I love crapping all over
Enthropic when they screw things up, I do think this one particular issue is not being represented properly. I'm
going to do my best to make everything as clear and correct as possible here. Starting with today's sponsor. Today's
ad is going to be a bit different cuz the way we're building nowadays is quite a bit different, too. Trigger.dev is the
best way to deploy your chaotic AI workflows. What I mean is all those jobs that you have to worry about failing 10
minutes in that are obnoxious to orchestrate and cue and deal with is suddenly way easier. What happens if
you're running a long running agentic workflow and it fails five steps in and each step takes 10 plus minutes? You're
in hell. And I've had to deal with this in so many projects and now that I'm playing with trigger more, I really wish
I did before. I knew them mostly as a way to queue up jobs in the background and that does comically more than that.
Here's a real project I spun up to play with these things. In here, we have a trigger directory which is fully managed
by the trigger CLI and workflow and it syncs live to the cloud. It's so cool. You might notice that I have some
external depths here like playright and playright core. That's because of the app that I'm building. What I built is a
simple little app that I can use to analyze all of my recent videos and then generate alternative thumbnails.
Obviously, it's not using me in them properly, so they're not all great. But the fact that I could whip this up so
fast with no code being written by hand, I just told it to use trigger and it succeeded is really, really cool. With
these kinds of jobs, like fetching from a browser or generating images or analyzing content, there's a pretty high
failure rate. So knowing that any one of these jobs can fail and then automatically be retrieded within 10
seconds is huge and has massively reduced the error rates on a service like this. Instead of the whole request
failing because one small piece failed, it'll just retry those parts. And defining them is easy as hell. These are
just TypeScript files in my codebase. I import their task in their logger functions. I define this task, generate
thumbnails. I give it an ID. I define the retries and then I define the run function which is what it actually does.
Here is the coal run function. It couldn't be easier to spin up. I have a higher level orchestrate that is
similarly simple. And here I create the tasks and I batch trigger and wait with all of the mapped videos in order to go
trigger all of those jobs. Get back type- safe results that I can then use in other steps. It couldn't be easier to
just spin these things up and orchestrate them. And if you do have problems, it couldn't be easier to look.
When you're running jobs, they all just appear in their dashboard. You can see recent activity. You can see what runs
are currently going. If I go back here and trigger a new run, you'll see they all appear in the dashboard. Here we
have the orchestration workflow, the fetch YouTube video sub job running. And we'll see live as all of these things
happen. We can even hop in here, see the logs, see the traces, and if you look at your own terminal, you can see all of it
here too, including a click to go view the logs. It's it's so good. They manage all of the info for you, so you don't
have to worry about configuration or timeouts or all of that. But if you do want to, it's fully open- source Apache
2 and you can host it yourself on Docker or on Kubernetes. And look at that, another job completed. Couldn't be
easier. Build bigger and better with less errors at soyv.link/trigger. Best place to start here is the original
thread from tar. And I'll do my best to break down how all of this chaotic rendering stuff works. Most people's
mental model of claude code is that quote, "It's just a TUI, but it should really be closer to a small game engine.
For each frame, our pipeline constructs a scene graph with React and then it has to lay out the element, rasterize them
to a 2D screen, diff them against the previous screen, and finally use that diff to generate the ANC sequences that
it has to draw. We have a 16 millisecond frame buff budget, so we have roughly 5 millconds to go from the scene graph to
the ANC being written. While we test Claude code rigorously, our users run Claude in a huge combination of terminal
and operating system setups. Here we found that in some setups we were triggering garbage collection too often
in our rendering pipeline. Some things you can't find until you ship. This is a legacy migration. We have to port our
entire rendering engine while making sure nothing userfacing broke. Doing this without cloud code would have been
impossible. yada yada clarification things. Why it took so long? These details are cool but not what we're here
for. What we're here for is the rendering characteristics. There's a lot of different pieces I want to focus on
here and I'm going to presume that you understand a little bit of React and how it works. I'm going to give a brief
overview for those who might not. But again, this is not a video about how React works in the browser. This is a
video about why quad code is slow and why some of it justifiable and other parts are not. First, we need to talk
about how React works. When React first dropped, there was a lot of controversy around it because of the different
rendering techniques it used. Before React, when you wanted to update something in the browser, you would
target the specific thing you wanted to change. We have this paragraph tag ID count as a number and then button that
increments with the ID of increment. In the olden days, the way you would write this code so that the button would
update this is you'd have a script tag somewhere in your site. The script tag would bind on button id increment this
specific behavior. So we'd have like const button equals document.getelement by id increment button.onclick on click
equals and here you would define the function that does the same thing but it selects the document element count and
then it changes the content of the count and this is how we would write JavaScript. The way that things were
bound was that the values exist either in JavaScript or on the page and then you have to select the elements that you
want to have specific behaviors bind those behaviors and those behaviors would do the rights out of band. You
would have an on click that you bind to button that selects this identifier and changes the content of it. I put this
script tag after for a reason. Usually you put the script tag first, but the way this mental model works really does
put it after. The JavaScript is changing the elements on the page. The page comes first. In React, the big thing we change
is we move the state first. So const count set count equals use state. We're starting with five. We'll put the five
there. And now instead of rendering this hard-coded thing, you render count. And then in the button when you define it,
you bind it on click to do the thing you want it to. So set count to count + one. The big difference between this and the
old way of doing things is that everything here has to be recomputed when anything changes because you don't
know where the changes happened. And this was the bold decision the React team made when they first built the
project. They said to simplify it, [ __ ] it. Who cares? just make it cheaper to compare. Previously, every change was
some amount granular. When a value changed, you would go manually update all the elements that should be
different as a result. With React, we kind of just said, "Fuck it. Check what changed as a result of the diff, and
then render that." Because if you were to just recreate all of the elements on the page every time something changed,
that would suck. But if you go through and check which elements changed, then post them to the browser, it ends up
working really well. The technique for this is called the virtual DOM. Effectively, all the elements on your
page are rendered as a tree inside of the React virtual JavaScript world. And when something changes somewhere in the
tree, it compares against the browser version to see if anything has shifted. And if it has, it then commits the
change to the browser. But it has to have this virtual representation that it can identify the changes in before it
does that. And there's lots of things that break the relationship between the real DOM and the virtual DOM. And it's
also worth noting that while the browser's DOM is slow, it is not super slow. It's just too slow to aggressively
update. You want to diff the updates a little bit. So, now that we understand this, it's important to understand
something about the terminal. Someone asked Boris, why do a diff versus just redrawing everything when you only have
around 400 characters that need to be rendered? Boris responded, terminals are way less efficient than DOM. This is a
crazy thing to realize and a lot of people struggle to get their heads around this. I know I did for a while.
Terminals are [ __ ] slow. They really are. There are some good terminal apps now. Like obviously I'm a huge Ghosty
fan. I use it for my default terminal every day. It's great. But man, the process of actually rendering updates in
the terminal is not very fast. There's a lot of layers to dive into about how terminal rendering works. And I'm going
to be honest here. I know a decent bit of the inner workings, but I don't know much of the terminology because it never
really mattered. I would just fight things until they worked. And I've been learning a lot both as I talk to the
people building these things and also try to more deeply understand them as I talk about them myself. One of the first
pieces we're going to have to talk about is the different rendering strategies in the terminal. There's a standard buffer,
which is the standard scroll buffer. When you do something in a terminal, like if I open up a new terminal, pwd or
cat cargo lock, these are all things that are being appended to the history of my terminal. And you can scroll
through it. It's the buffer that is built into the terminal with all of the history of what's going on, of all the
things that have been printed to it. All of this is just writing out to the terminal. All works great. All totally
fine. But what happens if you want your program to go backwards and update something earlier on? Now we have to do
some weird [ __ ] And this is where terminals go from really easy to kind of annoying. You have to use special escape
characters in Ansie to tell the terminal, I want to go back to this point and edit these things. And that
sucks. Since all updates are done by writing more text, you have to write a lot of text to update things from the
past. And that's where these anti escape characters come in and make things a lot harder and often much worse
performance-wise. If you have to use special characters to go update this thing instead of just rendering the new
thing, you are constantly increasing how much data is being sent down the wire. And this is where the performance
differences really start to show with the different techniques and also the difference between like something in the
browser versus something in the terminal. The browser is really throttled by how many updates you're
doing and how many things are being tracked. The terminal is throttled by how many bytes you're pushing through
it. And since a longer history requires more text to do the update because you have to get to the right point in the
history. And just like the ins indicating where you're going are higher, you end up quickly bloating the
context of this. Especially if you have a lot of special characters for things like styling, color rendering, and all
of that. It's surprisingly easy to send absurd amounts of data through this. And since you're collecting this string
buffer constantly to figure out what state the terminal is currently in and figure out what needs to change, you end
up using a surprising amount of memory in the process. And when you think about it, it all makes sense. When the
terminal was built to just have text up here line by line and you want to go back 15 lines and change something,
obviously the process for that isn't great. But then you think about all the cool apps people use like Neoim that I
don't even have installed. VI, we'll go with that. You think about all these tools that are really powerful to let
you do crazy things in the terminal. How could this possibly work? It's not using the standard buffer. There is an alt
mode that you can trigger as well. The alt mode ignores the standard buffer and doesn't append things to that standard
scroll buffer and scroll history. This is a big part of why something like open code works and feels so different from
cloud code. If I open up cla code in this project, I can select text. I can scroll. And you see here it's selecting
text all around cuz that's all just appended to the scroll buffer. If I kill it, that's part of my history. This was
a thing that occurred in the history of me running cloud code where if I run open code, it just took over my whole
terminal. When I close it, it's gone. It's not part of the history. This also means that its behaviors are different,
too. When you try to select things, it doesn't necessarily work. I put in some input and I try to select it. They had
to write all of this selection logic themselves, which is why when you let go, it's no longer selected. It copies
to clipboard programmatically using their stuff. This isn't using any standard terminal things. They had to
build most of it themselves because they're not using the standard buffer and standard rendering. You kind of have
to make a choice when you build a terminal app. Do you want to use the standard buffer and deal with the weird
performance characteristics and deal with the hell that is append only history or do you want to rewrite the
entire rendering mechanism for the terminal? I just restored an old chat in cloud code that has a ton of stuff in
it. And when I close the whole history was appended here. I just open cloud code again. Nothing in history. I'll
open it once more. Resume. Pick something else in here. close it. All of that history just got
appended to this buffer and they had to write a ton of additional text in order to tell it via the terminal to go
override everything that exists from this session effectively and append these new things. It is not trivial at
all. We could have a real deep meaningful argument about whether or not these tools should use the standard
buffer or if it's worth the fight to rewrite everything to make alt mode behave well. There's a reason open code
feels as much better as it does. It's because they're in alt mode. But the standard buffer, standard scrollback,
all of that lets you use your terminal more like the standard terminal it is. This is also why trying to
programmatically call open code via a wrapper of some form is going to be annoying. That's also why they're
building in all of the cool HTTP server stuff into it because you can't just read the results from standard out from
open code. And again, remember we are just appending text to make all of this work. So think about how hard it is to
do some of the things we've seen here. Like when I do slres, it pulls up this new UI here that fills my screen to the
exact height of my current screen, lets me arrow up and down between different things and also escape to cancel it and
wiping that from the visual scroll back. And now when I close all this, things happened. I actually lost my scroll back
when I did that, which I think is really interesting. There are some tasks where the cloud code team decided it is not
worth trying to patch the history via additional text. it is easier to just clear it out entirely. It is also of
note that different terminals handle things for these different things differently. A lot of the work building
something like cloud code is dealing with the fact that pasting into it behaves differently across every single
terminal. A while back, the way Cloud Code handled this is when you installed it, it would write patches to common
terminals like in this case Ghosty that would allow for the key combinations to be recognized by Claude Code for using
controlV instead of commandV for paste on a Mac. Weird, obnoxious, but I get why they had to do it. Copy pasting here
with blobs is not something that natively works. Like you can't just copy an image and then paste the image into
your standard in and expect it to work. So doing custom keybinds to trigger specific behaviors to get that stuff is
effectively the only solution. Ah we've been trying to solve these problems with terminals for as long as we have had
them. Encurses is the original classic TUI library built way back in the day literally 1993 as a standard for making
it easier to handle all of these types of updates in your CLI. The first course library was developed at UC Berkeley for
the BSD operating system in the 80s because they were making a textbased adventure game and they wanted ways to
update the UI. Wild. So there are clear benefits and negatives to each of these strategies. I get why people are going
between them. But you might be thinking, wait, I've used standard buffer apps that are pretty powerful and dynamic
that didn't have these performance issues. You have. They have lots of issues for sure. Like I I've been using
Codex a lot more lately. in codeex if you're not familiar with it. The cloud code alternative by OpenAI is built in
Rust. It wasn't originally. In fact, in my original video where I talked about it, it was in Typescript and I warned
them to not rush to a Rust rewrite because they were having issues with ink. Talk to me and we can figure out
those issues. And they didn't do that. They rewrote it in Rust and it has it benefits and negatives. But this is
still standard buffer. You can tell just by trying to select text. It's the easiest way to know what type of buffer
an app's using. select some text in it and you'll see here all of these are antsy characters. I can copy paste this
empty space that is making this gray background because that's not really empty space. That's just how the
terminals work. I believe they're using ratatouille which is the rustbased TUI interface similar to encurses but to
make good CLI in Rust. So what's the problem with cloud code then? Partially this if you're not familiar ink is
actually a really really cool project and I still use it for a ton of stuff. is React for CLIs. Part of React's model
that makes it so powerful is that this virtual DOM technique can work on things that aren't just browsers. So, if you're
referencing this virtual layout for your elements and making sure that they're rendering correctly in the real world,
like whatever the real world is to you, it can be a traditional DOM, it can be a native mobile app, it can be an iOS or
Android app in those cases, it can be the Xbox UI, it can be something crazy like a canvas using 3JS with React 3
Fiber, it can also be a CLI. And that's a big part of what React Inc. is. It's a set of components and CLI bindings to
let you update the terminal UI via components and React elements changing, but it still has to do that diffing. It
still has to do that rendering. It still has to figure out what changed and why and then rasterize those changes. And it
turns out that model is not great in an appendon system. React is great specifically when you can update an
element in the original thing that you're trying to control with React. If you can say, I want to change this
element now to be this instead, it's great. With terminal UIs, you have to append text to select the right element
via these crazy escape commands and hope it gets to the right place. And when React is pretty willing to just blow the
whole thing out and rerender it, you can quickly have these huge piles of text that are being sent to the terminal and
then parse in order to trigger an update. And that isn't fast. It can actually get pretty bloated pretty
quickly. The amount of text data being sent from this React render process to the terminal to trigger an update is way
more than it should be, especially when it's not actually doing an update. Even with all of that considered, as long as
you're only triggering the update when actual changes occur, it shouldn't be too bad. But there's a problem. Changes
can occur outside of the React code. You might be confused about what I'm referring to here. Let me show you.
Yeah, I just opened another tab which changed the size of the window and that broke a bunch of things in what we were
doing before because this was actually not quad code even though it shows it there. The buffer in the history in the
codeex run just broke entirely because the window changed size. And if I do CC here, expand, I shrink, whatever, it
figures it out well enough. I'll resume, pull out a history, resize. It takes a sec sometimes, but it always gets it
right. And it's doing that by rerendering. So, it's pretty consistently checking its understanding
of the world via its virtual DOM representation and then seeing if the real DOM believes it or not and then
making a decision accordingly. And these are all just things we expect our apps to do cuz most of us work in tools that
handle things like resizing well. Claude Code had to go pretty hard to figure these things out and get it right, but
they did. The experience is really good. And if you use almost any of these other CLIs and you do something like resize
the window, who knows what's going to happen because there isn't an event that triggers like there is in the browser
like window.onresize is a thing that you can see. You can't in the terminal. But why would you pick React at all? There's
a handful of reasons for this one. The biggest one by far is obviously people know React. like the number of people
who deeply understand these characteristics of CLIs and terminals and PTIs and all of that is very very
low. There aren't that many people who get it and even now I am very thankful for chatters like Bitplane who are
helping a lot to figure out these things and make sure I get my descriptions and terminology right. Bit plane's theory
for how Cloud Code handles this is by intercepting the SIG winch which is part of the like standard rendering process
for the terminal disabling scrolling in it querying the size of the virtual terminal the pseudo terminal and once it
knows the size it can recalculate where things should go patch and override the differences and then switch scroll back
back on again. Terminals have lots of weird characteristics like what happens if a line is too long. Yeah. So, how do
you do rendering like this bar the input then another bar that handles when you resize the window? You have to calculate
how big the window is. But there isn't really a window size in the terminal. What you have is the size of the
terminal process. So, if you have scrolling on, you don't know how tall or wide the window is. You need some way to
get it. So the theory for how Cloud Code is doing it is that they switch to alt mode or something like it, figure out
the dimensions of an empty page, use those dimensions to recalculate, and then trigger the update again to render
again. The terminal doesn't give us any of the things we need to do this. And that's the biggest problem. If you
thought the browser was bad in terms of standards, the terminal standards were built before terminals or standards were
either concepts and have been appended onto constantly since and are still missing a lot of these basic things that
we often need. And just for a fun example again with codeex which is doing this via a buffer still but is doing it
with the Ratatouille library instead. It handles resizing. Okay. Like when I make it wider it handled that. It didn't
rewrap the text though which gets really funny when you get too small and the text just slowly goes up the screen.
Like it's just it's refusing to touch things that have already been rendered that are in the buffer. See that? When I
just resized and made it wider, it didn't change the size of the previous thing. Again, if we compare with cloud
code, I'll start with it smaller. Tell me more. Make it wider. Everything above, before, after, all of it
rerenders, all of it is handled. They put a lot of work in to handle all of these things. The bar the Cloud Code
team has for quality of experience and behaviors like this working is really important to them. They've prioritized
this highly. And a big part of it to go back to why use React is the things the developers are familiar with. They're
familiar with web not just like working in the browser but the behaviors we expect from working in the browser. Like
when I pick a window like I don't know T3 chat and we change the size of the page at a certain point it collapses the
sidebar and then reopens it when it has enough space to use it. These types of things we just expect from good
responsive applications and now that more and more people are using the terminal they have to care. Cloud code
is in a weird spot because a lot of people who are using a terminal for the first time for cloud code, which is kind
of crazy. It's important to get right. And this is one of the cool things about ink is that it's using yoga. If you're
not familiar with yoga, I don't blame you. It's a a deep cut in the React world. If you're used to using things
like flexbox in the web and then you build a React Native app, there is no flexbox on iOS or Android. Yoga is an
attempt to perform flexbox layout calculations across every language and every platform. So you can do things
like a flexbox in a different place. And one of the things that makes Ink so cool is it lets you use flexbox like
primitives via yoga in the CLI. So you can make a box that has elements placed in certain locations and yoga will do
the calculations and then ink will create the right antsy characters to escape out and rerender the right things
in the right places. Oh [ __ ] Nean who used to work at Meta on Stylex is here. He's deep in the React Native world. C++
which have us work pretty much everywhere. Yep. I've seen so much crazy style and like math calculations for
graphic stuff in C++ for these reasons. Fun fact, the logic that renders the color and your Twitch chat color. What
because you pick a hex code and then we have to make sure is still readable in light mode and dark mode. The code that
does that transformation is written in C++ so that it works exactly the same on iOS and Android and web. Fun facts, the
the work that has to go behind these things isn't as simple as React is slow. This stuff goes way [ __ ] deeper than
anybody understands. And like the intricacies of why ink can get slow for certain projects in certain ways goes a
lot deeper than people seem to think. But there's a couple more important pieces to consider here. One of them is
who created the original cloud code. It was Boris. Boris used to work at Facebook. Boris was one of the key
people rewriting a lot of Facebook in React. He was one of the people that fully bought into the power of React,
the benefits and the negatives and saw what it looks like at scale and decided that the benefit to negative cost
benefit analysis worked out in favor of React even for a CLI that React's model was strong enough that it would benefit
greatly for these types of things. There's a lot of benefits to working in React, especially when you have a bigger
team. The way React kind of isolates the damage of a component is magical. It is really hard to screw something up above
where you are in the tree by adding a new component lower in it. Obviously, like if you screw up the CSS or you're
hitting some global or something, you can. But React's top- down way of working is also a top- down way of
thinking. There are whole categories of bugs that kind of vanish when we moved over to React. And these are the types
of bugs that a lot of companies are experiencing now that they're using new frameworks like spelt and solid where
it's a little too easy for things to fall out of sync if you don't bind the signal in the right place or update this
thing in the right way. In React, we kind of just blow everything out. So everything updates if anything changes.
This has its issues, but it also makes the model much simpler, which means devs of various skill levels and various
awareness can meaningfully contribute without stepping on other people's toes quite as badly. I'm just going to give
you guys some fun facts about Meta to help emphasize the point I want to make here. The main back-end language at Meta
is a language called Hack, which is a PHP rewrite in C++ that will compile PHP code to effectively native code to make
the backend way, way, way faster. At Meta, they're also investing heavily into crazy projects like the React
compiler, which will take existing React code and make it way faster. They're also investing heavily in things like
Hermes and static Hermes, which is a new runtime for JavaScript and static Hermes is a compiler for JavaScript to compile
down to native code. Apparently, hack has continued to evolve since according to Nean. Now, it's a custom runtime for
PHP called HHVM, like V and JavaScript, but for PHP, and there's a type checker for hack that's written in Okamel as
well. This might just sound like utter [ __ ] chaos, and it gets worse. They rejected git and use their weird
bastardized form of mercurial. I believe it's a fork that is pretty heavily forked at this point. Okay. Apparently
now it's sapling. The thing I want to emphasize here about how meta works and thinks that is so different is they
don't necessarily solve problems by moving off of them or building on top of them. They have this weird philosophy
there where they go underneath the problem and rebuild there. So when they noticed PHP was too slow but the
codebase at Facebook was too big, the crazy bold thing they chose to do is rewrite PHP, the math that they had done
was that effectively rewriting all of Facebook is more expensive than rewriting PHP and making it faster. And
it turns out they were right and they're still using this weird bastardized PHP- like thing to this day. With React, they
had explored all of the alternatives. They closely monitored what was going on in the ecosystem, all these alternative
ways to render web applications, and concluded that React compromises were the ones that made the most sense. And
more importantly, that Facebook's code bases had so deeply built into React that replacing it with something else,
even like a new way of managing state in React was not realistic. And instead of trying to find something that has its
own benefits and negatives, they could write a compiler that will recompile all of that code the right way. that it was
cheaper to compile everyone's React code than to encourage them to rewrite React a different way. And that doesn't mean
they're not exploring other things. Like as Nean said here, they attempt to move to a new language called skiplang, which
was really cool, but sadly the effort did fail. They try those things, but they often fall back on the same pattern
with things like static hermace, react compiler, and hack of it might be cheaper to rewrite the entire tool chain
than to rewrite the code we built on the existing tool chain. and they are one of the few companies that not only thinks
this way but has consistently succeeded with this mindset. It is clear that Boris still thinks the same way. I
cannot say for certain that this is how Boris thought, but I would not be surprised if it was. React might not be
the best solution for what we're building today. If it isn't, we will know that because of how successful the
project is and all of the pain points we are hitting, as well as how smart the models have gotten. If it turns out that
we picked wrong, we will be able to rewrite our core primitives using these new tools to get it right later on.
Thank you, Whisper Flow. Boris's goal was to move fast in a way where they could understand the way the codebase
works that anyone could come join in and make meaningful contributions that an AI agent can do that as well without one
change in one component breaking something else somewhere else. React's model made that really doable and they
chose to go with React and Inc. not because it was the best and most performant solution, but because it led
them to where they wanted to be the best way. And if it turned out it was wrong, they could rewrite the parts that make
it slow. And that's what they ended up doing. They're no longer using traditional ink. They're doing some
crazy forking. And it's my understanding that there are native parts of their fork now, too, which is really, really
cool. But they still have to deal with the hell that is the size of the buffer that they're handing around. And one of
the problems that gets hit a lot is that buffer of text is getting so big that garbage collection has to happen a lot
more. And when garbage collection happens, they miss a frame. But I'm pretty certain that Boris's philosophy
going into this, and I could be entirely wrong. And I'm sure lots of people on the Cloudco team will let me know if I
am. My honest guess is that he went in here not thinking his tech was right, but knowing his tech, if wrong in ways
that mattered, could be rewritten to be good enough. And that's the philosophy they took. It is the same philosophy
that leads to crazy things like buying bun. Just the sheer concept of taking a JavaScript runtime and tool chain like
bun and deciding that you should own this that comes straight out of the Facebook playbook. That is so meta it's
hilarious. That's the exact same philosophy as rewriting PHP buying bun. Like like these are the same way of
thinking and it makes all the sense in the world. Cloud code was an experiment. If it failed, none of these tech
decisions would have mattered. Now that it has succeeded, it's time to start handling these tech shortcomings. And
they are doing it and they're doing it in really interesting, powerful ways. And they are sharing a lot of those
things as well. I think that's really cool. The reason that this has issues and the reason people are getting so
upset here is because this framing that Tar put here is best put in internal framing. This is a way that someone like
Boris should describe this to the rest of the team so they can understand the way of building. Is this the correct way
to build a terminal app? No. I would argue there is no correct way because terminals [ __ ] suck to build in. Was
the initial starting point the correct way to flush out these ideas and see what people are interested in?
Initially, that made a lot of sense. Now that they are where they are, it is important for people who are working on
cloud code to have the right mindset to make sure they don't make dumb mistakes that cause performance to go down. And
this framing is one that I'm sure the whole team has internalized to prevent that. And to be clear, I'm not saying
they're doing this because all of the people working on cloud code suck. Some of the best engineers I know are working
on cloud code. One of them is a C++ engineer by trade. She is legendary. She's like part of the C++ steering
committee and [ __ ] and she's writing her first ever Typescript project as Claude code which is wild but like those
are the people they have on this team. These are really good engineers. I will [ __ ] on the way they run Cloud Code all
day. I will sit here and complain about it being closed source still non-stop. It would make all of this much easier to
talk about. But I just want to make sure we understand the philosophy that led us here and that this isn't some crazy
cringe tech decision. It's actually the reason a team of 50 people can work on something like Cloud Code and not break
each other's [ __ ] constantly. And you can see this most easily when you look at the alternatives people have made to
try and fix these problems. This is a really cool package that David made called Claude Chill. The goal of Claude
Chill was to prevent these synchronized output updates that cause claude code to be so slow because Claude Code sends
entire screen redraws in sync blocks, sometimes thousands of lines. Your terminal receives a 5,000line atomic
update when only 20 lines are even being shown, which causes lag, flicker, or jitters in the terminal, which makes a
poor user experience. Claude chill lets you launch Claude in a way that doesn't do that. Claude chill. Claude. So now I
have this cloud instance running that won't update in the same way. You'll notice it immediately went full screen
and the scroll buffer is different now from how it was before. Intentional. If I do resume, open up this thing and try
to scroll. The scroll is broken because it's not rendering all of the text. You have to command six to trigger look back
mode. And now the scrolling works. The thing I haven't tried yet though, yeah, they definitely delayed a lot of
the updates here. And you can see it is laggier and weirder when you do the resizing.
Oh god, that flicker. That was weird. That was very strange. This is still using the standard buffer. It's just
trimming a lot of the updates to said standard buffer. Instead of rendering all of the content, it just takes the
last however many rows and renders those instead. And it's doing it with a very interesting architecture where the
standard terminal is interfacing with the cloud shell proxy and then it sends the inputs and outputs, the control
bindings, all of that to the cloud code process and then pipes the results in with this additional filtering
preventing the updates from getting to the terminal. If you thought terminal UIs were simple, to be frank, you were
lied to. CLIs are hard. Getting CLIs to be good and performant is not impossible. These are difficult problems
with difficult solutions. And honestly, the fact that something like ink works well enough that you can vibe code a
good enough CLI is truly just wild. It's so cool that the CLIs we use are good enough that nobody knows how [ __ ]
awful it is to build CLI interfaces. It's really hard to do these things right. It really is. And while there are
plenty of demos of look at how fast I can write text to the terminal and it renders it fine, you'll notice none of
those are open source because as soon as you try to resize the window, they fall apart. As soon as you try to select
text, they fall apart. As soon as you try to copy something and paste it somewhere else, they fall apart. All of
these edges that make these things hard to do aren't seen when you do those quick demos. And if you want my thoughts
on how we solve this, we do it by throwing away these [ __ ] CLI entirely and just moving to a UI. I've been using
Conductor a lot more lately. It's a UI for cloud code and cursor that lets you just do it in a real app that you can
click between. You can command tab and switch between things. They even have hotkeys for switching which thread
you're in. You got multiple projects open at once. You can switch between things easily. This is where I've been
doing my vibe coding recently. The CLI all have been pissing me off. I have a feeling that a lot more people are going
to be moving to these UIs as well. There's a reason people still love cursor. There's a reason people love
this. There's a reason that Open Code is building a web UI as well and the Open Code desktop app. It's not there yet,
but it's getting there. Yeah, the terminal is the wrong place for UIs. I love terminals. I love T-Mox. I love
Screen. I love all of these tools and all the places that they have brought me. I love using my terminals to run git
and run dev commands. I am falling out of love with using my terminal to do things like cursor, cloud code, codecs,
and all of those things. And I suspect we will continue overinvesting in terminal UIs well past where it makes
sense to and we are seeing the limits of what you can do in the terminal. Bitplane just sent this crazy demo of
textual rendering apps within apps. So like full terminals that are fully functioning inside of this terminal is
just so cool. Like people push the limits of these things and it's really cool to see what people can do in the
terminal. But you need to understand like it's not a browser. It's not a window. It's not built for these things.
We are fighting it whenever we do any of these things. Even something as simple seeming as the like bars surrounding the
entry point inside of cloud code that is a non-trivial thing to do and to handle when I press keys that it's going in
between there instead of just rendering at the end like it normally does. Like all of these little details are really
hard to get right and terminals are fighting you the entire time you do it. I'm not saying we can't do awesome
things. I'm saying it is significantly harder in a terminal when everything is being appended. Hopefully, this helps
give you a better appreciation of why these decisions are made these ways, why ink actually does make sense when you're
trying to make a complex application with components that can be edited and reused and composed in ways that don't
break. And in the end, the thing that the team is optimizing for is not how many milliseconds does it take to render
an update. They're optimizing for how easy is it to make changes in this codebase and how likely is a given
change in one place to break things for other users and how meaningfully can we improve experience for users while
meeting this bar that we have which in their case is 60 fps. They make their mistakes. They have their problems.
Cloud code is not the fastest solution in the world. But honestly, I found that they hit a pretty solid balance here.
And I think cloud code is still totally fine to use day-to-day. And it makes sense that the only people complaining
about this aren't really using it. They're complaining because they want to complain. Do I notice the occasional lag
when I use Claude Code? Yeah, but it's rare and I suspect it is for most people. And if it wasn't, they would
have fixed this long ago. So, don't let all the outrage on Twitter get to you. Cloud Code is going in a pretty good
direction. They are making the right decisions in the right places for the most part. And while it is not the
fastest thing in the world, and React might not make sense in a technical sense for doing CLIs, there are good
reasons they used it. And there's a reason that their team is moving so fast and that cloud code is still so far
ahead of every other AI coding CLI I have used. And it hurts me to say that because believe me, I wish I could just
sit here and chill open code all day. That would be so fun. But I'm just trying to show you guys what I think
based on what I use every day. And I also want to defend the Cloud Code team a bit because as much as I give them
[ __ ] it hurts me to see them get [ __ ] for things that I don't really think they should. That's all I have to say on
this one. And until next time, keep rendering.
Cloud Code faces flickering and performance challenges due to the inherent limitations of terminal rendering, which operates as an append-only scroll buffer, making updates that modify previous lines expensive. Additionally, using React's virtual DOM diffing in terminals leads to overhead because terminals require complex ANSI escape sequences for backward updates, causing rendering delays and garbage collection pauses within a tight 16ms frame budget.
React's virtual DOM optimizes updates by diffing and batching changes before applying them to the web DOM, which supports arbitrary element manipulation. In terminals, which lack a mutable DOM and rely on appending text streams, this diffing approach results in performance costs since updating past output requires costly escape sequences and cannot be changed as flexibly as web elements, leading to slower rendering and flickering.
Using React provides developer familiarity, predictable component isolation, and easier collaboration, enhancing maintainability. However, this comes at the cost of terminal-specific rendering inefficiencies such as flickering and frame rate constraints due to terminal limitations. Cloud Code balances these trade-offs by iteratively refining their rendering pipeline, including forking libraries like 'Ink' and integrating native components to improve performance without sacrificing developer productivity.
Alternatives like Open Code use terminal 'alt buffer' mode to gain better rendering control and responsiveness but lose features like scrollback history and native text selection. Other tools such as Codeex use systems programming languages like Rust and libraries like Ratatouille for potentially better performance but still face fundamental terminal constraints. Workarounds often reduce updates to improve speed but introduce compromises in UI experience.
Terminals lack modern UI features like resize events and support for windowing, making responsive design tricky. Implementing copy-paste, text selection, and multiple windows requires complex custom code. Additionally, maintaining a steady 60fps frame rate is hard because terminals have limited rendering capabilities and rely on complex escape sequences, leading to performance bottlenecks and a less smooth user experience.
The trend is moving toward adopting full graphical user interfaces (GUIs) like Conductor for Cloud Code, which overcome fundamental terminal limitations by providing richer interactions and smoother rendering. While terminal UIs continue to evolve and push their boundaries, their legacy design imposes constraints that graphical frameworks can better address, signaling a shift in development focus toward more modern UI technologies.
Understanding terminal internals clarifies why performance issues such as flickering and slow rendering occur when using React's virtual DOM in a terminal context. It helps developers appreciate the complexity of applying web paradigms to terminals and guides them in designing workarounds or optimizations, such as minimizing backward updates or integrating native components, to improve performance while maintaining developer productivity.
Heads up!
This summary and transcript were automatically generated using AI with the Free YouTube Transcript Summary Tool by LunaNotes.
Generate a summary for freeRelated Summaries
Exciting New Features from React Labs: View Transitions and Activity Components
The React team has unveiled a range of exciting experimental features, including view transitions and a new activity component that enhances UI performance. These updates promise to simplify animations and state management for developers, making React even more powerful and user-friendly.
Why OpenAI Migrated from Next.js to Remix: An In-Depth Analysis
Explore the reasons behind OpenAI's shift from Next.js to Remix and how it impacts performance and development.
Exploring Monorepos, Node.js Ecosystem, and Modern Development Practices
In this episode of Syntax, the hosts tackle a variety of questions related to monorepos, the Node.js ecosystem, and the transition from musician to software engineer. They discuss the benefits of using monorepos for managing multiple layers of an application, the state of Node.js libraries, and the use of Cloudflare workers for server-side functionality. Tune in for insights on modern development practices and tools.
Understanding React Server Components: A Game Changer for Web Development
This video discusses the significance of React Server Components and their potential impact on web development. It explains how these components allow for seamless application serialization and integration between server and client components, marking a pivotal shift in how applications are built.
Understanding Headless, Boneless, and Skinless UI in Modern Development
Explore the concepts of headless, boneless, and skinless UI and how they reshape component libraries in modern web development.
Most Viewed Summaries
Kolonyalismo at Imperyalismo: Ang Kasaysayan ng Pagsakop sa Pilipinas
Tuklasin ang kasaysayan ng kolonyalismo at imperyalismo sa Pilipinas sa pamamagitan ni Ferdinand Magellan.
A Comprehensive Guide to Using Stable Diffusion Forge UI
Explore the Stable Diffusion Forge UI, customizable settings, models, and more to enhance your image generation experience.
Pamamaraan at Patakarang Kolonyal ng mga Espanyol sa Pilipinas
Tuklasin ang mga pamamaraan at patakaran ng mga Espanyol sa Pilipinas, at ang epekto nito sa mga Pilipino.
Mastering Inpainting with Stable Diffusion: Fix Mistakes and Enhance Your Images
Learn to fix mistakes and enhance images with Stable Diffusion's inpainting features effectively.
Pamaraan at Patakarang Kolonyal ng mga Espanyol sa Pilipinas
Tuklasin ang mga pamamaraan at patakarang kolonyal ng mga Espanyol sa Pilipinas at ang mga epekto nito sa mga Pilipino.

