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 have been operating 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 are using to run the business. It has 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.
We can rebuild
I visited what was still then East Germany in 1990 as part of the People to People Student Ambassador Program, which is when I took this picture of the ruins of the Frauenkirche Dresden, which had been destroyed during World War II. It has since been rebuilt.
I don’t think it’s in the cards, but it would be interesting to visit Dresden again and see the rebuilt version.
Anyway, this picture caught my eye again as I was looking back at my photo archives and looking at where my life stands.
But they do make me sleepy
Inspired by Claudine Chionh’s mention of using Open Library identifiers, I realized that I could link my isbn:
pseudo-URLs to the Open Library listings instead the ones on Bookshop.org, which sometimes went nowhere because they have a limited catalog. No affiliate revenue for me, but nobody is really reading this or buying books on my recommendations, so I don’t think I’ll miss it.
The Internet Archive would be a fascinating place to work. They are currently hiring a Website Software Engineer, which is way more front-end focused than I’m looking for, and a Director of Platform Engineering which calls for experience I don’t have. Too bad!
Or maybe flambéd
In Chris Ferdinandi’s article about different static site generators (SSGs), he says that they “store content in flat markdown files instead of a database” and that immediately brought to mind “Bake, Don’t Fry,” a blog post from 2002 by Aaron Swartz where he started exploring the concept of baked sites as compared to fried sites.
At the time, you had blogging systems like Blogger and Movable Type that stored their data in a database but generated static pages for viewing, and systems like b2/cafelog (this was pre-WordPress) and OpenACS that stored their data in a database but generated pages on the fly. The first blogging system that I remember that just used the file system to store all of its files was blosxom.
I started this blog on Blogger but quickly switched over to a homegrown PHP/MySQL system in October 2000. I have rewritten it from scratch once or twice since then.
I have been thinking about moving to a setup that baked and published a static version of my site. A reason I haven’t done it is because doing it all fried hasn’t been a performance problem so far.
I’m not convinced that I want all of the content in flat files and not a database. I guess I just don’t see the advantages of working with flat files for how I like to write, and I can’t imagine expecting less technical users deal with things like text files with front-matter and tools to manage those files.
It is strange that all of the popular static-site generators I’ve looked at have filesystem storage baked into their design. It should be rather straightforward to abstract that out a bit, and then you’d be able to pull your content from a database but generate static output. Like Blogger did twenty-five years ago.
Merch required
I knew I had posted about the illustrations in Metro’s signage before, but it turns out that it was twenty years ago from tomorrow.
I think they should make merch with some of these. They have a line of Rude Dude merch, but nothing leveraging all of these illustrations.
Curly’s Drive Thru
What follows is a paper I wrote for a course at Harvey Mudd College in March of 1995. It was a seminar in “Advanced Operating Systems” but really it was a hands-on lab class where we had small teams who each set up a Sun server and did various projects. It was taught by Mike Erlinger, now retired. If I remember correctly, the assignment for this project was for each of us to set up some sort of service or software on the system and write up what we did. My team’s server was named “Curly.”
Here is a scanned PDF of the paper, but I’ve also turned it back into HTML (actually Markdown) here. Mostly via OCR, so there may be typos. Unfortunately, I don’t have any of the configuration files or other code that is being talked about in the paper.
Curly's Drive-Thru
Serving Dynamic and Static Documents on the Web
This is a tour of Curly’s Drive-Thru - a sample demonstration of another stop on the so-called information superhighway, made particularly agonizing to read with the frequent use of fast-food metaphors.
A Brief History of the Web
Using the term in its broadest sense, the Web refers to all the information available 'out there' on the net, whether it is being served by the old stand-bys such as gopher and ftp, or the dominant method of what is usually called the Web, HTTP (or hypertext transfer protocol for those who like the long version).
HTTP and HTML developed almost concurrently with gopher (and it is almost unfortunate that gopher gained the attention it did before HTTP and HTML caught on). Originally developed at CERN, a research laboratory in Europe, the Web did not really start to take off until a program called Mosaic was developed at the National Center for Supercomputing Applications at the University of Illinois in Urbana-Champaign. Mosaic was a graphical browser for the Web, and slowly the power of a graphical interface to the vast resources of the Internet was realized.
Since then, the Web has exploded, with HTTP traffic alone taking up progressively more of the bandwidth of the net, and basically drowning out everything, except possibly Usenet news. The commercial possibilities of the Web are just beginning to be realized, with smaller companies and technical companies leading the way, but with everyone else close on their heels (witness the presence of something as mundane as Zima on the Web).
The Business Plan (HTTP and HTML)
As alluded to above, the primary type of document being pushed around the net as a Web document is called an HTML document. A derivative of SGML, HTML stands for Hypertext Markup Language, and ti is a way of adding information to text about what sort of information each part is (a header, a list, or just part of a paragraph of text), some information about how to display it, hypertext links, and other information.
The primary method of transport for this information is called HTTP, or Hypertext Transfer Protocol. HTTP is a stateless protocol that is similar in many ways to the basic gopher protocol, but more flexible. Combined with the MIME standards, almost any sort of data can be transmitted using HTTP.
Paving the Driveway (Installing a Server)
On Curly, I've elected ot install the NCSA (National Center for Supercomputing Applications, located at the University of Illinois in Urbana-Champaign) Web server, called NCSA httpd. Three other highly-regarded servers include the CERN httpd, BSDI's plexus, and John Hopfield's WN. All four of these are excellent servers, but I selected to install the NCSA httpd because I have found it to be very cleanly coded (thus easy to change) and powerful enough for my needs without all sorts of extra features that I don't need (such as CERN httpd's support for acting as a proxy server, or WN's extensive indexing support).
NCSA httpd can be obtained from ftp.ncsa.uiuc.edu, and documentation is located on the Web from http://hoohoo.ncsa.uiuc.edu/docs/Overview.html
. The most recent version as of this writing is 1.3R, which fixes the major security hole found in version 1.3, but version 1.4. is reportedly in the beta-testing stages and should be out "soon."
Because most of NCSA httpd's configuration is done through configuration files, and not at compilation time, compiling the server is very painless. One thing that must be decided before it is compiled, however, is where it is going to be installed. I decided to install Curly's Drive-Thru in /usr/local/www
— that's located on the partition with the most free space. This location is referred to as $HOME
in the rest of this document.
Wiring the Intercom (httpd.conf)
Most of the configuration for the server itself is done through the file $HOME/conf/httpd.conf
. Things that are configured in this file include where various log files go, which port the server should serve documents from, which user the server should run as, and how the server is running (from inetd
or standalone).
As I already mentioned, our Web service is based in /usr/local/www
, so this is what our ServerRoot
is set to in httpd.conf
. Three main log files are generated by httpd: the transfer log ($HOME/logs/access
), which logs each request sent to the server; the error log ($HOME/logs/errors
), where the server logs any errors that it encounters; and the pid file, where the server records its process ID (which is useful for killing or restarting the server).
Our server runs standalone, as opposed to being started by inetd
, because in a high-traffic environment, this is more efficient. If httpd
were started by inetd
instead of running standalone, it would have to parse its configuration files each time someone connected to the server, which causes an avoidable, thus undesirable, strain on the machine's CPU resources. Running the server standalone does inflict some small penalty in the form of memory being used by the server, but this is not a big deal because of the small memory footprint of NCSA's server.
Finally, although the server runs as root because it needs to connect to a priviliged port (80), it is smart enough to change personalities when it dives in the access files that it is going to serve. I have configured our server to turn itself into the "www" user and join the "www" group, which is the same user that owns all of the documents that are going to be served (convenient coincidence?).
Arranging the Menu (srm.conf)
Another configuration file, srm.conf
, controls certain aspects of how the server is going to serve documents. This is where you tell the server where the top of the document tree is (that is, the first file to serve to people when they access the site), where user's personal pages are stored, and various files and names to treat ni special ways.
Here I've set it up so that the top of Curly's Drive-Thru is located in /usr/local/www/curly
, and the document to serve when a user specifies only a directory and not a specific filename is index.html
. This allows the use of shorter URL's such as http://curly.cs.hmc.edu/
instead of the more complete http://curly.cs.hmc.edu/index.html
. I've also specified "www" as the directory in each user's home directory for their personal Web pages to be located. That is, if a user on Curly wants to offer a file on the Web called "mypage.html," they can place it in a www/
directory of their home directory and tell people to access it with a URL of the form http://curly.cs.hmc.edu/~user/mypage.html
.
It's also possible to set up aliases for certain frequently-used directories, such as where a collection of icons are contained. Also, a command must be put in this file to tell the server where all of the "CGI" programs are located. These are explained more fully later, but basically they are programs that might generate HTML files on their own or process incoming data from users.
Lastly, most of the srm.conf file is dedicated to configuring how the server presents directories where no index.html file is located. What the NCSA httpd does is to build a menu of files from the directory, which pretty little pictures for files and directories.
Setting the Prices (access.conf)
It is probably desirable for a server administrator to be able to regulate which users may access some files. For example, we might want to limit some items on our server to only on-campus users and not just anyone from the rest of the Internet.
The access.conf
file allows the server administrator to set up the default access permissions for directory hierarchies, and tell the server which of those permissions can be overridden by a file called htaccess in subdirectories. Some of the things that can be allowed (or disallowed) include symbolic links, CGI scripts, server-side includes, and server-generated indexes. Usually one would want to be more strict with directories that the administrator might not have direct control over, especially user's directories.
Our server is set up fairly liberally, with symbolic links and indexes allowed any place, and with access allowed to everybody by default.
Installing the Menu and Intercom (HTML and CGI)
As mentioned in the brief history of the Web given above, the primary type of document served over the Web is an HTML file. Such a file is very similar to an ordinary text file, but with special tags included to indicate where paragraphs start and stop, and where various hypertext links should be included. Because it's not really the focus of this document, and there are a plethora of other good sources about writing HTML files, I won't go into any details about such things.
One thing worth mentioning, however, issi the support provided by the NCSA httpd server for so-called "server-side" includes. This extends HTML files slightly by allowing them to contain "include" directives similar to what you might see in C source code. This is useful for doing things like including signatures on the bottom of each file that contain information about the author and when the document was last modified.
One of the most useful things that a Web server such as NCSA's httpd server can do is support the use of CGI programs. CGI stands for "Common Gateway Interface," and specifies how the server talks to auxiliary programs called 'gateways.' These programs can provide a window from the Web to other databases of information, such as HMC's UserInfo system. Typically, CGI programs have information passed to them by users through HTML forms, but they can also be used to do things like transparently serve the user different documents depending on where they are connecting from or what browser they are using.
To allow users the ability to have their own CGI applications hosted by Curly, I've installed a program called 'cgiwrap.' This program acts as a wrapper around user's CGI scripts, and does some very simple security/error checking to at least keep the user partially honest. It will not prevent the user form writing a CGI program that simply sends /etc/passwd
, but there is also nothing to stop the user from simply emailing that same file, so there is really not much of an additional security hole exposed here. It does allow the user the chance to shoot himself or herself in the foot by allowing access to some of their own files unwittingly, so its use should be monitored. Fortunately, cgiwrap creates a log of all the files that are accessed through it so user's CGI applications can be more thoroughly checked by hand to make sure they're not doing anything nasty. It's worth mentioning that NCSA httpd could (and maybe should) be modified to do the same sorts of checking, but it does not currently.
As an example of a CGI application, I've created a what I've called, rather simply, Jim's Quote Server. It is a pair of CGI scripts that allow users to add quotes to an online collection of quotes, and access that database of quotes (currently by getting a random selection, but eventually to include searching the database). While not particularly fancy, the application is a good example of using perl to write CGI applications, and how to handle different aspects of the service through a small number of individual scripts (for example, the same script is used to generate the form for submissions and to handle submissions).
Changing the Register Tape (Maintenance)
Once a Web server is up and running, the most time-consuming thing is adding documents and services. But, like many of the other servers on everyone's system, httpd generates a slew of log messages that need to be handled in some fashion. In addition to the typical archiving and scanning-for-important-things that one does with log files, some people also choose to generate statistics about their Web server from their logs and make those statistics available on their server as well. There is an excellent package available called getstats
which generates all sorts of tables of information, and probably even graphs, but I have not looked at installing it on Curly yet.
Customer Satisfaction (A Note on Standards)
Because the technology of the Web is evolving so rapidly, it is interesting to watch how the standards have emerged. In the beginning, there were relatively few people involved with HTTP, HTML, and other such issues of the Web, so very little was actually set in stone and most of what could be called standards were simply common practice. Many of the things that have since become standards, such as in-line images, were simply how the first person with the idea implemented them (in many cases the developers of NCSA Mosaic, one of the first major graphical Web browsers). Other standards such as CGI were developed once the need was recognized, and usually were simply developed by discussions between a few developers (in the case of CGI, the authors of NCSA httpd and the plexus server, mainly). Now that people have realized that there is money (and substantial amounts of it) to be made from the Web, there is much more politicking involved in developing standards. While NCSA Mosaic clearly violated existing standards (or at least standard practice) when it added support for inline images, not much of an outcry was raised because the standards weren't cemented and there were fewer people involved. When Netscape Communications (then Mosaic Communications) released the first version of their browser, Netscape, there was a rather large outcry about the features that it supported that were non-standard, such as the CENTER tag and the infamous BLINK. Netscape was blasted on the net for allegedly ignoring the HTML/2.0 and HTML3 standards under development.
This has continued with Netscape Communication's announcement and strong push for its security protocol called SSL. Many people on the net have been resistant to Netscape's proposal because they feel that Netscape is simply bullying the W3C (the World Wide Web Consortium, the organization responsible for the development of the Web) by confronting them with a protocol that is already implemented by the most popular browser, Netscape, which gives them a strategic advantage over their competitors. The discussions surrounding Netscape Communication's behavior in these issues is interesting because of the unique position of the company, and the untiring personal involvement of Marc Andressen, one of the original authors of NCSA Mosaic, and one of the founders of Netscape Communications.
Personally, I have adopted many of what have been dubbed "Netscapisms" in pages I've created because they should be simply ignored by most other browsers, and they are really nice for the vast number of users that use Netscape out there.
Would you like fries with that? (Bibliography)
(Unfortunately, this wasn’t included in my remaining print out of the paper.)
Is this Twig or Jinja? Maybe both!
A project I have been playing around with the last couple of weekends has been making a Python version of this site. The code, which is very rough because I barely know what I’m doing and I’m in the hacking-it-together phase as opposed to trying to make it pretty, is in this GitHub repository.
I am using the Flask framework with SQLAlchemy and Jinja.
I was interested to see if I could just use the same templates as my PHP version, which uses Twig, but there have been a few sticking points:
- The Twig
escape
filter takes an argument to more finely control the context it is being used in so it knows how to escape within HTML, or a URI, or an HTML attribute. Jinja’sescape
doesn’t take an argument. I was able to override it take an extra argument, but mostly ignore it for now. - Jinja doesn’t have Twig’s ternary
?:
operator. Not surprising, Python doesn’t either. I rewrote those bits of templates to use slightly more verboseif
blocks. - Jinja doesn’t have Twig’s string comparators like
matches
andstarts with
. Looks like I can get rid of the need for them, but I just punted on those for now. - Jinja doesn’t have a
block()
function. I think I can also avoid needing it. - Jinja’s
url_for()
method expects a more Python-ic argument list, likeurl_for('route', var = 'value')
but Twig uses a dictionary likeurl_for('route', { 'var' : 'value' })
. I was able to override Jinja’s version to handle this, too. - I’ll need to implement versions of Twig’s
date()
function and filter.
I had cobbled together a way on the Twig side to let me store some templates (side navigation, the “Hire me!” message on the front page) in the database, so my next trick is going to implement template loaders for both the PHP and Python versions so that is more cleanly abstracted. I have the Python side of that done already.
I hope to eventually create a Rust version of this, too, and it will be interesting to see what new complications using Tera will bring.
What Otta brought me today
I thought it might be interesting to go through the 10 positions that job site Otta suggested to me today, and write down some notes as I went through each one.
- Software Engineer, Chime: This is part of the “I Heart Ruby” team at Chime which supports the larger engineering team in how they use Ruby. Sounds like the type of team I would really enjoy being part of, but my experience with Ruby is very minimal, so not a great fit for me. Not applying.
- Full Stack Engineer, Stripe: This is on the Connect team. I applied for a different position about six weeks ago on the Developer Products team but didn’t get past the application stage. Stripe is also a Ruby shop, but they don’t mention it in the job listing so I am guessing Ruby experience isn’t a hard requirement. I have updated my resumé since the last time, maybe the newest tweaks will help. Applied (again).
- Senior Software Engineer, Airbnb: This is on the Guest Displays & Platforms team. It looks like they are a Java/JavaScript shop? Again, nothing in the job requirements. Looks like I haven’t actually applied at Airbnb before now, but I think most of the roles I have seen were more senior, and I have sort of complicated feelings about Airbnb that made it easy to skip over. Applied this time, though.
- Staff Backend Software Engineer, Self Financial: Part of the Consumer Verticals Card Team. Requirements include “fluency with the development of Python/Flask/FastAPI code” which is a stretch for me, and since it is listed as a Staff-level position, probably not right. Not applying.
- Senior Full-Stack Engineer, Toku: “Web 3.0” — hard pass. (And fixed my Otta filter to hide that industry along with “cryptocurrency.”)
- Senior Backend Engineer, HashiCorp : Don’t feel like I meet many of the requirements, and also not wild about the company’s turn away from open source. Not applying.
- Principal Software Engineer, Voxel51: Not qualified, not applying.
- Staff Software Engineer, dbt Labs: This role wasn’t right, and I also considered another Senior Software Engineer role but just don’t have experience with any of the components of their tech stack. Not applying.
- Core Software Engineer, ClickHouse: This is intriguing because I think my experience at MySQL is pretty directly relevant. Applied. (I had to submit it using Firefox because their application system decided I was a bot when I used Safari, even after completing a recaptcha.)
- C/C++ Software Development Engineer, Percona: This was funny to get right after ClickHouse, because it’s to work on Valkey, another column-store database. The job listing specifically asks for “deep understanding of Valkey/Redis” which makes me hesitant about applying, but I also have connections to Percona so I am going to reach out that way.
- Backend Software Engineer, DoorDash: This is a role I had actually saved a few days ago, but hadn’t really looked at closely. Now that I did and looking at their tech stack, I just don’t think it’s a good fit for me. Not applying.
So eleven possibilities whittled down to three applications through the usual channels and one possibility to explore via networking. A good day for Otta compared to most, although I think just the decision to write this forced me to look more closely at roles I may have otherwise dismissed more quickly.
The listings on LinkedIn were bleak today, and I didn’t apply to any of them.
I also got a rejection for another position after the initial screening with the hiring manager. Never a great feeling, but one I have certainly had to get used to as I continue to cast a wide net for my next position.
Awesomplete is... awesome
An earlier iteration of my blog software had an autocomplete widget for adding tags for when I was writing a post, but somewhere along the line I lost track of that version of the software, and I’ve just been winging it when I added tags to posts since then. Yesterday I ran across a reference to Awesomplete which is a lightweight autocomplete widget by Lea Verou, and now I’ve plugged it into the couple of places where I add tags.
I need to do some more behind-the-scenes work to clean up the tags in the system now, but this should help keep me on track in terms of knowing when I’m actually creating a new tag instead of using an existing one. (Like I could never remember if I have been using the “book” or “books” tag and would always have to look it up.)
Stuck on repeat
The job search continues and continues to wear on my soul.
I got a rejection for a job this week that was frustrating because it came about two weeks later than they said I would be hearing back. That there was no hint of an apology for this makes me more frustrated because it is the sort of courtesy that would have cost them absolutely nothing.
My feelings are pretty mixed. I wouldn’t have called it my dream position, but it looked like a compelling challenge that would have lined up with a number of my interests in a way that I never really expected to find. But there were some red flags about the company that I had talked myself into accepting. Maybe it is all for the best.
Meanwhile, I continue to apply, I continue to learn, I continue to try.
Taste the rainbow
When I added the support for dark mode, I had to add a little support to the syntax highlighting styles so they looked okay in both light and dark modes.
Today I went in and worked on the JavaScript code a bit to make it a little more modern and refined the styles a little bit more. I also added the language file for YAML, and built it out so it does a better job of highlighting some of YAML’s more special syntax.
I am sticking with this JavaScript-based syntax highlighting for now, mostly because it works.
Here is a YAML sample pulled from the 1.2.2 spec plus a couple of minor additions to show off additional syntax that is handled.
%YAML 1.2
--- !<tag:clarkevans.com,2002:invoice>
invoice: 34843
date : 2001-01-23
bill-to: &id001
given : Chris
family : Dumars
address:
lines: |
458 Walkman Dr.
Suite #292
city : Royal Oak
state : MI
postal : 48046
ship-to: *id001
product:
- sku : BL394D
quantity : 4
description : Basketball
price : 450.00
- sku : BL4438H
quantity : 1
description : Super Hoop
price : 2392.00
tax : 251.42
total: 4443.52
shipped:
- false
comments:
Late afternoon is best.
Backup contact is Nancy
Billsmer @ 338-4338.
Embrace the dark
I finally took some time to implement a “dark mode” style for this site. It will default to whatever you have your system set to, but there is also a set of radio buttons that you can use to force it to a particular mode. (It doesn’t save the setting, so you will have to do it on every page if you are browsing around.)
This talk by Sara Joy about light mode and dark mode was part of the inspiration for getting around to this, and this article about light-dark
from Bramus is where I stole borrowed the idea for handling the mode switching.
Because I am using the relatively new CSS light-dark()
function to implement this, it is possible (likely?) it will degrade less than gracefully on older browsers.
Acomplish accomplished
I was perusing some old messages in the archives of the PHP mailing lists, and I stumbled across an email from me where I shared a sample of code as an example of a “natural” language I had used. That’s some real Acomplish code!
[ Declaration of a function to add an item to a list, keeping the list sorted. ]
To insert an item 'the_item' sorted into an item 'the_container':
For each item 'the_place' in the_container,
If the_item < the_place,
Add the_item before the_place.
Return.
Append the_item to the_container.
Make a list named myList of { 2, 3, 7, 14}.
Insert 10 sorted into myList.
Print myList.
[ Prints 2, 3, 7, 10, 14, more or less. ]
And another historical bit of trivia in that email: I proposed a for ... in
syntax for PHP, although that eventually became foreach ... as
.