One of my favorite all-purpose engineering tools (maybe it's more accurate to call it a "technique", but I'm going to stick with "tool" here) is the ratchet.

In my career, I've had the benefit of some very long tenures at different organizations. I and teams I've worked on have launched new greenfield projects, I've maintained some codebases for more than a decade, I've done big bang rewrites and piecemeal migrations. I've worked with experienced and talented developers and complete newbies. I've also inherited a lot of code and systems. Some of those have been well designed, well tested, and well documented. Others have been... not so much those things.

I'm not dogmatically against rewrites. Sometimes that's the appropriate solution. Often though, it's not practical or feasible to rewrite a large codebase or existing system even if it's in terrible shape. It needs to be improved in place. The thing with systems that are already in bad shape is that making changes is risky. The larger the change, the riskier. It's often clear that the current state of things is bad, but you don't know exactly what "good" would look like or how to get there from where you are.

This is where the ratchet comes in.

A ratchet is two parts:

  1. any small change that improves the codebase or the system in some way.
  2. some safeguard that locks that change in place.

Fix a bug? Add a regression test to make sure the bug stays fixed. No automated tests at all? Add a "dummy" test suite that runs zero tests. Obviously that won't catch any bugs by itself, but it should be low risk to introduce (you're just adding test harnesses) and when you do start adding tests, you'll have the scaffolding there to fit them into. Set up a commit hook or Github action to run the dummy test suite. Again, it shouldn't introduce any risk but will get everyone accustomed to seeing tests pass as part of the development cycle. I've seen dummy test suites like this catch syntax errors or broken imports in code just by virtue of ensuring that the code is at least parsed and compiled before getting pushed out to production (we've all seen developers make "just a tiny change" and push without even running it locally; if we're honest, most of us have done that ourselves).

Is the code all over the place in terms of conventions? Add a simple linter tool (eg, flake8 or eslint). Most of them will let you enable/disable different rules. If you need to, start out by disabling every single rule so that it's not actually checking anything, but add it to the commit hooks or CI setup. Then you can enable one rule at a time later on as you gain confidence that they aren't breaking anything. Each rule that gets enabled prevents the codebase from ever having that problem again. Eventually, you might make enough progress that you're comfortable switching to an automatic formatter like black or go fmt or similar.

Is a deploy process manual, slow, and error prone? Write a Runbook entry documenting it as well as you currently understand it. Then start automating parts of the runbook. Add a simple end to end "smoketest" at the end of the deploy to verify that the deploy was successful. Before you know it, you'll have a completely automated deployment process.

None of these are revolutionary ideas. I just find it useful to think in terms of this "ratchet" mechanism when I'm improving a codebase, a system, or even a team's process. Make lots of small steps and make it easier for the system to naturally move towards a "better" state than a worse one. At some point the system dynamics take over and become self-reinforcing.

2019 Reading List

For the last few years, at the end of the year, I've been posting my roundup of new music for the year. I'm a little bored with that for now and thought that this time, I'd list the books I read this year and maybe some brief thoughts on them.

According to my Amazon order history, the first book I bought in 2019 was Hacking: The Art of Exploitation, 2nd Edition. I'd generally avoid anything with the word "hacking" in the title, but somehow this was recommended to me and I'm glad it was. It does a surprisingly good job of explaining basic exploitation techniques like buffer overflows, format string vulnerabilities, and shell code. The kind of stuff I used to read about in text files on usenet but never really got too far into. It reminded me that I really haven't thought much about assembly and machine code since university. Back in those days, my exposure was mostly writing MIPS and Z-80 assembly and I never really bothered with x86. That led me to pick up Assembly Language Step-by-Step which isn't terribly modern, but does a good job covering x86 assembly. Wanting to shore up the connections from assembly all the way to higher level languages, I also read Low-Level Programming: C, Assembly, and Program Execution on Intel® 64 Architecture, which was excellent. Its coverage of assembly isn't as thorough, but it gets much more into those weird bits of compiler and linker magic that I've long avoided having to deal with. Later in the year, I ended up getting The Apollo Guidance Computer: Architecture and Operation and I absolutely love it. I'm an old space nerd (aren't we all) and this is a fascinating look at the computer guidance system on the Apollo, including the hardware, the software, and some math and physics tricks they used to pull everything together.

