home, home on the web

On the occasion of implementing yet another iteration of my personal website, I thought this would be a good time to reflect on all my various web dalliances over the years.

In the Beginning

I created my very first personal website in roughly 1996. I was living in Miami at the time, and I had a dial-up account with Concentric. (Pretty sure that company was absorbed into some larger ISP about a million years ago.) This was before it was a “thing” for civilians to host on their own domains, so my site address was something like http://www.concentric.net/~username.

It was all poorly hand-coded HTML, which looked pretty shabby. My only saving grace was that everybody else was also making ugly personal websites, so mine didn’t seem all that out-of-place.

The end of my Concentric website came about in an unexpected way, in the year 2000. I was building a new page for my as-yet-unreleased BeOS newsreader, Pineapple News, but I was having some kind of technical problem. I posted a link to the new page in one of the BeOS-oriented USENET newsgroups, along with a technical question. Some kind soul helped me solve the problem I was having (pretty sure it involved editing the apache .htaccess file), but I also got a lot more than I bargained for.

The BeOS community saw what I was working on and went absolutely nuts. I got so many site visits in the next 12 hours that Concentric took my personal site offline. I got an email implying that they were going to terminate my account, if I did not find a way to curb the onslaught of traffic.

The BeOS Era

At this point, a helpful member of the BeOS community swooped in to help me out. He was a guy who worked for a gigantic national ISP, with basically unlimited resources. He offered to put me up on one of his company’s servers and I quickly agreed. I moved all my poorly-coded HTML off of Concentric and onto his site. His company was able to handle the onslaught of traffic I got in the next week or so without trouble.

It seems weird, but I cannot for the life of me remember what my website address was at this point. It could have been at beosdevelopers.org, because I know I had a site there for awhile. But my vague memory is that one was run by a different guy, and that I did not stay at that address for very long, and that I was only using it as a mirror.

As the months wore on, I switched to exclusively hosting my site with the benevolent ISP employee. He graciously hosted my personal website for many years. It was great to have free web hosting with an unlimited amount of bandwidth and disk space, both of which were very expensive at that time. But eventually, the constraints started chafing. Whenever I needed any kind of maintenance, my benefactor was slow to make changes. Since I wasn’t paying him, I couldn’t exactly get very upset about the situation. I offered to pay him for my hosting to get a little bit more attentive service, but he wasn’t interested in that type of arrangement. So it was time for me to move again.

Now I Have to Pay For It

The year was 2006. I didn’t know much about web hosts, so I just picked one semi-randomly that I had heard of: DreamHost.

By this time it had dawned on me that personal websites could easily have their own domain names, so it was time for me to buy one. As you are no doubt aware, the domain namespace is quite crowded, so I just jammed pairs of words together until I got something that was available, inoffensive, vague, and without any baggage. I settled on platinumball.net, a domain I still own to this day, although I’m not doing much with it these days.

My only real use for the new website I set up was to host home pages for my various software projects. I ported over the same old set of poorly-coded HMTL files that I had been dragging around for ten years by this point and called it done.

Along about 2009, I decided I needed a blog. When you think of blogs, you think of WordPress, amirite? So that’s what I picked. DreamHost has a streamlined process that makes it easy to set up.

The first thing I noticed about the WordPress admin interface was that it was way too complicated. It is just comically busy. Good luck trying to find any particular setting you want to change.

Almost anything I wanted to do required installing a plugin. And that plugin might depend on other plugins, which would also have to be installed. And all the plugins, along with WordPress itself, needed near-constant updates, to deal with the unending parade of security holes.

For a year or two, I dutifully went along with the program. I let WordPress and my cavalcade of plugins update themselves whenever they said they needed to. And then, one of those updates broke my blog. I got a whole bunch of incomprehensible error messages, something to do with database constraints, as I recall. I fiddled with it for an hour or three, having absolutely no idea how to fix it.

At this late date, I don’t remember what I had to do to get my blog back online. The part I do remember is that another series of updates broke my blog a second time, just a few weeks later. That’s when I stopped doing the updates. I no longer cared about security holes, if it meant I was now forced to fix other people’s PHP bugs.

My sickly WordPress blog trundled along like that for many more years, not getting any updates. I had disabled comments years ago, unable to keep up with the spam, and unable to work out exactly which pile of plugins I would need to install to curb it, so I think that closed most of security holes that might have been exploited. I don’t think I ever got hacked. I never noticed any of the spam injections that I saw infect many other WordPress sites, anyway.

