Skip to content
Tech News
← Back to articles

Dependencies should be fetched directly from VCS

read original more articles

Dependencies should be fetched directly from VCS Written on 12 Jun 2026

I’ve been writing Ruby at my new $dayjob in the last month. After spending most of the last decade writing Go it’s been a fun change of scenery. I did Ruby before (years ago) and I can’t really tell you which is “better” – Ruby is very different from Go in almost every respect and I find both quite effective in getting stuff done in their own way.

One aspect where I do feel Go is clearly better is dependency management; specifically the security aspect thereof. Go is not magically immune to malicious dependencies, but it is a lot more resistant to them chiefly because there is no “publish a package” step.

In Go dependencies are identified by URL, e.g. github.com/user/pkg. Go identifies which VCS is being used (git in this case) and fetches the tag or commit you specified in your go.mod file. The go.mod file serves as both a dependency specification and “lock file”. It lists exact versions; there is no ~>1.1. It includes both direct and indirect dependencies and lists your full dependency tree (the go command writes to go.mod).

A hash of all files is checked against known hashes on sum.golang.org to prevent tags from being replaced, and it uses a proxy to prevent repos from being left-pad’d.

There are more aspects to Go Modules, including security features, but I will skip over them for the purpose of this article. The relevant bit is “dependencies are identified by URL, the code is fetched directly from the VCS, and it does this for both direct and indirect dependencies”.

Auditing and updating dependencies is easy: I do git log -p old..new (usually via a forge web UI), read all the commits, and update the go.mod file. I don’t have many dependencies and those I do have don’t change much. It’s usually pretty fast. I don’t need to do careful in-depth reviews here; just look for suspicious stuff. Something like exec.Command(..) or http.Post(..) in a globbing library would stand out. It’s hard to really hide stuff.

I’ve been doing this for years for every dependency. As a solo developer. It’s easy. Some projects have much larger dependency trees and this becomes more time-consuming, but not hard or confusing. It’s still easy, just takes a bit of time.

For Ruby things are different as it has a “publish a package” step: you create a .gem archive and upload that to rubygems.org. You can put anything in there – no guarantee the .gem contents correspond to the source repo. To audit it I need to do something like:

curl -s https://rubygems.org/downloads/example-2.7.5.gem >old.gem curl -s https://rubygems.org/downloads/example-2.8.2.gem >new.gem mkdir old new tar xf old.gem -C old tar xf new.gem -C new (cd old && tar xf data.tar.gz) (cd new && tar xf data.tar.gz) diff -urN old new

... continue reading