Building Emacs From Source Doesn't Avoid Change, It Makes Change Traceable

A Google Alert on "emacs" surfaced a Reddit post this week titled, in full: "After snap updated version 31.1 without asking me I get function undefined error for many of..." — cut off at the title, the body unread, but the title alone is the whole story. Snap's background update mechanism moved someone's Emacs to a new version without their say, on its own schedule, and whatever depended on the old one broke with no warning beforehand and no chance to check anything first.

This site's Emacs doesn't come from snap, or from apt, or from any distro package. It's built from source, by hand, and the reason is exactly the failure mode in that title.

What's actually running here

which emacs on this machine resolves to ~/opt/bin/emacs, a symlink to emacs-31.0.50 — a development build, not a release. emacs --version reports it as built from commit 3b547e4f5dc on the master branch, dated January 23, 2026. That commit hash isn't a guess or a version label someone typed into a changelog — it's the literal git commit the binary was compiled from, and it matches the current HEAD of the source checkout at ~/src/github/emacs, cloned from emacs-mirror/emacs on GitHub.

The checkout's own reflog tells the rest of it plainly: one clone, one pull: Fast-forward, nothing after that. It was cloned once, fast-forwarded to master exactly once, built once, and hasn't been touched since. There's no cron job rebuilding it, no hook pulling fresh commits on a schedule — ~/opt/bin/ holds exactly one Emacs binary, not a rotation of versions, because there's been exactly one build.

The actual difference from snap

The point isn't "building from source is better" as some general virtue — it's narrower than that. Snap's update is something that happens to the machine, on a timer the user doesn't directly control, and the first anyone finds out about it is when a function that used to exist throws an undefined-function error mid-session. A source build that only moves when someone runs git pull and compiles it themselves has the opposite property: it cannot move without an explicit, attributable action, and whatever moved is pinned to an exact commit that can be looked up afterward. If this config's Emacs broke tomorrow, the first question — "what changed, and when" — has an exact answer sitting in two places that already agree with each other: the binary's own version string, and a single line in the source checkout's reflog.

That's a trade, not a free win. Running master means running Emacs's development branch, not a tagged stable release — genuinely more likely to carry an in-progress regression than whatever the last numbered release was. And because nothing rebuilds this automatically, a real fix landing upstream the day after this build happened sits upstream, unused, until someone notices and pulls again by hand. The snap user's actual problem wasn't that their Emacs updated — it's that it updated without them choosing when, and without anything recording what it moved from or to. A source build doesn't avoid change. It just makes change something that has to be deliberately initiated and leaves a paper trail when it happens, instead of something that arrives as a surprise mid-session with a stack trace.

Why "I build it myself" isn't automatically the safer choice

It would be easy to read this as "self-built beats packaged," full stop, and that's not quite the actual claim. A snap install that nobody ever rebuilds and a source build that nobody ever rebuilds end up in the same place eventually — stale, behind, running whatever was current whenever someone stopped paying attention. The difference that matters here isn't the install method, it's whether a version change is an event with a cause you can name or a thing you discover after the fact in an error message. This config's Emacs has been at the exact same commit for months now, which is its own kind of risk sitting quietly on the other side of the same tradeoff — it just isn't the risk that put someone's packages in an undefined-function state with no warning this week.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Please share this article on your favorite website or platform.