A Detailed Map of my Development Environment

/ 27 April 2022

I’ve come to realize just how not-vanilla the assorted elements of the environment I’ve set up on my Mac for various kinds of programing is. In an effort to keep track of what I’ve done for my own good I thought it worth sharing, as pieces of it may be worth those who also dabble in software/web development work knowing about.

Hardware/Physical Environment

Anyone’s work environment starts out in the physical world, usually a desk. I have my maxed-out M1 Mac mini sitting under my desk on top of a set of drawers, with two displays (which include speakers, USB ports, and a webcam/mic above one of them) and the Apple magic trackpad and keyboard on the main desk surface. Almost nothing else is on the surface of my desk regularly except for some sticky notes for note taking (the rare times that is not done electronically, usually with Notes or Tot), and a desk lamp that has some storage for things like scissors, my AirPod Pros (when they are at my desk) and such. I do also have a Lightning cable ready under the displays to charge the peripherals and attach my iOS devices if needed.

Also as I’m sure is the case with most professionals, just don’t ask us to show you the inside of our desk drawers. That said, my drawers are quite clean too, with primarily some additional tech equipment (including cleaning cloths) and writing utensils. Aside from the Mac itself, the top of the drawers (that are below my desk and thus usually not seen) include a USB hub (which has a card reader, the aforementioned Lightning cable, and the USB cable to my display’s USB hub always attached, and has 4 more free ports when needed) and my backup disk. It is here that I also have the power strip for everything, to keep the cables off the floor/away from cats.

I can easily look out the windows on either side of my desk whenever I want, but they rarely shine glare on the displays. These also means that for the handful of weeks a year in MN that the windows can be open I get nice fresh air.

Command-line and System-level Enhancements

All development centers on the command line and other system-level enhancements to some degree. The main command one enhancements I use for development are:

  • Homebrew for easier command-line tools management
    • Shell script run by launchd weekly to automate package updating, though a few packages get pinned so they only update when I check manually.
  • GPG Suite for standards-ready file encryption and digital signing of all my Git activity
  • Installed latest Git via Homebrew rather than using the one Xcode command line tools provides as that is not updated often
  • Installed wp and php so both are accessible everywhere
  • Installed Fish shell via Homebrew instead of using a shell that comes with macOS and have multiple customizations to its configuration (which I store in a Git repository). Any seasoned developer or command line user on UNIX systems will end up with many of these, a few of mine others may want to take note of:
    • Assorted colorizing of command output to make reading easier
    • A function to open man pages in Dash if installed, Preview otherwise, and only in the command line if using SSH
    • Alias that replace cat with bat and ls with exa * Aliases for each client site that let me type the domain of a site to expand the SSH connection command
  • Many .gitconfig settings, of which here are a sampling:
    • Any Git user should know about user.name and user.email for defining who you are for Git repos already, but there is also user.signingkey which allows you to let Git know which GPG key to use for all digital signing Git can do so you don’t have to provide it each time
    • core.excludesfile lets you define a global excludes list for all repos where you can put standard exclusions
    • color.(many things) lets you colorize Git output
    • With alias you can define internal aliases for common operations you do
    • credential.helper can be set to osxkeychain to let Git use the macOS Keychain mechanism for HTTPS repo credentials
    • status.submodulesummary = 1 is good to always show status of submodules when running git status on your repos
    • You can specify what merge and diff tool to use if you want to, say, always use Apple’s opendiff (FileMerge, provided as part of Xcode) tool for these, and also define default settings for those operations
  • ShellHistory is an app that provides a better experience for searching your shell history than what simply using history does. It can also be used on the command-line to search history inline.

Apart from Dash, which I alluded to above and has a dedicated function key on my Apple keyboard with numeric keypad, other system-wide developer tools I use include:

  • DevUtils is a great collection of helpful tools for all development. I usually access it via Raycast, which in its own right is an awesome Spotlight-replacement for me.
  • OpenIn allows me to selectively change what browser certain sites open in. I use this in part to open Local sites (see next section) in MS Edge only if it is running (where I assume it is running in dev mode), but otherwise Safari.
  • Paste is a good clipboard history manager which comes in handy a lot, but definitely for code editing, as does SnippetsLab for more longterm storage. Both of these also get dedicated function keys.
  • SSH Config Editor is a good way to easily make modifications to my SSH config files.

I use 1Password for virtually all passwords and other sensitive details I keep, a sizable portion of which are client-related (and an even larger portion of which are those that Watchtower complains about, because client credentials I can’t change really). But I’ve also started using the 1Password SSH Agent, which allows me to both use a single SSH key across all my devices, and protects that key behind biometric authentication. I no longer keep any SSH keys of my own besides those that are provided by 1Password. I also use the op command-line interface for 1Password to store the tokens used for things like the Linode and AWS CLI tools in 1Password rather than on the filesystem.

When you are programming in a given language it is also a decent idea to not rely on OS-provided packages of those languages (Swift, Apple’s own language, is about the sole exception). To that end Homebrew helps, like for PHP. But tools such a pyenv and rvm help for Python and Ruby, respectively. I’ve tried similar tools for PHP, but none worked on Apple Silicon back when I tried, plus anyway PHP is (mostly) provided inside Local. That said, I did have to separately build Xdebug for my Mac and include it in the PHP config within Homebrew to have access from the normal command-line.

Web Development Environment