After a few years, DreamHost sent me an email saying that the version of PHP I was using on my site was so old that they were about to force-upgrade me, whether I liked it or not. Not having the stomach to deal with it anymore, I took my tired old WordPress blog offline.

RIP, WordPress blog. Oh, how I came to loathe you. My diminished (and ultimately extinguished) desire to blog came about largely due to how much I hated dealing with WordPress.

Time to Join the Cool Kids

Probably the most value I got out of my personal website was that it had a portfolio page for many of the iOS apps I had created over the years. When I worked as a contractor, the fact that I had such a page to show to potential clients helped me out a lot.

In 2019, I found myself looking for a new job. My existing portfolio page was more or less the same poorly hand-coded HTML I had been using for more than a decade at that point. It was getting long in the tooth. It made me look like an amateur.

I am not a web developer at heart. I have no desire to learn all the latest HTML, CSS, and JavaScript tricks. So I needed to find a way to outsource creating a brand-new site for myself.

I read a lot of tech blogs and articles, so I had been hearing about “static site generators” for years. I knew they could be used to build your own website, without needing a database, and without having the maintenance of millions of lines of PHP thrown into your lap.

I didn’t know which site generator to pick, so I used my usual decision-making process: how about the most popular one that I’ve heard of? That turned out to be Jekyll.

I loved how easy it was to get started. I loved that I could buy a theme and install it myself. (Actually, that turned out to be the hardest part: installing a Jekyll theme is a drawn-out, fiddly process, and can basically be done only when you first create your site. That was true for the particular theme I bought, anyway.) And I especially loved how modern and beautiful my new site looked. Most WordPress themes look stale and antiquated by comparison.

The one big feature I needed that did not come shipped with stock Jekyll was an image gallery. There were third-party plugins, but they all seemed to need a lot of work to install and learn. I picked one that looked decent and grumbled through setting it up.

I didn’t want to have to port over every page from my existing personal site, which would be quite a long process, so I decided to buy a new domain name and set up a brand-new site. I am pretty sure I had checked to see if my own personal firstname-lastname-dot-com domain was available, some number of years ago, and found that it was not. Not surprising. One thing I’ve learned over the years is that there are a lot of people with my exact same first and last names, and we are often fighting each other over aliases on various social media sites. But I checked again, and what do you know, now it’s available. So that’s where I put my new site.

Overall, I would say that my Jekyll experience was pretty good. I would not recommend it to someone who is not technical, but if you are the right type of person, it is a reasonable solution. Best of all, it accomplished my number one goal: having a slick personal website, along with a portfolio page and résumé pages, which got the attention of a lot of potential employers. I got a new job in no time.

Trouble Brewing

Having a spiffy new website made me want to maintain it, by writing new blog articles and porting over pages from my old site. I had to put that on the back burner for awhile, because I had a new job that was taking up a lot of my time and all of my creativity.

Three or four months later, I was ready to write a new blog post. I had to remind myself how to generate a local version of my Jekyll site, for testing. Bad news: apparently, a recent Ruby update had tickled my image gallery into crashing. I was no longer able to build new versions of my site. I tried downgrading to older versions of Ruby and Jekyll, but my ignorance of the Ruby ecosystem meant that my meager efforts did not stand a chance.

I wrote up a bug report and posted it to the github repo for the image gallery I had picked. The author informed me that he wasn’t using that plugin anymore and was not planning on fixing it. “I’ll accept a pull request if you’ve got one, though,” he said.

The bug was of a type that pretty much any programmer has seen plenty of: terminal output indicated that Ruby had gotten hold of a null object and was trying to call a method on it that did not exist. I looked at the line that was causing the crash, but there wasn’t anything wrong there, of course. The bug, wherever it was, must have occurred much earler, when some function was supposed to return a usable object of some type, but had returned null instead.

Here comes one of my biases: I hate Ruby. Probably more than is reasonable or healthy, given that it’s just an innocent programming language that many people get plenty of use out of.

I remember one time, in an effort to overcome my bias, I watched a video of a conference presentation by Matz. He talked about the evolution of the language and where he planned to take it in the future. As I was watching this talk, I could not stop shaking my head in disgust. Everything he emphasized as important was something I think is useless or stupid. Every language feature that I think is important was glossed over or ignored entirely. I lasted about ten minutes and quit. Nope, no Ruby for me.