I stumbled on Introduction to the Theory of Complex Systems at a bookstore and couldn't resist it. It goes into network theory, evolutionary algorithms, statistical mechanics, and ties together ideas from mathematics, physics, biology, and social sciences. If you know me, you know that I'd be all over that. Afterwards, I wanted more and I'm pretty sure that The Road to Reality: A Complete Guide to the Laws of the Universe came up as a recommendation somehow. If you didn't know, I was a physics major for most of my undergrad before switching to electrical and then computer engineering. This is the book that I wish I'd had when I was struggling through my physics classes. It doesn't go deep into any of it, but Penrose explains so many of the concepts with a clarity that was definitely missing from my classes and textbooks. I'm not sure it would be that understandable without at least some basic college physics and math background, but if you ever took a general relativity or quantum mechanics class and felt like you didn't really get it, this is definitely worth checking out.

That kind of got me thinking about those concepts in those physics classes that I'd learned to use but never really felt like I understood. As a physics major, you don't necessarily have to take a lot of math classes, and there are a lot of things that get introduced in your physics classes as tools but aren't really explained that well. Back when I was in school, we didn't have Wikipedia or Youtube or Amazon (and I certainly didn't have disposable income to spend on expensive math books that weren't required for my classes) so unless you took a class on a topic or knew someone with that expertise, it was hard to fill in those gaps. By the end of my physics classes, I remember feeling like I was solving problems by pattern matching and mechanically applying memorized solutions and could get the right results but I no longer really knew what was going on. A few of the important ones that I definitely missed out on were tensors and topology. We used tensors plenty in physics classes, but I remember mostly just thinking of them as multi-dimensional matrices and a set of mechanical rules that you used to manipulate subscripts and superscripts in a way that let you do higher dimensional calcuations without having to write out a million terms. Every once in a while a professor would do something weird with them and we'd just have to take it on faith that it was valid, but it always left me feeling like there was a lot more that I didn't understand.

This realization that I could now fill in those gaps led me down another rabbit hole of math books that occupied quite a bit of my year. I started with some youtube videos and various online resources that helped a lot. Then I picked up Tensor Calculus because some of those Dover books are inexpensive hidden gems. This one wasn't. It might be fine if you are more of a mathematician, but I'm still fundamentally coming at things from a physics/engineering perspective and not that interested in proofs and derivations. The one that was much better for me was An Introduction to Tensors and Group Theory for Physicists. The preface basically described my exact situation of "lingering unease" around the concept and does a great job of filling in the blanks. It also made connections to group theory that I hadn't really thought about. Group Theory comes up more often in computer science and is something I feel more comfortable with but it did remind me that I never actually took an Abstract Algebra class. I was also vaguely aware that despite seeming like it should be unrelated, Topology is pretty heavily based on Abstract Algebra and that was another topic that I wanted to fill in. So I got A Book of Abstract Algebra. That one I think does fall into the "hidden gems" category of Dover books. Highly recommended. I followed it up with Introduction to Topology which was... ok. It was clear enough and I could follow it, but it was pretty dry and I don't feel like I gained any real insights from it. I didn't come away from it thinking that Topology was amazing and having a different perspective on things. I just kind of feel like now I know a bunch of definitions and theorems. I also got Counterexamples in Topology which was highly recommended and I can see being a valuable reference, but it's really not one that you just sit down and read cover to cover. Somewhere in the middle of my Dover math books spree, I also read Geometry, Relativity and the Fourth Dimension which was pretty shallow but a very quick and fun read.

