Terminal-First Development: My Daily Workflow as a Software Engineer
TL;DR
I spend most of my working day inside the terminal. WezTerm is my main workspace, Fish is my shell, LazyVim is my IDE, Yazi is my file explorer, LazyGit is my Git UI, btop is my system monitor, and mise handles my runtime versions. I still use GUI apps when they make sense, but for coding, navigation, files, Git, and quick system checks, my default is TUI first.
This setup is not about being anti-GUI. It is about reducing context switching. My hands stay on the keyboard, my tools stay fast, and my workflow feels like one connected environment instead of a pile of separate apps.
Why Terminal-First?
As a software engineer, I jump between code, logs, Git branches, local servers, scripts, containers, SSH sessions, and random project folders all day. The terminal is already where most of those things happen.
At some point, opening a GUI file explorer, then a separate IDE, then a Git client, then a terminal panel inside the IDE started to feel backwards. I wanted one place where everything was close:
- Open project
- Browse files
- Edit code
- Search text
- Run tests
- Manage Git
- Monitor system resources
- Ask AI tools for help
That place became my terminal.
The biggest benefit is not looking cool. It is flow. Once the keybindings become muscle memory, moving through a project feels faster than dragging windows around.
WezTerm Is My Developer Cockpit
My daily driver terminal is WezTerm. I do not treat it as just a terminal emulator. I treat it like a lightweight workspace manager.
The setup has a few important ideas:
- Tabs at the bottom
- Opacity and a dark theme
- Nerd Font fallback stack
- Workspace session save/load
- Modal keybindings for panes and tabs
- Lock mode for avoiding shortcut conflicts
The modal part is probably the most important. I used to use Zellij, but these days I do not use it anymore. Instead, I brought the parts of that workflow I liked directly into WezTerm.
Pane Mode
I press Cmd+p to enter pane mode. From there:
| Key | Action |
|---|---|
n | Split pane horizontally |
d | Split pane vertically |
h/j/k/l | Move between panes |
x | Close current pane |
f | Toggle pane zoom |
p | Open pane selector |
r | Enter resize mode |
This makes pane management feel close to Vim/Zellij muscle memory without running another multiplexer layer.
Tab Mode
Cmd+t enters tab mode:
| Key | Action |
|---|---|
n | New tab |
x | Close tab |
h/l | Previous/next tab |
r | Rename tab |
I also use Cmd+1 to Cmd+9 for direct tab jumps. Simple, predictable, fast.
Session Restore
The most useful part is session management. I use the resurrect.wezterm plugin so I can save and restore workspaces.
My main shortcuts:
| Keybinding | Action |
|---|---|
Cmd+Shift+S | Save workspace state |
Cmd+Shift+L | Load saved session with fuzzy picker |
Cmd+Shift+D | Delete saved session |
Cmd+Shift+N | Create new named workspace |
Cmd+Shift+R | Rename current workspace |
It also auto-saves workspace state every few minutes. This is the feature that made me feel like WezTerm was enough without Zellij for my daily work.
Lock Mode
Cmd+g enters lock mode. This blocks my custom WezTerm shortcuts while I am inside tools that need similar keybindings, especially Neovim.
This sounds small, but it matters. Terminal-first workflows can easily become a keybinding war between the terminal, shell, editor, file manager, and multiplexer. Lock mode gives me an escape hatch.
Fish Is My Interactive Shell
I moved from Zsh to Fish because I wanted a shell that feels fast and helpful without a huge framework around it.
Fish gives me:
- Autosuggestions
- Syntax highlighting
- Good completions
- A readable config file
- Abbreviations instead of hidden aliases
- Tide prompt
- fzf integration
- zoxide integration
I use abbreviations for the commands I type constantly. For example, l, ll, and la expand to eza commands. cat becomes bat. cd becomes z through zoxide. I also have shortcuts for project folders, Poetry commands, virtualenv activation, and PHP version switching.
The important thing: abbreviations are visible. They expand before I run them, so I can still edit the full command. It feels safer than aliases while still saving keystrokes.
LazyVim Is My IDE
My editor is Neovim with LazyVim. I moved away from the JetBrains ecosystem because I wanted a lighter, more personal environment, and Vim motions became worth the learning curve.
LazyVim gives me a strong starting point:
- LSP support
- Treesitter
- Formatter integration
- Git integration
- Diagnostics
- Debug adapter support
- Plugin management with lazy.nvim
My language setup covers the stuff I touch often:
- Go
- Python
- PHP
- TypeScript
- Java
- Rust
- YAML
- Docker
- Helm
- JSON
For Python, I use Pyright and Ruff. For older PHP projects, I use Intelephense because some stacks still need PHP 7.x compatibility. For Rust, I use rust-analyzer.
I also have small keymap tweaks that fit my habits:
jjexits insert mode<C-d>and<C-u>keep cursor centerednandNkeep search results centered<Tab>and<Shift-Tab>move between buffers- Project scratchpads through Snacks.nvim
This is the part I like most about Neovim: the editor slowly becomes your editor. Every small tweak compounds.
Yazi Replaces Finder for Daily File Work
I rarely open the GUI file explorer for development work. I use Yazi for browsing files from the terminal.
Why Yazi works for me:
- It is fast
- It is keyboard-first
- It opens exactly where my shell already is
- It fits naturally with Neovim and terminal workflows
- It keeps me from leaving the current context
For project-level navigation inside Neovim, I use Snacks.nvim explorer and picker. For broader filesystem browsing, moving things around, and quick inspection, Yazi is my terminal file manager.
This combination is important: I do not need one tool to do everything. Neovim handles code navigation. Yazi handles filesystem navigation. The shell connects both.
LazyGit Keeps Git Visual Without Leaving the Terminal
I like Git on the command line, but I also like having a visual overview. LazyGit is the sweet spot.
It gives me:
- Branch overview
- Staging/unstaging
- Commit history
- File diffs
- Interactive rebase flows
- Conflict visibility
I still use raw Git commands when they are faster, but LazyGit is great when I need to understand a branch or review many changed files.
My Git setup also signs commits and rewrites GitHub URLs so cloning and pushing behave how I expect.
btop Is My System Dashboard
For system monitoring, I use btop. It gives me CPU, memory, disk, process, and network information in a clean TUI.
This matters because development machines get messy. Local servers, Docker containers, language servers, browser tabs, and AI tools can quietly eat resources. Opening btop is faster than hunting through Activity Monitor.
Again, it is not about avoiding GUI at all costs. It is about using the fastest tool from where I already am.
mise Handles Runtime Versions
I use mise for runtime management. Instead of juggling multiple version managers for Node, Python, Go, Java, Rust, and Bun, I keep one tool in charge.
My global setup includes:
- Bun latest
- Go
- Java LTS
- Node LTS
- Python 3.11
- Rust latest
This keeps project setup predictable. When I enter a repo, I want to focus on the project, not remember which version manager owns which language.
AI Tools Live in the Terminal Too
AI is also part of my terminal workflow. I use tools like OpenCode, Claude, Codex, Qwen, and Codeium depending on the context.
Inside Neovim, I have OpenCode integration so I can open an AI assistant in a terminal split and send context from the editor. Codeium gives me inline completion while coding.
The pattern is the same as the rest of the setup: keep the assistant close to the work. If I am editing code, I do not want to constantly jump to a browser tab, copy context, paste code, then jump back.
The Actual Daily Flow
A normal work session looks something like this:
- Open WezTerm
- Jump to a project using Fish abbreviation or zoxide
- Open Neovim or Yazi depending on what I need first
- Use LazyVim for code, search, diagnostics, and editing
- Use panes/tabs in WezTerm for servers, logs, tests, and scratch commands
- Use LazyGit when Git needs visual attention
- Use btop if the machine feels heavy
- Ask AI from inside the terminal when I need help exploring or drafting code
Everything is close. That is the whole point.
Trade-offs
This workflow is not perfect.
The obvious trade-offs:
- Initial setup takes time
- Keybindings can conflict
- Some GUI apps are still better for specific jobs
- Debugging setup can require more manual config than a full IDE
- You need to maintain your own environment
But for me, the trade-off is worth it. The speed and ownership make the setup feel good. I understand my tools because I assembled them piece by piece.
Should You Go Terminal-First?
You do not need to copy my setup exactly. In fact, you probably should not.
But if you spend a lot of time in the terminal, try moving one more part of your workflow there:
- Use Yazi instead of your file explorer for a week
- Try LazyGit instead of a GUI Git client
- Build a few WezTerm keybindings around your habits
- Try Fish if your shell feels slow or bloated
- Use Neovim for one project instead of forcing a full migration
Terminal-first development is not a religion. It is a direction. Move the parts that reduce friction, keep the GUI parts that still help, and build a workflow that makes you faster without making you miserable.
For me, that workflow is WezTerm, Fish, LazyVim, Yazi, LazyGit, btop, mise, and a lot of keyboard muscle memory.
Quiz Time!
1 question
Pick 1 answer for each question. You can change your answer anytime.