I felt that loathing rising again, as I tried to find the bug in that plugin. Ruby code is so ugly. I could not concentrate and think like a programmer.

So that’s where I stopped trying. My blog sat frozen in time, for almost three years.

One good thing about site generators is that they spit out a pile of static files that will more or less always continue to work, no matter what. My inability to generate a new version of my site had no bearing on the current, working version. This is far preferable to, say, the usual WordPress failure modes, where you’re executing tons of PHP and you’ve got a live database that could break at any time, due to changes at your web host that are beyond your control, or a successful security breach by spammers.

I abandoned Jekyll and started looking for some other blog engine to use. I decided that whatever I picked, it must be implemented in Python, my preferred interpreted language. That way, when it inevitably breaks, I will stand a fighting chance of being able to fix it.

The First Contender

I spent several weeks of my free time trying to master Django CMS. Not a site generator, unfortunately, and it would once again force me to have a live database. But I considered those to be acceptable trade-offs, given that it would put me back into the Django world. I used Django to create a prototype for an aborted project a few years ago and I liked it a lot. The documentation is excellent. And I have an idea for another Django project I plan to build in the future, so it would be cool to flex those rusty Django muscles again.

Alas, I didn't account for the fact that Django CMS is hideously complicated. The documentation, such as it is, strikes me as poor to non-existent. Just keeping all its dependent Python packages in usable shape is almost more day-to-day maintenance than I want to take on. Despite all that, I planned to power through the rough parts and emerge with enough working knowledge to maintain my personal site with it.

I finally gave up while trying to figure out how to apply a theme. It is of course possible to wire up Bootstrap to this thing, but the “documentation” I found is comically inadequate. I could not get a clear idea of the steps I would need to complete, let alone start completing them.

Django CMS looks to me like a product that can only reasonably be used by people who are willing to make it a profession. A group which very much does not include me.

I briefly considered cutting back to just plain Django and writing a small amount of custom code to add a few CMS-like features. That might have been a viable strategy if I was already up to speed on Django itself, but I discovered I had forgotten too much, and it was going to take me more time than I was willing to invest to get back to competency.

The Second Contender

While performing web searches for Python-based site generators, Pelican showed up over and over again, so I decided to give it a shot.

My first few days with it were promising. I was making good progress learning the things I would need to know to build and maintain my site. But I kept finding myself twisting my ankles in potholes that, to me, indicate that its designers lack taste.

First example. Like most site generators, Pelican has the concept of “static content,” i.e., files that should be passed straight through to the output unchanged, like images and CSS files. But as I discovered the hard way, if you've got a file in a static directory that Pelican thinks it should process, like an HTML file, then it does indeed put it through the usual page translation pipeline. That is very much not what I want, and it is also the exact opposite behavior of every other site generator I have ever tried. I reported it as a bug, but given the response I got, that is apparently “working as intended, won’t fix.” Okay, fine, not the end of the world. I fabricated a workaround and kept going.

Second example. In Pelican’s configuration file, you set SITEURL = "https://example.com" to indicate the address your website will live at. But if you put an actual address in there, then when you are testing your site locally, it won’t work. For testing, you have to leave it blank. Great, so now it falls on my shoulders to work out how to have one value for SITEURL while testing locally and another when I do the final deployment. Once again, this is not a problem I have encountered with any other site generator.

I was getting pretty close to deploying a live version of my site, so I needed to figure out how to install a theme. (I could not use the default theme, which is, frankly, ugly.) It looks like the accepted Pelican way is “here is our ’simple’ theme, feel free to customize it to taste.” Nope, absolutely not. If I was willing to do that, I would have little use for a site generator.

I bought a theme that I used for my Jekyll site, which I would be willing to do again. Or maybe I could find a way to incorporate Bootstrap or something similar. I noticed that Pelican uses Jinja2 to render its themes, so I figured that this is just a matter of finding an existing theme of that type that I could press into service.

The Third Contender

When I performed a web search for “jinja2 theme,” the very first result turned out to be one of the documentation pages for Nikola, a site generator I had never heard of until that very second. My first thought was “maybe I can scavenge one of these Nikola themes and bolt it on to my Pelican site.” My second thought was “why not just try out Nikola itself?”

