How I set up macOS for web development
preamble
One of the weird things I enjoy doing is installing and configuring a computer for a needed development environment.
At a previous startup, I was involved with onboarding every new hire (dozens, over the course of a year or two). One aspect of our engineering culture was that developers needed to be extremely empowered to ship and deliver code and product features. As such, our goal was that each new hire would deploy code to production on their first day. Does that sound insane to you? Each new hire was paired with an experienced engineer and a specific ticket would be carefully groomed such that the trainer would certainly be able to be able to resolve it, but also that would expose the new hire to a decent selection of the product and codebase. So, that first commit really was mostly a puppet act, but the new hire was there present, typing each key, pressing commit, working through CI, etc. This was so important to me as I find a lot of the anxiousness with starting a new job is the self-doubt about your ability to actually perform the job you were hired for. I found that ripping this band-aid off on day 1 really set up most engineers for a lot of success. It wasn’t designed to be stressful, it was more a testament to a lean and agile development process. There was a lot that went on behind the scenes to set these new hires up for success.
If you’ve never had to attempt to navigate the absolute minefield that is installing and configuring a legacy/enterprise application for local development, let me describe it to you - hell. If you’re lucky there’s a setup guide you can follow. When the last time the guide was updated - no clue. At least in the nodejs and Ruby on Rails world, there tend to be a few dependencies that are just a nightmare to get running. Needing specific versions of databases, JDK, etc.
But here’s the crazy thing - I actually kind of like doing that. Getting so lost in the dank abandoned corners of StackOverflow posts with no replies and you don’t even have enough rep to save a hint for others. Decades old Github issues, long since abandoned by a maintainer, closed by bots. It’s a real time struggle against the inevitable decay of the digital world. I especially like bearing that weight so that some hapless young new graduate doesn’t have to ask themselves existential questions because they can’t figure out how to install OpenJDK.
So, I’d find myself literally speed-running our development setup process. I’d get a freshly wiped laptop from IT, start a timer, and speed run our “Getting Started” guide. I think I got it down to under 30 min and that’s with a full DB restore (multi-gig). Before a new hire would start, I’d usually squirrel away some time to run through it again to make sure that first day went as smoothly for them as possible.
Naturally, this interest extends into my personal development environment. I just really like installing and configuring development tools on macOS! I’m constantly tweaking and experimenting with new tools and processes. I’ve spent a lot of time experimenting with nix, devbox, brew (but not really mac ports), and I’ve finally come to a place where I’m pretty comfortable with my disaster recovery plan.
backup files
I just zipped up all the folders in my home folder and stuck them on some external storage. I do have Time Machine set up, but I like to go totally “fresh”.
I have some specific configs/profiles for Sublime Text and OBS Studio, which I manually archived and restored. For Sublime Text, I archived ~/Library/Application Support/Sublime Text/Packages/User/
. For OBS, I archived the entire ~/Library/Application Support/obs-studio/
directory.
Before deleting anything, it’s well worth taking some time to consider what you absolutely must have working and don’t want to have to play with. Ideally these files will be backed up (potentially also symlinked into iCloud). It’s also worth setting some goals for what your system will look like when you’re done. For me, running this blog is a goal.
reinstall macOS
Or, if you feel like waiting around for the rest of your life to install an outdated version of macOS, use the built in system recovery.
dotfiles
In a previous post, I discussed moving my dotfiles to my iCloud Drive. This is a great option as it can be end-to-end encrypted and is very integrated into macOS. After I’ve logged into iCloud, I cd
into ~/Library/Mobile\ Documents/com~apple~CloudDocs/dotfiles
which contains this setup script:
This does a few things:
- symbolic link iCloud Drive into my home directory, called ‘iCloud’
- create my .zshrc by symlinking into iCloud Drive
- symlink my .ssh settings/keys/identities
- symlink my
$XDG_CONFIG_HOME
.config directory to iCloud Drive - symlink a
notes.md
file I keep in my home directory - copy non-free fonts from iCloud Drive into the macOS fonts folder (nested folders work fine!)
All our configs and settings (including nano syntax highlighting!) are now ready for use! At least anything that respects the xdg base directory specification↗ .
Homebrew
I’ve settled on homebrew↗ as my package manager of choice. It’s just powerful enough to let me automate a lot of stuff, but yet simple enough that I can hack on it when I need to. Nix/devbox ultimately didn’t work for my local development needs, though I admit that’s likely due to lack of skill on my part. Maybe don’t name like 5 things the exact same thing?
Anyways, with a Brewfile, you can simply run brew bundle
and it will install everything in your Brewfile. You can even specify a Brewfile, so you could have multiple if you wanted to be able to bundle dependencies in that way. There’s even a strange brew bundle exec
command, which I intend to research further as a means to achieve some of the isolated environment features nix et al bring to the table. See the full documentation. Using mas
, I can install apps from the Mac App Store, such as Affinity Designer. I also install fonts using homebrew font casks. Here’s my current Brewfile:
For Mac App Store apps, make sure you open and are signed into the store before trying to install these apps or it will fail. It’s also a bit finicky, but I’ve had success.
At this point, I have all my packages and their config files ready to use!
maintenance
brew update
- this refreshes homebrew, basically updates the list of available packages and versions. It’s needed to make sure you get the latest version of a package when you install it.
brew upgrade
- upgrade (update to the latest version) all your brew packages
brew cu -a
- provided by brew-cask-upgrade. Passing the -a
flag will update apps that have their own built in auto updater. I like it because then I can update the apps when it’s convenient for me, not when they are booting up. This also runs brew update
.
brew bundle cleanup
will list any brew stuff you’ve installed that is not in your Brewfile. I find it very helpful just to get a diff vs my expected development environment. Then I can review to either add it to the Brewfile, or run brew bundle cleanup --force
to remove them.
brew cleanup
& brew autoremove
- theoretically cleanup also runs autoremove, I still run both of them.
If you follow my advice and manually run brew update
on a regular basis and before doing any major ‘brewing, you can safely turn off homebrew’s auto-update feature before it runs any command: export HOMEBREW_NO_AUTO_UPDATE=1
You can also disable hints for using homebrew with export HOMEBREW_NO_ENV_HINTS=1
closing thoughts
I was really impressed with how smoothly my most recent wipe went. There seem to be inevitable macOS tweaks I have to do after a fresh setup, but I sort of like restarting that way. I should also mention that this setup would likely work pretty nicely across multiple machines.
If you have any ideas on how I can improve this setup, please send me a Reply and I’ll check it out.