Why init.el Stays Small, Even Though the Config Doesn't

init.el on this machine is fifty-four lines long. That number hasn't grown much in a long time, and I used to think that meant my Emacs configuration was small. It isn't, really — it's just not in that file. The actual configuration runs to a few hundred lines, split across six small files by concern, and init.el itself does almost nothing except say, in order, which of those six to load.

What the top-level file actually does

Open init.el and there's no package configuration in it at all — just a load call per concern:

  • core/packages — package archives, package-vc-install bootstrapping, auto-update scheduling
  • core/ui — completion framework, theme, line numbers, folding, the stuff that's true regardless of what I'm editing
  • core/vc — Magit and diff-hl, nothing else
  • core/project — Projectile, ripgrep, flymake/flycheck
  • web/webmode and web/phpmode — everything specific to the kind of work I actually do most days
  • ai/llm — one package, one backend

That structure buys something the flat, everything-in-one-file version never did: I can look at init.el and get an honest table of contents for what's configured, without reading a single line of the "how." When something's slow to start or behaving oddly, I already know which sixty-line file to open instead of grepping through a thousand-line one.

Where the weight actually goes

Most of the real content lives in core/ui.el and the two web-mode files, and that's not an accident — it's roughly proportional to how much time I spend in each of those contexts. The completion stack is Ivy, Counsel, and Swiper rather than the newer Vertico-and-consult combination everyone reaches for now; I picked a stack years ago and it still does the job, so it hasn't been worth the churn of switching just to be current. orderless and corfu ended up layered in more recently for in-buffer completion, which tells you the boundary I actually draw isn't "no new packages" — it's "no new packages unless the old answer stopped being good enough."

The PHP setup is its own dedicated file, and it's the least minimal thing in the whole configuration on purpose: php-ts-mode for tree-sitter-based parsing, phpactor as the language server with its own Company backend, lsp-ui configured specifically enough that I turned off the sideline diagnostics and kept the peek window, because that's the combination that's actually fast to work with on a real Drupal codebase. twig-mode sits next to it for exactly one reason: Drupal templates are Twig, and syntax highlighting for the templating language a CMS actually uses isn't optional if you're in them daily.

The part that doesn't fit the tidy version of this story

If I'm honest, the "nothing unearned survives" framing oversells how disciplined this actually is. There's a commented-out rjsx-mode block at the top of the web-mode file that's been dead for a while. There's a fully-written, commented-out LM Studio backend sitting right above the Ollama configuration that's actually active. I don't delete these the moment they stop being used — I comment them out and leave them as a trail, because six months later "I tried this and here's exactly what it looked like" is more useful than a blank space where it used to be. That's not minimalism. It's just a different kind of bookkeeping than the deleted-cleanly version implies.

The one module that's genuinely new

The most recent addition is also the smallest: ai/llm.el is a single use-package block for gptel, pointed at Ollama running on 127.0.0.1 with a local coding model, not a cloud API key. Streaming's off, it talks over curl, and that's the entire configuration. It earned its thirty-two lines the same way everything else did — by replacing something that was already a real cost (switching to a browser tab, or a separate terminal, to ask a question about the code already open in front of me) rather than because a local LLM setup felt like the kind of thing a serious Emacs user should have.

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.