Holy cow, it was like night and day. I made progress with this thing faster than any other site generator I’ve ever tried. Largely because there were no potholes for me to twist my ankles in. All the negatives I’ve mentioned for the earlier generators I tried did not exist. The documentation is adequate, and the software seldom violates the “principle of least surprise.” Even down to the small details, Nikola shows good taste that other site generators lack. Its default locations for site files make more sense than its competitors.

Nikola has one config file, named conf.py. It is the only file you will see in the root of your site directory. Perfect! This kind of attention to small details inspires confidence. Pelican has three or four config files, depending on which things you configure, and they have unwieldy names like pelicanconf.py and publishconf.py. (If you’re wondering why I think this exhibits poor taste: you don’t need to include “pelican” in the name of a config file. It’s right there in the root of your Pelican site, of course it’s going to pertain to Pelican. Adding “conf” at the end means you have named this file twice.)

The big lesson I have taken away from trying all these different kinds of site maintenance things is that I usually give up when I discover that applying a custom theme is going to be difficult or impossible. (Admittedly, I’m sure I could jump through those hoops if I really wanted to. But I don’t. I am not a web developer and I don’t want to become one.) So this time around I started with trying out themes, rather than putting it off to the end. Once again, Nikola passed this test with flying colors. I still need to figure out how to customize the pre-built theme I picked, but I’m sure I can figure that out, eventually.

I have been harshly critical of all the other site-generating software I tried and rejected, so it seems only fair to mention my one big gripe with Nikola: its anti-documentation bias. From the Nikola handbook: “DON’T READ THIS MANUAL. IF YOU NEED TO READ IT I FAILED, JUST USE THE THING.” Sorry dude, that is not even remotely realistic.

Another manifestation of Nikola’s anti-documentation bias: when you create a new site, the default configuration file you get is over a thousand lines long, most of them commented out. Good grief, that is just intimidating. Put that stuff in the docs, for crying out loud.

(On the other hand, the fact that the author is anti-documentation might well account for the fact that Nikola has so few foot-guns to sabotage yourself with. If you’re expecting your users to “JUST USE THE THING,” then the software better work exactly the way most people would expect it will, with no surprises.)

But that particular wart was not hard to get past. The author might be against documentation, but he nonetheless provided enough of it for me to port all of my existing Jekyll content over to this new site generator. The page you are reading right now was created with Nikola.

Regurgitated Dreck

This post is already way longer than any rational mortal is going to read. So why not throw in yet another unnecessary section, just to vent my spleen a little more?

Many (most?) of the blog engines I considered, including Nikola, use reStructuredText as their default document format. This is, without a doubt, the worst markup language I have ever encountered. It’s like its designers thought XML was a little too readable, and needed to be taken down a peg.

And how about the name: reStructuredText. It sets my nerves on edge. Too many staccato syllables that don’t sound good together. And the way they’ve chosen to capitalize it is just plain ugly. Every time I see it typed out like that, it looks like a typo I need to go back and correct.

Pretty much every convention, construction, and rule in this messy thing is wrong, according to me. Starting an identifier line with two dots, a space, the identifier name, and a colon: ugly. Similar lines for images, but with two colons: ugly. Delimiting sub-element names with a colon at the beginning and at the end: ugly. And let's have a look at one of the many ways you can format URLs:

    `Site Name <https://example.com>`_

This is just breathtakingly ugly. The backtick is the ugliest of all punctuation characters. The underscore is somewhere in the top-ten-ugliest. I would have a hard time imagining a worse scheme than this, even as a parody. Where would you even go from here? Poop emojis as delimiters?

If any blog engine I tried had forced me to use rEsTrUcTuReDtExT as my primary document format, that would have been a deal-breaker. No way am I typing ugly junk like that for the rest of my blogging career.

Fortunately, everything I evaluated allows you to use Markdown as an alternate format. There appear to be a few places where Nikola requires you to use rEsTrUcTuReDtExT, like in image galleries, but they are few and far between. And in those cases you are shielded from having to write very much of it.

Epilogue

So here I am, on the umpteenth iteration of my personal web presense, spanning more than 20 years.

It was difficult to get to this point. I don’t know about you, but when I spend weeks teaching myself some new technology, only to wind up abandoning it ... I find that to be demotivating. I tend to not want to think about that particular subject for awhile. That’s why my site laid dormant for so long.

