What Makes a Programming Language Good

18 January, 2011

A year and a half ago, I quit being a professional programmer. For the four years or so previous to that, I'd made websites for a living, either for clients or for my own startup.

At that time, if you'd asked me what matters in a programming language — what makes one good — I would probably have talked about features. The languages I knew (and loved) best were Ruby and Javascript so I would have sung you a song of the epic deeds of dynamic languages: the clean uncluttered syntax, the power of easy extensibility, etc.

At that time I thought of a programming language's virtues as inherent to the language. Having struggled, and mostly failed, to learn C and PHP before picking up Ruby, I felt strongly that I'd succeeded with Ruby due to its technical elegance. I'd failed with C and PHP for very different reasons: C because it was too technical, too close to the machine, PHP because it was too messy and cluttered. Ruby was the right balance.

But then I packed up my laptop and went to art school where I share a floor with 200 other students, most of whom are beginner programmers. Hence, I've found myself doing a lot of helping with other people's code. This help ranges from debugging a simple typo, to working with someone until they really get objects for the first time, to teaching a semester-long weekly tutorial on web technologies.

Simultaneously, I've been learning a lot myself. I've wildly expanded the number and variety of languages that I work with. While I haven't been programming professionally, I have been doing so creatively in a way that's required me to take on everything from ATMega8 assembly to C++, Java, and Python.

These parallel experiences have simultaneously deepened my love for Ruby while completely transforming my understanding of what exactly Ruby got so right. While I started off thinking it was Ruby's intrinsic qualities that made it good, now I realize that most of what I love about Ruby are not language features, but rather systemic and cultural qualities that I took for granted at the time never having experienced their lack.

Put simply, Ruby makes it easy to find and install code, easy to figure out what the code does and how to use it, and easy to run the code and see its results. That may sound like a statement so simple as to approach absurdity. Getting, writing, and running code are pretty much the only things that programmers do. However, it is increasingly shocking to me how painful these things can be in each new language I learn. I think the reasons for this are partially cultural and partially historical and I'll return to them at the end of this post, but first I want to be more specific about what I think Ruby does so well — the parts of the Ruby ecosystem every other language most desperately needs to emulate.

Getting Code

It sounds kind of crazy when you type it out, but Rubygems might just be Ruby's best feature. Rubygems is a package manager. It makes it possible to install Ruby libraries from the internet with a single command and then to use them with a single line of code. Importantly, Rubygems doesn't force the user to think about where on their system the code is installed; they don't need to know anything about paths or multiple versions of libraries or nested dependencies or anything like that. They just install the library and then use it. Rubygems also has a built in search command that lets users find libraries so, for example, if you need a gem to wrap the Foursquare API, you'd immediately find four results which you'd then be one additional command away from installing.

Rubygems has two major characteristics that distinguish it from other languages' dependency management systems: it is centralized and it takes the existence of the internet for granted. The gem system is organized around Rubygems.org, a community-maintained site that provides free hosting for gems and their documentation as well as APIs that make the command line discovery and publishing tools work so well.

While anyone is free to run their own gem server or distribute gems by Jazz drive or whatever other means they find appropriate (in fact the current rubygems.org started off as Gemcutter, a competitor to the original centralized gem host), in practice 99% of publicly available code is on rubygems.org. And the technical infrastructure around gems makes it simple for users to configure their machines to access multiple hosts transparently. As we'll see over and over again, this is an example of Ruby's culture which values competition to come up with the best way to do something combined with a rapid total adoption of new systems whenever consensus is reached.

These characteristics also make it extraordinarily easy to publish a gem. It is a simple matter of moving your code into a couple of files in a specific format and then running some command-line tools that package and submit the gem via rubygems.org's API. There's no hosting to setup or maintain. It really is fire and forget.

Figuring Out Code

But Ruby programmers don't tend to fire their code and forget it. They tend to write thorough blog posts and clear READMEs, produce slick tutorial videos, and publish regular podcasts explaining it and teaching people how to use it. This is the next "feature" of Ruby that I most miss when dealing with other languages: the culture of documentation.

With many other languages and frameworks, when you google looking for code or answers, you're most likely to find forum threads, q&a sites, and a heap of broken links. With Ruby, you'll usually find the gem's Rubyforge page and a recent blog post. To continue the Foursquare example I started above, I just popped "ruby foursquare api" into google. The first two results were the Rubyforge site for the API wrapper and a blog post from November about it.

