
Barnum with Robert Balicki
Robert Balicki explains Barnum’s reliable AI workflows and Isograph’s approach to composing GraphQL data, UI components, and efficient rendering.
Episode Description
Robert Balicki explains Barnum’s reliable AI workflows and Isograph’s approach to composing GraphQL data, UI components, and efficient rendering.
Episode Summary
Robert Balicki joins Anthony Campolo and Dev Agrawal to discuss his path from statistics and workplace automation to Meta’s Relay team, Pinterest’s web platform, and open-source development. He demonstrates Barnum, a TypeScript DSL backed by a Rust runtime for orchestrating reliable, parallel AI workflows. Barnum separates deterministic control flow from narrowly scoped LLM tasks, enabling large refactor campaigns, automated pull-request maintenance, retries, and eventual resumability without leaving agents in charge of every decision. The conversation then turns to Isograph, Robert’s data-driven UI framework. Isograph associates GraphQL-like selections directly with components, compiles their combined requirements into efficient queries, uses a normalized store, and rerenders only affected UI. Robert argues that local reasoning, build-time knowledge, and constrained automation can make both developers and coding agents more reliable while reducing context, boilerplate, network waterfalls, and unnecessary computation.
Speakers
- Anthony Campolo
- Robert Balicki
- Dev Agrawal
Chapters
00:00:03 - Robert Balicki and GraphQL Migration at Pinterest
Anthony welcomes Robert Balicki and introduces him as an experienced GraphQL developer now working on AI projects. After recalling an earlier React Miami stream with Dev, Robert describes his current role on Pinterest’s web platform team and his prior work on Meta’s Relay team.
Robert explains Pinterest’s challenge in moving web screens from REST to GraphQL without rewriting entire pages or introducing extra network requests. His Relay Migration API lets components become indifferent to their data source, allowing teams to migrate from the leaves upward and switch a complete screen only after testing performance, logging, and backend behavior.
00:02:44 - From Statistics to Workplace Automation
The discussion shifts to Robert’s programming background. Although he briefly encountered Logo, he did not seriously begin coding until his first job after college, where he produced market-research reports in Excel and PowerPoint. By automating repetitive work, he reduced a process that consumed weeks each month to roughly fifteen minutes.
Robert connects that experience to his education in mathematics and statistics. He recalls moving away from political science after finding statistical analysis more precise and rewarding than repeatedly writing broad essays. Learning concepts such as p-values showed him how quantitative methods could support clear, testable statements and helped lead him toward software.
00:05:41 - Rust NYC and Finding a Team at Meta
Robert recounts working at several startups before joining Meta. His involvement with Rust NYC played an unexpected role: after he presented a React-like Rust framework that compiled to WebAssembly, someone from Meta in the audience helped bring attention to his work during the hiring process.
He describes Rust NYC’s continued growth and the possibility of sharing speakers among affiliated meetups. At Meta, Robert searched internal code activity for New York teams using Rust, found an engineer nearby, and effectively invited himself onto that team. The placement exposed him to demanding coding standards and eventually brought him into Relay.
00:09:34 - Learning Relay and Developing as a Speaker
Robert explains that joining a platform team responsible for Relay was very different from contributing to an ordinary product feature. COVID interrupted his onboarding, and he later realized that failing to complete it had forced him through avoidable confusion while learning a sophisticated framework largely through independent investigation.
Dev praises Robert’s React Conf talk reintroducing Relay, especially its structure and clarity, and asks how public speaking became part of his work. The group recalls influential conference speakers and older React presentations, while Anthony observes that React, GraphQL, Relay, and Flux were originally complementary pieces of one architecture even though open-source adoption often treated them separately.
00:13:27 - Why Conference Talks Are Valuable Artifacts
Robert traces his comfort with presentations back to childhood performances and high-school speech and debate. He views talks, whether delivered at conferences or meetups, as unusually valuable artifacts because they communicate technical ideas to audiences who may never read documentation, source code, or long-form articles.
Public work has also directly benefited his career. Interviewers at Pinterest recognized his talks and side projects, while his Rust presentation helped create the Meta opportunity. Speaking makes conference networking easier because attendees have an immediate topic to discuss, though Robert cautions that preparing three talks in one summer left him overwhelmed and practicing until the last minute.
00:17:42 - A Broken Wrist Accelerates AI Adoption
Asked about his early AI work, Robert admits that he recognized AI’s importance but initially remained focused on Isograph and efficient manual programming. His habits changed after he slipped and broke his wrist, making his thumb-heavy external keyboard setup difficult to use with only one hand.
With help from voice transcription and Claude Code, he created a one-handed computer workflow involving custom keyboard layers and layouts. The experience made the value of closed feedback loops obvious. Without tests and structural safeguards, the agent quickly produced poor changes, so Robert built checks that explored key-binding states and verified that modifications were deliberate.
00:20:19 - How Barnum Evolved from a Refactoring Pipeline
Robert’s next experiment used one agent to identify refactors and other agents to implement them, potentially in parallel. Early attempts were chaotic: agents failed to commit changes, shared files unsafely, and behaved unpredictably. These failures suggested that the work needed an explicit directed workflow rather than a loose collection of long-running agents.
A JSON workflow tool gradually became a typed builder in TypeScript, then an abstract syntax tree interpreted by a Rust process. That progression produced Barnum. Its TypeScript DSL describes control flow, parallel work, asynchronous operations, and LLM invocations while keeping type information and orchestration outside the agents themselves.
00:25:30 - Code Mode and Programmatic Tool Orchestration
Anthony asks for a definition of code mode. Dev explains that ordinary agents call one tool, wait for its output, and decide on the next call. Code mode instead lets a model write a small program that invokes several tools, applies loops and conditions, filters results, and may launch subagents before returning only the useful outcome.
This approach can save tokens because routine data transformations happen in code rather than repeatedly passing large outputs through a model. Robert agrees with the overall purpose but emphasizes reliability: the outer program should enforce requirements such as running tests, rather than trusting an agent that can skip checks, comment them out, or choose an easier path.
00:30:27 - Removing Humans from Well-Defined Workflows
Anthony describes his own practice of asking one model to review another model’s changes, which introduces independent context but still allows the reviewing agent to cut corners. Robert argues that stronger guarantees require moving critical decisions into deterministic orchestration rather than relying entirely on model judgment.
Barnum and code mode are especially useful when humans need to leave the loop, such as API-driven processing, event-based code review, or thousands of repetitive migrations. Open-ended feature development still benefits from close human direction because architecture and product intent remain unsettled. Well-defined transformations, by contrast, can be constrained, verified, and repeated automatically.
00:32:24 - Why Barnum Uses a TypeScript DSL
Before showing code, Robert explains why Barnum is expressed as a DSL inside TypeScript. Natural-language instructions are ambiguous, difficult to compose, and impossible to statically verify. Plain JavaScript is familiar but does not naturally guide developers toward safe, maximally parallel processing of directed dependency graphs.
A specialized language such as Haskell could express those semantics, but it would sharply limit adoption. TypeScript offers a practical middle ground: familiar syntax for developers and models, combined with custom semantics inspired by Effect. Robert argues that the successful path for pervasive AI automation must also be the performant and reliable path, not merely the easiest prototype.
00:37:05 - Generating Refactor Pull Requests at Scale
Robert begins the Barnum demonstration with several automatically generated Isograph pull requests. The simple example inserts diagnostic print statements, and he openly notes imperfections caused by repository state and parallel activity. The purpose is to show how a workflow can repeatedly create small, bounded changes rather than pretend every generated patch is flawless.
He describes a catalog of roughly twenty-six refactors used in larger codebases. Examples range from replacing inappropriate logical OR defaults with nullish coalescing to eliminating representable impossible states and narrowing prop types based on actual call sites. The catalog reflects code-quality rules Robert personally cares about and can explain precisely.
00:41:09 - Why Hundreds of Refactors Can Be Useful
Anthony connects the demonstration to his work teaching enterprise developers how to use AI coding tools, where identifying and implementing refactors forms much of the curriculum. He observes that promises of hundreds of weekly pull requests often sound like feature delivery, while Barnum is primarily suited to numerous focused maintenance changes.
Robert says large codebases contain enough recurring problems to justify that scale. New features require product judgment and evolving architecture, but repetitive cleanup can be specified and checked. He also describes using Barnum to babysit a stack of dozens of human-directed pull requests by rebasing branches, rerunning flaky checks, and landing changes when their conditions are satisfied.
00:44:55 - Barnum Programs, Handlers, and Parallel Execution
Robert opens Barnum’s documentation and shows a compact workflow that lists files, iterates over them, migrates components, collects results, and runs the program. Although the syntax resembles ordinary TypeScript, it constructs an AST that is serialized to a Rust runtime, where available operations are scheduled in parallel.
Handlers provide the connection to real work. Each handler is a typed unit that can read files, invoke Claude or Codex, call command-line tools, or perform ordinary computation. Barnum therefore acts as structured glue: the DSL controls dependencies and execution, while handlers remain flexible enough to integrate whatever processes a specific automation requires.
00:48:23 - Running the Extraction and Implementation Pipeline
Robert runs his refactoring CLI after clearing its state. Verbose logs show agents examining files, reporting completed edits, and moving work through the pipeline. One stage discovers possible changes, while a later stage implements them and creates a draft pull request through command-line tooling such as Claude’s programmatic mode and GitHub’s CLI.
The demonstration underscores that Barnum is not hiding a proprietary model API. It invokes ordinary processes, captures their streamed output, and coordinates the results. The displayed pull request is intentionally simple, but it confirms that typed orchestration, model execution, repository changes, and GitHub operations can be connected in one repeatable program.
00:53:13 - Queues, Recursion, and Explicit Loops
Robert walks through the pipeline’s control flow. An extraction consumer repeatedly dequeues files, processes available work, sleeps when appropriate, and recurs. An implementation consumer reads discovered refactors from another queue. The discussion briefly turns to the current enthusiasm for agent loops.
Robert finds the framing overstated because programming languages have long provided loops, conditions, recursion, and other richer control structures. In his view, these concepts belong in code, where behavior is explicit and inspectable, rather than being advertised as special model capabilities. Barnum uses familiar programming structures to constrain model activity to smaller, more reliable subproblems.
00:57:38 - Structured Refactor Discovery Across a Repository
The extraction handler reads a target file, invokes Claude, and requires a response matching a Zod schema. Each result can include a refactor name, affected locations, a change summary, motivation, and correctness guarantees. That typed output becomes work for later stages rather than remaining an unstructured conversation.
Although discovery begins from one file and one refactor definition, the underlying agent can inspect imports and related files when necessary. It can also implement changes spanning several modules, such as splitting an oversized file. The initial file is a scoped entry point, not an absolute restriction on repository awareness.
01:02:08 - Detailed Migration Rules and Smaller Agent Contexts
Robert explains that the quality of automation depends on precise refactor descriptions. A nullish-coalescing rule may need only a short warning about empty strings, while adoption of the Relay Migration API can require several pages of patterns, exceptions, and expected behavior. The framework works best when the desired transformation is already understood.
Dev offers a SolidJS 2.0 migration as another strong use case. Rather than giving every RFC and the whole application to one enormous session, developers can separate identifying outdated patterns from implementing each migration. Smaller sessions avoid irrelevant files, reduce token usage, and improve correctness by giving each agent one narrowly defined responsibility.
01:06:35 - Keeping Agents Narrow and Babysitting Pull Requests
Robert reinforces the value of minimal invocations: read a file, decide whether one rule applies, and return a specific JSON shape. Discovery can be denied write access, while implementation receives a separate environment and task. This division reduces accidental edits and makes each stage easier to inspect.
He then shows the pull-request babysitting workflow. A deterministic categorizer assigns branches to states such as bypassed, requiring retargeting, needing a rebase, failing checks, ready to merge, or still pending. Each state has an appropriate action, allowing routine repository management to proceed without asking a model to reinterpret the entire situation every time.
01:11:32 - Deterministic Outer Loops and Limited Retries
Dev notes that pull-request categorization does not require an agent because repository status can be expressed procedurally. Robert agrees: the more work that can move into deterministic code, the better. Models are reserved for tasks such as diagnosing and fixing failed checks, where interpretation is genuinely useful.
The repair workflow checks out a branch, invokes an agent with relevant failure context, commits successful fixes, and retries only a bounded number of times. After repeated failure, it parks the pull request instead of wasting tokens. Dev describes this as replacing the human outer loop with programmable orchestration while preserving focused prompts inside it.
01:16:24 - Concurrency, Worktrees, and Resource Limits
Dev raises the idea that multi-agent orchestration becomes a concurrency problem. Several agents modifying one repository resemble threads sharing memory, making queues, resource acquisition, isolation, and Git worktrees important. Separate worktrees can give each refactor an independent state while messages and queues coordinate the broader process.
Robert says Barnum provides low-level primitives and runs available work as parallelly as possible, while user code controls scarce resources. A workflow may limit concurrent Claude processes or assign only as many refactors as there are worktrees. In practice, repository rebasing and corporate CI capacity can become more significant bottlenecks than the orchestration runtime itself.
01:21:43 - Portability and Barnum’s Resumability Roadmap
Anthony asks whether Barnum is tied to Claude Code. Robert confirms that it is not: handlers could invoke Codex, OpenCode, or any process able to produce extractable structured output. Claude already works for his needs, so he has not prioritized additional integrations, but the architecture imposes no meaningful model dependency.
Robert then contrasts Barnum’s planned execution model with code mode. A long code-mode program may replay nearly all prior work after a late failure, and external operations often pass through a model. Because Barnum represents the workflow as inspectable data, it can eventually persist progress and resume near the interrupted node rather than repeating hundreds of completed steps.
01:26:33 - Human Approval, Isolated Handlers, and Multiple Runtimes
Dev points out that pause and resume are useful not only for crashes but also for human approval. A workflow may reach a decision gate, wait a day for input, and then continue from that exact position. Robert agrees that this would be cleaner than continuously polling inside a loop.
Barnum handlers are exported functions executed in isolated processes, limiting their ability to interfere with one another and making failed units easier to re-run. Since Rust merely launches those processes, handlers need not be JavaScript. Future workflows could combine Python, Bash, custom model runtimes, and DSL frontends in other languages while retaining one execution engine.
01:31:01 - Why an AST and Rust Runtime Sit in the Middle
A viewer asks why Barnum needs an AST and Rust rather than running directly under ts-node. Robert says Rust offers a language he trusts for runtime correctness, while the intermediate representation separates orchestration from effectful handlers. That isolation supports clearer reasoning, retries, resumability, and higher-order control structures.
Dev clarifies that the deeper question concerns the intermediate execution layer rather than Rust itself. They compare Barnum with durable-execution systems such as Temporal. Robert argues that Barnum’s explicit values and composable constructs make operations such as recursion and wrapping arbitrary work in retries natural, even though more established platforms may be easier to adopt and commercialize.
01:36:46 - GraphQL Composition and Local Reasoning
The conversation moves to Isograph, beginning with GraphQL’s core composability. A component can declare a fragment containing exactly the fields it needs, while parent fragments and page queries automatically include those requirements. Updating a leaf component therefore updates the final network query without manually tracing every ancestor.
Robert contrasts this with REST-oriented data fetching, where adding or removing a field requires finding all relevant queries and determining whether other descendants still need it. Developers rarely perform that full investigation, so payloads accumulate unused fields. GraphQL allows applications to grow more complex while preserving local reasoning instead of forcing one person to remember an entire component tree.
01:42:29 - Isograph Associates Components with Data at Build Time
Robert explains that conventional GraphQL applications maintain a conceptual one-to-one relationship between a fragment and the component consuming it, but most frameworks do not understand that relationship at build time. Isograph makes the association explicit and uses it as part of compilation.
An Isograph field can represent a component that closes over the GraphQL data selected alongside it. Parent components select that field and receive a ready-to-render component, passing only ordinary non-graph props when needed. Changing the child’s data selection requires no prop threading or parent update, reducing boilerplate and allowing developers or smaller models to work safely within one local unit.
01:47:44 - Generated Queries and Coordinated Lazy Loading
Robert shows generated GraphQL for a Pokémon example, including fields such as form, key, number, species, and sprite image. At runtime, Isograph uses generated artifacts to read matching values from the network response and supply them to the components associated with those selections.
Build-time knowledge also lets Isograph coordinate deferred data with deferred JavaScript. Marking a field as loadable can asynchronously fetch both the component code and the data it requires, rather than asking developers to manage those concerns independently. Robert also argues that schema-aware discovery can reduce duplicate components by making canonical UI fields visible through editor completion.
01:52:52 - UI-Specific Fields Without Polluting the Global Schema
The group clarifies that Isograph’s syntax is a GraphQL-like superset. Fields such as a repository link or image display may not exist in the backend schema; Isograph augments the project’s view of the graph with client-side UI and computed fields. Selecting one can yield a React component rather than ordinary JSON.
Robert illustrates why locality matters with a hypothetical user’s favorite restaurants in their hometown. Avoiding a network waterfall can force a highly product-specific relationship into a shared GraphQL schema visible to every client. Isograph can instead define that requirement within one web project, where it disappears when the feature is removed rather than leaving permanent schema clutter.
01:57:26 - Moving Isograph Computation to the Server
Robert describes a future direction in which selected Isograph fields can execute on the server. A formatted date, a personalized restaurant list, or another project-specific computation could remain locally defined while moving server-side for performance, secret access, or proximity to backend data.
This model resembles React Server Components in that parts of a dependency graph are hoisted to the server, but Robert believes Isograph can avoid some of that architecture’s limitations. He also clarifies adoption: Barnum is used extensively in his Pinterest work, while Isograph is not a Pinterest product and currently has enthusiastic use at startup Bolt Foundry.
02:02:19 - A Lightweight Scanner with a Real Build Step
Dev asks how deeply Isograph’s compiler analyzes React source. Robert explains that it primarily scans for specially formatted Isograph literals using straightforward pattern matching. It does not need a full semantic understanding of JavaScript or inspect which JSX branches render at runtime.
A build step is still necessary because those literals generate reader artifacts, parameter types, and combined query text. The generated TypeScript changes as selected fields change, giving components accurate data types. Isograph also inlines fragment-like composition itself, meaning the same frontend model could eventually generate SQL, tRPC calls, or custom backend operations instead of GraphQL alone.
02:07:51 - Persisted Operations and the Normalized Store
Robert explains that generated operations can be registered at build time and represented by an identifier at runtime. The client sends that ID, and the backend looks up the approved operation. This indirection improves security by preventing arbitrary expensive queries and limiting accidental access to fields that should not be publicly selectable.
The conversation then turns to caching. Isograph writes network results into a normalized store keyed by identity, and components independently read their declared data from that store. Navigating from a list to a detail page can therefore render already-known headings immediately while missing sections suspend and load within their own boundaries.
02:13:38 - Minimal Recalculation and SolidJS Comparisons
Because Isograph knows the dependency graph, it can avoid recomputing downstream values when an upstream change does not affect the derived output. Robert uses formatted dates and clocks as an example: if milliseconds change but the displayed second does not, the UI need not render again.
Dev suggests that this fine-grained behavior sounds naturally compatible with SolidJS. Robert agrees that Solid’s once-constructed, stateful component model aligns well with Isograph’s dependency knowledge. React still works, but its ability to render functions repeatedly before mounting creates a less direct fit than a model where construction and reactive updates are more clearly separated.
02:18:10 - Build-Time Memoization and Component Boundaries
Robert compares Isograph with runtime-oriented systems that dynamically track data dependencies. Runtime construction can support flexible queries for only missing fields, while Isograph and Relay trade some of that flexibility for stronger build-time optimization and artifact generation.
He wants Isograph to encode more control flow and transformation logic in its literals so the compiler can create efficient memoization boundaries automatically. The challenge is to support useful filtering and derived values without recreating an entire second JavaScript language. Meanwhile, the live demo requires several setup fixes because Robert is using a new computer and an unfamiliar local environment.
02:23:42 - Pet Demo, Final Guidance, and Total Duration
The completed pet application demonstrates Isograph’s user experience. Artificial network delay makes the upper portion of a detail page render from cached data while a lower section waits, and subsequent visits load immediately from the normalized store. Robert explains that changing one pet field would rerender only the component that reads it, avoiding increasingly expensive top-down updates across long paginated lists.
The hosts thank Robert and joke about Rust’s learning curve before sharing his StatisticsFTW social handle. For anyone starting with Barnum, Robert recommends pointing a coding agent at the project’s best-practices documentation, which captures common mistakes and workflow patterns. The episode ends at 02:29:54, the total duration.
Transcript
00:00:03 - Anthony Campolo
And we're live. Welcome back everyone to AJC and the Web Devs. We have a very special episode today. We have a new guest, never been here before, Robert. Really happy to have you. You're a GraphQL OG, you're working on some cool AI stuff. So why don't you go ahead and introduce yourself and let our listeners know, uh, who you are and what you do.
00:00:26 - Robert Balicki
Awesome. Hey folks, my name is Robert. Super pumped to be up here. I guess Dev and I go at this point in time way back, but like it's the first time— no, it's the second time that I'm doing a stream with you. So I'm like really, really excited about this to keep this tradition going.
00:00:42 - Anthony Campolo
I was curious about that actually.
00:00:44 - Dev Agrawal
React Miami.
00:00:45 - Robert Balicki
Yeah, there we go.
00:00:47 - Anthony Campolo
Yeah. Oh, what year?
00:00:50 - Dev Agrawal
24, I believe.
00:00:52 - Anthony Campolo
24. Okay. I was there in 2023.
00:00:56 - Dev Agrawal
Yes.
00:00:56 - Anthony Campolo
I loved it. It was really fun.
00:00:58 - Robert Balicki
That is, it's my favorite conference. I want to go one day as not, well, the first time I went was not as a speaker, as an attendee. And that's the way to go because last or this year I went as a speaker and I was just holed up in my room practicing right, right until the very end. So I got to miss all the really cool events. Oh well. Oh well. It's all good.
00:01:18 - Anthony Campolo
Yeah.
00:01:19 - Robert Balicki
So My name is Robert. Quick intro is that I currently work at Pinterest where I'm on the web platform team. Before this, I was at Meta on the Relay team. At Pinterest, the primary thing that I've been working on is helping the company adopt GraphQL on web. And the sort of the big reason why that's not super easy is because if you start converting a screen to use GraphQL, you're not only fetching data in a different shape, you're also fetching it from a different endpoint. So you kind of have a couple of bad options. One is rewrite the whole screen, good luck. And the other is make more network requests, which hurts performance. Um, so sort of the, my sort of marquee thing there at Pinterest has been working on the, uh, Relay Migration API. Relay is a framework for building, uh, GraphQL or data-driven apps powered by GraphQL, which I worked on at Meta. Um, and the Relay Migration API essentially allows you to allow to make components agnostic about where they get their data from. So you can start at the leaf and make the component not care whether it gets REST or GraphQL data and then sort of build your way up. Once you get to the root, run some experiment and actually flip on GraphQL for an entire screen at once. And that's pretty cool because a lot of times you turn that on and you discover, hey, there's something, some missing logging on the backend or performance isn't quite where you got where you wanted it. So this is kind of nice. Yeah, anyway, really excited to be on here.
00:02:44 - Anthony Campolo
No, that's super cool. We're gonna get deeper into GraphQL probably in the second half of the episode. I had said before the show, I'd be curious to hear your kind of coding backstory. I always like to know how people, you know, first learned to code. We've never met before. I was a bootcamp kind of student. I originally had a music major and then got into all this stuff through, you know, later in life, like my late 20s. And Redwood was kind of how I became into open source. And that was, you know, a GraphQL framework. Didn't use Relay, used Apollo Client. So, I've said that actually the one GraphQL project that I never really went super deep into was Relay. I went deep into almost all of them, which is kind of ironic because I would argue Relay is probably the most important GraphQL project ever in certain ways. So, I would Definitely gonna be curious to hear more about that from you. But yeah, so what was your first line of code, your first language? How did you first start programming?
00:03:43 - Robert Balicki
I think I did a little bit of Logo. I don't know if y'all remember that one.
00:03:49 - Anthony Campolo
Oh yeah, heard of it.
00:03:51 - Robert Balicki
The little triangle that moves around. But I think the biggest— I didn't really do all that much coding until I was, let's say, 22 or so, and I had my first job out of college. And it was building reports in Excel and doing them in PowerPoint and stuff like that, doing like market research. And I just ended up like automating a lot of my job. And that was awesome. Like it took something that used to take several weeks per month and like turned it into like a 15-minute process, basically. No, that made me— no, no, I majored in math and stats. So I did a little bit like statistics programming and stuff like that, but like, yeah, gotcha. No coding standards, right? At that point.
00:04:38 - Dev Agrawal
That explains the username.
00:04:39 - Robert Balicki
Yeah. Yes, I think that's— I got my Statistics FTW Twitter account around that time.
00:04:45 - Anthony Campolo
So yeah, like the— what do you, what do you think of that quote? There's lies, damn lies, and statistics.
00:04:53 - Robert Balicki
I think it's awesome. I think honestly, honestly, like statistics is I'm very happy that I studied statistics, even more so I think than studying math. Both are very interesting. I ended up— I started out majoring in political science, and during my freshman year, I wrote one essay on the history of the Peloponnesian War on the Melian Dialogue. And like, I used it 3 times in different classes, and I was like, oh, this is not fun. Like, but if you have the opportunity to get away with it, you do. And on the other hand, I took like the stats and poli sci course, and it allowed me to like make these precise statements. Um, like my first introduction to like p-values and stuff like that. And I was like, this is so much cooler rather than sort of the airy foofy, um, world of writing that I, that I had come from.
00:05:41 - Anthony Campolo
Yeah, no, that's, that's super cool. Uh, it's a really interesting background. Um, so just a little more on that. How did you get involved in Meta? 'Cause I'm assuming you weren't hired to like do GraphQL or maybe you were. How, how did that work?
00:05:58 - Robert Balicki
I worked for a bunch of startups when I got into tech and then I ended up changing and getting a job at Meta. I actually run Rust NYC and I had presented about a framework that I built for building web apps that where you write Rust and it compiles, I mean, superficially looks like React and it compiles into WebAssembly and somebody from Meta was in the audience. So I think they kind of pushed for Me in the back? I don't know.
00:06:29 - Anthony Campolo
rust.nyc?
00:06:31 - Robert Balicki
Yeah, rust.nyc is the— I think that's our domain. Yes, that's it. You can find it. You can get access to the Discord there. We actually are associated with a bunch of other meetups at this point in time like Rust Boston. There's— we're starting a couple in Florida. We have some going in LA and San Jose, so like the empire grows.
00:06:55 - Anthony Campolo
That's cool. Let me know if you ever want to get a St. Louis one going.
00:06:58 - Robert Balicki
Oh yeah, we absolutely would love that. Um, and we can provide whatever support you need as well. I'm thinking like what would be really cool is to have a circuit where we basically share speakers, and if they happen to be in whatever destination, then we can arrange for them to have a, you know, a meetup on relatively short notice.
00:07:16 - Anthony Campolo
No, I love that you are involved in the meetup stuff because, um, I used to do a lot of the Jamstack meetups, mostly virtual because I got into coding in 2020. So it was like the time to do online meetups. So I remember when I was like, I think it was, you know, September, October, November, I had like, uh, I would do, there's, I remember one day I did 2 meetups in one day. Like I was like, hit Seattle and then I hit, you know, like Oregon or whatever. So. Yeah, it was an interesting time to be doing meetups.
00:07:49 - Robert Balicki
Yeah, a lot of them, like, they didn't survive COVID. It's kind of sad. Yeah, I'm glad that we kept Rust NYC going less frequently during COVID proper, but like we kept it going. So to answer your earlier question, I joined Meta and then you do this team matching thing, or at least at the time you do this team matching thing. And I ended up just—
00:08:10 - Anthony Campolo
I've heard about that. They kind of give you, you try out a bunch of different groups to see where you would be best placed.
00:08:18 - Robert Balicki
Yes. And I had one criteria, which was to join a team that used Rust. And so I would just sit there looking at the internal sort of GitHub tool Fabricator and looking at PRs that touch Rust files and then looking up the person in the Space View tool and being sad because everybody was in the Bay Area or Seattle or whatever. And then one day I found somebody who was based in New York. I was like, oh, this is awesome. I sat down next to him like, I'm joining your team. And he was like, you should probably talk to my boss. And so by doing that, I got on the team and it was awesome. It was really, really high coding standards and it was very difficult for a long time. And I was in the middle of onboarding when COVID happened and I was like, whatever, I'll finish it in a couple of weeks. And then I sort of forgot about it. So I realized like a year later that I'd gone through some amount of unnecessary pain and struggle as a result of having never completed the onboarding and never really just gotten from other folks on the team like, hey, this is how it works. And Relay is a fairly complicated framework. So it took a lot of just spelunking and learning. And I don't think I was— I think now I would be much better at doing something like that, having done it once. But at the time, it was a lot.
00:09:34 - Anthony Campolo
Of course. Yeah. First time you do anything in your tech career, it's always so daunting. Like, I remember when I first got my job, you know, it was like, it was terrifying. I was so worried about every little thing I was doing, you know? And now that we got like agency It's, it's, uh, you have so many better resources available to you. Very true.
00:09:55 - Dev Agrawal
And even inside a tech company, if you're just like working on an, like some sort of a feature or app team, that's different. But you're working on Relay, which is like a platform that's probably used by a bunch of different teams within the company, um, which is like a different kind of experience. And I, I think the way, um, Well, I first discovered, uh, you and your work through a talk at React Conf, like reintroducing Relay, which I think was an amazing talk. And that kind of, uh, I think the, the way that you kind of talked about those concepts, the way you introduced, uh, the framework, I think, uh, like that caught, that caught my attention more than the framework itself. And I think over time, the like more talks that you have given, I think that's Uh, one, one of the things that I appreciate a lot about, uh, your talks and content, um, which is like just, uh, the way that you, uh, structure things, the way you explain things. And I think you put a lot of effort into them as well. So I'm curious when, like, how that happened, like how you kind of got into, uh, I guess like giving a talk about Relay. Instead of just like work, like writing some code for it. And maybe like, did you kind of discover that, oh, this is something that I want to do more?
00:11:18 - Robert Balicki
Thanks. I actually really appreciate that. I do try to put an effort into my talks. I think that some people are kind of amazing at giving talks. There's that one guy that comes to mind that does like the I don't know if you're the most famous YouTube guy that does like a bunch of really cool things. Um, that really doesn't matter. No, no, no, I'm not talking about somebody like that. Somebody who does, uh, conference talks from back in the day.
00:11:45 - Anthony Campolo
I mean, for me it was Rich Hickey.
00:11:47 - Dev Agrawal
Um, is that Dylan who did The Art of Code?
00:11:51 - Robert Balicki
No, no, there's like, there was one conference talk about like what was like, what was stuff like, it was from the perspective of the '50s.
00:12:00 - Anthony Campolo
Oh, Joe Armstrong.
00:12:02 - Robert Balicki
I don't think I'm thinking of him. I'm thinking of somebody else, but I do like Joe Armstrong actually. OK, whatever, whatever. There are just some people that have—
00:12:12 - Anthony Campolo
I'm very curious if it comes to you. I want to know because whatever it is, I'm probably here. Yeah, no, this is great. This is the kind of diversions I like because I love conference talks and I haven't seen your Relay talk, but I'll check it out after the show because I've seen a lot. I've watched a ton of talks from those conferences specifically, the, those React the React Conf ones. When I first was getting into all this stuff, I was trying to get a sense of what all these tools were. So, I was going back and I was watching all these talks, like Pete Hunt and stuff like that. And that gave me a ton of context for what was going on in things like React and GraphQL and Relay. And what are all these things? How do they fit together? Because what I've said about the interesting thing about GraphQL is Facebook had this whole stack where they had React and GraphQL and Relay and Flux, like even before Redux. And then all those things were kind of broken apart and introduced to the open source world in a way where it wasn't clear that they were all supposed to be put together. And I feel like that made it really confusing for people if they didn't understand that all these tools were meant to be a certain part of a larger architecture.
00:13:23 - Dev Agrawal
Yeah.
00:13:27 - Robert Balicki
Yeah, so I guess to Dev's question, like I had been— I've been doing presentations for a long time. I remember in 4th grade like being a bit of a class clown, like liking to do that. I did one where I pretended to be that Simpsons character. Hi, I'm Troy McClure, that one, doing something like that. And I remember my parents being like, nobody's gonna get this reference. And I'm like, it's— you're not the right age for this. Like, yes they will. And yeah, and then I did speech and debate in high school and I did—
00:14:01 - Anthony Campolo
Oh, you're a speech and debate kid.
00:14:03 - Robert Balicki
That's funny.
00:14:04 - Anthony Campolo
I had a good friend who won the speech national championship.
00:14:08 - Robert Balicki
Oh, fancy.
00:14:09 - Anthony Campolo
His name's Blake. He went to the 2 big speech schools, Kentucky and the other one.
00:14:16 - Robert Balicki
Okay. But yeah, I think like conference talks and talks in general, it doesn't even really have to be conference talks, talks at meetups, like there are these super high value artifacts, right? Um, I think a lot of people should be just putting more effort into them. Um, and I also happen to enjoy it, so maybe it's easy for me to say. Um, and obviously, like, uh, therefore I lean into it. Um, but I do like to give good conference talks. I think it's really— it, it does allow you to reach a different audience and more people. So for example, at Pinterest, when I interviewed there, sort of everybody that I interviewed with, not everybody maybe, but at least some of the people knew about some of my side projects and some of the conference talks I had given. And it coincided with, I had just gotten into GraphQL Conf. So I was about to, uh, so that was nice. Like, I got to just say like, hey, I'm about to speak here, which I think makes you look a lot better. Um, likewise, like when I said at Meta, like somebody in the audience had seen me. Um, so I think that's cool. I think honestly, like, it's, it's worth it for folks to do it. Folks should do more stuff like that. Um, and then in terms of effort, like, uh, I put a lot of effort this summer. I put in, I did 3 conference talks and it was too, too many. So I would not recommend doing that. I was very overwhelmed for a month and a half because of that. And that's why React Miami was so last minute. You know, I was like in my hotel room until literally the moment of. But I would still recommend it. Because, yeah, you know, it's fun. It's good. It's a great way to meet people. It's a lot easier to meet folks at conferences if you are a speaker. Because folks will come up to you or you have something concrete to talk about, talk with somebody about. You don't have to be like, so what kind of stuff are you into? You know, and like try to find the overlap. Instead, you can just say like, hey, I liked your talk about, I don't know, Redux or, you know, TanStack or whatever. And then of course, if you're giving a talk, you want to give a talk about it, you know, with other folks. I mean, at least most of the time. So anyway, that's my little spiel.
00:16:38 - Dev Agrawal
Nice. Yeah, I always feel like being a regular attendee at a conference is like the worst thing to do because like, oh, it's like the worst place to be, uh, even as a volunteer. Like, as volunteers, you usually get free tickets to a conference, like they don't have to pay, and but they still have like a, I guess, like an access to more, I guess, backstage things and a better reason to maybe talk to people. Obviously, as a speaker, you have a lot more Um, um, uh, like a much easier way kind of breaking the ice. Um, but I, yeah, I think maybe we can, maybe we can talk about conferences forever. We can also talk about, uh, um, GraphQL for a long time. I think we'll circle back to GraphQL, uh, Relay, and Isograph in the second half. But yeah, I, I'm really interested in hearing, uh, what was your like early work with AI? Uh, what are the things that you kind of, uh, uh, that you were excited about, that you struggled with, and, uh, how did you end up at Barnum?
00:17:42 - Robert Balicki
Yeah, yeah. So, so I, um, definitely knew about—
00:17:49 - Anthony Campolo
I'm getting some echo.
00:17:53 - Dev Agrawal
Is it from my side?
00:17:54 - Anthony Campolo
It might have been. Um, yeah, it's gone now. Okay, go ahead, Robert.
00:17:59 - Dev Agrawal
I'll put on my earphones.
00:18:01 - Robert Balicki
Yeah, um, yeah, so I knew about Bitcoin in 2008, and then I looked at it and I was like, man, the oracle problem, it's insurmountable. Like, who's gonna, you know— anyway, so I live with that regret. And then AI, I was very abundantly clear among my friends and I that like AI is going to change the world. Like, I don't know, A couple of years ago. I don't really know what the timeline is. But then I still like was more focused on Isograph and just getting very good at, you know, efficiently making code changes manually. And then it really took until this year, which was months after sort of the winter of everyone using 4.5 for all their side projects, which is an eternity. I slipped and I broke my wrist. And so I couldn't type because I use an external keyboard and like it's very thumb heavy and I just kind of couldn't do it. And I ended up deciding at that point in time, one, my wife turned me on to WhisperFlow. And 2, I decided to try to use Claude Code for everything. So I sort of vibe coded my way into a way to use my computer entirely with one hand. So just kind of like a layer system plus typing plus like a custom keyboard layer.
00:19:26 - Dev Agrawal
Layout.
00:19:26 - Robert Balicki
And also, it was really clear from doing that, like the— how much you benefit from infrastructure or from closing the loop, I guess people call it. And how important that is. Like, I would have just like kind of dived in and started working and been able to maintain code quality if I was doing it manually. But like very quickly, the AI was just like dishing out slop. And I needed to just come up with this custom framework for essentially writing unit tests of key bindings and stuff like that. I mean, it's not really a custom framework. It was just like, really, it was just like, explore this state space and make sure that whenever you make a change, it's intentional. And like, did the AI— I mean, it helped the AI, but it would still make these massive changes and not actually review them. So whatever.
00:20:19 - Dev Agrawal
Okay.
00:20:19 - Robert Balicki
So after that, I did what everybody else wanted to do, I think, which is decide that I wanted to build a pipeline where one agent identifies refactors, and then a bunch of other agents, maybe in parallel or something, implement them. And like the first times I did that, like, it was just kind of bonanza. It was like kind of bananas. Like they would not commit their changes no matter how much I asked. There was like one file which was being used as sort of the just to store everything. And like, okay, so I had some pretty bad primitives. And like, it would have been a lot better if I just like used a database or something for these tasks, which I did not. But it became very quickly clear to me that what I needed, or what I wanted was this, like a workflow, and like a, like a DAG. And so I ended up building this sort of like this JSON-based workflow tool. Um, and at the time it was, it was using a bunch of agents which were long-lived that would read from some sort of— that would call this binary and that would give them tasks whenever they were ready to have a task. Um, I didn't really— I hadn't worked out how to run Claude -p quite yet. Um, plus I think like I was doing this mostly for work stuff. And we have like an interesting setup at work. And running Claude ephemerally like that was not one of the blessed workflows. So at the time I needed that. This turned into like a JSON builder pattern. So I realized that like it's just better to build this stuff in TypeScript. You have more type safety. Like the JSON stuff was very stringly typed. Like I could enforce the shape, right? But like if you're saying the next step is X, you don't really have any guarantee that X actually exists. So that turned into essentially a couple of steps further into, okay, now we have this JSON builder pattern, but like what are we actually building? Well, we're building a workflow. Okay, so now instead of building the workflow directly, you're sort of building an— I mean, that's basically an AST. And that turned into now we should execute this AST. As if it's actually a programming language. And that ended up being Barnum. So, the gist of Barnum is that you have— it's a DSL in TypeScript where you write some stuff that hopefully looks like intuitive TypeScript. It looks a little bit like Effect. That generates an AST that gets serialized and sent to a Rust process where it's executed. Or interpreted, I guess, maybe if you're being super persnickety. And on the Rust side, we execute the AST and sort of like schedule a bunch of async stuff and what have you. And one of those async tasks is invoking an LLM. And so the idea here is that Barnum has, like, has 4, I think, goals is how I usually think about it. One is it should be really easy to invoke LLMs from it. That's trivial. That's true of everything. The 3 real ones are, one, it's high-level and focused on control flow and type safety. 2 is that it makes it really easy to handle parallel work and asynchronous work. 3, it is really easy for agents to write. Because ultimately, and I think if you have those 3 things, then what you have is the ability to essentially do something similar to code mode, Um, the Anthropic thing that I guess was just a couple weeks old now, where the first step is you describe the problem or something like that, and the agent builds a program, and then that program is invoked and it in turn calls a bunch of LLMs. And that is how you get to do several things. One is you constrain the behavior of the LLMs. So for example, if the LLM— if you're just asking an LLM, hey, one-shot this feature, and there's 100 different sub decisions that you have to make, like, good luck, it's going to make bad decisions. It's mostly going to make lazy and easy decisions rather than really holding itself to a high bar. And secondly, it's going to be expensive because it's going to do stuff like list all the files in the repo. And then, I mean, let's say you have 1,000 files. Well, that's 1,000 files that are now in context. And then it's going to marshal those into a JSON. Thing to print out or something like that. That's multiple times that these strings go in and out for no reason. Listing all the files in your repository, that's the thing that you should be able to do just using, let's say, JavaScript. That's the idea behind Barnum. It allows you to constrain the agents and by doing that you have more reliability and they're cheaper because you're not doing stuff that you shouldn't be doing with agents. In the agentic world.
00:25:30 - Anthony Campolo
Awesome. There's one thing that I want to kind of talk about before we get into actual code examples. Could we define code mode? Dev, I saw you tweeting about code mode also. I've seen other people talk about code mode. I'm not up on this yet. I'm more of a Codex user than a Claude Code user. So enlighten me. What is code mode?
00:25:52 - Dev Agrawal
You want to take that, Robert?
00:25:54 - Robert Balicki
No, you go for it.
00:25:55 - Anthony Campolo
I'd be curious for both your takes though.
00:25:57 - Dev Agrawal
Okay, the way that I think about code mode is basically that, um, like currently agents call like one tool, wait for the result, then they call the other tool. Um, the, uh, the best that harnesses are doing right now is parallel tools where models will, uh, like write multiple, like 3 or 4 tool calls at once, then wait for all of them to finish, and then take the next step. Code mode is essentially a way for a model to write some logic that invokes a bunch of functions, um, uh, like basically uses tools as function calls within that script. And then, uh, which means it can also combine like control flow loops It can do like basic data transformation inside. So if you have a tool that returns 1,000 things, uh, an, uh, an LLM, uh, can just write like, okay, call this function and then do a .filter and filter out everything that's higher than, or that's, I don't know, like have some predicate basically. So instead of calling a tool directly, it writes code that will call a bunch of tools. And then one of those functions could be triggering a subagent. So within that workflow, it can do additional things. And the question— the answer to why is that it's more token efficient. It's, uh, it's easier. A lot of tasks, uh, are pretty straightforward to like put together in a sequence. Like an LLM doesn't really need to do all the steps individually. It can just orchestrate them and only have to look at the final result. Um, and honestly, right now, like I'm a heavy user of Hermes Agent right now. And more than half of my Hermes tool calls are Python scripts, and I would love to replace that with actual code mode. That's my brain dump on code mode.
00:27:54 - Anthony Campolo
Yeah, so then Robert, what I would be curious then to know is, so I hear a lot of things that overlap there, so what makes code mode different from what you're doing?
00:28:07 - Robert Balicki
Yeah, well, I can go and talk a little bit about the differences, but I think that the key thing to answer the why question is that, like I said earlier, it's that if you are trying to get more reliability out of your LLMs, you don't want the LLM to be responsible for key things. For example, if your LLM can skip unit tests, can comment out unit tests or whatever, before making PRs and landing PRs, then at some point in time for a sufficiently large task or sufficiently large number of tasks, it's going to start cutting corners. On the other hand, it's fairly easy and sometimes really valuable to say, I would not like LLMs to skip unit tests. You should never commit something that I—
00:28:55 - Anthony Campolo
they just didn't do that.
00:28:57 - Robert Balicki
Yeah. The way that they cannot do that is by having the LLM not be the outer layer. Right now, if you're using an agent to do stuff, then the agent is the outer layer and it can do whatever the hell it wants. Huge advantages to that. It's very flexible. It can adapt and whatever. But on the other hand, if you have a task that you know the outlines of it, and it might be make this change and then run TypeScript, even something like that, doing that in a programming language, means that there was no agent on the outside that could just skip the TypeScript. Maybe the agent could modify the file and then hack the mainframe or whatever, you know what I mean? But realistically, within the stuff that it will do, it will not really— that prevents it from cutting corners. That's the most important one. Now you may think, that's fine. Working with an agent directly is fine for all the things I want, all the things I do, and that might be true. It's actually most of the time for writing features, the proper way to do— the proper thing to do is to work with an agent because that kind of fits well and you're reviewing the code at the end of the day, or at least kind of sort of looking at it. And if stuff is— if you have a human in the loop, then a lot of this stuff is not as high priority or high impact because you're verifying it on a step-by-step basis because you're seeing what it gives you.
00:30:27 - Anthony Campolo
You're like, wait a second, you messed this thing up. You go, Tell it that. I will say the ad hoc way that I tend to deal with this is I just have an agent review another agent, but like, not like you just have Codex review Codex, you have like Claude Code review Codex or like, because then it will step outside of the context to actually review it. But that's still then they could cut corners in the review. So that gets me part of the way there, but it's not like a real way of solving the problem like you're trying to do.
00:30:58 - Robert Balicki
Yeah. And so, yeah, so really it comes down to like, what situations do we want to remove the human from the loop? Okay, maybe you're doing some AI work as part of like an API and you're— or you're just doing 10,000 refactors. Like those refactors might be converting a file from, I don't know, JavaScript to TypeScript or something like that, right? Like kind of annoying to do individually. Maybe there's a little bit of thinking involved. But by and large, like, the task is well-defined and not open-ended. So, it's in situations like that where you want to use something like Codemode or Barnum. And both Codemode and Barnum sort of fill a very— fill the same niche, I guess you could say. And they make very different decisions on how to do that. But, yeah, that's, I think, the thing.
00:31:47 - Anthony Campolo
Did we answer this? I don't know if we answered this question directly. Does this only make sense for background tasks? I'm not sure exactly which part of the conversation he's even asking about here.
00:31:56 - Robert Balicki
I think the, yeah, like, well, I guess I think it makes sense if it's an API, if it's something that's like there's no human in the loop, right? That could be a background task, could be something that's part of an API. It could be automatically reviewing, you know, if you're doing something like reviewing code in response to an event. Without any human in the loop. So I think the answer is yes, that background tasks fit for this. Not maybe not only for background tasks, but yeah.
00:32:24 - Dev Agrawal
I think we can make it much easier to understand with a demo of some sort if you have that set up.
00:32:29 - Anthony Campolo
Yeah, I was gonna say at this point let's, let's get into the actual start looking at some code here. And also I like that you said it's a TypeScript DSL because, um, that was the one thing that going into this I was like, I'm— I don't know any other programming languages. So that will help.
00:32:44 - Robert Balicki
Yeah. Actually, yeah, I think that that's— it's an on-purpose decision. On the one hand, like, English sucks. Or whatever vernacular you do sucks. Because you can't— it's not composable. It's not clear what you're referring to. There's no equivalent of static type checking or anything like that. Like, if you have an extremely well-written English language description of a task, you don't know that it is correct, and it is never 100% correct. You have to run it to see what happens, and even if it runs correctly, you're not 100% certain that it is correct. Um, and okay, so instead of using English or vernacular, you should, uh, use a programming language. Why not use JavaScript? Well, JavaScript is really hard in JavaScript to write correct programs. And in particular, the kind of stuff that you mostly want to do is it's kind of a DAG-like structure a lot of times. And in particular, efficiently going through a DAG of parallel work is something that JavaScript is notoriously poor at, a poor fit for. Okay. So why not use a completely different language? Haskell is really good at expressing that. Well, if I told you that you should you would be better at writing AI if you wrote Haskell. That might be true, but also very few people would take me up on that offer. So, the missing last— the missing 4th option is a DSL inside of TypeScript that gives you different semantics. So, it looks more like Effect. But it is in the lingua franca. Both the lingua franca for humans and also lingua franca for AIs. That's why code mode writes JavaScript, I think. It's fine. It's great because there's a lot of adoption of code mode relative to Barnum. That's fine. But I still think it's worse because of all the reasons that— because it's just basically bare JavaScript. I, for example, played around with code mode a little bit. And I asked it to do something that basically had like 3 phases. It would complete phase 1 entirely, then it would start phase 2, then it would do phase 3. But like sort of the ideal way to do something like that is to imagine that it's a tree and that there's like little inputs that go into all the thingies and you just kind of work on whatever task is available. If you ask the agent to do that, maybe it will do that, but it will not naturally— but it doesn't naturally fall into that pit of success. And so you end up with these, um, you end up with a lower ceiling for the performance, um, than, or rather it takes more effort to get good performance and stuff like that. And I think the point of AI is that the happy path has to be the performant path if we're going to start to use it everywhere and really unlock the benefits. Um, yeah. Okay. So that's, that's mostly my responses to Parasocial Fix's, uh, questions. Um, yeah, thank you for guiding the conversation. Uh, yeah, so no, good. Yeah. Um, so should I share my screen is what you're saying?
00:36:03 - Anthony Campolo
Yes. Yeah.
00:36:04 - Robert Balicki
Okay, so let's do this.
00:36:06 - Dev Agrawal
Um, and it's completely fine if the, if the demo is, uh, uh, if it doesn't properly work first time, or if it has—
00:36:14 - Anthony Campolo
you can also show like the docs page if we want to start talking about like concepts first. Uh, or you can go straight into the, into the demo.
00:36:21 - Robert Balicki
I need to enable sharing, so I will be back in hopefully a matter of minutes.
00:36:25 - Dev Agrawal
Sure.
00:36:27 - Anthony Campolo
Um, we got a question here about Beads. Have you ever used Beads Dev? It's like some orchestration tool. Um, it gets you a bunch of agents kind of working in, in parallel. Um, Beads sounds interesting. Um, I've— Parasocial, I've looked into a couple of those orchestration tools. I haven't taken the dive on any of them yet. But I feel like that's kind of where a lot of this stuff is going, is higher-level abstractions to get multiple agents kind of doing stuff in concert with each other. Okay. All right.
00:37:05 - Robert Balicki
Y'all see my screen? Excellent. Yes.
00:37:08 - Anthony Campolo
Cool.
00:37:09 - Robert Balicki
So, as you can see here, I haven't actually done a lot of stuff in Isograph for quite a while. I created 3 PRs today.
00:37:18 - Anthony Campolo
That's what—
00:37:19 - Robert Balicki
that's so productive. Oh my goodness. And you read X. Yeah, that's fine.
00:37:27 - Anthony Campolo
I—
00:37:28 - Robert Balicki
these are not good PRs. The point is, all these PRs did was add a bunch of eprintln comments at the top of files. And They're actually doing a poor job of this because they didn't— I guess I didn't include to reset hard the repository or something like that, and it kind of like swallowed a few into it, or some stuff was running in parallel. Who the hell knows? Yeah, this one only has one file. It's only supposed to modify one file based on the description. Okay, so this is our goal, is to make a bunch of automated refactors. Actually, I'm going to limit it to one file at a time. Because it doesn't really help. It doesn't really help. But like this runs in parallel by default. Okay. So I actually do this at work a lot. And one of the things that I've done is I listed something like 26 possible refactors that I think are— don't change behavior and are good to do almost in every— in any file where we can find them. These could be simple. These are things like, I don't like the use of 2 pipes to— instead of 2 question marks, because 2 question marks is more clearly saying this is the default value. And so I have one description of a refactor that is that. Other ones are like, hey, there are some impossible states. There are some representable impossible states. OK, so there's like a loading Boolean state.
00:39:00 - Anthony Campolo
Variable.
00:39:01 - Robert Balicki
There's also a nullable promise value variable that's in state and an error. You know, you can't have all 3. You can't have error and the okay value at the same time.
00:39:12 - Anthony Campolo
Okay, question. So are all 26 of these ones you came up with yourself, or did you do an analysis also with the AI to help find them?
00:39:21 - Robert Balicki
I think I just mostly picked the ones that I care about.
00:39:25 - Anthony Campolo
You went through your— and actually wrote these all out to then direct it what to do.
00:39:30 - Robert Balicki
It's both. One, I described them and then I had the AI turn that into descriptions of the refactors to do, and then I would sort of read them and go back. I think the key thing to note is that you want to bootstrap what you're doing. You don't really want to write the code yourself if the, if the code is code. But like I said, you should go back and forth with AI and then that will like expand that out into like exactly the thing that runs and you sort of examine it and you're like, hey, you made a bug here, whatever. And then you sort of go from there. Same thing is true of Barnum. Same thing is true of writing these refactors that we're gonna do. But yeah, there's like 26 refactors ranging from really small to actually pretty big changes to ones that sort of involve looking at many files at once. So, for example, is every single prop that we have for every component like actually passed somewhere? And if not, can it be— are all the possible values passed? So all the variants pass. So for example, we might accept a string, but it can only be 2 possible concrete values. One of the refactors will change that to the type— will make the type only those concrete values. OK, but like the point is, I've done that and I've that way shipped hundreds. I'm not exaggerating here. I've shipped hundreds of PRs that just do that. And they have never, as far as I know, broken anything except for in one particular case where TypeScript was lying. And then I sort of updated the refactors to handle that case. And yeah, so that's kind of cool.
00:41:09 - Anthony Campolo
This is super interesting to me because I've been on this contract for the last like 7 months now. And I'm working for a very large payroll company to basically like teach their developers how to use AI coding tools. And I do a 2-week workshop for them, and, like, 3/4 of it is based just around refactors and, like, finding refactors and making refactors and doing exactly what you're talking about. So, and I think, and I've been doing that a lot on my own projects. So when I first heard you, when I first read the description, it says, like, you know, this project is for, like, shipping hundreds of PRs a week or something like that. I think most people in their mind, they think, you know, like 100 feature PRs. They don't necessarily think, like, This is 100 refactor PRs. And so I think the first question would be like, why would you need to make that many refactors?
00:42:01 - Robert Balicki
The codebase is large and there's a lot to improve in the codebase. Like I think that there's actually, so yes, most people think about using AI to ship new features and I think this is a poor fit for using AI to ship new features. Well, at least like the refactor part of it. Because you're not doing the same task repeatedly if you're shipping new features. It's more like you're kind of ideating and figuring out what's the best architecture for this thing.
00:42:29 - Anthony Campolo
That really requires a human in the loop.
00:42:31 - Dev Agrawal
Yes.
00:42:31 - Anthony Campolo
Whereas what you're looking at, you're finding the space in which it can do a lot of this work independently of a human having to constantly— because if you had to check every one of these refactors, that's just completely out of the question.
00:42:43 - Robert Balicki
Yes. And so I still use it for like making changes when I, when I have a human in the loop. So for example, I actually did— I had like 60 stacked PRs. It's 60, 70 or something like that to refactor one of our main surfaces. And basically I wanted to break it up into smaller parts and do all the things that you would expect to do in a, in a general cleanup of this thing. But that was very human-driven. But in the end, like I had 60, 60 whatever stacked PRs, and I ended up using Barnum, the second half of what I'll show you, to babysit those PRs, rebase them until they pass, and rerun the failed things that are flaky, and then essentially land them as needed. And that was cool because, like, that's actually— I'll talk about that later. But I think that the key thing, the cool thing about that is that It's a convenient way to write it. And you'll look at the logic that's in the babysitting thing. And you'll see that, yeah, that like all of that stuff kind of makes sense. Like you do need to categorize the PR and then you do need to do one or the others. And like writing that in English, like it won't be done reliably. And I did try to get LLMs to rebase PRs and land them for me. And like half the time it would just get confused about what the diff is and be like, hey, this, this PR is empty. Like, let me just close it. And then I would like, I'm not paying attention, so I wouldn't notice, but some changes just wouldn't land. And that's silly. Like, LLMs are bad at a lot of stuff. They're great at some things, but they're also bad at being— doing the thing that you need them to get right reliably. Yeah. So, okay. So, there's 2 aspects to Barnum that I want to show you. One is the generating the PRs and then one is the babysitting. And then there's— I'm not gonna show you the babysitting in In— actually, I'll just look at the code, but we won't see it doing the rebasing.
00:44:40 - Anthony Campolo
All good.
00:44:41 - Robert Balicki
Yeah, whatever. There's no open source, there's no CI basically. Just as long as tests pass, I can merge them and I usually merge straight to master. OK, cool. So let's take a look. So you can continue on.
00:44:55 - Anthony Campolo
I'm going to just hop off screen for 1 minute to go refill my drink, but I'll be listening.
00:44:58 - Robert Balicki
Yeah, cool. OK, so here's Barnum. barnum-circus.github.io for folks who want to check it out. Would definitely recommend it. The nice part about this is that there is a quick start, and if you just point the LLM at this quick start and sort of tell it what to tell it that you want it to do, that it does a decent job of writing a refactor. And that's how I built— I mean, a Barnum workflow, and that's how I built most of these.
00:45:29 - Anthony Campolo
Okay.
00:45:30 - Robert Balicki
So, what does it feel like to write Barnum? Well, basically, you are doing something like this. Oh, this is actually— hopefully the current docs are up to date because I want to be using the latest. Yeah. This is what it looks like. So, for example, you call list files.iterate. For each of those, you migrate a component and then you collect it and then you run it. And this And what will this do? List files here is something that returns an array. We're going to iterate on that and then we're going to call migrate component. That is something that takes an item in the array and it's all fully statically type checked. What actually happens here? This generates a description of a program, an AST that gets serialized, sent to this Rust process. That Rust process sort of executes it maximally parallelly. So this is a parallel iteration, for example. And then it finishes and that value is sent, is serialized back to JavaScript. And when we await this value, we will, we'll get the result of doing that. And then you sort of run this JavaScript like you normally would and it kind of does the right thing. Okay. So what is list files here or migrate component? What is migrate component? Well, migrate component here is a handler. Handlers here are essentially just chunks of code that can do whatever they want. In this case, it doesn't really do anything. It logs something and it says that it didn't migrate it. It's like a useless little stub. But the point is you can do whatever you want here. You can read the file, you can call Claude, You can invoke Codex, whatever. And so Barnum essentially allows you to glue a bunch of things like this together and do what you actually want to do. So let's take a look at ref2, which is what I call the refactory, and take a look at process.
00:47:43 - Anthony Campolo
Increase your font by just 1 or 2.
00:47:45 - Robert Balicki
Absolutely. I can.
00:47:47 - Anthony Campolo
That's great.
00:47:48 - Robert Balicki
I'm blind as a bat too, and I appreciate it larger than that even for myself. For example, we might call extraction consumer. Extraction consumer here is going to loop and it's going to find these refactors and then it's going to put them in a queue. Let me actually dive into what this actually looks like when I run it. So if we take a look at ghost tea. OK, so first thing I'm gonna do is I'm gonna clear the state out of this thingy and then we're gonna run it.
00:48:23 - Anthony Campolo
Your font on your terminal now.
00:48:24 - Robert Balicki
Yes, yes, yes, yes, yes, yes. OK, and now I'm gonna run it. So I'm just gonna invoke some sort of JavaScript and this happens to end up invoking the pipeline. But like there's like, you know, it's whatever, it's a CLI, it does some other stuff. And I point it to this, these refactors and this is literally how I use it at work and also how I'm going to demonstrate here. We're going to run it. What is this going to do? It's going to reset the queues. All this logging I'm just doing myself, it's just calling functions. Then Barnum queues them together. When it gets going, I'm not sure why it's taking slightly longer, it's going to start invoking LLMs. One of the things that it's going to do is, for example, Well, I'm just sort of very verbosely logging the heck out of all this stuff. And you'll see that like one of the things the LLM said is, hey, the refactor has been successfully applied. All 7 print line statements have been inserted and blah, blah, blah. Okay, cool. So it's actually doing that.
00:49:29 - Anthony Campolo
So it's logging back its own chat responses.
00:49:33 - Robert Balicki
Yeah, exactly. I just happened to do that. So yeah, nothing about this has really—
00:49:37 - Anthony Campolo
because if you, if you were vibe coding this project itself, you would want those logs to be fed back into your agent. That is creating the project itself.
00:49:44 - Robert Balicki
Yeah. So really all I'm doing is, let me see, it's called -p maybe. Is that what you're supposed to do with Claude? Is it Claude -p? Is that what we do? Yeah. Here we go.
00:49:55 - Anthony Campolo
For programmatic access. Yeah.
00:49:57 - Robert Balicki
Yeah. I'm literally just doing this. I'm writing some sort of file and then I'm executing that file and that file happens to contain Claude -p. Okay. When I execute this, I take a look at the, this and, you know, the output and then I stream it to standard out. So it's like nothing, not really anything special.
00:50:18 - Anthony Campolo
It's a CLI, like you built a CLI. Again, this is great. This is everything I'm building right now. The AI is exactly like what you're doing right now. It's exactly the type of crap I'm building, which is the CLI piping all this different stuff together, you know?
00:50:31 - Robert Balicki
Yep. OK, so I don't actually usually log this amount of verbosity. Because it's kind of ridiculous. But like, the point is it does a bunch of stuff. And then eventually, this will finish. This is sort of the extraction part. Wait, no, this is implement now. So now it finished extracting the refactor, and then it will implement it. And cool, it created a draft PR here. And let's take a look at this.
00:50:57 - Dev Agrawal
So can you explain what you mean? Sorry, go ahead.
00:51:00 - Anthony Campolo
I was gonna say, so does this have like a GitHub token? Or like you have it giving permissions? Like, how is it connecting to, um, or I guess Claude Code is just doing that.
00:51:09 - Robert Balicki
Uh, Claude Code is just— well, no, I'm just calling GitHub or gh pr create or whatever, essentially invoking the GitHub CLI also. Probably, probably. Yeah, yeah, great. Um, I say probably because again, everything is sort of bootstrapped together with me and the agent working together.
00:51:28 - Anthony Campolo
Yeah.
00:51:28 - Robert Balicki
Um, so excellent work. It created a perfect PR doing exactly what I wanted it to do. It's very, very impressive. Um, so actually, let's take a look at that before we continue on. Yeah, sorry.
00:51:39 - Dev Agrawal
Yeah, I was just wondering, what, what do you mean when you said extract or refactor before implementing it?
00:51:46 - Robert Balicki
Uh, sorry, that is— why is this not, um, uh, the— what I mean is that there's one process that loops over a bunch of input files. So we have these input files. There's only one here in this input. Normally I would have every single file in the repository. There's one process, one thing that loops over each of these files, analyzes them for potential refactors. So in this case, this is the refactor. I don't know how to do word wrap. No, it's not there. But like, you know, it It says add eprint line, and I basically send this to Claude, and then I have a whole bunch of stuff around it. So, for example, at work, I don't actually know what happens here. This was, this was Vibe adopted in order to work with Isograph. Like, at work, I have a bunch of stuff that it attempts to run. So, for example, make sure that TypeScript passes, make sure that lint passes, and so on and so forth. Um, and then there's other agents that will re-review it to make sure that nothing is in a broken state or we didn't do any— we didn't cut any corners and stuff like that. Um, but I'm being a little bit vague here, right? Because the point is like, it's a programming language to do whatever the hell you want, and I just happen to be doing that. But the— I think that the bigger point is like, what actually does it feel like to do this? Um, oh, okay, so let's see.
00:53:13 - Anthony Campolo
Reference. Also, I wasn't seeing these comments in the chat. I think we kind of answered this.
00:53:18 - Dev Agrawal
Does—
00:53:18 - Anthony Campolo
how do you actually drive the LLM? You're doing it via the CLI agent, not by the API. Yeah, I think you say it's called softwrap instead.
00:53:29 - Robert Balicki
Oh, I think I'm looking at— I might be looking at an old version. It's okay. Okay, softwrap. Thank you. Thank you. Oh, wonderful. I just started using Zed and GoCTUI on I figured it's time to upgrade. But yeah, essentially what I'm doing is this. When I run the CLI with process, right? Like, or when I run it, I'm running this and this calls run pipeline. I think this is outdated. It should just be .run. But I might be on an old version. I guess I probably just haven't pushed the latest.
00:54:03 - Anthony Campolo
The minutia is not super duper important.
00:54:05 - Robert Balicki
Fair, fair, fair, fair. Okay. So, Then this will run this thing and then it will run this thing, which is the second thing here. This one will run these in parallel. The extraction consumer and the implementation consumer we were just talking about earlier. In particular, the extraction consumer will loop. Deque a file from the queue. And if it finds a file, it will run this sum branch. If it doesn't, it will just sleep and then it will recur. But it probably shouldn't. Because if you're done with files, you're done with files. So I think that that's like, we probably don't need to have this outer loop. But this is a loop.
00:54:57 - Anthony Campolo
So I was going to ask about the loop. So because everyone's talking about loops right now in the AI world. So Is this leaning into that loop-based workflow or is this an incidental loop? This is not super important.
00:55:10 - Robert Balicki
I have opinions on how loops are done. Like loops are a very simple primitive and it's bananas to me that Claude is like, hey, you can run stuff in a loop and it's this amazing feature when we have programming languages that have loops. They have many types of loops. Um, they have loops with conditions. Um, it's just kind of weird that we're like doing that, that like loop and goal are these like amazing primitives. Like, this is silly. Like, programming languages are just better ways of expressing that. And if that allows you to corral the logic that the LLMs are doing into sort of like subparts and thus get more reliability and so on and so forth. They just don't need this stuff in LLMs. Anyway, I think that's my overall opinion here is that this is a better way to write things. What do we do? DQ file. Nice. GD does go to definition. That's not that bad. It's a handler like we just described, which is say it just does some arbitrary stuff. In this case, it probably looks at the list, like some folder of files and it uses that as a queue. And then if we find one, then yeah, you know, I'm pretty sure I have an updated version of all this stuff that looks better. And I— so this whole time I'm going to be trying not to talk about how I accidentally pulled the wrong version. No, it's fine. It's fine. It's fine. No, no, it's fine.
00:56:50 - Anthony Campolo
It's fine.
00:56:52 - Robert Balicki
OK, so And now we're just sort of like describing some sort of workflow. So we get this claimed file. It's a reference to essentially something that has this shape. We don't ever get this shape in JavaScript. This is like all on the Rust side, and the Rust side will call this kind of stuff and it will do this, and then eventually we'll loop over each of the refactors and we will essentially call this thing and this will— sorry, I'm going to make this slightly smaller because I'm very bothered by the— there we go. OK, at least it kind of looks a little nicer now. Is that still big enough for folks?
00:57:35 - Anthony Campolo
That's perfect.
00:57:36 - Dev Agrawal
Yeah, that's good.
00:57:36 - Robert Balicki
OK, cool.
00:57:37 - Anthony Campolo
Yeah.
00:57:38 - Robert Balicki
So it'll bind this and then what do we do? We'll call ExtractRefactors and ExtractRefactors. What does it do? Well, it reads the file. And then it calls Claude and it expects some sort of array value back. But that's not really what's important. What's important is that it— this thingy will make sure that you have that Claude returns this schema of some sort of JSON that returns this Zod— that upholds this Zod schema. And so, okay, so that's going to be something I refactor name, what locations change. A change summary, a motivation guarantee. I don't even know if like this is necessary. It's like maybe just these would be necessary, but whatever, it's all vibe coded and it works. That's the important part. And okay, references, find all references, G+Shift+A. Man, this is like weird. I'm still learning.
00:58:39 - Dev Agrawal
You can just have your VS Code keybinds inside if that's what you're more familiar with.
00:58:44 - Robert Balicki
Yes, I have, I have a lot of custom keybinds. OK, you know what, I, I'm already lost. OK, so we extract the refactors, right? And that gives us an array of items, right? And then we're going to call advance or finish. So this is another handler, and I think it seemed to have changed something or other somewhere, because now everything's— we just try undoing things.
00:59:09 - Anthony Campolo
Like it's a relatively small amount of code for the overall project.
00:59:18 - Robert Balicki
Yes. And the point is, okay, so I mean, I could continue running through this, but like, it's not really like particularly interesting code. It like does what you expect conceptually. We have a list of files, we have a list of refactors, we create a cross product of those. For each of those, we run an agent to say like, hey, please read this file and tell me whether this refactor exists or whether any number of these refactors exist in this file. If so, generate essentially a set of instructions and then somewhere later this will advance or finish, blah, blah, blah. And then we will get to— let me find.
00:59:59 - Anthony Campolo
Okay, so I'm totally with you on all of this. My one big question right now is you've mentioned that it looks at a single file for these refactors, that seems like it would be fairly limiting then in terms of the overall types of refactors you could do.
01:00:17 - Robert Balicki
It focuses on one refactor, one file at a time, but it's an agent. It can do sort of whatever it wants. So it will still change a bunch of files all at the same time. I maybe I even—
01:00:31 - Anthony Campolo
I guess I just look at it this way, like a common refactor you might see would be like an overly long file that needs to be broken up into like 5 modules, you know, something like that. So is that the type of thing it could do or that it just—
01:00:42 - Robert Balicki
Oh, it can.
01:00:43 - Dev Agrawal
It can.
01:00:43 - Robert Balicki
Yeah, yeah.
01:00:44 - Anthony Campolo
Okay, great. That was my big confused part here, but I think I was on it.
01:00:49 - Dev Agrawal
I think one other example to maybe put what— explain what maybe Anthony was trying to say is let's say I have a file where, and it imports like 3 other files, and to determine if a certain refactor is needed, I also need to go and look at those 3 other files. Um, but I might also have other— maybe I have other agents looking at those files as well. Uh, so, uh, I, I guess again, because you're just calling Claude here, you can tell— you can, uh, you just prompt Claude like and ask it like, hey, does this file need this refactor? And Claude is— yeah, Claude has the read tool, which means it's— it sees the file and then it can go ahead and read other files from the code base and it can easily figure out that, hey, this is needed or this is not needed, basically.
01:01:39 - Robert Balicki
Yes.
01:01:40 - Dev Agrawal
Was right.
01:01:41 - Anthony Campolo
Yeah. And then my follow-on question for that is that what are its heuristics in terms of what requires a refactor versus not? Because it's a slightly subjective thing.
01:01:53 - Dev Agrawal
Yeah, I think that those things would be like things that you explain in your refactoring description. Like these are the decisions.
01:02:01 - Anthony Campolo
Right, because you're starting by explaining what the refactor has to be in the first place, so it doesn't have to make that decision. Gotcha.
01:02:08 - Robert Balicki
Yeah, exactly. It's just like in practice, these extraction instructions that I have are sometimes really short, like the question mark, the 2 pipes to double question mark, pretty simple. It's like change it always, make sure it doesn't change any behavior. If the left side is a string that could be empty, like be careful, whatever, that kind of stuff. And other ones are like multiple pages of descriptions. So for example, I have some that essentially adopt the Relay Migration API, which is kind of akin to adopting Relay. And that's like, it's essay, multiple essays worth of like, here is the correct pattern. Here's the— and if I could break that up into smaller parts, I think it would've been worth it. But like right now, I think the best way to use Barnum in its current state ends up being a very detailed description of exactly the refactor that needs to happen. Yeah.
01:03:11 - Anthony Campolo
I'm also getting the sense that these refactors are not just simplifying your codebase. They are like, like you're talking about bringing on GraphQL versus not, which like, that's a different type of refactor from like, I'm trying to cut down on tech debt, you know?
01:03:24 - Robert Balicki
Yeah, exactly.
01:03:26 - Dev Agrawal
One example that I'm working on right now is like I'm trying to migrate a bunch of SolidJS projects to Solid 2.0, and there's like a bunch of RFCs.
01:03:35 - Anthony Campolo
This would be really good for that, actually.
01:03:38 - Dev Agrawal
Exactly. Yeah, yeah, because the, uh, some of them are like simple enough, like you just rename a function and it mostly does the same thing, uh, but some, like there might be some few cases where just renaming the function is not enough and you need to do additional things. And there's a few APIs that have completely changed. And so some of them have like a really complicated decision tree almost, and you have to look at a bunch of files. So obviously if I take all of these RFCs, dump it into a single Claude session and say, go and refactor this app, I don't expect that to work in a million years. Maybe Mythos can figure that out.
01:04:17 - Anthony Campolo
I tried that. You're correct. It didn't work.
01:04:21 - Dev Agrawal
Exactly. Yeah. But, um, what, and this is kind of what I eventually landed on was that, uh, take like a bunch of individual, uh, refactors that need to happen and kind of explain like different scenarios in detail and then split them across different Claude sessions or different like LLM sessions. Like, okay, I am going to run one session. That just goes and looks at all the create effect usage and just marks the ones that need to be migrated or that need to— like how they need to be migrated and then have a different session that then goes in again and actually does the refactors. There's a lot of different optimizations that you're doing. The first is that you're giving agents a very specific task, of like looking for one kind of refactor, one kind of pattern. And then the other thing is you're kind of splitting the concerns of identifying an implementation, because if you have the same, uh, the agent that goes and identifies refactors is gonna have a lot of files in its context that don't need to be refactored because it, it's like its job is to filter them out. So the agent that goes ahead and implements them It's not gonna have any of that in context. And it's gonna— and I think the thing that we know about LLMs is that the less context we use, the more performance or like the more correctness we are— we can get out of them. And obviously like cost, like instead of like 200,000 token sessions, you can be done like, You, you probably are gonna have like 100, 200 agent sessions, but if all of them are like within 50K tokens, then it's probably much cheaper than like letting, letting Claude go or keep going on a task and compacting and then go hits 200K again, compacts, hit 200K again, which is the worst way of looping, but that's the only way of looping that people have kind of been advertising so far because the people who are advertising looping are model providers.
01:06:35 - Robert Balicki
Yes, actually, that's, that's a really good point. I, I missed that earlier, uh, when I was talking about the litany of advantages, is that because you can essentially tailor the invocation of Claude to something very narrow, it's literally read this one file and tell me whether this applies and return something with the following JSON shape. Like, that's as like minimal— one, it's, it's very— it's a good use of an agent because, um, the agent will— code mods, stuff like that, kind of difficult to use for something more complicated. Okay, adding like an eprint line in the beginning of every function, like, yeah, we could do that with a code mod. Um, but like anything more complicated than this, then an agent is the perfect thing for it. But you also want to use it Uh, in as narrow and tailored way as possible.
01:07:26 - Anthony Campolo
Um, this is something Parasocial actually said a while back was, remember code mods? The whole industry had just died because of LLMs. Yeah, I remember code mods were a big thing for Redwood because we would always want to create a very smooth upgrade path. We would give all these code mods to upgrade your stuff, but it's just very complicated.
01:07:49 - Robert Balicki
Um, yeah, so to answer your question earlier, so we can basically do whatever we want. And if we wanted to just read the one file, well then, you know, just change the allowed tools that you pass in. And again, like, this is not some— this is not some magic API. It's just something I wrote which ends up invoking Claude, and you can sort of do it however you want. But what's nice about this extraction is it can't write any files. It can't do other stuff, at least potentially. I don't really know whether I think it will not do other stuff, at least hopefully. So that's kind of cool. Okay. So we extract the refactors and then what we do is we put them in another queue and then we call this implementation consumer. And that does sort of the, the reverse of that. It reads from that queue and implements and generates a PR. And that's sort of more of the same. And the— I think the more interesting thing to look at is actually just this babysit thing. So, for example, again, hoping that this is— yeah, this is good. At some point in time, we need to process one PR. Let me see where this is. How do I— yeah, okay, cool. And that Is cap— is that— that is called right after categorize PR. So categorize PR will— it's essentially a handler that returns some sort of— man, I don't know how to— I still don't know how to go to definition. It didn't work though. So we have this PR category schema, right? Okay, so it's— the PR is going to be categorized in one of 5 things. Bypass automation. Okay, if I need to force land it for some reason or it has been force landed, it needs to be retargeted because force landed. We've internally at Pinterest, we have some tags that you can use to force to skip CI. Okay, the point is like that, that's not, that doesn't matter.
01:09:56 - Dev Agrawal
Force.
01:09:57 - Robert Balicki
Yeah, exactly. Okay. These are the ones that are more applicable. Okay, so maybe this is a stack of PRs, and now this PR doesn't point at master, it points at a branch that has either landed or has been abandoned. So we need to retarget it, we need to rebase it and then change the PR target master. Needs rebase. Okay, there's more than one commit in this branch, and we need to rebase it. So the parent commit has landed and been deleted. Some sort of checks are failing, or it's ready to merge, or some sort of checks are still pending. So there's like canonical things to do, right? And what do we call it, categorize here. So what do we do when we categorize PR? Well, okay, I mean, it's the, it's the kind of thing that you would expect, it's just some JavaScript function that's in a handler. And it does whatever you want, you know, like, It does things and it returns values and checks, is it— is there conflicts, in which case it needs a rebase and whatever. The actual stuff here is not super interesting. What only matters is that— let me— if I can find it, hopefully, I don't really know how to— I guess I could look for references. Find all references. Oh no, it's create handler. I didn't want that. I wanted to categorize PR. Uh, yeah, okay, so here, um, and then we ultimately call process one PR, right?
01:11:32 - Dev Agrawal
So does categorize PR call, uh, uh, an agent at any point, or is it all just procedural code?
01:11:39 - Robert Balicki
Uh, I think that one probably doesn't invoke an agent, right?
01:11:44 - Anthony Campolo
Okay.
01:11:45 - Dev Agrawal
So it's also a good showcase of like a lot of times you can take a lot of work out of the agent's kind of plate.
01:11:53 - Anthony Campolo
Yes.
01:11:53 - Dev Agrawal
When you can just like, if it's simple enough and you can express it deterministically, then why the hell not? Like why would you give an agent a task?
01:12:02 - Anthony Campolo
So obviously you want to do that if you can.
01:12:07 - Robert Balicki
Yes. And then we process one PR. And this has this fix failing checks pipeline. And that does— that invokes an agent that will essentially look at what checks have failed, preload those into context, maybe, and then tell the agent, hey, here's what happened, please fix it. And then actually, we might as well Go to the definition of that. So what is this going to do? It's going to check out the branch, it's going to run fixes, and if it passed— okay, actually I do have a retry loop. I'm really on an old version of code right now for some reason. That's fine. This actually just loops several, some number of times and then retries. And what's nice about this is that there's actually— that's the kind of thing that you want control over. I limit the number of retry attempts to something like 3, because if they have failed 3 times in a row, it's probably something is not going to be solved by throwing more tokens at it. And the PR gets in this like sort of parked state where the agent just stops trying to fix it, and otherwise it will commit and push the branch. And that, as you might expect, just does a bunch of stuff in like, you know, sort of deterministic land. But yeah, the reason I wanted to talk about that, about like babysit, was exactly like Dev said, like, the more you can move into the deterministic world, the better. And also to just simply point out that categorizing a PR into those 5 things and handling each of those distinctly would be a nightmare to express in English. And on the other hand, it's actually quite nice and easy to express in what sort of amounts to a functional language. And that is a huge improvement. And then, but then you want somewhere inside of this to be able to invoke an LLM. So the moral of the story is that the outside should be a programming language and that invokes LLMs on the inside. Those should be as simple and as light as possible.
01:14:31 - Dev Agrawal
Yeah. Oftentimes with agents, there are conversations about inner loop and outer loop. What you're trying to say here is that a lot of times humans are the outer loop. If I'm using Codex or Claude directly, I am the outer loop every time the agent does something and stops. I have to look at it and then go and trigger another session, which like basically I am the bottleneck of how much work is happening, how many tokens I'm spending. Yeah, you're not— sorry if— yeah, but if that outer loop is something you can orchestrate, you can loop and you can keep like run over and over again. It gives you like a higher level primitive to define what kind of work you want to do. I think this is kind of what Boris was talking about when he said that he doesn't write prompts anymore, he writes loops. But I think what people miss is that like a part of the loop is writing the prompt itself. So writing a loop doesn't mean you're not writing prompts anymore. It's just that you're writing prompts in a very different way. Where they describe a very specific small amount, like a unit of work instead of like a high-level go and do this thing.
01:15:56 - Robert Balicki
Yes, I think that's exactly right. Like, yeah, it's like we want to describe these workflows. Describing them with English is not gonna get us to the reliability where you can actually remove humans. But ultimately what we're doing is describing these workflows. And I think that like people call that building loops, which is kind of silly. But yeah.
01:16:24 - Dev Agrawal
Yeah. I think, yeah, one thing that I would love to get maybe some of your thoughts on is like I saw somewhere a while ago that like agent orchestration is going to become like a concurrency problem. And I think this was kind of obvious in some of your— some of these workflows when there were— there was like a queue and you have to acquire a file. Because if you have a bunch of things running in parallel on the same code base, it's basically like running multi-threaded programs with shared memory. Like, there are a lot of similarities. This is why work trees are, are an important concept because It's basically like forking or forking memory so that different threads can— I actually don't know too much about concurrent, like, multithreaded programming and how that works. I'm more of a, like, just have every thread just have its own little piece of state and then serialize messages back and forth, like kind of the Erlang model. What I saw a decent bit of in Barnum was like, there's a queue and you have to acquire resources. And so how, like, what does that kind of look like? Like, how often are you doing work trees? Where are you implementing work trees? I think that also becomes like a thing, like how many work trees do you have at the same time for any project? Yeah.
01:17:56 - Robert Balicki
Yeah. So everything is sort of done in userland. Like Barnum provides some like low-level primitives, but like it doesn't have a strong opinion on, on this sort of things. However, you're right that like in practice you do want to— well, actually has a strong opinion that sort of everything happens maximally in parallel. And so that means that like if you are trying to do a bunch of refactors at once, well, each thing needs its own its own work tree. Or you need some way of limiting the number of refactors that are in flight at a time so that each one has a work tree that is available. And yeah, I guess I don't have like a super deep answer here. I mean, there are some stuff within Barnum of how we do like parallelism on the Rust side. But like, you're absolutely right that essentially Everything is a parallel— everything's ultimately a parallel problem. And I think the, the Barnum model makes sense here, which is sort of because everything is maximally parallel, what that means is that any work that is available to be done is being done. And it's up to you to have the chain— have the code that will essentially not proceed and do more work if there are not enough resources to do that work. So do you want to run like more than 5 Claude Codes, even if invoked with -p at once? Probably not. So there probably should be some sort of primitive that you have that like gives you access to a Claude Code and limits that to 5. And Barnum could do a little bit better there. Right now, you're— I'm sort of doing that in user land by essentially limiting their— limiting it to sort of 2 agents in parallel that generate the refactors and some larger number of agents that like implement them. But in practice, the bottleneck is not that. The bottleneck is like rebasing and landing and like our CI was not prepared, was not built to handle this sudden onslaught of hundreds and hundreds of PRs. So, I'm not super concerned about making Barnum as— like making this workflow as performant as it possibly could be in theory. So, and then there's another reason that I'm not like super concerned about that. I think that the parallelism is important. I think correctness is important. But in the end, like, what are we doing? We're invoking processes that invoke LLMs. And an LLM can take minutes at a time. So, like, if you have, like— so the important thing is running stuff in parallel. It's not like saving— it's not shaving milliseconds off of the invocation path. Yeah. I mean, there's cool stuff I do on the Rust side. But in the Barnum workflow land, it's sort of you just do whatever you want. And I happen to have some patterns that I think work well. But I don't have all of the answers quite figured out yet. Like, this works for what I'm doing with it at Pinterest, and I should probably make it more usable in like a variety of other work, a variety of other circumstances. But like, in the end, like, it works well for what I'm doing.
01:21:20 - Dev Agrawal
So yeah, makes sense. And you, you won't know what works, what patterns work for other use cases until people actually start using it for those use cases. So low-level primitives make sense until patterns emerge and you find certain things that can be built into the language that, okay, now here's a way to do the— to do something that everyone wants to do in terms of—
01:21:43 - Anthony Campolo
Yes, I was gonna ask you where the framework was going to go, so you guys literally just answered that before I could even ask it. So That's great. I do want to make sure we have time for some of the Isograph thing. So I had just a couple kind of wrap-up questions before we go on to that. It seems like this would be fairly portable if you were to do like Codex programmatically, OpenCode programmatically. There's nothing really about Claude Code that this is tied to right now. Is that correct?
01:22:14 - Robert Balicki
Exactly. Exactly.
01:22:17 - Anthony Campolo
Cool. That's— then that's dope. So do you Have you experimented with those, or for you Claude Code is working just fine? You, you don't feel the need to, to try the other ones?
01:22:26 - Robert Balicki
Claude Code is working fine. Yeah, I've mostly— once I got it working with Claude, I wasn't super interested in, for just for fun, figuring out how to make it work with Codex, but it should just be the same, right? It's just invoking some process, invoking it, and then enforcing that the LLM responds with some JSON that we can extract from some long stream of text. Um, yeah, and also, like, I think to add on to where's, where's Barnum going, um, there's a few other things that I wanted to do that I want to achieve with Barnum. For one thing, like, and this actually gets at why I think, um, code mode is not ideal, is not ideally designed. The way code mode works is that if something crashes and you re-invoke it, then you replay everything. You have this one file that, let's say, loops over 1,000 files, does some work, and then processes it. We did 999 files and we failed to process the 1,000th file. That is going to replay everything. It's going to re-invoke the LLMs, except it's not actually going to invoke the LLM. It's just going to pass the data in and it's going to pass it back, and then the JavaScript is going to do extra work and it's going to do other stuff. Which is good, but it— and that gives us the advantage that the code that you write in code mode just looks like plain old JavaScript. But it has the disadvantage that everything that touches the outside world has to go through an LLM. Earlier we were talking about listing all the files in your repo. If that goes through an LLM, man, you're just burning context. That costs money to list all the files in your repo. In order to, I don't know, find all the JavaScript files. You can't do that with code mode, which is bonkers to me. Secondly, if you are doing a lot of work on the JavaScript side, for example, you list all the files in your repo and you filter them down, every time you replay, you have to redo that work. That also means that the JavaScript has to be not idempotent, it has to be pure. So no access, no. So obviously you can't access the file system. That's why I skipped the LM. 2, you can't use stuff like math.random. You can't use stuff like new Date and so on and so forth. But like, if you're using some external library and it happens to use math.random as part of one of its algorithms, or it does some logging, like it's going to just randomly break and that's not good. So code mode, like it makes these decisions which are I think, make it easier to adopt at the expense of being the correct model. Okay. So what does that have to do with the future of Isograph— of Barnum? Excuse me. Isograph is my other project that for folks on the call, we're about to get into.
01:25:15 - Anthony Campolo
Yeah.
01:25:17 - Robert Balicki
This thing, it describes a workflow, but it is not actually executed on the JavaScript side. So it is a data structure that is introspectable, and it will be executed by TypeScript. But that also means that every single stage in this execution, we know how— I mean, we know what it is. So if you stop in the middle of a Barnum execution and then you restart, we have all of the information to restart from exactly where you left off. Maybe this assess worthiness was in flight, okay, we'll have to re-invoke that. But that's small and hopefully contained. It's not like this— it's not necessarily a massive thing. And you can re-invoke— if you re-invoke it, hopefully it does the correct thing. I don't have this in this version of Barnum. Earlier versions had this, and then I did a refactor, and then I didn't choose to bring it back quite yet. But the idea is that you want this to be automatically serializable, automatically resumable, And in that sense, yeah, okay, so that's one thing that I want that I think is going to be— that is pretty important to actually being able to use this for a larger variety of use cases.
01:26:33 - Dev Agrawal
One quick thing I would add there is that crashing, crashing, yeah, that's definitely a big use case, but a bigger use case for this sort of like pause and resume is just human in the loop because sometimes a workflow like halfway in needs an approval from a user. And if I'm away from my computer, I might not see that for like another day or so. So the next day I come back and I approve it, it should— I want it to start from right there.
01:26:58 - Robert Balicki
Yes, that's a good point. And right now you would do that by just having a loop that sort of pulls, which is fine, but also maybe not the most ideal way to do this compared to just like short-circuiting and sort of waiting for some input.
01:27:15 - Dev Agrawal
Yeah.
01:27:16 - Robert Balicki
Then the other thing is that all of these, I didn't actually mention this earlier, but the key thing about these handlers is that they're exported JavaScript functions and they each run in their own process, in an isolated process. That's nice because now they can't interact with each other. Everything is as enclosed as possible. That specifically means for this case, well, we can just re-invoke that function if we happen to crash while assess worthiness is— Jesus, I have to figure out how to disable that garbage. And in particular, well, the way we invoke those is from the Rust side. We invoke a process that calls Node or calls pnpm or calls whatever and executes that little— that JavaScript. And okay, that sounds— what's special about JavaScript? Nothing. So we could also allow you to invoke Python, Bash, whatever custom runtime for invoking Claude or something like that, sort of more directly as part of this. Yes, exactly. Yeah. So in particular, it sounds super useful for doing stuff like orchestrating your Python, like your ML workflow. In a way that is accessible to folks that are more used to JavaScript. Or I mean, there's also nothing special about this. Like all we're doing here is some light transformation, generating an AST and then serializing that and sending it to the Rust side where the real work happens. Like this is actually super simple. Let's take a look. Do I have Barnum? If I have, let's see, Barnum here. Let's see if we have this constant. Oh, that's, that's not a JavaScript file. Those are Markdown. Okay, I think I can go to definition here.
01:29:27 - Anthony Campolo
Yeah.
01:29:28 - Dev Agrawal
See?
01:29:28 - Robert Balicki
It's really simple. It's just—
01:29:29 - Anthony Campolo
Zed dev?
01:29:33 - Robert Balicki
I'm very new to Zed, as you can tell. So, this like— this constant function, which just happens to be one of the things you can do, it just returns some AST thing. And then that gets generated into some sort of tree that gets composed and whatever and serialized and sent to the Rust side. Nothing about this is specific to JavaScript. We could just as easily have a DSL in Python, a DSL in whatever your language of choice is, in Ruby even, and it would work just as well.
01:30:07 - Dev Agrawal
Or a completely custom DSL.
01:30:10 - Robert Balicki
Yep. Actually, I do want custom stuff. That's one of the things I do want ultimately. But you got to pick your battles.
01:30:23 - Anthony Campolo
Use your technical innovation tokens or points. Someone had a term for that. You should only like bet on one big new piece of tech per project. If you bet on too many, like you use your innovation tokens, I think is what they called it.
01:30:39 - Dev Agrawal
Yeah, I never followed that. I always like, every time I have a new project, I pick like 3 to 4 new things that I want to play around with.
01:30:46 - Anthony Campolo
And I, we got a question here, and then after that, do you want to go to the Isograph stuff to keep us on track? So I don't get why we need AST step. Why do we need Rust? It could just be running inside ts-node. And while you do that, I'm gonna just use the bathroom real quick.
01:31:01 - Robert Balicki
So yeah, um, so the reason we want to use Rust is that Rust is— it makes it easy to actually write code that is more correct and more trustworthy. There's 2 aspects to this. On the one hand, there is this layer here where this is invoked to construct the AST, and then these handlers are run in separate processes, and then there's something in between. We could in theory, just do this entirely in JavaScript. But I think if we did this entirely in JavaScript without at least having an intervening layer that called a bunch of processes in their own— a bunch of like handlers in their own process, well then everything could sort of reach out and have whatever side effects you want— they want. And if you have whatever side effects you want, then you kind of struggle to have the guarantees that everything is actually correct in the way that you want. But that being said, obviously Rust and JavaScript and everything else, it's Turing complete and you can sort of do whatever you want in whatever language. So I think the most important part here is that there is— one, it's— I like Rust. I think it's a great language. And 2, the disconnect between the runtime and the invoked handlers and the isolation of the handlers is somewhat important. Um, at least in theory that's important.
01:32:45 - Dev Agrawal
Yeah, so yeah, yeah, I think, um, that makes sense. I think the focus of the question was probably not on like why Rust, but more on like why does there need to be an intermediate step? Like why do you need a step that constructs the AST, then another, another step that interprets or executes that AST and then calls out to these external processes rather than the workflow itself being a Rust or a TypeScript function that gets executed dynamically. And yeah, so you're saying that the isolation between the orchestration and the handling is important. And I think one of the reasons why that's important is the pause and resume thing that you mentioned earlier. You just cannot do that in vanilla TypeScript unless you introduce, I guess, more syntax of some sort or use workflow, use step that Vercel has. Then you add a custom compiler there. But you can't— yeah. Maybe that's one of the reasons. But yeah, that's—
01:33:56 - Robert Balicki
yeah, that's right. I think the isolation is not easy to— well, yeah, you need the isolation for exactly for the resumability and just for ease of reasoning. Because, yeah, I mean, that's basically for that. I mean, you also get some other benefits from the isolation, like you can have higher-order functions really easily. Retry this thing 3 times. That's a really easy thing to do and it wraps whatever you want and it doesn't necessarily know anything about what's happening on the inside. It's kind of nice. But yeah, I mean, the real answer is like, I like Rust. I think that this is a— it's a pleasant way for me to ship a lot of code in a small amount of time. With respect to Temporal, this is also really similar to Temporal. I think it has better— some better properties than Temporal. For example, I think composability in Temporal is not very good. I think it's really hard to do stuff like, um, have higher-order functions, retry things, and have the actual behavior that I want. So in particular, this loop thing here, this gives us a function recur, and whenever recur is encountered, well, it has a return type of never. Um, whenever it's executed, well, then we tear down this AST and we re-execute this AST in this case. And so we dequeue another file at that point in time. So recur also occurs here. And so if I, for example, like move this up, well then, I mean, it still continues to work and I can pass recur to some sort of like wait 5, you know, sleep then, right? Recur. And that will just work like recur is just a value that gets passed somewhere else. And so on and so forth. All these kinds of things, like, I can do because I was— I made a very specific, deliberate choice. And I think that many of these other libraries, Temporal, they have more users, because what they're doing is more approachable. But it limits the ceiling. And I'm not necessarily building a business out of Barnum. So I'm okay with—
01:36:11 - Anthony Campolo
like a whole platform. Like, it's not just a library, right?
01:36:14 - Robert Balicki
Yeah, yeah. And that's probably where the money's at. It's like doing the, doing the, um, the replayable work. Um, I don't even remember what the term is. Uh, durable execution. Yeah, yeah.
01:36:29 - Anthony Campolo
I only know about this because Swyx worked at this company for like a year, and so I used to always listen to every interview Swyx would ever do. So I heard her talk about Temporal for like a whole year, and I'm like, this is solving a problem I do not have. Yeah.
01:36:46 - Robert Balicki
Um, but yeah, so that's Bardom. I do hope folks try it.
01:36:50 - Anthony Campolo
This is very, very cool. Um, I will definitely try it out, um, because I got lots of refactors I always want to do. Um, let's get into Isograph a little bit. Um, I looked at the docs and I'm like, I see GraphQL queries and it's being fed into some sort of React-like component syntax. And that's pretty much all I need. Like, I'm sold.
01:37:14 - Robert Balicki
Um, nice.
01:37:15 - Anthony Campolo
There's nothing about this that would confuse me because this to me is just how all programs should be written forever.
01:37:23 - Dev Agrawal
Yeah.
01:37:24 - Robert Balicki
Um, so Isograph, I guess, like, let's, let's take a quick detour, talk about GraphQL. What is nice about GraphQL? What's nice about GraphQL is that you have a few things. Is that you have fragment-like compose— you have composability. So, you might have like a homepage or a user detail fragment. And in there, you might spread the user avatar. And that means that whenever you— the user avatar fragment. And whenever you modify the fragment, for example, you might add the email or the image URL or the ID or whatever. Well, that gets sort of automatically added to the parent fragment, all of the parent fragments. And ultimately that bubbles up to a bunch of queries. So you essentially are able to define— and each of these fragments is associated with one component in your codebase ideally. And that means that when you modify one component, you can modify its fragment to have exactly the fields that you need locally and no more and no less. And that automatically bubbles up to whatever queries, and these queries will fetch exactly the data that happens to be needed by the current configuration of your page. You could reason locally and everything ends up being correct. Now, if you have something else, let's say TanStack and REST, well, you have some bad options. For example, you're modifying some deeply nested component. If you add a field, it's not so bad. Now you go find whatever queries, and add like the email field to those queries, right? And maybe that's like selecting it from REST. Maybe the backend now has to start returning email. I don't know exactly what it is, but you make some change and you start getting email. But now if you stop using that email field, well, that's tough because now you have to go to these queries, remove the email field. Maybe you have to kind of do some research to determine is any other subcomponent in the tree using that email field? And the answer is nobody does that amount of research. Search. And so queries get bloated. They get filled with fields that are not used. Okay. Most people will look at that description and say that sounds theoretical. That sounds like a big company problem. I only pass data down a couple of layers. And that's also a problem. You have like limited the amount of complexity that your app is able to absorb. And you are prevented from breaking up your components into smaller and smaller parts, even if that's the correct thing for your particular use case because you need to be able to reason about an entire tree in your head at once. So once again, all non-relay, non-Isograph frameworks make the wrong tradeoff. They sort of limit your complexity and make it so that if you try to do the right thing, you are overwhelmed.
01:40:20 - Anthony Campolo
Real quick, what would you say to someone who would say that's all great, But I already know how to do REST and the overhead I'm gonna get from GraphQL is not worth solving that problem for me.
01:40:33 - Robert Balicki
I, I think it depends on the situation. You, for example, might come back from a week-long vacation and you come back to your codebase and in the meantime, dev has made so many changes to it and now you don't know what it's like. The reason that worked before was because you or some other code owner understood everything. But now changes have been made.
01:40:54 - Anthony Campolo
Scratch yourself. Yeah.
01:40:55 - Robert Balicki
Yes, yes, exactly. And the same thing could be said about Git. Okay, I'm the only person using, modifying this repository. I don't need these fancy branches. Like, what's the point of that? You know, like, but you still want to use Git for— and the reason is that even though you are not multiple people, you are multiple people, one person in the morning, and then you've forgotten what you're doing on the evening and then whatever. And furthermore, the developer experience cost of using Git is so low that it's worth it even on single-person projects that have a linear history. Same thing is true for GraphQL. You want to be able to reason locally when you modify components and not load the, the entire code, all the code into your head. Okay. Loading code into your head. That's using context. Okay. Context is expensive. If you are an LLM trying to make changes, the less you have to reason about the entirety of the codebase, the cheaper and more reliable and better. So there's, there's a lot of reasons why you want to use GraphQL.
01:42:04 - Anthony Campolo
And LLMs are where they work better when they have a schema to go along with. There's, and there's some sort of thing that can guarantee, you know, different types and how it can understand the whole architecture and how it all fits together. So, like if you could point at an RFC or something or like a spec like GraphQL, then there's a whole set of things it already can figure out how to do within that world of that convention.
01:42:29 - Robert Balicki
Yes. Okay. So, now we're gonna jump a couple steps forward. Why Isograph?
01:42:36 - Dev Agrawal
Okay.
01:42:36 - Robert Balicki
So, with GraphQL, you have fragments. And each fragment is associated with a specific function. So you have like this user detail avatar and it reads the fields that are needed by the user detail component. And that has to be a one-to-one connection because otherwise maybe you're over-fetching or you're under-fetching. So you don't want to reuse fragments. Um, just despite what Apollo's docs will have led you to believe and so on. You don't want to reuse queries. You want to just have exactly the one query per screen. And it composes correctly based on all of the things that are in the thing, in the screen.
01:43:15 - Anthony Campolo
Okay.
01:43:16 - Robert Balicki
So if the user detail component, I'm, by the way, just like on a random page here, I wasn't actually thinking about whether this is the correct, maybe quick start guide has a simple example. Yeah.
01:43:28 - Anthony Campolo
You should just pull up, like, I mean, even better would be just like the graphql.com fragment, like a page. So people can get a sense for 'Cause if, if you don't know GraphQL, like a fragment itself is a very specific kind of part of it because you have, you know, queries and mutations. But fragments is kind of how you can compose different GraphQL queries together, right?
01:43:55 - Robert Balicki
Yes, exactly. So, there we go. We'll do this repository link.
01:44:03 - Anthony Campolo
Okay.
01:44:03 - Robert Balicki
So, in particular, maybe you have a— this is actually in our, like, internal GitHub demo on Isograph. If you have a component, a repository link component, this function right here that happens to read some sort of— essentially, you can think of this as a fragment for now. So, there are some fields on repository, which is a type in your GraphQL schema. You might read the name, ID, owner, login, whatever. Well, there's a one-to-one correspondence between the fragment and the component that uses that data.
01:44:39 - Anthony Campolo
Right. Yeah. Because you're pulling out these specific things with the GraphQL query. Each of them is going usually to some sort of like HTML fragment to have it composed with a component. That's why it fits so well together with something like React. Yes.
01:44:53 - Robert Balicki
Yes. Now, With every framework except for Isograph and to some extent Houdini, which is another great framework, there— that component, the fact that the, um, the fact that one function reads a particular fragment is not known at compile time and you can't take advantage of that. But on the other hand, with, uh, Isograph, we know the fact that this repository link component reads this data. This is exactly one thing for the, for this, for our, for our purposes. So in particular, if you search for repository link, and I don't have the, the language server installed, I actually haven't done anything with Isograph on this new computer yet. You'll notice here that we have this parent. I don't know what this parent is, but it's some sort of GraphQL field. And we select the repository link on it directly. Now, the GraphQL schema does not have this repository link, um, as part— as a field on it. Um, but because we defined this repository link here, uh, or here rather, um, we can now just directly select it. So now conceptually, what are you doing? You're starting with the homepage. You're selecting the body. The body might have a current blog post. The blog post has a blog header, and you're just selecting all these components through the fields. And you just receive them and they're already pre-bound to the data that they end up using. Here, this repository link, what do we do with it? Again, forgive the syntax errors because I guess I haven't run— I need to run the compiler in here because it generates a bunch of files. In this repository link, in here, parent.repositoryLink, that's a component. Um, that happens to know about all the fields that it selects. And so here, what if we change repository, repository link to select some other fields? Well, nothing here changes. We're not passing any data down, but it happens to be— it happens to know that it's closed, that closes over those fields. Okay. We do pass setRoute. That's not part of the graph data. That's just a regular prop that we define here. And so that's nice. That means that you can essentially define your entire app as like a set of nested components. And each of these components close over the data that they happen to use. And this, just like with GraphQL, generates a query for all the fields that are needed by a given page. So let's just say, let's just search for queried text. Oh, so on our Pokémon demo, like this generates this query. And this is sort of what is executed.
01:47:44 - Anthony Campolo
Pokémon, which includes the form, key, number, species, and the sprite image.
01:47:50 - Robert Balicki
Yeah. And yeah. So this is like the query that is actually executed at compile time. And sorry, not actually executed when you run the page. This will get all the data.
01:48:05 - Anthony Campolo
Throws the string over to the GraphQL endpoint.
01:48:07 - Robert Balicki
Exactly. And then this at runtime will— well, basically we generate a bunch of files which you don't need to look at, but they're just these JSON things. Let me find a slightly better one. Yeah, cool. It's a bunch of JSON things. It's this AST. And using this AST, what we're going to do is when we read this pet updater— I know it's small— when you read this pet updater, component, it will use this AST to read out the fields that it knows came back from the network response and render the components that we're talking about here. So, okay, so that's a lot of like technical description. It's not super— let me, let me actually talk about why this matters. Well, one, there's, there's no way to mess this up. You can make whatever changes you want to repository link and nothing has to change anywhere else. You don't have to reason globally. So that means you or an intern or Opus— I mean, I don't know, Haiku can reason about these. And like, you don't have to be that smart. You can just make the changes and everything just continues to work. There's just like so little boilerplate compared to any other framework. Secondly, there's some advantages to this— to these 2 being associated at build time. So, for example, if you defer the JavaScript— sorry, if you defer the data for some subpart of your tree, like let's say you have— you fetch the blog post and it takes a long time to fetch the comments section. So, you defer that. GraphQL has a facility for essentially fetching that as essentially a follow-up network response. In GraphQL and Relay and every other framework, like if you're going to defer some data like that, well then you need to manually also probably asynchronously load the JavaScript for that component's thing. But because we know at build time that the component JavaScript and its data are both— well, there's one place where you can defer them both. And thus, if you search for something like @loadable lazy load artifact true, if you do this— whoa, what just happened? I want to do this. This is like a bunch of tests. But if you happen to do this, well, then this is a broken test.
01:50:44 - Anthony Campolo
For—
01:50:44 - Robert Balicki
because it's showing some sort of broken state on purpose. This image display wrapper here, this image display, the data for that image display component and the JavaScript will be asynchronously loaded when we render this component. So that's like one of the benefits of, of being able to connect these. But there are more benefits. There are lots of benefits. One of the other benefits is like, imagine your large company codebase, how many user avatar components do you have? Probably like 50 gajillion. And the reason for that is because it's like not really easily discoverable. And so you have so many duplicate things. But on the other hand, here, if you are on a user and you start typing .avatar or something, it'll just suggest— it'll suggest that for you if we have the— if we have the language server installed, which is not installed. So therefore it will cut down on essentially duplicate components. And instead, you will be softly pushed into having the one right user avatar component that actually works for, you know, sort of all the use cases. Uh, yeah, that's the spiel.
01:51:52 - Anthony Campolo
Thanks. Yeah, cool. Things like @loadable, those are directives, right?
01:51:59 - Robert Balicki
Uh, it looks like a directive, but it's really an Isograph thing.
01:52:02 - Anthony Campolo
It's really Isograph thing. Okay. Yeah, that was the the thing that mainly makes it different from just pure GraphQL. Because you're not just sticking to the spec, you're building things into it. They're GraphQL-esque queries, but they're not exactly the same. Yes.
01:52:21 - Dev Agrawal
Or at least it's like a superset, like all the GraphQL behavior with some added syntax. Kind of like TypeScript as with JavaScript?
01:52:32 - Robert Balicki
Yes. So for example, this blog item here, like this image display wrapper, is it— it's a field that we're selecting on image. But like type image here, it doesn't have that field. So we're like, we're augmenting the schema with a bunch of other stuff, right?
01:52:51 - Anthony Campolo
Yeah.
01:52:51 - Robert Balicki
Yeah.
01:52:52 - Dev Agrawal
And the things you add to the schema, that's— it's not just data, it's UI essentially though. So when you fetch, when you write, or when you execute a GraphQL query and you select those fields, you don't just get JSON, you get a React component that you can just return from your— from the React component that fetched that data, and you'll just— you'll have the UI for it. Yes, which is pretty cool.
01:53:18 - Anthony Campolo
Yeah.
01:53:19 - Robert Balicki
Um, so this might get a little bit in the weeds Okay. But one of the things that you might notice about Graph— one of the things about GraphQL is that in theory, you should not be removing fields from the schema. You should only be doing forward— you should not be doing backwards incompatible changes, of which removing a field is one. Now, you also have an issue where let's say you have a user and then you have this user's favorite restaurants, right? Okay. So the way you would define that as a field called favorite restaurants on the user.
01:53:51 - Dev Agrawal
Okay.
01:53:51 - Robert Balicki
Right. Oh, well, what if we have like user's favorite restaurants in a given city? Okay. So now we have user and then we have city and then we have favorite restaurants on that like weird combination of like stuff. Right. But like, okay, so maybe this is hometown, right? User hometown favorite restaurants. Right. If you look at that, why are we doing this? Well, the answer is that if we were to fetch the user and fetch their hometown and then fetch the restaurants, that would be a network waterfall. GraphQL's raison d'être is to avoid network waterfalls. You end up structuring this as part of the GraphQL schema. You have the user and then their hometown and then favorite restaurants in hometown on user. Okay.
01:54:41 - Dev Agrawal
Right.
01:54:41 - Robert Balicki
But like, that's also really awkward because, well, now it's not really favorite restaurants on a hometown, on a town. It's just, it's the users. It's some tuple of the user plus the hometown and then favorite restaurants on that.
01:54:55 - Anthony Campolo
You take on a lot of complexity into the schema for the sake of the simplicity of the query.
01:55:00 - Robert Balicki
Yes. And for the performance of it. Now, what exact, why do we want that? And the reason is that some particular version of some particular product wanted to show you the favorite restaurants in your hometown because it's hometown celebration month on Yelp.com, you know. And okay, only web does that because web iterates faster than iOS and Android or something like that. Who cares? Um, but this extra cruft gets added to your schema and it's one visible to Android and iOS. 2, it's useless on their thing. And 3, it is specifically serving the specific needs of a specific UI. Okay. How do we add fields in a particular repository such that it is only accessible in a particular piece of UI? Well, that's exactly what this is. This repository link does not get added to the schema in the abstract. It gets— it's visible as part of this Isograph project. And if this Isograph project no longer wants to use the repository link, well, then it kind of disappears. Next version doesn't have it.
01:56:08 - Dev Agrawal
Okay.
01:56:08 - Robert Balicki
Now this executes on the client. Okay. Repository link is a component. Maybe it's not the best example, but I think we have something like formatted date. Okay. Just imagine we have a formatted date. It somewhere exists. I just don't know what it's called.
01:56:24 - Anthony Campolo
Takes a JavaScript date, turns into, you know, yyyy-yyyy- Exactly.
01:56:29 - Robert Balicki
Yeah.
01:56:30 - Anthony Campolo
Yeah. Okay.
01:56:32 - Robert Balicki
It's nice to define that in as if it was a client field, just like these. But it would also be nice to execute that on the server. And so the next thing that Isograph will be doing, I mean, in theory, I mean, there's too many things for me to do with my limited time. But one of the things I want to do with Isograph is to allow you to move the execution of this thing onto the server. So now you can do exactly what we just discussed, the favorite restaurants in your hometown. You can define that localized to a specific project. It can execute on the server for performance, and your Android iOS teams are none the wiser. And then when you modify things, well, whatever, it changes. The next version has a different version of hometown favorites. You know?
01:57:26 - Dev Agrawal
Yeah.
01:57:26 - Robert Balicki
So, that's the idea. And the net effect of all of this is that your app is a tree of— is like this DAG of stuff that needs to happen. And Isograph is, I think, a pretty good way of expressing this sort of DAG-like, tree-like workflow. And if you have this tree, you sort of can look at it in multiple different ways. One way is like some work gets hoisted to the server. Okay. That's kind of like React Server Components. It's just basically what I described, except React Server Components has a bunch of limitations that this gets to avoid. It has another benefit, which is that it's— there's a big company behind it. So, you know, there's tradeoffs. But in theory, this is a better model.
01:58:20 - Anthony Campolo
You can also do things like give API keys or stuff. Like, you can do queries that can do other stuff if you're running it on the server. Because this was the thing that when I worked at my GraphQL company, StepZen, it was a hosted GraphQL endpoint, but it would be locked down from the top. So, you'd have to run your GraphQL queries in like a serverless function. And so that would kind of push you to doing it on the server. But then you would think a lot about your actual query and then get the data you need. And like you say, then you get exactly what you want for each page. Yes.
01:58:53 - Robert Balicki
Yeah, that's exactly right. Like you have— like you can— there's a lot of stuff you can execute on the server, maybe secrets, maybe you want stuff moved up there for performance, maybe you have— and also maybe the backend thing is written in a different language. In this case, what are we doing here when we reference this image display? We're saying from the perspective of this image display wrapper, well, this is loadable. Let's just talk about a simpler example. This image display wrapper here, we don't know anything about it. All we know is that there's some function that has some return value. Well, whatever, we have a return value. Happens to be a component, but let's just say it's a string. We don't care how that string was calculated. We don't care where it was calculated. All we're doing is we're saying we want this particular string. What if that function, what if that image display wrapper was written in Python? Well, has to execute on the server. The server is the only one that knows how to execute Python. But we could now intersperse again because of that isolation that we talked about earlier, Um, a lot of, a lot of work in sort of a tree-like thing. And yeah, anyway, so I'm gonna leave it there that there's a lot in common between both of the projects.
02:00:15 - Dev Agrawal
Yeah, no, that's right, definitely.
02:00:17 - Anthony Campolo
And this is being used at Pinterest right now, you were saying?
02:00:21 - Robert Balicki
Uh, no, no. Um, there's a, there's a startup using Isograph. Barnum, I'm using Barnum very extensively at Pinterest to ship a very large number of PRs. And I've, some other folks at Pinterest and elsewhere have used Barnum, but like, I haven't really focused yet on the, on the marketing. I haven't really strongly focused on marketing it yet. So, okay.
02:00:51 - Anthony Campolo
Uh, this company that is called, uh, also one of you two right now, there's a like radio bleeding through or something.
02:00:59 - Robert Balicki
You gotta go ahead and guess that that's not me.
02:01:01 - Anthony Campolo
Yes. Yeah.
02:01:04 - Robert Balicki
Uh, Bolt Foundry. There we go.
02:01:07 - Anthony Campolo
This company, I've heard of Bolt Foundry.
02:01:10 - Robert Balicki
What? You know them? That's so cool.
02:01:12 - Anthony Campolo
They, they didn't— okay, hold on. Bolt Foundry hosted GraphQL Texas, didn't they?
02:01:19 - Robert Balicki
Not sure. I— they're based in New York and Utah as far as I know. But let's see, there's, uh, maybe for all I know.
02:01:29 - Anthony Campolo
Yeah, they, they hosted, they hosted a GraphQL meetup that I, I did. It may not have been because I did a bunch, but that is why I've heard of Bolt Foundry.
02:01:39 - Robert Balicki
So yeah, yeah, um, yeah, they're very happy users of Isograph. And, um, yeah, there are so many interesting things about GraphQL that I would—
02:01:54 - Dev Agrawal
oh, sorry, not GraphQL, uh, GraphQL as well, but Isograph specifically— that I feel like I want— I could talk about forever.
02:02:04 - Anthony Campolo
I, I think the way that if you want to come back for another episode, Robert, that we could do all on Isograph, that would be great. We still have more time. But, um, just saying, I want to throw that invitation out there.
02:02:14 - Robert Balicki
Oh yeah, we'd definitely be happy to.
02:02:16 - Anthony Campolo
Yeah, yeah, go ahead, Dev.
02:02:19 - Dev Agrawal
Yeah, I mean, in, in specific, I think the way that, uh, the way that you decide to compose queries and components together, that, that, uh, that feels very nice, that there kind of eliminates so much of boilerplate. Like, it's not a framework that comes with a bunch of hooks that you need to learn how to use. Uh, or like custom components, anything like that. Uh, am I still okay? Yeah, yeah, you're still here.
02:02:46 - Robert Balicki
Yeah.
02:02:47 - Dev Agrawal
Um, yeah, and, uh, so, okay, one, one thing that I was wondering is, is there— you mentioned that there's a build step, um, that kind of like goes through your routes and compiles together like one giant query that can fetch all the data. For that page. I'm guessing that compiler like literally goes through your React components to look at— like, does it have to look through your React code to see what components you're rendering or just this query?
02:03:19 - Robert Balicki
No, it, it's pretty dumb in the sense that it looks for Isograph literals and these Isograph literals, um, are matched with a regex. So it looks for literally exactly this and then attempts to process them. And it also makes sure that they are— that they— well, it checks like a very few small amount of other things. Um, and so it's basically looking for this pattern here. Um, but it doesn't know anything about JavaScript. And then there are lint rules. So these lint rules will enforce that this is— well, are there lint rules? No, there are no lint rules. No, there are not. There should be. Anyway, we also enforce that this is exported and so on and so forth. There are limited things that we do enforce to ensure that this is done correctly. But in terms of understanding JavaScript, not at all.
02:04:20 - Anthony Campolo
Got it.
02:04:20 - Robert Balicki
OK. Yeah. You could, for example, fool this by doing something like this. I mean, that actually is not a multiline.
02:04:30 - Anthony Campolo
Okay.
02:04:30 - Robert Balicki
Whatever. JavaScript doesn't have multiline things. But like, you know, whatever.
02:04:38 - Dev Agrawal
Yeah. So, it sounds like something that could— like that might not need a build step. Or am I off here?
02:04:44 - Robert Balicki
Oh, no. It needs a build step. It will— I think I will not be able to find this because it's Yeah, it's not in whatever Z marketplace there is. Does not include the extension. TIL, we should publish it a bunch of other places.
02:04:59 - Anthony Campolo
Not surprising to me. Yeah.
02:05:01 - Robert Balicki
That's one of the things that the patients have said. Yeah. This is converted into a bunch of files. So, in particular, that resolver reader that we talked about. So, it's gonna have this author and title and content fields and so on. And this blog item more detail is on there as well. It will also generate a— this param type here. So, basically, when you add and remove fields from this, this gets modified. And so, now if you hover on this blog item thing here, you know that you have these types here. And that can't be inferred from the JavaScript. I mean, from the— that can't be inferred by TypeScript. I'm not gonna try to go down that route. I don't think it's a good route.
02:05:48 - Dev Agrawal
Right.
02:05:49 - Robert Balicki
It's technically impressive to do that. But like, no.
02:05:53 - Dev Agrawal
Like building Doom in TypeScript only at the React level.
02:05:58 - Robert Balicki
Exactly.
02:05:59 - Anthony Campolo
Yeah.
02:06:00 - Robert Balicki
And what's also interesting about this is that like, I mean, this isn't— it isn't anything that a backend knows how to execute. But we still have this query text here. This other query text up.
02:06:13 - Dev Agrawal
Yeah.
02:06:13 - Robert Balicki
Like this one, right? Okay. That's a pretty boring-ass query. But like this thing is generated also. And one thing that's nice about this is that— so, one of the benefits of GraphQL is you have fragment-like composition. But there's no fragments to be found anywhere in these query texts. We have done the inlining ourselves. And so, that means that we are essentially doing fragment-like composition in user land. So, we can instead of generating GraphQL here, we could generate SQL. We could generate tRPC. We could generate your custom backend code that says like, hey, data db.getNode, getTypeName, getId, whatever. Like, and then return, package that up and generate it for you. And Isograph is written in a way that is generic in the sense that there is an interface that we implement that knows how to generate GraphQL. And there would be just as— we just have to have another interface that implement— that we implement that would generate SQL or would generate whatever custom backend stuff that you want. And then again, we're not sending this— well, there are 2 ways to do this. One is you can send this string to the backend, which is sort of the easy no-build process kind of way. But the other one is that you would send you would register this at build time, get an ID back, and then you would send that ID at runtime. That ID will— the backend will look up that ID in a database or something, execute this, and send that value back to the frontend.
02:07:51 - Anthony Campolo
It allows you to create a level of indirection between the actual GraphQL query and how the frontend accesses it.
02:07:56 - Robert Balicki
Exactly. It adds security because if you accept any arbitrary GraphQL, Um, well, then you could have something that sort of DDoSes your backend, right?
02:08:06 - Anthony Campolo
Yeah.
02:08:07 - Robert Balicki
Or exposes information that you don't want to expose to users but it's part of your GraphQL schema. And oops, nobody noticed.
02:08:15 - Dev Agrawal
So nice. Uh, yeah, I'd be curious to see this. I'd be curious to see this work with some sort of a, uh, a sync engine maybe where the same query can run both on the server and on the client. And when you receive any data from server, you also store it on the client side. So, you can do like instant— instant navigation. I guess Relay kind of does some of that. Like there is a normalized cache. What is like the caching and optimistic story if you have one in Isograph?
02:08:53 - Robert Balicki
Yeah. So, While I figure out how to install PNPM and so on. I thought I installed PNPM already. I guess I haven't. I mean, this is a really new laptop. So in Isograph, you have essentially— did it work? Command not found, PNPM. Why? It's probably not in the—
02:09:17 - Dev Agrawal
it's probably there, but it's not in the Might need to restart the terminal or something.
02:09:23 - Robert Balicki
Yeah, let me try that. That's a good idea. Um, nope, still not there. It's not on the path. Yeah, okay. Um, weird.
02:09:31 - Anthony Campolo
npx pnpm.
02:09:33 - Robert Balicki
That's what I needed to do. Um, okay, so Isograph has a normalized store. So everything that we get from the backend gets keyed by— that's keyed by ID gets put into a normalized store. Then when we actually read the data, we read the data from that store. There's 2 separate processes. The network responses right into the store. Completely coincidentally, reading reads from that same store. We hope that the responses give all the data that we need for the frontend and it happens to work out in that way. But that's not structurally guaranteed. This means that we can do a lot of really cool things. So for example, in Isograph, like in Relay, if you try to read a component and some data is missing, it will suspend. And so that means that if you navigate from a list view to a detail view, well, maybe you have enough information. Fine. Goddamn it. Maybe you have enough information. You know, it's not that Isograph is hard to run, it's that I don't know how to install pnpm.
02:10:49 - Dev Agrawal
Hey, if Claude cannot also figure it out, then it's probably— you're fine.
02:10:55 - Robert Balicki
Yeah. I think I just need to add this to my ZSharpC. If you navigate from a ListView to a DetailView, The outer component, let's say the one that shows the title of the detail view, will already have enough data inside the store. So, it can immediately render. If you wrap the rest of the content in a Suspense boundary—
02:11:22 - Anthony Campolo
I give up.
02:11:27 - Robert Balicki
Then you will immediately render the upper part. Then the bottom part will suspend and you'll show a spinner there. And then eventually it will pop in. So yeah, so there's actually a really good story here, but it actually goes beyond just the naive thing, which is what I described is also in Relay essentially, and possibly in other frameworks as well. But one of the things that you know about IceGraph is that you have this DAG of work that is being done. And one of the things that we are sort of doing in, but not fully, and the way it will work is that ultimately you will have everything in— all the precomputed stuff is stored in the Relay store. So, for example, the formatted date. That is some sort of function that depends on the actual raw date. And then you have something like the current day display clock, right? That depends on the formatted date. Okay. So, when the underlying date changes, well, then we recalculate the formatted date. Okay. But maybe the formatted date doesn't show the year and only the year changed or whatever. It doesn't show the seconds and it only shows minutes. Right? So now we can short circuit and we don't have to rerender the clock.
02:12:43 - Dev Agrawal
Right?
02:12:44 - Robert Balicki
Okay. Maybe the clock shows seconds, but whatever. The milliseconds changed and the seconds didn't change. So we can short circuit and rerender the clock and not rerender the clock. And the whole thing will be like this tree of work that we have calculated, and we will try to calculate the minimal amount of work that needs to be done in response to changes to the underlying data. And that is kind of a universal problem. A lot of what you want to do is the minimum amount of work. The way you make apps be performant is you do less work. It's not that you get better at doing the existing work, it's that you figure out You're smarter about doing less work. So, you have to keep track of what goes in— what flows into what. And that's how with Isograph, like when you make a change to something or other, you'll only see the components that actually need to rerender actually rerender.
02:13:38 - Dev Agrawal
It sounds— to me, it sounds like the perfect framework to pair with Isograph is not React but Solid.
02:13:46 - Anthony Campolo
Of course you would say that, dev.
02:13:48 - Dev Agrawal
SolidJS.
02:13:50 - Anthony Campolo
Team member, undisclosed affiliations.
02:13:54 - Robert Balicki
Yeah, I, I don't think you're wrong. Um, I think that then what's nice about Solid— there's a lot of nice things about Solid. Um, and one of them is the fact that it's like a little bit more stateful. Like the components actually have construction, are constructed once, and that corresponds really well to what Isograph is doing in React. Like you have this idea that the the component can render any number of times before it mounts. And that's kind of like a constructor, but it's also really a problem. But on the other hand, with Isograph, we know, because we know that there's a query route and this query route can reach the blog detail component, that this blog detail component needs to be constructed. And it can be changed and modified and handled by the framework. So, that's the idea there is that we have more hooks than React sort of allows you to do. It's sort of like the class version of React would have been a better fit for Icegraph. I mean, it works just fine because the rendering part is relatively small. But like it did it. Okay.
02:15:07 - Anthony Campolo
Let's see.
02:15:07 - Robert Balicki
Let's see. Wow. That's so fast.
02:15:09 - Anthony Campolo
How did it—
02:15:10 - Robert Balicki
did it like— Oh, probably because I already had the things in there. OK, so let me just take a look at the thingy, I think.
02:15:19 - Dev Agrawal
Yeah, it might have run pnpm install earlier, which means you have everything cached.
02:15:25 - Robert Balicki
Yeah, it probably did it right because— OK, so we want to do pnpm dev pet-demo, named after our favorite host. And this will build a bunch of stuff in Rust. Did you catch all that? So anyway, so then yeah, we're mostly building the, the Babel— not the Babel plugin, the SWC plugin. And what are we— of course, what are we complaining about here? Cannot find— oh, I need to add the target. Let me just— that didn't even copy it. Okay. So, I just need to add wasm32-wasm1 target. See what— the thing is we don't even need that because we only need that when we're changing it. We don't actually need it to run the thing. I could be a little bit better about not requiring that. Um, uh, yeah, so eventually this will— we'll get there. Um, so that's the thing, if you work on a project, you don't know all the steps that are actually required to bootstrap it.
02:16:49 - Anthony Campolo
Um, it's just running, it's been running on your machine for so long.
02:16:53 - Robert Balicki
Exactly. And at some point in time, I installed that Okay, cool. So, it should be doing that and it should work pretty well pretty quickly. Cool. Target installed. I believe it. Okay. And yeah. And then we can actually, like, show off some of the—
02:17:16 - Dev Agrawal
Yeah. But I understand the point that it's— Isograph does a lot of work in that goes into making a React app performant to kind of like reduce re-renders and to— because basically if you have a component and you know exactly what that component depends on, what data that component depends on, and you have that in a normalized store, when that updates, you can go and re-render exactly those components. Instead of like, you don't have a top-down tree, a top-down like tree re-render where one component re-renders and all the children re-render. And then the only way to put a brake on that is like memoization.
02:18:03 - Robert Balicki
Yes.
02:18:04 - Dev Agrawal
This is like an almost like an additional layer of memoization.
02:18:10 - Robert Balicki
Yeah. It's actually nice. It's smarter about the memoization too. Because that didn't even— that was not even correct. Because it has more information than is known at runtime, right? It has the ability to look at a bunch of stuff and throw stuff away and do the right thing despite not needing that. Like one of the— because it's very similar. I don't know if you all have seen Fate. Fate is something that Christophe Chedeau not Shido, Christoph Nakazawa, has released. And it is similar to Relay. It's similar to Isograph. But it does that sort of at runtime. So you actually have a data structure that does all this stuff. And that allows you to do— that allows you to do a few more things. For example, dynamically construct queries for just the fields that are missing. But on the other hand, it prevents you from doing more stuff at build time, which is, I think, the trade-off that Relay and Isograph, uh, try to make. Um, yeah, so let's see. I have no idea why these, these build things are, are failing. Um, oh yeah, it's because there's a newer version of, uh, I'm on a newer version.
02:19:34 - Anthony Campolo
Yeah, yeah.
02:19:34 - Dev Agrawal
Rust, okay.
02:19:35 - Robert Balicki
Rust, yeah. So as you can tell, new versions of Rust are released and I have to do— I have to make changes as a result.
02:19:51 - Anthony Campolo
Yeah.
02:19:52 - Robert Balicki
And so what's nice about this is that, like, you have some bad tradeoffs in React. You, for example, need to specifically break your components up into subparts in order to theoretically get some of the performance benefit.
02:20:15 - Anthony Campolo
Okay.
02:20:15 - Robert Balicki
Same thing is true in Isograph. Like, it may— if you want to have that memoization layer between the formatted date and the clock, well, then you need to have a separate formatted date thing and the clock can't read the date directly. One of the things I want to do with Isograph is essentially have a way to encode more functionality into the query itself so that at build time we know that the formatting occurs and it flows into the clock and you don't need to do anything. You don't need to say like, I specifically— I mean, you can just do it in the natural way.
02:21:00 - Dev Agrawal
Are you kind of talking about like—
02:21:03 - Robert Balicki
yeah. But so you would dynamically—
02:21:04 - Dev Agrawal
like breaking apart components at build time?
02:21:08 - Robert Balicki
Yes, exactly. Because you're able to encode more of the logic inside of the Isograph literal. And you want it to focus on sort of not everything. So you probably want it to focus on like control flow and filtering and things like that. Because otherwise you go down the path of reinventing a version of JavaScript that has different semantics but is like equally complicated. And it's gonna be very hard to integrate with other stuff. But yeah, that's the— that's one of the ideas that I have about that as well. But yeah, but in general, you can do— you can be much more aggressive about caching with with Isograph because, you know, all the— because the natural thing to do is to have these intermediate, these intermediate things like formatted date and stuff like that. And then they become these sort of memoization boundaries. All right. It says it worked. Let's see if it works.
02:22:08 - Anthony Campolo
And I can go for another like 10 or 20 minutes, but I actually have to start wrapping up soon. So my workshop starts in a half hour.
02:22:15 - Robert Balicki
Oh, excellent. Okay, I won't keep you long. Let me, uh, show— let's just do one final demo.
02:22:20 - Anthony Campolo
And, and, Dev, you should also, if you have any kind of final wrap-up questions, um, for me, I would just, um, the only thing would be share like your socials and where people can get in touch and learn more.
02:22:36 - Dev Agrawal
Uh, can't find Isograph React.
02:22:39 - Robert Balicki
Yeah, I probably just needed to Run pnpm install. Why is it not finding it? It should be finding it. I thought it would run this by— oh, I probably need to run build. There's like another— one of these other ones needs to be run. Actually, let's just— I don't remember where exactly it is. Is it here? No, it's here. PnP.
02:23:09 - Anthony Campolo
Like a mono repo structure you're in?
02:23:11 - Robert Balicki
Yes. Gotcha. I need to build the JavaScript.
02:23:18 - Anthony Campolo
Watch libs.
02:23:18 - Robert Balicki
That'll, that'll do it. Yeah. Okay.
02:23:23 - Dev Agrawal
Right. So this is the mono repo with Isograph itself and some demo applications, right?
02:23:34 - Anthony Campolo
Yeah.
02:23:34 - Dev Agrawal
Yeah, now it should be able to find because I'm guessing Isograph-react points to a version of the local package.
02:23:42 - Robert Balicki
Nice.
02:23:42 - Anthony Campolo
Got your query, got your cards.
02:23:46 - Dev Agrawal
Yes.
02:23:46 - Anthony Campolo
Bunch of pets. Classic GraphQL demo.
02:23:49 - Robert Balicki
Exactly. Okay, so let's go ahead and add some delay here.
02:23:54 - Anthony Campolo
Let's just say this shit makes me so nostalgic. You guys have I have no idea. I haven't done GraphQL in like 3 years.
02:24:02 - Robert Balicki
Nice. Nice. Okay. So, let's go ahead and give it 1,000 milliseconds of delay. Okay. And then close this out. And— oh, I called it 2 seconds of delay. So, now we have loaded the— like Mikayla's name and image. And so, when we navigate here, you're gonna have a 2-second delay. Well, one, it has to get the JavaScript. And then it's gonna show the top. And then— oh, that's interesting. Oh, let's try that again. Not sure why that happened.
02:24:32 - Anthony Campolo
Yeah. Loader.
02:24:33 - Robert Balicki
Yeah. So, you notice that it showed the top first immediately and then the bottom. Even though the server is running locally, this takes exactly 0 seconds in practice. But now if we press back, well, it goes instantly. The images re-download because I guess it's Next.js and images are not really They're not, they're not iSCSI problem. And if you go back to this demo again, now it's gonna— everything's gonna load immediately, even though, even if we were to make another network request in the background. So now if we go here, like state builder validate.
02:25:09 - Dev Agrawal
Yes.
02:25:09 - Robert Balicki
Well, you can, you can opt into that. Yes, you could do that if you want.
02:25:14 - Dev Agrawal
Okay.
02:25:15 - Robert Balicki
But it's sort of up to you, whatever you want. So here, if we go to— oh, I don't even have the— wow, I don't even have the React DevTools. That's how new this computer is. Oh, item is inactive. Enable now. Re-enable. Okay. So here, if we have the— do we have the React DevTools? How do I enable them? Where do they go? No, I don't know how to— Do the React DevTools.
02:25:46 - Anthony Campolo
Uh, DevTools in a while.
02:25:48 - Robert Balicki
Yeah, they're enabled.
02:25:49 - Anthony Campolo
Like dev, I went over to the dark side.
02:25:52 - Robert Balicki
Solid.
02:25:53 - Anthony Campolo
Yeah.
02:25:54 - Robert Balicki
Um, okay.
02:25:56 - Dev Agrawal
Well, in theory, it might be an application. I think, I think that it, it's now moved into some other tab. I don't know which one.
02:26:04 - Robert Balicki
Uh, okay.
02:26:05 - Dev Agrawal
Application knows or not?
02:26:07 - Robert Balicki
No.
02:26:07 - Dev Agrawal
Okay. I thought it was remembering something else.
02:26:13 - Robert Balicki
Yeah. Anyway, whatever. If we were to show that, then if you were, for example, to change the best friend here, none of this stuff would rerender. None of this— all this other stuff down here. It would just show the new one here. And here, if you touch this, it will just rerender this exact specific component. Despite all of these components reading from the same— from the same object, essentially. And theoretically, I mean, if you were to do this as a React component, you would thread data down from the root and pass it to every child. And so that pet has changed because now its check-ins have changed and it's, you know, its best friend has changed. So you would pass that data down and everything should recalculate and rerender. And that's incredibly costly. Especially if you have— if you're paginating and you have like hundreds of items, like the first couple pages might be performant and it gets slower and slower and slower. And with something like Isograph, you just rerender the components that actually have changed. So it ends up staying snappy.
02:27:21 - Anthony Campolo
Nice.
02:27:22 - Robert Balicki
Yeah. I think that that's a good place to end it.
02:27:27 - Anthony Campolo
Yeah. Very cool, man. Thank you for coming on and sharing both these projects. Very, very interesting. And, um, it's great that they're open source and that, you know, anyone can, can try these out. So that's what we're always all about here. Um, yeah, uh, why don't you just share—
02:27:43 - Dev Agrawal
except that they're both in Rust, which means you need a PhD before contributing.
02:27:50 - Anthony Campolo
Not with the agents, buddy. It will write whatever I ask it to.
02:27:55 - Robert Balicki
Um, yeah, so Worth learning Rust. I think it's a great language. I would encourage you all to learn it though.
02:28:03 - Anthony Campolo
All right, so you're at x.com/statisticsforthewin.
02:28:09 - Robert Balicki
Yes, exactly. Let me show that. Can I jump to my profile? Yeah, here we go.
02:28:16 - Anthony Campolo
Yeah, and then I think I've got links to Isograph and all that stuff I'll have in the description of the YouTube video.
02:28:27 - Dev Agrawal
And what would you say is the easiest way for someone to use Barnum? Let's say, let's say I used Codex right now and I'm kind of tired of, uh, long sessions or short sessions and I want to adopt, uh, Barnum to re— into refactor work, to do automations, background, whatever. What would be the— do I just go to Codex and say that start using Barnum on this project? Yeah, is there like a skill.md file that teaches agents how to write Barnum?
02:28:58 - Robert Balicki
If you point Barnum— if you point them to this, uh, best practices doc, um, it does a pretty good job. I mean, this is, this is basically every single issue that I've ever encountered when asking it to do stuff. So I just have it look to look at the best practices and read the docs. And it tends to be after that pretty good.
02:29:23 - Anthony Campolo
The—
02:29:23 - Robert Balicki
this tends to be pretty good at that point in time at writing Barnum workflows.
02:29:28 - Anthony Campolo
Can you add .md to your URL and just get a Markdown page?
02:29:31 - Robert Balicki
I should, I should do that. The answer is no.
02:29:36 - Anthony Campolo
Okay.
02:29:37 - Robert Balicki
Which is, yeah, you know, low-hanging fruit.
02:29:40 - Anthony Campolo
DocuSource.
02:29:41 - Robert Balicki
Yeah, it's DocuSaurus. Oh, so maybe we do have it.
02:29:44 - Anthony Campolo
I know a DocuSaurus.
02:29:46 - Dev Agrawal
Doc, I see one.
02:29:48 - Robert Balicki
Yeah. Um, that's good. Why go off? It does exactly what I need, you know.
02:29:54 - Anthony Campolo
I know Sebastian and, uh, he's super cool, so he's really awesome. Yeah, great.