Launch HN: Infield (YC W20) – Safer, faster dependency upgrades
Here’s a short demo video: https://www.youtube.com/watch?v=diCGmtMUeRU
We’re launching today with support for Ruby packages. If you’ve ever run `bundle outdated` or upgraded a Rails app, Infield is for you. You can try it on your own project at https://app.infield.ai/hn. Upload your Gemfile and Gemfile.lock (no email/name/cc required) and we’ll show you Infield on your code.
I (Steve) have been building open source software and commercial web apps for more than a decade. I spent the last year personally upgrading Rails apps by hand for companies in order to research this problem. I'm convinced that every company is re-inventing the wheel and doing by hand a bunch of toilsome work that can be done with software.
As one example, I was working as a consultant upgrading an app to Rails 7. This company was using the attr_encrypted gem to encrypt information at the database level. Rails 7 brings built-in support for encryption in a way that's incompatible with this gem. Having hit this same problem at two other companies I already knew how to handle the migration; but if I hadn’t, they could have risked their most sensitive customer data (this is what you tend to encrypt). After that project I started building a personal database of "upgrade experiences" and before long felt sure we could make useful software.
The time-consuming part of package upgrades is not coding—it’s mostly risk assessment, research, and project planning. If I’m on a maintenance rotation and have half a day to pay down some technical debt, which package upgrades should I look at? I might end up spending that time trying to upgrade something only to get blocked and give up. Worse, many breaking changes are subtle and won’t be caught by CI. I’ve brought down production only to find an issue was buried in a changelog I didn’t read!
Infield scans all of your dependencies to prioritize upgrades based on effort (how much work is this? Is it risky?) and impact (will upgrading fix a security issue? will it get me onto a supported branch?). We can do this because we use GPT to read the changelog for every package you rely on. Changelogs are broken apart into discrete changes and classified according to the keepachangelog.com standard. Then a human expert reviews the output. We can spend more time researching each package than you because we’re going to re-use this work for every future customer doing the same upgrade.
Sometimes you want to do a complex upgrade like Rails that might be blocked on other packages being upgraded first. For this case we run an optimization based on the PubGrub algorithm to solve for a suggested target version of all your dependencies that will be compatible with the next version of Rails. We group and order these blockers into an "Upgrade path" you can follow.
Most of the existing work in this space is security monitoring software like Dependabot or Snyk. These tools are primarily sold to security teams to let you know about CVEs that affect your dependencies. They’re reactive, a way to let developers know when they need to upgrade something but not how to do it. Our goal with Infield is to make it so easy to keep dependencies up to date that you’re always on the latest versions.
Infield is $60/mo/repo and we’re launching today with support for Ruby. Javascript and Python are probably next, but we’re very interested to hear which language you feel this pain in most acutely. Ruby is first since the consolidation around Rails allows us to really nail the experience for a focused set of packages.<...
39 comments
[ 3.1 ms ] story [ 22.1 ms ] threadAnyways, god speed my dudes.
Is your frustration mostly from needing compile all this non-python stuff across environments?
Frankly, it's one of the reasons that we're seeing a lot of excitement around stuff written in Rust, but with Python bindings[1][2]. Cargo makes it so easy to build across various platforms and then just produce statically compiled platform specific python wheels. If PyTorch and TF had been written using Rust, any other other ultra high perf language with a tool-chain as reliable as cargo, a lot of folks lives would be easier.
[1]: https://www.getdaft.io/
[2]: https://www.pola.rs/
E.g. if I'm banging my head against a wall trying to upgrade to the newest version of PyTorch for some graph embeddings feature and everything breaks, knowing it's high risk doesn't help me. I still need to get the thing installed so I can use the feature.
We have a command line tool you can use to send your lockfile to our server so we can process it, but you have to use our web app to view the results.
Toward the middle of last year I re-connected with Andy (our third cofounder) who I went to school with and have wanted to work on a company with for a long time. We did a bunch of customer discovery / product work to figure out how to take what I learned doing this by hand and turn it into a software product. That was exciting enough that we decided to bring Andy on as a third co-founder and pivot our YC company.
Allison and my background is in building data businesses. Before Infield and Syndetic we worked at a startup in the beverage industry where we standardized inventory data for every alcohol product sold in the US. As we got into building Infield we didn't expect to use LLMs at all. We imagined a similar human-in-the-loop expert system to what we've built before.
I've been extremely impressed with recent language model's ability to handle unstructured changelog text. For example, consider the following snippet of a changelog:
Language models can carry the context through, so we are able to not just parse this apart into discrete changes (which I could figure out how to do with a regex) but also bring in context and categorize them. It can do this generically and really feels like something new.> World-class Engineering teams use Infield to make dependency maintenance time twice as productive.
Specifically, "to make dependency maintenance time twice as productive." Feel like it ought to say "to cut the time they spend maintaining dependencies in half". I know that's wordier and maybe that's not quite it, but the wording as is feels a bit tortured to me.
----
My biggest pain isn't with the plan. It's with the actual upgrade process. Things _always_ break. Every single time we do a major upgrade, there's this long tail of things we need to fix. Even worse is the undocumented changes that break. This gives me nightmares.
The problem with your product is four parts:
* It needs to solve much more of the problem. It's not really valuable to know that I need to change stuff. I can figure that out by running my package managers update tool and seeing what breaks.
* When things break, it's unbelievably time consuming to fix them. Even worse is the person tasked with doing the upgrade is likely not the subject matter expert on broken code.
* I need to know what's broken that wasn't documented. This is by far the hardest part of our upgrade cycles. It keeps me up at night knowing a major upgrade could break things that aren't covered by unit tests.
* Because dependency upgrades are so hard, we tend to do them in large swoops. We set aside a week or two of pain and power through them. Given you're charging $60/m, this suggests:
-----I think this tool would be extremely value to me (likely $60/m/repo) if it:
* It ran the repo's test suite against progressively newer versions of dependencies. Showing when and where unit tests fail.
* Provide some means of "intelligent E2E" testing. Fire up our API/application. Tell us what changes when dependencies are upgraded.
* Fan out broken code to the subject matter expert. If an upgrade from X to Y is broken, look at the `git blame` to figure out who knows how to fix the code. Ping them asking them for help.
> My biggest pain isn't with the plan. It's with the actual upgrade process. Things _always_ break. Every single time we do a major upgrade, there's this long tail of things we need to fix. Even worse is the undocumented changes that break. This gives me nightmares.
Planning is one way to make those major upgrades go wrong less often. Our idea is that we can break large upgrades down into incremental changes that are individually safe and mixed in to your regular dev cycles, so that when you go to do the large upgrade you're not making such a big change all at once. This means things like fixing every deprecation ahead of time and making sure all the other dependencies you use are compatible with the version you're upgrading to.
We're calling this "upgrade paths" in our tool. Something like a major rails version upgrade is terrifying because of all the moving parts. Often you may have a dozen other gems that need to be upgraded in order to upgrade Rails. You said "If I can incrementally upgrade one package on every PR until I'm up to date, that'd be amazing.". We want to make that the flow for major framework upgrades, so you're merging in incremental improvements in the blocking dependencies ahead of time.
Undocumented breakages are for sure on our roadmap. We have two types in mind today: - Changes that are missing from the changelog - Incompatibilities across gems (for instance there was a time recently where if you had both datadog and newrelic installed and used elsaticsearch upgrading the newrelic gem would break prod)
We have some ideas on how to figure this out automatically (like reading code diffs with GPT rather than just changelogs), but the best way is to see upgrades out in the wild. As we see more and more upgrade experiences from our customers we'll be able to catch these issues and build this dataset.
I'd love to hear other ideas for making major framework upgrades safer.
> * It ran the repo's test suite against progressively newer versions of dependencies. Showing when and where unit tests fail.
Would this be something like `git bisect` for upgrades? We run your CI to figure out where you can upgrade to without something breaking? We're trying to figure this out statically by reading changelogs and building up a database of breaking changes. Our roadmap is to make this database better over time by pulling in more and more undocumented changes (by seeing customer upgrade experiences and by sourcing information from places like github issues). In my experience it's more common (and much more dangerous) that things break when your dependency changes in a way that wouldn't be caught by CI.
I want to be able to drop my email address and only hear back when support for $MY_FAVE_LANG is available.
We have our own database of every version of every rubygems package alongside its runtime dependencies (like you see at https://rubygems.org/gems/pundit).
Then we parse your Gemfile and Gemfile.lock. We use the Gemfile to figure out gem group and pinned requirements (we run turn your Gemfile into a ruby AST since Gemfiles can be arbitrary ruby code; we use bundler's APIs to parse your Gemfile.lock). This gives us all of the dependencies your rely on.
Then we let you choose one or more package that you want to upgrade and the version you want to target (let's say Rails 7.0.4.3).
Now we have [your dependencies and their current versions], [target rails version], [all of the runtime dependency constraints of these gems]. We run this through a dependency resolution algorithm (pubgrub). If it resolves then you're good to upgrade to that version of Rails without changing anything.
If this fails to resolve, it's because one or more of your current dependencies has a runtime restriction on rails (or another indirect gem being pulled in by the new rails version). This is where the optimization part comes in. The problem becomes "what is the optimal set of versions of all your dependencies that would resolve with the next version of Rails". Currently we solve for this set trying to optimize for the fewest upgrades. As our dataset of breaking changes gets better we'll change that to optimizing for the "lowest effort".
Happy to elaborate.
> We use the Gemfile to figure out gem group and pinned requirements (we run turn your Gemfile into a ruby AST since Gemfiles can be arbitrary ruby code [...]
Here's an example Gemfile:
This cannot be statically analyzed. And this is not a made-up example either! It comes from the Rails project here: https://github.com/rails/rails/blob/fdad62b23079ce1b90763cb5...This makes it impossible to statically determine the direct dependency requirements of a project.
> Now we have [your dependencies and their current versions], [target rails version], [all of the runtime dependency constraints of these gems]. We run this through a dependency resolution algorithm (pubgrub). If it resolves then you're good to upgrade to that version of Rails without changing anything. > > If this fails to resolve, it's because one or more of your current dependencies has a runtime restriction on rails (or another indirect gem being pulled in by the new rails version).
This actually isn't that big of a problem for Bundler, which uses pubgrub, which to my understanding is deterministic. A deterministic algorithm means you can actually take requirements and simulate builds. There are two places where I would be hesitant:
1. This determinism only works if you also know what the universe of possible dependencies looks like. In many corporate environments, this is not true! Many corporate environments use private registries that mirror public dependencies, and may have a set of dependencies available that looks different from the public registry, which means your simulated builds will resolve to incorrect dependency versions.
2. As you move to support other languages, many other tools use non-deterministic dependency resolution algorithms. In particular, NPM is famously non-deterministic, which makes it impossible to simulate an NPM build.
---
When you're trying to just determine that there exists a build that resolves properly, these issues aren't particularly painful. At $DAYJOB (and what I suspect you will want in the future), we are often trying to predict the exact build of a user given a new set of dependency requirements (e.g. so we can predict their new vulnerabilities), which means doing accurate build simulations is much more important.
One note - we're not as concerned with predicting exactly the user's build as I think your $DAYJOB might be. We need to scan the space of possible valid resolutions that'd do a big framework upgrade, then pick an optimal (least effort) option from that space and chart your path towards it. Concretely that'll mean opening incremental PRs against your codebase where we provide lockfiles that are individually valid and accumulate in getting your upgrade done.
Providing lockfiles is a really interesting idea! That certainly solves the "we need your non-deterministic build tool to reproduce an exact build that we found" problem.
We haven't explored this route yet because a lot of our customers use tools that don't support lockfiles (e.g. Maven - Java in general has a lot of legacy stuff).
If you want to build off of our work, our dependency analysis bit is open source: https://github.com/fossas/fossa-cli
I started using Renovate on a few project recently and I am impressed. Not quite yet to the point of letting it merge dependency updates for me, but for unearthing them and creating a pull request (after which my tests run automatically) is already a big help.
The dashboard looks really well done, especially the path identification.
I'm curious why you decided to start with Ruby. Anecdotally, it doesn't seem as big of a problem as other languages with either (a) more upgrades or (b) worse package managers.
Disclosure: I'm the cofounder of https://grit.io which also does some dependency management, but focused on automating the actual code changes.
You mentioned that implementing an upgrade is difficult because of the actual code change but also the testing and identifying what the changes are in the first place. We're starting trying to do a really good job of the last one - what are the changes in this next version, are they breaking, do they break for your codebase, and what should you do if they are? We want to move from there to automating as many of the code changes as we can, but my sense is the biggest value is in giving you the confidence that we at least captured all of the changes you need to be concerned about.