252 karma · joined May 1, 2013
(Which is no different than what you would do as a human)
My personal OSS projects don't have the scale to necessarily make this worth it, but at work I run three pipelines using Barnum (https://barnum-circus.github.io/). First, one that ingests files, identifies refactors (from a pre-approved list), and places a precise description of the refactor to be done in a queue; second, one that reads from said queue, implements and creates PRs (there is a lot of "check that the PR is correct" here as well); and a third that babysits PRs until they land. I've landed hundreds of PRs in this way, with very little effort on my part.
It's my opinion that Isograph gives us the benefit of GraphQL, without many of its limitations. Which is to say, if you start from scratch, you can avoid mistakes.
This describes a future iteration of Isograph. Currently, much of what's described here is on the roadmap. But it's coming!
Taking a moment to create a workflow where these steps are separated (or rather, having an LLM build this workflow) and the LLMs are asked to just do minor leaf tasks increases your resilience to nerfed models.
For example, this demo (https://github.com/barnum-circus/barnum/tree/master/demos/co...) converts a folder of files from JS to TS. It's something an LLM could (probably) do a decent job of, but 1. not necessarily reliably, and 2. you can write a much more complicated workflow (e.g. retry logic, timeout logic, adding additional checks like "don't use as casts", etc), 3. you can be much more token efficient, and 4. you can be LLM agnostic.
So, IMO, in the presence of tools like that, you shouldn't bother using /loop, code routines, etc.
And throughout this, if I'm ever curious about how the ideas relate to some other topic, I can just ask the agent, "Are we designing XYZ right now? Categorically, is it this?" Lots of really cool discussions to be had.
I might be less enthusiastic if I was just shipping CSS changes and the like.
Months! That's not a long time.
But it doesn't have to be that way. You just have to put an effort into shipping fewer, better features as opposed to more features. The projects I'm working on (e.g. agent orchestration, because who isn't nowadays) have a small surface area and high payoff and thus are uniquely well positioned for this.
If I couldn't use an LLM, I would still work on this, and it would have roughly the same architecture. But because I'm able to go probably 100x as fast, I'm able to be much more ambitious. Or rather, I'm able to discover that my initial ideas were not on point and pivot, and not have any sense of sunk cost
Anyway, to each their own.
- you don't have a normalized cache. You may not want one! But if you find yourself annoyed that modifying one entity in one location doesn't automatically cause another view into that same entity to update, it's due to a lack of a normalized cache. And this is a more frequent problem than folks admit. You might go from a detail view to an edit view, modify a few things, then press the back button. You can't reuse cached data without a normalized cache, or without custom logic to keep these items in sync. At scale, it doesn't work.
- Since you don't have a normalized cache, you presumably just refetch instead of updating items in the cache. So you will presumably re-render an entire page in response to changes. Relay will just re-render components whose data has actually changed. In https://quoraengineering.quora.com/Choosing-Quora-s-GraphQL-..., the engineer at Quora points out that as one paginates, one can get hundreds of components on the screen. And each pagination slows the performance of the page, if you're re-rendering the entire page from root.
- Fragments are great. You really want data masking, and not just at the type level. If you stop selecting some data in some component, it may affect the behavior of other components, if they do something like Object.stringify or JSON.keys. But admittedly, type-level data masking + colocation is substantially better than nothing.
- Relay will also generate queries for you. For example, pagination queries, or refetch queries (where you refetch part of a tree with different variables.)
There are lots of great reasons to adopt Relay!
And if you don't like the complexity of Relay, check out isograph (https://isograph.dev), which (hopefully) has better DevEx and a much lower barrier to entry.
https://www.youtube.com/watch?v=lhVGdErZuN4 goes into more detail about the advantages of Relay
But yeah, IMO, GraphQL doesn't justify itself unless you're using a client like Relay, with data masking and fragment colocation.
And if you also combine this with the fact that JS and fragments are statically associated in Relay, you can get: - entrypoints - 3D (if you just defer components within a type refinement, e.g. here we load ad items only when we encounter an item with typename AdItem https://github.com/isographlabs/isograph/blob/627be45972fc47.... asAdItem is a field that compiles to ... on AdItem in the actual query text)
And all of it is doable with the same set of primitives, and requiring no server support (other than a node field).
Do let me know if you check it out! Or if you get stuck, happy to unblock you/clarify things (it's hard for me to know what is confusing to folks new to the project.)
(And don't forget lack of generics, no support for interfaces with no fields, lack of closed unions/interfaces, the absolutely silly distinction between unions and interfaces, the fact that the SDL and operation language are two completely different things...)
If you're interested in entrypoints without the Meta internals, you may be interested in checking out Isograph (which I work on). See e.g. https://isograph.dev/docs/loadable-fields/, where the data + JS for BlogBody is loaded afterward, i.e. entrypoints. It's as simple as annotating a field (in Isograph, components define fields) with @loadable(lazyLoadArtifact: true).
TLDR the incremental compiler rewrite is finally bearing fruit. Namely, because we no longer have a batch compiler (i.e. we don't bail on the first error), we can
- provide LSP results (hover, goto def, etc) on non-broken parts of your isograph literals, even in the presence of errors
- surface those errors in VSCode, and
- fix those errors with auto-fixes!! (https://www.youtube.com/watch?v=6tNWbVOjpQw&t=314s) Which is to say, select a field that doesn't exist, and let the compiler create the isograph literal declaring it.
It's a great feeling to see this level of DevEx
#2 downloading the same fields multiple times
#3 downloading unneeded data/code
Checks out
TLDR, you get nice features like: if the field you're selecting doesn't exist, the extension will create the field for you (as a client field.) And your entire app is built of client fields that reference each other and eventually bottom out at server fields.
This is not surprising: Apollo only recently added support for data masking and fragment colocation, but it has been a feature of Relay for eternity.
See https://www.youtube.com/watch?v=lhVGdErZuN4 for the benefits of this approach:
- you can make changes to subcomponents without worrying about affecting the behavior of any other subcomponent,
- the query is auto-generated based on the fragment, so you don't have to worry that removing a field (if you stop using it one subcomponent) will accidentally break another subcomponent
In the author's case, they (either) don't care about overfetching (i.e. they avoid removing fields from the GraphQL query), or they're at a scale where only a small number of engineers touch the codebase. (But imagine a shared component, like a user avatar. Imagine it stopped using the email field. How many BFFs would have to be modified to stop fetching the email field? And how much research must go into determining whether any other reachable subcomponent used that email field?)
If moving fast without overhead isn't a priority (or you're not at the scale where it is a problem), or you're not using a tool that leverages GraphQL to enable this speed, then indeed, GraphQL seems like a bad investment! Because it is!