-
Notifications
You must be signed in to change notification settings - Fork 22
Introduction to gps
With all the hoopla around package management in Go, and the challenges with the problem in general, it's reasonable to assume that a compiler-adjacent tool like gps
will be an arcane, inscrutable engima. And, if you were to try to understand it code-first, you might draw that conclusion. We hope that if you approach gps
with the right mindset, though, it needn't be bad at all.
Now, the ultimate result of gps
's work is to automatically dump a bunch of dependency code into your vendor/
directory. But that's not the best place to start from, as it tends to set folks in the frame of mind that "well, gps
is probably more or less an elaborate, imperative shell script, grinding through and checking out a list of dependencies." If we were to translate that kind of thinking into a meatspace analogy, it might look like this:
Go to the store for some broccoli, then pick up the dry cleaning, then pick up the kids from school.
Nice, procedural, and straightforward - much like checking out a list of deps! If you try to grok gps
while expecting it to fit into a box like this, however, you will have a bad time.
gps
, at its heart, is a constraint solver. Fancy term, but we can capture its essence by rephrasing our analogy:
The dry cleaning needs to be picked up before they close at five, and we need broccoli for dinner. Oh, and the kids need a ride home when school gets out at 3:15!
It's the same basic problem, just subtly shifted: rather than prescribing an exact procedure, this approach outlines the requirements - aka constraints - that a solution must satisfy. The job of a constraint solver is to examine the constraints alongside the available options and look for a set of options that meet all the constraints. In our analogy, this involves doing things like:
- Considering interdependent constraints: how far apart are the store, school, and dry cleaner?
- Discovering new requirements: does the car need gas? where's the gas station?
- Discovering new options: our neighbor Steve grows broccoli, can we...uh..."borrow some?
- Making choices: which trip to do first? when to leave?
There's probably a lot of solutions that would fit these requirements. A constraint solver's job is to efficiently search for a solution - often through a potentially large space of possible solutions - to preferably find the "ideal" one, but at minimum one that works.
In general, constraint solving fits well with dependency management because of the structure of the problem: you've got dependencies, those dependencies may have dependencies, everything's got different versions of their dependencies that they work with, and for each dependency, there's likely at least a handful of versions to pick from. gps
is designed to encompass all the possibilities, move efficiently through them, and come to a solution that will successfully compile.
The general problem of versioned dependency management is hard - though in practice, usually not pathologically so. Shortcuts can be taken, but they tend to hamper user choice or create brittle dependency ecosystems, and invariably have nasty failure modes that the user has to deal with.
Consequently, gps
attacks the general question through a constraint
solving approach drawn from a combination of research, demonstrated industry
best practice, and good ol' fashioned borrowing of other peoples'
code, all with a careful eye to meeting the Go
ecosystem's particular requirements.
Most importantly, gps
strives to hide the complexity of the problem. It
provides an implementing tool considerable control, but within a confined set
of choices that help keep the problem sane.