Itʼs 2022 and Iʼve now been programming computers for three decades. At the start of my fourth decade, I review what Iʼve done and what I learned from it.
My Mom bought an IBM PC in 1987 as a tool for her writing. My brothers and I installed some games, but the PC was no big deal. Around 1989, my family was heading out to a soccer tournament and I had nothing to read. In urgent need of reading material, I grabbed the MS-DOS manual for Momʼs PC on my way out the door.
I loved it. I read 200 pages that day while ostensibly cheering my brothers. When I got home, I tried every command I had learned. For the next couple years I spent my free time on system administration. I configured our family computer, reinstalled the operating system more often than my parents preferred, and fixed problems with neighborsʼ computers (a good income in middle school).
In those two years of system administration, I hadnʼt done any programming. I had written some batch files, but they used no branches; not even
goto. They were just plodding sequences of commands.
When I upgraded to MS-DOS 5.0 in 1991, I saw QBasic and tasted the nectar of the gods. I produced nothing useful during this time, but I loved making the machine follow my imagination. The QBasic interpreter gave such quick feedback: I wrote code and something happened immediately.
Between 1991 and 1996, I wrote a lot of QBasic. I made good money during the summer of 1995 programming for a cabinet manufacturer. I reprogrammed how he calculated lumber requirements and I wrote a small inventory database. It was rewarding to see my code doing something real.
In the autumn of 1996, I took AP Computer Science. The class used Pascal. At the time, Pascal frustrated me because the compiler was slow compared to my QBasic interpreter, I sucked at pointers, memory management was tedious, and I wished we were using the recently-released Java language instead. Be careful what you wish for.
Eventually I learned to enjoy Pascal. I asked for (and received) Borland Turbo Pascal for Christmas one year. The compiler was faster than I thought. Records were cool, and data structures with pointers were amazing. Memory management remained tedious. My teacher shushed us with her tales of fighting dragons near COBOL before returning to civilization. I found a Prolog book on the classroom shelf; amazed that such mysteries existed in this new world.
During this time I wrote one of my favorite programs. Our class had five students and one of them loved his Caps Lock. He wrote as much of his program in upper case as he could. He resisted attempts to persuade him towards lower case. So I wrote a DOS TSR in C that waited for Caps Lock to activate, paused for one second, then turned off Caps Lock. I installed it on his workstation and had a great laugh watching the keyboard light flick off as he tried to use his beloved upper case.
The next summer I got a job programming Java. Since I had spent the last few months manually writing data structures in Pascal, I adored Javaʼs standard library. Someone else had created all these data structures for me and I got to focus on the interesting parts of my program. I also loved garbage collection. Beyond that, I grew to hate Java. The grass was not greener. After BASIC and Pascal, Java just seemed so complex. Despite everything I had read, classes and inheritance felt like a messy mistake once I used them. Everything required three layers of abstraction and none were necessary.
I spent the next three years at University writing Java to earn money, but Java and I only grew apart. When I graduated, I swore off computers and Java. The latter stuck, the former didnʼt.
Aside from one programming class in Standard ML, I did very little programming in college aside from my Java job. I thought I wanted to design CPUs, but ended up enjoying programming much more. I wish I had taken more comp-sci classes, especially ones about operating systems and compilers.
Anyway, I didnʼt use Standard ML enough to have an opinion of it. I didnʼt see the point of functional programming, didnʼt think to ask, and none of my professors described its benefits. I wish they had. When I rediscovered functional programming later in life, I loved it. I did think that MLʼs type signatures were cool. I had no special opinion about types in Pascal, but MLʼs were snazzy. It was a fun puzzle to match up all the
ʼb. ML also gave me a solid understanding of recursion.
I graduated from University in 2000. Since I had sworn off computers, I spent two years hiking around the desert and reading paper books. Eventually I bought a laptop and started programming again. I donʼt remember how, but I chose Perl. I loved it immediately. It had the snappy development cycle of my old QBasic interpreter; with arrays, hashes, regular expressions, and many built-in modules, it had a large enough standard library that I could be productive quickly; and it had automatic memory management. Java had soured me on objects and class hierarchies, so dynamic types felt like an empowering rebellion.
Ironically, Perlʼs lack of structure gave me discipline. The compiler wasnʼt going to help me. I was on my own. I became strict with myself about how I structured code. I documented my code better. I chose names more wisely. Perl taught me the value of automated tests.
In 2005, I read Higher Order Perl by Mark Jason Dominus. It completely changed programming for me. Presumably this is what my Standard ML class in college was supposed to do, but this time it stuck. That year I started learning Haskell. I was only experimenting and writing toy programs, but it helped solidify many of the functional programming ideas from HOP, and it awoke gentle memories of Standard ML: a type system could be friendly and helpful, not abusive.
Anyway, Perl was a great thing in my life. It helped me rock my first real job: data analytics and reporting. It helped me land my first contract job: programming financial systems for a bond auction company. It let me help my brother with his first startup: selling used video games. Then he and I used it to create another startup: PriceCharting.
During the last couple years of the decade, I wrote numerous commit messages explaining that I was fixing a silly bug and that a decent type system would have noticed this before it landed in production.
By now PriceCharting was doing well and I was spending too much time managing servers. We decided to migrate to App Engine so that Google could manage servers while I wrote code. At the time, that meant using Python, Java, or Go. Java was out because it was Java. Python was out because it had no types, and I was sick of writing those commit messages. That left Go. Transitioning from Perlʼs TMTOWTDI to Goʼs strict conventions was rough. I didnʼt like it at first, but App Engine was rad and we were all convinced that parallel CPUs were the future, so I pushed forward.
Iʼm glad I did. This decade of programming in Go has been my most productive by far. Most of that is because Iʼm a better programmer. Iʼve seen most problems before and have a good sense of what works and what doesnʼt. But Go deserves some credit too: its harsh simplicity and deep embedding of the Unix philosophy were eye opening. Using Go is like having Rob Pike and Ken Thompson download their lifeʼs wisdom into your brainstem.
First Java and then Perl had led me to an ugly place where I was too lazy to write code anymore. If there wasnʼt a module on CPAN, I threw a tantrum. Go taught me that itʼs often better to write it myself. Thatʼs not because Iʼm amazing and other programmers are terrible, itʼs because dependencies and abstraction arenʼt free. Iʼm not sure that Go finds the perfect balance here, but itʼs close.
Now that I worked for my own company, I was also free to experiment with cool stuff. I wanted to use the right tool for the job. Enter Prolog and Haskell.
That Prolog book from high school haunted me. I decided to finally figure it out, and loved it. To this day, Prolog still makes me smile more than any language Iʼve used. I rewrote PriceChartingʼs natural language processing in Prolog. I rewrote our web scrapers in Prolog. I migrated a bunch of our test suite to Prolog. I gave a couple talks about Prolog. Prolog wouldnʼt run in App Engine, so I wrote a Prolog interpreter in Go so it could. All of it was loads of fun.
At the same time I rewrote our pricing algorithms and HTTP proxies in Haskell. I did some Bitcoin stuff in Haskell. I taught my seven-year-old daughter Haskell.
It was informative to do Prolog (logic programming without types) at the same time as Haskell (functional programming with strong types). Theyʼre so similar and so different. I tried Mercury, hoping that Prolog and Haskell could have a beautiful baby together. The baby was beautiful, and it taught me a lot about logic programming, but I spent too much time working on types and not enough time being productive.
I decided to sell PriceCharting to my brother. Heʼs good with Perl and Go, but didnʼt ever learn Prolog or Haskell. I began rewriting the Prolog and Haskell pieces in Go so that he could maintain the code after I left. As Go, Prolog, Haskell, and Mercury simmered together in this tasty stew, I realized some things: the right tool for the job is the one that youʼre productive with; types are a helpful tool as long as theyʼre kept concrete and simple (Haskell and Mercury went too far); focus on one thing at a time (Prolog predicates do too much); concurrent and parallel may be the future of CPUs but theyʼre not the future of human cognition (our brains hate multitasking).
During the transition away from PriceCharting, I switched to OpenBSD. Itʼs still the OS on my main laptop and main server. The community has taught me a lot about getting things done. They sometimes get flack for still using CVS and C. Whatever. While the rest of us are flapping our lips in blog articles, theyʼre getting stuff done. Use what works for you.
For the last three years, Iʼve written Go almost exclusively. I still fool around with Prolog and Haskell, but I come home to Go every night. Iʼve written some of the best code in my life. A quick thought and the Go flows from my fingers, the pattern of lights is right, and stuff gets done. We even have generics now (or we will in Go 1.18).
But Iʼve started writing those commit messages again. Interfaces with private methods are no substitute for sum types. Manually terminating every switch with a default panic about exhaustiveness is exhausting. And, there are always mistakes hiding in the boilerplate of my error handling.
We lost Eden. I canʼt fight the cherubim and flaming sword to get back there. Iʼm not looking for perfection, but I do feel a bit like Daniel Boone: when he saw smoke from his neighborʼs chimney, he knew it was time to move on. When the rewarding challenges of a frontier call my name, the comforts of productivity at home may not be enough; maybe itʼs better beyond the next ridge. But maybe itʼs Java.