Hire me! (click for details)
I am a software developer. After teaching myself to code on Apple II computers in junior high school and building software for our family-owned telecom business, my degree in Computer Science from Harvey Mudd College gave me the grounding for a career in software development where I was able to let my programming freak flag fly: I worked for Knowledge Adventure, an educational/games software company (where I first started leading teams of developers), HomePage.com, an internet startup (where we developed a white-label free web hosting platform), and MySQL Ab (where I led the web development team and then transitioned over to the server development team).
Through this time I was also involved in the open source community. In
college, I was involved in the very early days of Linux, and I took over
maintenance from Linus of the “root” disk used to install Linux before
distributions. While working on websites for Knowledge Adventure, I got
involved with the the PHP project, and
became a founding member of the PHP Group. I helped set up and maintain some
of the collaborative development infrastructure, including the mailing lists
and online documentation feedback system. Then at HomePage.com,
I contributed work we did related to mod_perl
, and became a member of the Apache Software Foundation.
After MySQL Ab was acquired by Sun Microsystems (and then further by Oracle), I co-founded Raw Materials Art Supplies, a retail store in downtown Los Angeles which I operated as general manager for the last 15 years. I built Scat POS, a point of sale and ecommerce system using PHP and MySQL that we used to run the business. It involved a lot of integration work (payment processors, product feeds, shipping), and I automated as much as I could to focus on our artists' needs. This enabled us to build our business from scratch into one of the top independent art supply stores in the country.
I’m primarily looking for individual contributor roles. PHP/MySQL is the core of what I do best, but I'm comfortable with full-stack web development, Linux system administration, C/C++, Docker, and confident in my abilities to jump into any tech stack and get up to speed quickly.
View my LinkedIn profile for some dates and details, and my GitHub profile for some of my open source contributions. Contact me to discuss opportunities.
As jacked as it sounds
Dries Buytaert, founder of the Drupal project, wrote a great article on “Solving the Maker-Taker problem,” about how Drupal built a system to recognize the contributions of community members and their sponsors. I am not wild about the “Maker-Taker” terminology because it gives me Election 2012 flashbacks, but I don’t have anything better to propose. (It was this post by Ben Werdmuller that brought the article to my attention.)
By transparently rewarding contributions and fostering collaboration, we can build healthier open source ecosystems. A credit system can help make open source more sustainable and fair, driving growth, competitiveness, and potentially creating thousands of new open source businesses.
It looks like there is a lot to like about the Drupal contribution credit system and their approach to community contribution in general.
This idea of how money and other benefits should flow within the free software (and open source) ecosystem has been on my mind for over 32 years and it is frustrating to me that I still don’t feel like I know how I feel about it.
It has been easy, at times, to feel like I have ended up on the wrong side of the deal. It felt pretty good when I made money (a little) from MySQL’s sale to Sun Microsystems. I also feel pretty dumb when I’m working alongside people on open source projects where they’re getting paid and I’m not, or someone else entirely is landing investments and spinning up large companies based on the work of communities to which I’ve contributed.
But this isn’t just a feeling I have encountered in my open source work, it was also something I felt when we ran our art supply store. It was a frustrating feeling to hustle to cut a good deal for a non-profit organization where you know the staff there is being paid a better salary than you could afford to pay yourself, and more often than not they would just rely on the big online suppliers rather than even bring the business to us, the small local business.
Maybe my feelings are complicated because I never managed to become post-economic. My version of becoming post-economic was supposed to be running an art supply store, and instead it turned me sub-economic.
Feel like a cheap bouquet of flowers
I took this photo back in 2005 of a building at 3rd and Broadway in downtown Los Angeles as it was just starting to be converted into residential lofts. Gutted, full of promise, and destined for better things. I can relate.
Configuring third-party Apt repositories with Ubuntu
I just upgraded my primary development machine to the latest Ubuntu LTS (24.04.1) and it disabled the third-party sources I had set up for things like WezTerm, Tailscale, Syncthing, and the GitHub CLI. (It warned it would be doing this.)
When I re-enabled them, I finally made sure they were set up in the modern-ish way and no longer complained about keys that were installed using deprecated methods.
Now, the keychain for each repository is in the /usr/share/keyrings
directory in a file named something like tailscale-archive-keyring.gpg
. Some of the keys were mashed together in /etc/apt/trusted.gpg
before, but apt-key list
showed those were keys that I had individually under /usr/share/keyrings
so I just went ahead and deleted /etc/apt/trusted.gpg
entirely.
For each repository, there is either a repository
.list
or repository
.sources
file in /etc/apt/sources.list.d
. The .list
files are in “one line format”, and the .sources
files in a friendlier key-value format called “deb822”. The formats are explained the sources.list
(5) man page.
For example, here’s the /etc/apt/sources.list.d/tailscale.sources
:
Enabled: yes
Types: deb
URIs: https://pkgs.tailscale.com/stable/ubuntu
Suites: noble
Components: main
Signed-By: /usr/share/keyrings/tailscale-archive-keyring.gpg
And /etc/apt/sources.list.d/wezterm.list
:
deb [signed-by=/usr/share/keyrings/wezterm-fury.gpg] https://apt.fury.io/wez/ * *
The files in my /etc/apt/sources.list.d
were a bit of a jumble, where some had been renamed with an extra .distUpgrade
extension and the deb822-formated files had Enabled: no
. I moved everything back into the proper filenames and changed those yeses to nos, and now when I run apt update
it pulls all from all of the appropriate sources and doesn’t complain about any deprecated keys.
Instead of referencing a key file in the Signed-By
line of a source, you can also directly embed the ASCII representation of the key. With that, the tailscale.sources
looks like:
Enabled: yes
Types: deb
URIs: https://pkgs.tailscale.com/stable/ubuntu
Suites: noble
Components: main
Signed-By:
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=AIVM
-----END PGP PUBLIC KEY BLOCK-----
I converted the binary format of the key (the .gpg
file) into that text representation with:
$ gpg --keyring tailscale-archive-keyring.gpg --no-default-keyring --export -a
Put a pin in it
I did a first cut of moving my bookmarks on to this site instead of using Pinboard.
I still need to wire up a way to create new bookmarks, but that should be pretty straightforward.
Each bookmark can carry with it an excerpt and a comment. That way I can clip a little text for the bookmark while I’m creating it. None of the existing bookmarks use that yet because Pinboard did no such thing.
Things to be done aside from adding a way to add bookmarks include adding an Atom feed, really wiring them up to search, figure out how to be marking these up in a more IndieWeb-friendly way, and thinking about whether I want to take snapshots of the bookmarks. (Those would probably only be accessible to me to avoid copyright headaches.)
The oldest sins the newest kind of ways
When I wanted to get away from using Discord to participate in the online PHP and IndieWeb communities, I did still want a web-based interface that provided access to the backlog of conversations from when I was offline, which IRC servers don’t generally do on their own.
I landed on using The Lounge, which has worked out very well.
I run it on my home server in Docker and it is exposed to my Tailscale tailnet so if I ever was on the road, I could still access it. The configuration is pretty straightforward. There’s a docker-compose.yml
file:
version: '3.9'
services:
tailscale:
image: tailscale/tailscale:latest
hostname: thelounge
env_file: ./.env
environment:
- TS_SERVE_CONFIG=/config/thelounge.json
- TS_STATE_DIR=/var/lib/tailscale
volumes:
- ts_state:/var/lib/tailscale
- ./config/tailscale:/config
- /dev/net/tun:/dev/net/tun
cap_add:
- net_admin
- sys_module
restart: always
backend:
image: ghcr.io/thelounge/thelounge:latest
env_file: ./.env
volumes:
- lounge_state:/var/opt/thelounge
expose:
- "9000/tcp"
restart: always
volumes:
ts_state:
lounge_state:
And config/tailscale/thelounge.json
:
{
"TCP": {
"443": {
"HTTPS": true
}
},
"Web": {
"${TS_CERT_DOMAIN}:443": {
"Handlers": {
"/": {
"Proxy": "http://backend:9000"
}
}
}
},
"AllowFunnel": {
"${TS_CERT_DOMAIN}:443": false
}
}
There is an .env
file that sets TS_AUTHKEY
and TS_EXTRA_ARGS
. It looks kind of like this:
TS_AUTHKEY="tskey-client-{something}?ephemeral=false"
TS_EXTRA_ARGS="--advertise-tags=tag:container --reset"
Never was a cloudy day
Another month in the rear view mirror, and time for me to reflect on my job search and remind people that I’m still out on these streets looking.
Some days it feels like I am not any closer to having a job than I was when this all started. I have a lot of applications behind me, a handful of screening calls with recruiters, a smaller number of interviews, and as of yet no offers. I have been ghosted on countless applications and once after a screening call. (That position still gets re-listed periodically.)
I am in the interview process with a very large tech company with a drawn-out interview process. I have very mixed feelings about it. Do I really want to work there? Am I going to pass through their interview hoops? A big factor in those mixed feelings is that if an offer does happen, it will require moving to another city and working out of an office several days a week.
Some of the rejections, even when they came early, have been hard. There have been positions at a few different companies that were focused on building internal tools for the company, which is something I believe I would be particularly well-suited to do, so I have been looking harder at listings that push that button.
I am grateful to everyone who has offered support, and leads on positions.
Into the blue again after the money’s gone
A reason that I finally implemented better thread navigation for the PHP mailing list archives is because it was a bit of unfinished business — I had implemented it for the MySQL mailing lists (RIP), but never brought it back over to the PHP mailing lists. There, it accessed the MySQL database used by the Colobus server directly, but this time I exposed what I needed through NNTP.
An advantage to doing it this way is that anyone can still clone the site and run it against the NNTP server during development without needing any access to the database server. There may be future features that require coming up with ways of exposing more via NNTP, but I suspect a lot of ideas will not.
Another reason to implement thread navigation was that a hobby of mine is poking at the history of the PHP project, and I wanted to make it easier to dive into old threads like this thread from 2013 when Anthony Ferrara, a prominent PHP internals developer, left the list. (The tweet mentioned in the post is gone now, but you can find it and more context from this post on his blog.)
Reading this very long thread about the 2016 RFC to adopt a Code of Conduct (which never came to a vote) was another of those bits of history that I knew was out there but hadn’t been able to read quite so easily.
Which just leads me to tap the sign and point out that there is a de facto Code of Conduct and a group administering it.
I think implementing a search engine for the mailing list archives may be an upcoming project because it is still kind of a hassle to dig threads up. I’m thinking of using Manticore Search. Probably by building it into Colobus and exposing it via another NNTP extension.
Surprise, it’s a new release of Colobus!
Because there’s nothing that quite says “hire me” like polishing your Perl bona fides, I have finally made a new release of Colobus, the NNTP server that runs on top of ezmlm and Mlmmj mail archives. (It was actually three new releases, I had to work out some kinks. More to come as I work out more in testing.)
The Mlmmj support and some of the other tweaks are really just pulling in and polishing changes that had been made to the install used for the the PHP.net mailing lists. There are also a few bug fixes I pulled in from Ask’s fork of colobus that the perl.org project uses.
I did add a significant new feature, which is a non-standard XTHREAD id_or_msgid
command that returns an XOVER
-style result for all of the messages in the same “thread”. The code to take advantage of this new feature for the PHP mailing list archives is on the way.
Writing documentation in anger
As I continue to slog through my job search, I also continue to contribute in various ways to the PHP project. Taking some inspiration from the notion of “good trouble” so wonderfully modeled by John Lewis, I have been pushing against some of the boundaries to move and expand the project.
In a recent email to the Internals mailing list from Larry Garfield, he said:
And that's before we even run into the long-standing Internals aversion to even recognizing the existence of 3rd party tools for fear of "endorsing" anything. (With the inexplicable exception of Docuwiki.)
I can guess about a lot of the history there, but I think it is time to recognize that the state of the PHP ecosystem in 2024 has come a long way since the more rough-and-tumble days when related projects like Composer were experimental.
So I took the small step of submitting a couple of pull requests to add a chapter about Composer and an example of using it’s autoloader to the documentation.
The PHP documentation should be more inclusive, and I think the best way to make that happen is for me and others to just starting making the contributions. We need to shake off the notion that this is somehow unusual, not choose to say nothing about third-party tools for fear of “favoring” one over the other, and help support the whole PHP ecosystem though its primary documentation.
I would love to add a chapter on static analysis tools. And another one about linting and refactoring tools. Maybe a chapter on frameworks.
None of these have to be long or exhaustive. They only need to introduce the main concepts and give the reader a better sense of what is possible and the grounding to do more research on their own.
A big benefit of putting this sort of information in the documentation is that there are teams of people working on translating the documentation to other languages.
And yes, contributing to the PHP documentation can be kind of tedious because the tooling is pretty baroque. I am happy to help hammer any text that someone writes into the right shape to make it into the documentation, just send me what you have. If you want to do more of the heavy lifting, join the PHP documentation team email list and let’s make more good trouble together.
Choosing kindness
I really liked these thoughts from Hannah Aubry on choosing kindness, and the whole thing is worth a read.
So, I have learned to choose kindness because it doesn't cost anything, and it's priceless. It can change someone's day, month, or life trajectory.
If I am optimistic it is because I am still holding on to this ideal and think there are enough other people out there who do, too.
It is something I try to do every day. I don’t always get there, but the trying continues.
$20, same as downtown
Why CVS and Target Locking Up Products Is Backfiring - Bloomberg
None of that larceny is going to be thwarted by turning stores into plexiglass wastelands. But for the segment of theft happening off shelves, is putting products behind barriers an effective prevention measure? Although actual data is scarce, the answer seems to be yes. But that yes comes with significant caveats. Locking up merchandise “does work in the sense that it reduces theft” in the most basic way possible, says GlobalData’s Saunders. “The problem is it also reduces sales.”
I found this to be a really well-reported and balanced piece on the (growing) practice of locking up products by retailers.
This is something we always struggled with at our store. We were required by state law to have spray paint either behind a counter or in locked cases, but everything we did beyond that was prompted by our own experience. Paint markers were locked up or behind our counter. Expensive sets were behind our counter. We had a line of oil paint in stick form (“pigment sticks”) that were locked up. We came close to putting doors on an expensive line of oil paints.
We were a small business so we didn’t have great numbers on shoplifting. The monetary value of what was stolen was not a significant factor in our closing, but having to worry about it and manage it absolutely was. Arguing with customers about whether they had to leave their bag at our counter, or whether someone was “following them around,” is awful. Sometimes we caught shoplifters red-handed and had to worry about how they would react when they were confronted. I was threatened with a gun once, and we had several physical altercations over the years. Sometimes we noticed an item was missing after the fact and were able to pull video footage of it being stolen by a frequent customer. It was all soul-crushing, in aggregate.
So much of society just feels totally unsustainable right now, so put this down as one part of it.
Retain that dear perfection
An adjustment to my résumé that I made a while ago was to split out the software engineering work I did for our store into a position at Imperial Dog, Inc. which is the name of the corporation that did business as Raw Materials Art Supplies and served as the production company behind some of Celia’s short films.
I used the title of “Staff Software Engineer” as a sort of anchor to the level that I see myself at, but in hindsight that may have been too aggressive when applying to lower-titled roles like “Senior Software Engineer.” So I’ve updated it to “Consulting Software Engineer” which feels sort of vague and non-leveled and hopefully not similarly off-putting.
I am progressing along the interview path for a few positions now, so I have some hope that this drought will end soon and I can escape this particular purgatory.
I have slowed down on applying to jobs as I hold out hope for the current prospects, and I have been getting more involved with the PHP project again in some of the same ways that I was involved 20+ years ago.
I tackled a couple of projects in the documentation, including filling in some of the missing history of the project, removing some ancient documentation for XForms (a technology that never really got traction), and adding background and supporting material about the hash functions. A couple of these were picked up from outstanding issues in the repository, or inspired by conversations on the internals mailing list about possible upcoming deprecations.
I am also trying to dive in on the infrastructure and governance sides, which is slower going. I have mostly documented who the members of the PHP organization on GitHub are. I have access again to the machine where the mailing lists run, so I have been working on getting its configuration properly handled within the big ball of code that the team uses to manage the project infrastructure, and trying to help clean that stuff up and make sure more of the setup is known and documented. The team supporting all of this infrastructure had dwindled over the years but things have been kind of chugging along.
One way to sum up what I am trying to do by getting involved in the PHP community again is to leverage my privilege (from past participation and the usual privilege I carry) to clean up some of the on-ramps for new people to get involved. It moves more slowly than I would like at times, but I am glad to have this time now to apply pressure to the problems that I see. Seeing how the Python community has organized themselves is an inspiration.