At a deeper non-day-to-day level, Ruby has amazing materials for beginners: Chris Pine's "Learn to Program", which may be the best introductory programming text I've ever seen and, of course, the legendary _why's Poignant Guide to Ruby. It also has great podcasts and about a billion blogs tracking all the most recent developments.

Ruby has these things because they are valued by the community. People like Geoff Grossenbach at Peepcode can make a living creating awesome, beautifully well-crafted educational materials because Ruby programmers are willing to pay for something so good. I know from personal experience that people who write about Ruby well and often get offered conference gigs and jobs.

Writing Code

When you're working on software anything that isn't thinking, typing, or watching your code run is extraneous bullshit. This includes waiting for your editor to launch, waiting for your code to compile, editing build scripts, creating arcane directory structures, and modifying configuration files or boilerplate code, amongst many other things.

There is simply no other development environment I've found that includes as few of these things as the Interactive Ruby Console. Being able to input code and immediately see the results means you can explore an API, build a data structure, or test out an algorithm at a rate limited only by your mind and fingers.
While other languages and platforms have similar setups (notably Javascript with Firebug and the WebKit Error Console) in my experience, code written in IRB better translates into "real" code in a project than any other such system.

Further, while I know there are people who like IDEs, for me they serve to cripple the exploratory play of coding so that it's about as fun as filling out tax forms. A big exception here for the Processing and Arduino IDEs which, though imperfect as text editors, really do eliminate an immense amount of bullshit usually associated with building and running programs in Java and C++. But, in general, if your language, platform, or environment, has such a complex dependency management situation, maybe you should solve that by, I don't know, improving the packaging system, rather than also ruining the editor.

Granted the command line also has a learning curve and can be intimidating for beginning users. But I don't think these compare in any way to the problems created by IDEs or complex builds. There's even an eccentric tradition in the Ruby community of trying to automate and GUI-ify the running of command line scripts to address this problem (see, for example Aaron Quint's Vegas and Locomotive for Rails from back in the day).

Having only the tab key, the up arrow, and the return key between having made changes in your code and seeing them run on the command line or in the browser is priceless.

Conclusion

Obviously there are ways in which these extrinsic qualities are made possible or at least easier by some of Ruby's intrinsic properties. Rubygems would be harder to implement in a static language that couldn't override its own built-in "require" method. The interactive console is mainly the domain of interpreted dynamic languages (even though compiled Erlang has one too).

But other languages with very similar technical aspects, notably Python, have not succeeded as fully on important things listed here mostly for cultural reasons. For example Python has at least three package distribution systems you'll encounter when attempting to install software of any complexity (eggs, easy_install, and plain old download files into place). And great ideas in Python that need standardization (such as WSGI) have failed to achieve universal adoption only to be picked up by the Ruby community to great effect (i.e. Rack).

Why? I don't know enough about that community to really answer, but since it's basically the only major contrast between them I'll bet it has at least partially to do with the languages' difference in age.

What makes Ruby different? Why does Ruby get these things right where so many other languages get them wrong? How did Ruby acquire the good parts of its culture? I think the answer lies, mainly, in Ruby's youth. Ruby was first created in the mid-90s and didn't even begin to achieve adoption until 2003 or 2004. Hence it is the first widely influential language born and bred in the web era.

Nearly all of the positive aspects I described in this post would be hard to imagine without the omnipresence of the internet. The Ruby community comes to consensus quickly because all of its communication tools have always been online (and as Web 2.0-ish communities rather than old school geek slugfests) and many of its users are "digital natives". It uses centralized but loosely-coupled technical infrastructure (such as Rubygems.org and Github) because that's the kind of infrastructure the internet encourages. Reputations are made online via public contributions to code and documentation rather than privately through corporate jobs and invisible projects.

If I were a zealot for one of the older languages (C++ or one of its frameworks; Java or one of the new languages running on the JVM, etc.) I wouldn't be porting web application design patterns or bragging about performance. I'd be making sure it's stupid simple for web-connected users to find and install libraries, that no google search ever returned an 18-month old forum thread, that any user who wrote awesome blog posts was treasured, that creating a new project or running an example app was always a one click operation, and, above all, that getting, writing, and running code on my platform was easy and surrounded with joy.