javascript-today

From Neovim to Doom Emacs: A Reluctant Migration

For years, Neovim was home. Lua config, a curated plugin list, LSP wired up just right — it felt like mine. But over the last year, “mine” started to mean “mine to constantly fix.”

The breaking point

The thing that finally pushed me out wasn’t a single event, it was death by a thousand paper cuts:

  • A plugin update would silently change its API, and suddenly my statusline or completion setup would throw errors on startup.
  • Neovim itself would ship a breaking change to its Lua API between minor versions, and two or three plugins I depended on wouldn’t catch up for weeks.
  • :Lazy sync became something I dreaded instead of a routine command — every sync was a gamble on whether I’d lose an evening to config archaeology.
  • My init.lua had grown into a pile of workarounds for workarounds, and I couldn’t always remember why a given shim existed.

None of this is a knock on Neovim itself — the core editor is excellent, and the plugin ecosystem is genuinely powerful. But I was spending more time maintaining my editor than using it, and that ratio had gotten backwards.

Why Doom Emacs

I’d looked at Doom Emacs a few times before and bounced off it — it felt like trading one config-heavy system for another. What changed my mind was realizing that Doom isn’t “no config,” it’s curated, versioned config. Doom pins module versions, tests combinations of packages together, and gives you doom sync / doom upgrade as deliberate, atomic steps instead of “whatever git pull on twenty repos happens to produce today.” When something breaks, it’s usually because I changed something, not because two unrelated plugins drifted out of sync overnight.

So I made the jump: doom-emacs, evil-mode for the keybindings I already had muscle memory for, and a slow process of rebuilding my workflow module by module instead of file by file.

What’s been easy

Evil mode did most of the heavy lifting here — modal editing, hjkl, visual mode, macros, all familiar. Doom’s out-of-the-box defaults (which-key, a sane leader key, project management via projectile) meant I wasn’t starting from zero the way I would’ve with vanilla Emacs.

What I’m still adjusting to

The honest answer is: a lot, still. I’m not fully fluent yet, but I like where this is headed.

The biggest adjustment has been fuzzy finding and search. I leaned on Telescope constantly in Neovim — file search, live grep, buffer switching, LSP references, all through one consistent, predictable interface. Doom’s equivalent is spread across consult, vertico, and projectile, and at first that felt like three tools doing what Telescope did as one. SPC SPC for files versus SPC / for grep versus SPC , for buffers didn’t map cleanly onto “just open Telescope and type” the way my old habits expected.

Terminal integration took some relearning too. Neovim’s :terminal (or a plugin like toggleterm) was simple and modeless. Doom’s vterm is more capable but has its own quirks around when you’re in evil-normal-state versus insert-state inside the terminal buffer, which tripped me up more than once early on.

None of it is worse, exactly — consult-ripgrep is arguably more powerful than Telescope’s live grep once you know how to drive it — it’s just different enough that my fingers had to relearn where things live.

Where I’ve landed

A few months in, I’m not looking back. My config breaks far less often, upgrades are predictable, and the parts that used to eat my evenings — plugin compatibility, Lua API churn — just aren’t a category of problem anymore. The learning curve on search and terminal workflows was real, but it was a few weeks of friction, not an open-ended maintenance tax.

Common Doom Emacs keybindings

A cheat sheet for anyone making the same jump. All leader-key sequences assume the default SPC leader in evil mode.

Keybinding Action
SPC SPC Find file in project (Telescope’s find_files equivalent)
SPC / Search (grep) in project
SPC , Switch buffer
SPC . Find file relative to current directory
SPC f f Find file
SPC f s Save file
SPC f r Recent files
SPC p p Switch project
SPC b b Switch buffer
SPC b d Kill (close) buffer
SPC w v Split window vertically
SPC w s Split window horizontally
SPC w d Close window
SPC g g Open Magit (git status)
SPC g b Git blame
SPC c a Code action (LSP)
SPC c d Jump to definition
SPC c r Rename symbol (LSP)
SPC ` Open terminal (vterm)
SPC o t Toggle terminal popup
SPC h k Describe key (what does this binding do?)
u Undo (evil mode)
SPC q q Quit Emacs

Still learning, still occasionally reaching for Telescope muscle memory — but glad I made the switch.

Common Doom key-bindings

Some common Doom emacs key-bindings