At the heart of a web development environment is the running of local copies of websites. If you are editing a site live on the internet over SFTP you are playing a very dangerous game indeed (and an even more dangerous one if you are using FTP). I use Local for this task. Each client has their own local site I update from their live site once or twice a year, and I also have a few local sites for pure experimenting and running WordPress betas. I use Git to move code to the live sites when changes are ready to go live. I make a few adjustments to the sites that Local creates:

  • Add two files to the root folder that enable the WP-CLI from anywhere in the folder structure. The first of which I actually also use on live websites with the appropriate path; the second of which includes the socket path for the site’s database:
# wp-cli.local.yml
path: app/public
require:
  - wp-cli.local.php
  
# wp-clsi.local.php
<?php
define('DB_HOST', 'localhost:/path/to/local/site/mysqld.sock'); # find this in Local's Database tab

error_reporting(0);
@ini_set('display_errors', 0);
define( 'WP_DEBUG', false );

  • Add some content to the wp-config.php that send debug messages to a log file and aid in forcing Jetpack to staging mode:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', '/path/to/site/folder/logs/php/debug.log' );
define( 'JETPACK_DEV_DEBUG', true );
define( 'JETPACK_STAGING_MODE', true );
  • I usually have a backups folder in the app folder that is where I store copies of the database backup files I have imported to create the local sites
  • If the live site uses a service to send email, rather than using the server itself, make sure that plugin is removed from the local site

While client sites use WordPress, some family websites (this website and the Tenseg website among them) use Jekyll. I store the Jekyll site source files in folders alongside Local site folders, but do not need any special software beyond the Jekyll tools themselves.

Visual Studio Code is my primary home for web development work, and increasingly actually many more areas of programming. I have a significant number of useful extensions that I rely on for whatever the project may be:

I am also mainly using workspaces that have additional setup that streamlines my environment. Particularly I have:

  • Named each folder so the Explorers are prettier
  • Explicitly list each folder for Intelephense since it is not well aware of workspaces
  • Include a number of Tasks:
    • Ones that use my lbf tool to automatically start and manage the associated Local site. Most of the time Local is just hidden, and is controlled only by these tasks.
    • Ones for running tests and launching the dev mode of MS Edge (/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge --remote-debugging-port=9222)
  • Included all my debug configurations, which are just PHP and Compounds to let me quickly start everything together, but the JS configs themselves must be in their project folders to work correctly

I use BBEdit for pretty much any other code editing tasks. Specific additions I have made to it are:

  • Text Filters to make JSON pretty (python -m json.tool) and unserialize PHP serialized data (print_r(unserialize(trim(fgets(STDIN))));)
  • Reviewed every setting and tweaked them to my needs (I tend to do this sort of thing with every app I install)

I use ForkLift as my SFTP client. Each server has its own favorite, and I like that Forklift is generally as unobtrusive as Finder, so it makes moving files quite integrated into my daily life.

I use TablePlus as my MySQL client. Again, each live server has a favorite, but here so do all my Local sites since Local’s extension for TablePlus didn’t understand the Setapp version when I first found it. Note, though, that I also launch TablePlus via a Shortcut for the moment since otherwise it seems unaware of the 1Password SSH Agent.

Paw is an awesome API tester app, which I use to experiment with REST APIs and it can even export calls as code ready for almost any language and system with its extension support. Any site which uses custom APIs I tend to have a Paw project for where I can easily experiment with endpoints as they’re under development.

Though a built-in Apple utility, it seems worth noting that Console is a great app for reviewing logs from web servers. It helps that Nginx defaults its logs to have the .log extension, but even if your log files do not you can open them in Console. As they’re appended to you are able to just scroll down to see new content. I like that I do not have the ability to edit log files this way, rather than opening them in BBEdit.

Apple Ecosystem Development Environment

I do very little development of apps or tools for macOS these days. But when I do, naturally Xcode is the primary environment. What I do that may be a bit different here is that I’ve symlinked many of the apps bundled in Xcode (Simulator, Instruments, FileMerge) into my home folder’s Applications folder to make them easier to use separately. Part of this is that the iOS Simulator is sometimes convenient in web development, as an iOS device that has access to my Local sites.

One thing that I have done in Xcode to make it a bit more like VS Code is created a Behavior that opens a Terminal to the project root, since there is no integrated Terminal. That is simply a script that runs open -a Terminal "$SRCROOT". I actually have also unified a keyboard shortcut across Xcode, VS Code, and Finder to open Terminal to the relevant folder from all of those apps, since the command line is such a significantly useful environment for me.

A few other Mac app centered utilities that I have that are tangential to development include:

  • Apparency and Suspicious Package both give me insight into the dependencies and security state of Mac apps
  • MacUpdater ensures that I regularly install updates to the apps I have installed that are not from the App Store or Setapp (which manage their own updates)
  • Lingon helps me manage launchd and any custom jobs I run

Conclusions

In addition to the above apps and settings I have taken the time to unify basic settings in all my development apps as best I can. I both make the text color schemes match closely to one another (and work best in my all Dark Mode environment) and utilize a ligaturized version of SF Mono everywhere. Sidebar settings match my accent color and so forth as well in VS Code.

I documented as much of my development environment here as possible in the hope that it may inspire others with some of my ideas. My development environment is constantly changing, so this snapshot will not necessarily be totally accurate even a few months from now. I will append this post with updates as needed, which I am sure there eventually will be updates.

Discussion