But here I am, with a brand-new freshly-generated personal site, and a lot of ideas for new blog posts. Maybe I will stick with it this time?

mac backup script

Most backup systems assume you are lazy and inattentive, and if they don’t do pretty much everything for you, it won’t get done. So let’s say you dutifully set up your backup software as directed, “set and forget.” It starts humming along, doing backups every so often on its own schedule.

Fast forward to months or years later. Your primary copy of your data has been ripped away from you, due to misadventure, fire, theft, or equipment failure, and you want to get it back. Surprise! Your backups stopped working shortly after you enabled them, for whatever reason: the backup disk failed or is no longer connected to your computer, the disk filled up, or the software is misconfigured. The most recent “backup” you have is many months out of date.

Even if I could 100 percent trust that “automatic” backups would never fail, I still wouldn’t want to do it that way. If you’re a programmer, you would never set up git so that it automatically commits everything you’ve changed every hour or so, would you? Me neither. I feel the same way about my backup software. I want backups to occur only when my files are in a known-good, consistent state.

That suggests manually-initiated backups. And that’s exactly what I did, for many years. I configured my backup software to stop backing up automatically. At the end of the day, or whenever I was going to be away from my Mac for more than an hour or two, I manually started a Time Machine backup and a BackBlaze backup.

That worked well enough, I guess. But there were problems.

When I’m about to step away from my computer, it’s often because I’m mentally exhausted. I don’t have a lot of brain-power left over for doing fiddly things like picking the exact right menu item, twice. This meant that one or both of my two regular backups would often not get done. Later, when I returned to my Mac, there was no good way for me to see if those backups had completed successfully.

At some point in there, I managed to screw up my Mac so badly that I needed to completely wipe it and start over. That’s when I discovered that, in this particular circumstance, a Time Machine backup is not ideal. It’s a lot better to have an exact clone of your old hard disk you can start from. So I added a third daily backup to my routine, via Carbon Copy Cloner.

And with that, my daily backup routine was officially too unwieldy to perform manually anymore. Automating activities such as these is why we have shell scripting.

Backup AppleScript Menu

After a lot of trial and error, I managed to produce a bash shell script that performs all three of my backups automatically. It also generates and manages logs that show what happened during every backup. I start my script with a small AppleScript in my Mac’s script menu, so that I don’t have to fiddle with a terminal window every time.

My backup script performs the following steps:

  • Turn off the Mac’s monitor.

  • Start three simultaneous backups: Time Machine, BackBlaze, and Carbon Copy Cloner. Redirect all of these programs’ terminal output to three separate session log files.

  • Wait for the backups to finish. This could take as little as ten minutes or as long as several days, but usually finishes within two hours.

  • Combine all the session logs into one master backup log file, and save it in a particular directory.

  • Delete old backup logs that are older than two weeks or so.

  • The backup is now complete. If the monitor is still turned off, we can assume a human is not using the computer, so put it to sleep. If the monitor is now on, then a human has started using it before the backup completed, so do not put the computer to sleep.

My backup script also performs a secondary purpose: It can show you the contents of the most recent backup logs, so you can judge for yourself if it completed correctly or not. This works even if a backup is currently in progress, in which case it will show you what it has got so far. If you’re really curious, you can navigate to the backup log directory and examine all the logs for the past two weeks.

Sounds easy enough, right? I am going to guess I put in at least three solid weeks’ worth of work on my backup script, over a period of many months, before I had nailed all the corner cases. I am stubborn like that.

For starters, when I first attempted to write this script, back in 2016, BackBlaze did not support command-line-initiated backups. I sent an email to a guy who works at the company, and I am happy to report that he implemented that feature, just because I asked nicely. That counts as dazzling customer support in my book.

(I’m not going to name The BackBlaze employee here, because I don’t want the poor guy to get drowned in emails from people bugging him with their pet grievances. If you think your issue is as important as I thought mine was, I suspect you can get ahold of somebody in the company some kind of way, the same as I did.)

If you’d like to try my backup script for yourself, download this zip file, and follow the instructions you find inside.

Note that you don’t have to automate all three types of backups, like I do. You can choose just one or two backup methods instead. I’m sure the script could be modified to support other types of backups, if you are so inclined.

If you have feedback or an idea for some kind of improvement, drop me a line.