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 syncbecame 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.luahad 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.