The last math book I picked up that's worth mentioning is Mathematical Methods for Physics and Engineering: A Comprehensive Guide. This is another book that I wish had existed when I was an undergrad. It's big and extremely thorough. It basically covers all of the math you would need for an entire physics or engineering undergrad education. I really can't think of a single mathematical concept, tool, or technique that I encountered in my undergrad physics and engineering that isn't covered in it. For any given topic, there's probably a better introduction or a more thorough treatment in some other book, but I've never seen any other book with the same breadth. It's become my goto math reference and lives on a shelf by my desk now.

As a "palate cleanser" during my math refresher, I read The Poetics of Space which has been on my recommendations list for years and come up in numerous conversations. I don't feel like I got as much out of it as I could have because I'm just not that familiar with the french poetry that he bases his discussions off of. I'm sure it's one of those books that really needs to be read in its original French, but even in translation, it's beautifully written and evocative.

I've been vegan for a while but only recently thought to actually read anything on the topic. Why We Love Dogs, Eat Pigs, and Wear Cows: An Introduction to Carnism is probably the best introduction to the ethical and moral philosophy behind veganism and animal rights. The Sexual Politics of Meat: A Feminist-Vegetarian Critical Theory makes a strong case for veganism as an important part of intersectional feminism. It was written in the 90's though and both veganism (eg, at the time "vegan" wasn't a commonly used term, so it only talks about "vegetarianism") and feminism have changed quite a bit since then, so it needs to be read with that in mind. One chapter jumps right into fairly graphic descriptions of sexual violence without any warning, so that's something to be aware of. On a completely different axis, How Not To Die: Discover the foods scientifically proven to prevent and reverse disease, while not explicitly "vegan", is a great scientific look at nutrition from the doctor behind and supports a vegan diet as at least a good starting point for avoiding many of the diseases and causes of mortality that plague the modern world.

I've been living in London for a few years now and in 2020, Phoenix and I become elligible for Indefinite Leave to Remain. Our main obstacle is passing a test that involves knowledge of British history, culture, and government. There's an official guide, Life in the United Kingdom that covers everything that could be on the test along with practice tests and I'm pretty sure I could pass the test by cramming those for a bit. But I've got plenty of time and if I'm going to be living here long term, I figure I might as well know a bit more. Also, if you hadn't noticed, the last few years have been pretty eventful in British politics and have exposed much of the world to the idiosyncratic and confusing way that very important decisions are made over here. In an effort to understand how these things work and the historical context behind them, I have been going through some "very short introduction" books to build up some background knowledge. So far I've gone through The British Empire, Nineteenth Century Britain, Twentieth Century Britain, The British Constitution, and British Politics. I'll probably read a bunch more before I'm done.

Those books all fit into various themes or categories. In between them were a bunch that were pretty random:

Finally, the one piece of fiction I managed to read this year (to be fair, I read a lot of fiction in 2018, so I was taking a bit of a break on purpose) was Into the Dark (Dark Devices Book 1), which happens to have been written by my coworker. He wrote it for NaNoWriMo and self-published, which I strongly support. It's not very long and leaves the story set up for future continuation, but it's a nice fantasy read and reminds me of a slightly sci-fi'd up take on old D&D Underdark books.

TAGS: books 2019

Continuously Deploying Django with GitHub Actions

[Edit 2019-09-21: Updated with the new YAML syntax]

A couple years ago, I wrote a post covering how I set up a continuous deployment pipeline for my personal Django apps using Docker and Jenkins.

A lot has changed since then so I thought it was about time I updated. Especially since this weekend I switched from Jenkins to GitHub Actions.

So, a quick disclaimer before I go on: GitHub Actions is currently in public beta. They have been clear that it is not yet considered stable for production and comes with no warranty. It could change and break my setup at any time. They also haven't released any information about what it will cost when it is fully out. These are my personal apps so I'm fine with all of that. I'll try to remember to update this post when the beta is over, but in the meantime, use it at your own risk.

My old post covered the basic setup and, honestly, the general approach hasn't changed much; I've just been swapping out the pieces. It's worth reading the old post for more details, but I'll recap the basic approach here so the rest of this post can just cover the new GitHub Actions stuff.

The old approach to deployment was that I ran Jenkins server alongside my app servers. When there was a push to the master branch on GitHub, it would trigger a build there, which would do the following:

Jenkins used to run those steps with a fairly small shell script described in the old post. At some point, I converted that to a "proper" Jenkins pipeline specified in a Jenkinsfile. I never wrote about that, and I'm replacing it now, but it's up here if you are curious. It did some things better than the the shell script version and made for a much nicer overall experience in the Jenkins web interface, but mostly it made me glad that I don't often have to code in Groovy.

Jenkins worked OK for me but it's always been a pretty awkward part of the stack and not a lot of fun to run and keep updated.

So when Github Actions came out and I got access to the public beta, I decided to see if I could replace my Jenkins setup.

What I came up with is seems to work pretty well.

Actions for a project are stored as code in a .github directory in your project. There's a nice web UI for editing actions, but it's worth looking at the code. My old blog post covered the deployment of my antisocial feed reader app, so for consistency, let's look at how the GitHub Actions setup looks for it.

I have two workflows. The first just sets up and runs Jessie Frazelle's branch-cleanup-action GitHub action which keeps things tidy by deleting merged branches. She has a great blog post on how it works that helped me start to get my head around Actions.

The main deploy workflow, in .github/workflows/deploy.yml starts with:

        branches: master
name: deploy
    name: Build docker image
    runs-on: ubuntu-latest

The first step (actually technically two steps) that runs is:

    - uses: actions/checkout@master
    - name: Build docker image
      uses: actions/docker/cli@master
        args: build -t thraxil/antisocial:${{ github.sha }} .

That builds the docker image, tagged with the git SHA, which GitHub Actions conveniently exposes in the github.sha variable. As I mentioned before, with my Dockerfiles, the unit tests run during the build, so this step also serves as a good check on PRs.

The next two are fairly self-explanatory:

    - name: docker login
      uses: actions/docker/login@master
    - name: docker push
      uses: actions/docker/cli@master
        args: push thraxil/antisocial:${{ github.sha }}

They log us into the Docker Hub and push the docker image there. The only new bit is that the login step uses the secrets field to grant itself access to some secret settings that are stored in the GitHub project settings.

The bulk of the deploy work (that I had to do, at least) is in the next stanza:

    - name: deploy
      uses: thraxil/django-deploy-action@master
        APP: antisocial
        KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
        PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
        SSH_USER: anders
        WEB_HOSTS: ${{ secrets.WEB_HOSTS }}
        CELERY_HOSTS: ${{ secrets.CELERY_HOSTS }}
        BEAT_HOSTS: ${{ secrets.BEAT_HOSTS }}

That uses a custom action that I placed in its own repo so I could easily re-use it across my projects.

The great thing about GitHub Actions is that they are just Docker containers that get some specific environment variables and shared directories set up and run how you need them. GitHub Actions will find the Dockerfile in there, build it (if it hasn't already cached), and run it in the appropriate environment.

That means that it's easy to package up pretty much any common deployment tool you can think of as a GitHub Action (if someone else hasn't already done it) or just put your own together if you know how to make a Docker image and write a little shell script.

If I were doing this from scratch or if it were a more complicated deployment process, I'd probably grab or build an Ansible action and do the rest of the deployment that way. In my case though, it's only a couple steps and I pretty much already had a shell script written (see the old post). So I just made an action that pretty much runs that script in a container, with some tweaks to make it work in the new environment.

The Dockerfile is pretty minimal. Just builds off debian:stable-slim (which GitHub highly recommends to keep images small and using a common base to maximize caching), installs openssh-client (the only package we need that isn't already there) and drops in a small script.

That script should look familiar from the old blog post. It just does a few additional things to deal with the GitHub environment variables and setting up a valid SSH config. All of the variables that it needs are either set via env in the workflow config above, or they are stored in the project settings as secrets (SSH private key, etc.)

Finally, since I like to use Sentry to track exceptions and Sentry does a better job if you tell it when you deploy new code, I use a community published action to publish a new sentry release for the project:

    - name: sentry release
      uses: juankaram/sentry-release@master
        ENVIRONMENT: production
        SENTRY_ORG: thraxil
        SENTRY_PROJECT: antisocial

That's basically it. I'm mostly pleased with the setup. It took me a few hours to figure out all the pieces and work through some stupid bugs (my own) to get it working how I wanted, but now it's pretty solid.

Like I said earlier, I really like that it works by just stringing together Docker containers. That means there's never any question about when GitHub Actions will support some tool. If you can stick it in a Docker image, you can use it. Configuration is straightforward once you've spent some time with it (and the Web UI is surprisingly usable and powerful).

I feel like I'm only scratching the surface of what it's capable of (I'm not even running anything in parallel). The more interesting uses will be less of this "traditional" kind of deployment pipeline and will take better advantage of the Actions' direct access to the rest of GitHub's APIs. Right now it feels like the community is still figuring out what those possibilities are and I'm excited to see what patterns emerge.


The #shelfie hashtag has been coming up lately. I'm bad at social media, but I thought I'd post mine here.

I moved from the US to Europe a few years ago and I had to massively cut down my book collection. In the last few years, I've started accumulating books again, but I've been a bit more purposeful this time around. So most of the books on my shelves are ones that I decided were important enough to bring across the ocean with me or that I've wanted to have for reference since the move. Fiction and "lighter" non-fiction that I'm only going to read through once I try to buy in electronic formats so it doesn't take up precious space in my flat.

shelf 1

My books are vaguely organized though not totally consistently. The top shelf here is the really random stuff, including the few bits of fiction I have in physical form, Dutch language books (that's "Charlie and the Chocolate Factory" in Dutch), and of course, one of my small paintings in the front.

The middle shelf is sort of C++, Linux, Networking, and low level systems stuff.

Bottom is mostly Cloud, DevOps, and SRE related.

shelf 2

These shelves are right below a window so I had a hard time getting a clearer photo without it washing out a bit.

Top shelf here is focused more on CS and distributed systems. Some classics here like SICP, CLSR, the Dragon Book, CTM, etc.

Bottom shelf is the fun one with my weird art books. The foil wrapped one is the NASA Graphics Standards Manual. The old looking one next to it is an 1800's edition of Elihu Vedder's illustrated version of the Rubaiyat of Omar Khayyam.

shelf 3

Top here is math and random programming books that didn't fit on the other shelves.

The bottom shelf has a few electronics books and random large ones that won't fit elsewhere. The filing box is mostly full of printed out CS papers.

TAGS: books

2018 Music

It took me a little longer than I'd planned, but here is my yearly music round up for 2018 just as I've done for 2017, 2016, and 2015.

As usual, this isn't exhaustive and only includes bandcamp links. I have no other commentary other than that I enjoyed these. My tastes run towards weird, dark, loud, and atmospheric. Enjoy.

TAGS: music

Behind the Music

I posted my yearly music roundup yesterday, which I've done for the last three years. Today I thought I'd just take a moment to explain how I go about creating those posts. Eg, there are 165 albums in the last post and I link both the artist and album pages on each. Do you really think I manually typed out the code for 330 links? Hell no! I'm a programmer, I automate stuff like that.

First of all, I find music via a ton of different sources. I follow people on Twitter, I subscribe to various blogs' RSS feeds, and I hang out in a bunch of music related forums online. So I'm constantly having new music show up. I usually end up opening them in a new tab until I get a chance to actually listen to them. Once I've listened to an album and decided to save it to my list, my automation process begins.

I'm a longtime emacs user, so I have a capture template set up for emacs org-mode. When I want to save a music link, I copy the URL in the browser, then hit one keyboard shortcut in emacs (I always have an emacs instance running), I paste the link there and type the name of the artist. That appends it to a list in a text file. The whole process takes a few seconds. Not a big deal.

At the end of the year, I have this text file full of links. The first few lines of this last year's looks something like this:

** Woe -
** Nidingr -
** Mesarthim -
** Hawkbill -
** Black Anvil -
** Without -
** Wiegedood -

For the first two years, I took a fairly crude approach and just record an ad-hoc emacs macro that would transform, eg, the first line into some markdown like:

* [Woe](

Which the blog engine eventually renders as:

<li><a href="">Woe</a></li>

A little text manipulation like that's a really basic thing to do in emacs. Once the macro is recorded, I can just hit one key over and over to repeat it for every line.

Having done this for three years now though, I've noticed a few problems, and wanted to do a little more as well.

First, You'll notice that the newest post links both the artist and the album. This, despite the fact that I only captured the album link originally.

Second, if you look closely, you'll notice that not all of the bandcamp links are quite the same format. Most of them are <artist><album-name>, but there are a few anomalies like or or The first of those was a link to a specific track on an album, the latter two both link to the "artist page", but if an artist on bandcamp only has one album, that page displays the data for that album. Unfortunately, that's a bad link to use. If the artist adds another album later, it changes. Some of the links on my old posts were like those and now just point at the generic artist page.

So, going from just the original link that I'd saved off, whatever type it happened to be, I wanted to be able to get the artist name, album name, and a proper, longterm link for each.

I've written some emacs lisp over the years and I have no doubt that if I really wanted to, I could do it all in emacs. But writing a web-scraper in emacs is a little masochistic, even for me.

The patth of least resisttance for me probably would've been to do it in Python. Python has a lot of handy libraries for that kind of thing and it would've have taken very long.

I've been on a Go kick lately though, and I ran across colly, which looked like a pretty solid scraping framework for Go, so I decided to implement it with that.

First, using colly, I wrote a very basic scraper for bandcamp to give me a nice layer of abstraction. Then I threw together a real simple program using it to go through my list of links, scrape the data for each, and generate the markdown syntax:

I just run that like:

 go run yearly.go | sort --ignore-case > output.txt

And a minute or two later, I end up with a nice sorted list that I can paste into my blog software and I'm done.

2017 Music

For the third year in a row, here is my roundup of music released in 2017 that I enjoyed.

One of the reasons that I've been making these lists is to counteract a sentiment that I encounter a lot, especially with people my age or older. I often hear people say something to the effect of "The music nowadays just isn't as good as [insert time period when they were in their teens and twenties]". Sometimes this also comes with arguments about how the internet/filesharing/etc. have killed creativity because artists can't make money anymore so all that's left is the corporate friendly mainstream stuff. I'm not going to get into the argument about filesharing and whether musicians are better or worse off than in the past (hot take: musicians have always been screwed over by the music industry, the details of exactly how are the only thing that technology is changing). But I think the general feeling that music now isn't like the "good old days" is bullshit and the result of mental laziness and stagnation. We naturally fall into habits of just listening to the music that we know we like instead of going out looking for new stuff and exploring with an open mind. My tastes run towards weird dark heavy metal, so that's what you'll see here, but I guarantee that for whatever other genres you are into, if you put the effort into looking just a little off the beaten path, you could find just as much great new music coming out every year. I certainly love many of the albums and bands of my youth, but I also feel like the sixteen year old me would be really into any one of these as well.

OK, I know I've said that I don't do "top 10" lists or anything like that, but if you've made it all the way to the bottom of this post, I do want to highlight a few that were particularly notable: Bell Witch, Boris, Chelsea Wolfe, Goatwhore, Godflesh, King Woman, Lingua Ignota, Myrkyr, Pallbearer, Portal, The Bug vs Earth, Woe, and Wolves in the Throne Room.

Plus special mention to Tyrannosorceress for having my favorite band name of the year.

TAGS: music

In the Wild

Last night, I was scanning the /r/guitarpedals subreddit. Something I have been known to do... occasionally.

I see this post:

reddit post

OK, someone's trying to identify a pedal they came across in a studio. I'm not really an expert on boutique pedals, but I have spent a little time on guitar forums over the years so who knows, maybe I can help?

Clicking the link, there's a better shot of the pedal:


Hmm... nope, don't recognize it. Close the tab...

wait a minute

OK, yeah, the pedal doesn't look familiar, but the artwork on it sure does...


That's one of my drawings from about 2008.

So at some point, someone out there built a custom guitar pedal, used one of my drawings for it, it ended up in a recording studio somewhere, someone else found the pedal in the studio, took a picture, posted it on reddit, and I stumbled on it.

Anyone who's known me for very long knows that I post all of my artwork online under a Creative Commons Public Domain license. I'm not a career artist and it's not worth the hassle for me to try to restrict access on my stuff and I'd rather just let anyone use it for whatever they want. So this obviously makes me very happy.

I've had plenty of people contacting me over the years asking to use them. That's unnecessary but appreciated. My paintings and drawings have appeared on dozens of websites and articles. There are a couple books out there that include them (besides the Abstract Comics Anthology that I was actively involved in). I know that there's at least one obscure death metal album out there that uses one of my paintings for the cover. I've had a few people say they were going to get tattoos, but I've never seen a photo of the results, so I can't say for certain whether anyone followed through on that.

This is the first time that I've run into my own work like this randomly in a place that I wasn't looking.

BTW, no one has yet identified the pedal, so obviously, if you know anything about who built it, let me know.

TAGS: art

Ghosts of Frameworks Past

Recently, Github added the ability to archive repositories. That prompted me to dig up some code that I wrote long ago. Stuff that really shouldn't be used anymore but that I'm still proud of. In general, this got me remeniscing about old projects and I thought I'd take a moment to talk about a couple of them here, which are now archived on Github. Both are web frameworks that I wrote in Python and represent some key points in my personal programming history.

I started writing Python in about 2003, after spending the previous five years or so working mostly in Perl. Perl was an important language for me and served me well, but around that point in time, it felt like every interesting new project I saw was written in Python. I started using Python on some non web-based applications and immediately liked it.

Back then, many smaller dynamic sites on the web were still using CGI. Big sites used Java but it was heavy-weight and slow to develop in. MS shops used ASP. PHP was starting to gain some popularity and there were a ton of other options like Cold Fusion that had their own niches. Perl CGI scripts running on shared Linux hosts were still super popular. With Perl, if you needed a little more performance than CGI scripts offered, if you ran your own Apache instance, you could install mod_perl and see a pretty nice boost (along with some other benefits). Once you outgrew simple guestbooks and form submission CGI scripts, you needed a bit more structure to your app. Perl had a number of templating libraries, rudimentary ORMs, and routing libraries. Everyone generally picked their favorites and put together a basic framework. Personally, I used CGI::Application, Class::DBI, and HTML::Template. It was nowhere near as nice as frameworks that would come later like Ruby on Rails or Django, but at the time, it felt pretty slick. I could develop quickly and keep the code pretty well structured with cleanly separated models, views, and templates.

Python wasn't really big in the web world yet. There was Zope, which had actually been around for quite a while and had proven itself to be quite capable. Zope was... different though. Like, alien technology different. It included an object database and basically took a completely different approach to solving pretty much every problem. Later, I would spend quite a bit of time with Zope and Plone, and I have a great deal of respect for it, but as a newcomer to Python, it was about ten steps too far.

So, in the meantime, I did the really predictable programmer thing and just ported the stuff I missed from Perl to Python so I could write applications the same way, but in a language that I had grown to prefer.

Someone else had already made a port of HTML::Template, htmltmpl. It worked, but I did have to make some fixes to get it working the way I liked.

Similarly, Ian Bicking had written SQLObject, which was a very capable ORM for the time.

My major undertaking was cgi_app, which was a port of CGI::Application, routing HTTP requests to methods on a Python object, handling the details of rendering templates, and providing a reasonable interface to HTTP parameters and headers.

It looks pretty basic compared to anything modern, and there are some clear PEP8 violations, but I remember it actually being pretty straightforward and productive to work with. I could write something like


from cgi_app import CGI_Application

class Example_App(CGI_Application):
    def setup(self):
        self.start_mode = 'welcome'

    def welcome(self):
        return self.template("welcome.tmpl",{})

    def param_example_form(self):
        return self.template("param_example_form.tmpl",{})

    def param_example(self):
        name = self.param('name')
        return self.template("param_example.tmpl",{'name' : name})

if __name__ == "__main__":
    e = Example_App()

drop that file in a cgi-app directory along with some templates and have a working app. It was good enough that I could use it for work and I built more than a few sites with it (including at least one version of this site).

I quickly added support for other template libraries and mod_python, the Python equivalent to mod_perl.

I don't remember ever really publicising it beyond an announcement here and I didn't think anyone else was really using it. Shockingly recently though, I got an email offering me some consulting work for a company that had built their product on it, were still using it, and wanted to make some changes. That was a weird combination of pride and horror...

By around 2006 or 2007, the landscape had changed dramatically. Ruby on Rails 1.0 was released in 2005 and turned everything on its head. Other Python developers like me had their own little frameworks and were writing code but there wasn't really any consensus. Django, TurboGears,, and others all appeared and had their fans, but none were immediately superior to the others and developer resources and mindshare were split between them. In the meantime, RoR was getting all the press and developer attention. It was a period of frustration for the Python community.

Out of that confusion and frustration came something pretty neat though. A few years earlier, PJE had come up with WSGI, the Web Server Gateway Interface, but it hadn't really taken off. WSGI was a very simple standard for allowing different web servers and applications to all communicate through a common interface. At some point the Python community figured out that standardizing on WSGI would allow for better integration, more portability, simpler deployment, and allowed for chaining "middleware" components together in clever ways. Most of the competing frameworks quickly added WSGI support. Django took a little longer but eventually came around. The idea was so good that it eventually was adopted by other languages (eg, Rack for Ruby).

JSON had mostly pushed out XML for data serialization and web developers were beginning to appreciate RESTful architecture. I began thinking about my own applications in terms of small, extremely focused REST components that could be stitched together into a full application. I called this approach "microapps" and was fairly evangelical about it, even buying '' and setting it up as a site full of resources (I eventually let that domain lapse and it was bought by spammers so don't try going there now). Other smart folks like Ian Bicking were thinking in similar ways it seemed like we were really onto something. Maybe we were. I don't really want to take any credit for the whole "microservices" thing that's popular now, but I do feel like I see echoes of the same conversations happening again (and we were just rehashing SOA and its predecessors; everything comes back around).

Eventually, after writing quite a few of these (mostly in TurboGears), almost always backed by a fairly simple model in a relational database, I began to tire of implementing the same sort of glue logic, mapping HTTP verbs to basic CRUD operations in the database. So I wrote a little framework. This was Bourbon (WSGI can be pronounced "whiskey").

Bourbon let me essentially define a single database table (using SQLALchemy, which was the new cool Python ORM) and expose a REST+JSON endpoint for it (GET to SELECT, POST or PUT to INSERT, PUT to UPDATE, and DELETE to DELETE) with absolutely minimal boilerplate. There were hooks where you could add additional functionality, but you got all of that pretty much out of the box by defining your schema and URL patterns.

For a number of reasons, I mostly switched to Django shortly after that, so I never went too far with it, but it was simple and clear and super useful for prototyping.

In recent years, I've built a few apps using Django Rest Framework and it has a similar feel (just way more complete).

I don't have a real point here. I just felt like reminiscing about some old code. I've managed to resist writing a web framework for the last decade, so at least I'm improving.

Late to the Party

A little behind the curve on this one (hey, I've been busy), but as of today, every Python app that I run is now on Python3. I'm sure there's still Python2 code running here and there which I will replace when I notice it, but basically everything is upgraded now.

TAGS: python