I recently overhauled my development environment, so I wanted to document its current setup and the thinking behind it.
My approach to the development environment
I focused on three main principles when setting it up:
- Pay close attention to security
- Use switching apps as a way to switch contexts
- Keep things as minimal as possible
I am not particularly good at multitasking, so I use different applications for different purposes. The act of switching applications itself becomes a cue for my mind to shift to another task. By “minimal,” I do not simply mean reducing the number of applications. I try to give each one a clear role and avoid adding more settings or plugins than necessary. Many modern tools also come with excellent defaults, so I generally use them as they are without extensive customization.
Applications
Zed
I switched from VS Code to Zed as my editor. I was never particularly attached to any editor, nor did I have major complaints about VS Code. I tried Zed while reviewing my development environment, and it ended up becoming my regular editor. I currently use it close to its default state, with almost no additional plugins or configuration.
Ghostty
I switched from the default macOS Terminal app to Ghostty. Again, I do not customize it much and use mostly the default settings.
SnippetsLab
I use SnippetsLab to keep code snippets and notes while I work. I have moved between various tools, including Obsidian and Notion, but for now I have settled on SnippetsLab. I will probably stick with it for a while.
1Password
In addition to passwords, I use 1Password to manage environment variables and AWS credentials.
One feature I find especially useful is that I do not have to store the .env files used by my projects in plaintext.
I generally set it up as follows:
- Sign in to the 1Password desktop app and enable the Developer features
- Go to
Developer>Environmentsand create an environment - Add the required environment variables to it
- Open its
Destinationsand selectLocal .env file - Choose the target project’s
.envfile as the destination and mount it
When I need to use those environment variables from the shell, I configure the project’s mise.toml to load the .env file.
This means I no longer need to keep secrets in plaintext inside a project, while values for different environments can be managed in 1Password. This is the approach I currently use.
Hardware
I use a company-issued 14-inch MacBook Pro (2023). Sometimes I use the MacBook Pro’s built-in keyboard, while at other times I pair an HHKB Type-S with a Magic Trackpad. The Magic Trackpad’s battery has deteriorated considerably, and it is still a model with a Lightning port, so I would like to replace it at some point.
AI agents
It feels as though we have entered an era in which AI agents are a natural part of the development environment. I currently use Codex and Claude Code. I think their standard capabilities are generally sufficient, but there are several areas of front-end knowledge I want to supplement, so I add relevant skills. I install skills shared across projects at the user scope through the GitHub CLI.
for agent in codex claude-code; do
gh skill install anthropics/skills frontend-design --agent "$agent" --scope user
gh skill install ibelick/ui-skills fixing-accessibility --agent "$agent" --scope user
gh skill install ibelick/ui-skills fixing-metadata --agent "$agent" --scope user
gh skill install ibelick/ui-skills fixing-motion-performance --agent "$agent" --scope user
gh skill install microsoft/playwright-cli playwright-cli --agent "$agent" --scope user
done
Depending on a project’s structure and purpose, I also add skills such as:
ibelick/ui-skills baseline-uivercel-labs/agent-skills react-best-practicessupabase/agent-skills supabase-postgres-best-practicesvercel-labs/skills find-skills
Too many skills become difficult to manage, so rather than installing everything from the outset, I only add what I need when I need it. Before using a third-party skill, I also make a point of checking its publisher and repository contents.
Tool management
I use Homebrew and mise to manage CLI tools and runtimes. Their responsibilities are roughly divided as follows:
- Homebrew: Installing GUI applications and mise itself
- mise: Managing versions of language runtimes and CLI tools
- dotfiles: Managing configuration files
For npm, I explicitly specify the registry to use.
$ npm config get registry
https://npm.flatt.tech/
Given the security issues surrounding npm packages, I use this setting to control where packages are retrieved from.
dotfiles
No matter how much time passes, dotfiles remain wonderful. With my current setup, I first install Homebrew and then set up mise. After that, I link the configuration files managed in my dotfiles repository to their expected locations, and the basic environment is ready.
The overall flow looks like this:
Homebrew
↓
mise
↓
Link configuration files
My goal is to minimize manual work while keeping the setup understandable when I revisit it later.
Conclusion
This review focused on the following points:
- Give each tool a clear role
- Use switching applications as a way to switch contexts
- Avoid adding unnecessary settings and plugins
- Do not store secrets in plaintext
- Use AI agents and third-party skills safely
- Make the environment reproducible from dotfiles
Rather than simply reducing the number of tools, I try to separate their responsibilities, keep configuration to a minimum, and use them safely. I plan to keep working with this setup for a while.