Architecture¶
This article describes the overarching architecture of Degasolv, together with some explanation about some of the design decisions.
Background¶
At one of my previous jobs, I was a Build Engineer – a person who built the code that the developers wrote and made it available. I had lots of dependency problems coming at me from all different sides:
- One module in language A depending on another from language B
- Developers working with a language (at the time) with no clear package manager ecosystem (cough C++ cough)
- Package manager developers breaking builds with backwards-incompatible behavior
- A dependency graph that looked like a dream catcher
So I decided to build a tool that would do these things:
- Resolve dependencies the right way, safely
- Even resolve dependency chains for different package systems (apt, pip, java)
- Be super versatile and generic, able to be plugged into an arbitrary build script
Core Resolver¶
At the core of Degasolv is a monster method called resolve-dependencies. It
is a rather large method with a backtracking SAT-solver-ish design. Originally
it was written to have a conflict-strat of exclusive
and a resolve-strat of thorough
hard-coded. In other
words, the “first class” original use case of Degasolv was a SAT-solver-class
depedency resolver that only allowed a single version of any dependency, and
ensured that all parties depending on that dependency had a chance to agree on
what was chosen. These options were later added to allow Degasolv to act more
like maven and give any Building Engineer using Degasolv useful “handbreaks” to
change how resolution was being done in-house so that it could be modified to
conform to business needs. Other options, such as list-strat<list-strat> and
search-strat<search-strat> were added as time progressed as well for similar
reasons, and also, frankly, to fix bugs (behaviors that were never originally
intended).