Coding Horror

programming and human factors

Monkeypatching For Humans

Although I love strings, sometimes the String class can break your heart. For example, in C#, there is no String.Left() function. Fair enough; we can roll up our sleeves and write our own function lickety-split:

public static string Left(string s, int len)
{
if (len == 0 || s.Length == 0)
return "";
else if (s.Length <= len)
return s;
else
return s.Substring(0, len);
}

And call it like so:

var s = "Supercalifragilisticexpialidocious";
s = Left(s, 5);

Fairly painless, right?

But with the advent of C# 3.0, there's an even better way -- extension methods. With an extension method, we "extend" the String to add the missing function. The code is fairly similar; I'll highlight the changed parts in red.

public static string Left(this string s, int len)
{
if (len == 0 || s.Length == 0)
return "";
else if (s.Length <= len)
return s;
else
return s.Substring(0, len);
}

And now we can call it as if this very method existed on the String class as shipped:

var s = "Supercalifragilisticexpialidocious";
s = s.Left(5);

Pretty slick. It's difficult not to fall in love with extension methods, as they allow you to mold classes into exactly what you think they should be. This is fairly innocuous in C#, as extension methods only allow you to add new functionality to classes, not override, remove, or replace anything.

But imagine if you could.

Well, that's exactly how it is in other, more dynamic languages such as Javascript, Python, Perl, and Ruby. Something as prosaic as C# extensions is old hat to these folks. In those languages, you could redefine everything in the String class if you wanted to. This is commonly known in dynamic language circles as monkeypatching.

monkey patching

If the idea of monkeypatching scares you a little, it probably should. Can you imagine debugging code where the String class had subtly different behaviors from the String you've learned to use? Monkeypatching can be incredibly dangerous in the wrong hands, as Avdi Grimm notes:

Monkey patching is the new black [in the Ruby community]. It's what all the hip kids are doing. To the point that smart, experienced hackers reach for a monkey patch as their tool of first resort, even when a simpler, more traditional solution is possible.

I don't believe this situation to be sustainable. Where I work, we are already seeing subtle, difficult-to-debug problems crop up as the result of monkey patching in plugins. Patches interact in unpredictable, combinatoric ways. And by their nature, bugs caused by monkey patches are more difficult to track down than those introduced by more traditional classes and methods. As just one example: on one project, it was a known caveat that we could not rely on class inheritable attributes as provided by ActiveSupport. No one knew why. Every Model we wrote had to use awkward workarounds. Eventually we tracked it down in a plugin that generated admin consoles. It was overwriting Class.inherited(). It took us months to find this out.

This is just going to get worse if we don't do something about it. And the "something" is going to have to be a cultural shift, not a technical fix. I believe it is time for experienced Ruby programmers to wean ourselves off of monkey patching, and start demonstrating more robust techniques.

Try to imagine a world where every programmer you know is a wannabe language designer, bent on molding the language to their whims. When I close my eyes and imagine it, I have a vision of the apocalypse, a perfect, pitch-black storm of utterly incomprhensible, pathologically difficult to debug code.

I was just looking at random PHP plugin code the other day, and it was, frankly, crap. But that's because most code is crap. Including my own. It is, sadly, the statistical norm. That's why sites like The Daily WTF are guaranteed to have more material than they can possibly ever publish for the next millennia. (Note to self: invest in this website). I can only imagine what that PHP plugin code would have looked like, had its developer been granted the ability to redefine fundamental PHP keywords and classes at will. These are the sort of thoughts that drive me to drink Bawls. And that stuff is disgusting.

You might say that PHP, sans the fundamental dynamic language ability to monkeypatch, is just another crappy Blub language. But there's also a ton of incredibly useful PHP code out there. So it seems to me that the ability to monkeypatch doesn't stop people from producing a huge volume of useful code, even in a kind of.. horrible language. Some of it is even good!

While I acknowledge the power and utility of dynamic language monkeypatching, I know enough about programmers -- myself absolutely included -- to know the vast majority of us have absolutely no business whatsoever re-designing a programming language. There's a reason some of the most deeply respected computer scientists in the world end up as language designers.

Perhaps then, given the risks, monkeypatching should mean reaching for the meta-hammer as infrequently as humanly possible. This is a position that Avdi himself espouses in a followup comment:

I'm afraid a lot of people have missed the actual meat of my argument -- that dynamic extension of classes is currently overused in Ruby, in ways that are:

  • Needless - another technique (such as a mixin, or locally extending individual objects) would have worked as well or better.
  • Overcomplicated - the use of a monkey patch actually created more work for the author.
  • Fragile - the solution is tightly bound to third-party internals, reducing the usefulness of the plugin or gem because it is prone to breakage.
  • Excessively wide in scope - by hardcoding extensions to core classes, the author takes the choice to scope the change out of the plugin/gem user's hands, further limiting utility.

My point is that there are alternatives - often alternatives which are actually easier to implement and will make your plugin or gem more useful to the user.

While I enjoy the additive nature of C# extensions, even those are enough to make me a little nervous, as mild as they are. Full-blown dynamic language monkeypatching goes even further; it might even be the ultimate expression of programming power. Is there anything more pure and godlike than programming your own programming language?

But if wielding that power doesn't scare and humble you a little, too, then maybe you should leave the monkeypatching to the really smart monkeys.

Discussion

iTunes is Anti-Web

Ever find yourself clicking on links to music or videos and getting blasted in the face with this delightful little number?

We are unable to find iTunes on your computer.

That's right -- links to any sort of music, TV shows, movies, podcasts, audiobooks or anything else available through Apple's iTunes store requires custom software to be installed on your computer before they will display thing one to you.

Is it so unreasonable to expect links in your browser to resolve to, oh, I don't know, web pages containing information about the thing you just clicked on? Is there anything more anti-web than demanding users install custom software to display information that could have just as easily been delivered through the browser?

So here's what we know:

  • Apple wants us to install iTunes.
  • iTunes is necessary to sync media with Apple's iPods and iPhones.
  • iTunes is only available for Windows and OS X.
  • iTunes is required to browse or buy anything from the iTunes Store.

That's all well and good for people who own iPods and iPhones -- and happen to be running Windows or OS X, I suppose.

But what about the rest of the world? Why lock them out with the ultimate login barrier? We might like to browse the iTunes Store, too. At the very least, I might want some basic information about the media I just clicked on. Right here in my browser where I already am. Information like what the heck it is, some artwork, maybe some audio clips, how much it costs -- sweet talk me. Make me want to buy it through the Apple Store. Dazzle me with your simplicity and ease of use. Beguile me with your wares!

Or, you could bludgeon me with the digital equivalent of a giant stop sign.

Hey, "it just works". Except when it doesn't.

I'm certainly able to click through to eminently purchaseable media on dozens of other places on the web using nothing more than my web browser. Let's imagine, for a moment, how utterly ridiculous it would be if I had to install the Amazon application to browse and purchase media from Amazon.

unable to find Amazon on your computer

And yet this is exactly how the iTunes Store works. Or doesn't work, depending on your perspective.

I can understand requiring iTunes once you want to sync your media with Apple hardware devices -- although I would argue syncing should really be a fundamental, built in function of the operating system. But I'm not trying to sync anything! All I did was click on a link. It's downright user hostile to demand installation of a special application merely to browse the store, and it is most certainly against everything the web stands for and was built on.

The last "application" I can recall needing to install to get to things online was AOL.

AOL 2.5 main menu

And we all know how great that turned out.

For all the buzz about the Apple "it just works" mystique, the current iTunes Store design surely doesn't -- at least not the same way the rest of the web does. And I, for one, can't get behind that.

Discussion

Spartan Programming

As I grow older and wisereven older as a programmer, I've found that my personal coding style has trended heavily toward minimalism.

I was pleased, then, to find many of the coding conventions I've settled on over the last 20 years codified in Spartan programming.

300-movie.jpg

No, not that sort of Spartan, although it is historically related. The particular meaning of spartan I'm referring to is this one:

(adj) ascetic, ascetical, austere, spartan (practicing great self-denial) "Be systematically ascetic...do...something for no other reason than that you would rather not do it" - William James; "a desert nomad's austere life"; "a spartan diet"; "a spartan existence"

I've tried to code smaller, even going so far as to write no code at all when I can get away with it. Spartan programming aligns perfectly with these goals. You strive for simultaneous minimization of your code in many dimensions:

  1. Horizontal complexity. The depth of nesting of control structures.
  2. Vertical complexity. The number of lines or length of code.
  3. Token count.
  4. Character count.
  5. Parameters. The number of parameters to a routine or a generic structure.
  6. Variables.
  7. Looping instructions. The number of iterative instructions and their nesting level.
  8. Conditionals. The number of if and multiple branch switch statements.

The discipline of spartan programming means frugal use of variables:

  1. Minimize number of variables. Inline variables which are used only once. Take advantage of foreach loops.
  2. Minimize visibility of variables and other identifiers. Define variables at the smallest possible scope.
  3. Minimize accessibility of variables. Prefer the greater encapsulation of private variables.
  4. Minimize variability of variables. Strive to make variables final in Java and const in C++. Use annotations or restrictions whenever possible.
  5. Minimize lifetime of variables. Prefer ephemeral variables to longer lived ones. Avoid persistent variables such as files.
  6. Minimize names of variables. Short-lived, tightly scoped variables can use concise, terse names.
  7. Minimize use of array variables. Replace them with collections provided by your standard libraries.

It also means frugal use of control structures, with early return whenever possible. This is probably best illustrated with an actual example, starting with raw code and refactoring it using the spartan programming techniques:

I don't agree with all the rules and guidelines presented here, but I was definitely nodding along with the majority of the page. Minimalism isn't always the right choice, but it's rarely the wrong choice. You could certainly do worse than to adopt the discipline of spartan programming on your next programming project.

(hat tip to Yuval Tobias for sending this link my way)

Discussion

The Problem With Code Folding

When you join a team, it's important to bend your preferences a little to accommodate the generally accepted coding practices of that team. Not everyone has to agree on every miniscule detail of the code, of course, but it's a good idea to dicuss it with your team and decide on overall approaches and philosophy beforehand. It promotes team harmony, and more than that, it's just common courtesy. As they say, when in Rome, do as the Romans do.

I've always been wary of cowboy coders who rolled into an ongoing project on fresh horses and immediately started dictating terms. It's a very short trip indeed from there to Who Wrote This Crap, and the predictable, inevitable finger-pointing at the foolhardy programmers who came before you begins. Don't be that guy or gal. Work with your team, not against it.

Still, there are some coding preferences people may feel.. strongly.. about. If that's the case, try to clear the air and address those strong preferences up front, as early as possible. Don't let them simmer. For me, the use of #region is one of those things. I tried to make myself clear in this twitter message:

Twitter message from codinghorror about #regions

So what is #region? It's a named hint you place in C# or VB.NET code to set a code folding point. Any code placed inside that region is, by default, collapsed when you re-open it in the editor. Here's a random example from the Log4Net project:

C# region example, log4net.Util.NativeError

Immediately I have a problem: I can't see anything! I have to manually expand those sections to browse any of the code in this class. It is possible to configure the Visual Studio IDE to not fold any of the regions when files are opened, but this is the out of box behavior, so that's what most developers will see. And of course there are keyboard shortcuts to deal with the regions:

Ctrl+M, Ctrl+M Collapse or expand the block you're currently in.
Ctrl+M, Ctrl+O Collapse all blocks in the file
Ctrl+M, Ctrl+L Expand all blocks in the file
Ctrl+M, Ctrl+P Stop outlining mode. (Ctrl+M, Ctrl+O resumes)

Here's the really sick part: once you expand the above log4net code there's literally three pages worth of code there! After you strip out all the massive XMLDoc comments and the dozen or so #region directives, you could have had all the code at your fingertips with a minor flick of the mouse wheel, in a simple scrollable layout.

I daresay being able to see the damn code is more important than having it meticulously segmented into six pointless little named buckets, but apparently a lot of programmers can't get enough of stuffing their code into pointless little named buckets. It's as if they've forgotten what the scroll bar -- and incremental search -- is for.

The #region directive drives me bonkers. It's not evil, per se, but I feel it is criminally overused in practice and heavily prone to abuse. I strongly urge you to think about how you're using code folding, because as I see it, there are a lot of downsides:

  1. Folding directives are glorified comments. #region has zero meaning to the compiler; it's a hint to the editor to allow code folding. It doesn't do any namespacing or scoping. Why, exactly, are we writing code to accommodate the editor? It boggles my mind that we'd add significant lines of code to our project that do nothing but offer organizational hints to the editor. Even traditional comments are a better value for your keystroke, because they can be more expressive. And folding is certainly no substitute at all for bona-fide refactoring.

  2. Folding is used to sweep code under the rug. Got a bunch of boring boilerplate code that makes your eyes water? A slew of ugly, gnarly code that nobody in their right mind wants to look at? Hide it in a region and fold that sucker into oblivion! Problem solved, right? Hardly. Your project is now full of crappy code that you can't see. That's worse. Much worse! Code that hides from you is code that will rot in the most putrescent and painful way possible. Your code should be front and center at all times -- exposed to as many programmers' eyes, and as much healing light, as possible.

  3. Folding is used to mask excessive length. The presence of folded code can lull developers into a false sense of what clean code looks like. Under the cover of folding, you can end up writing long, horrible spaghetti code blocks. If the code needs the crutch of folding to look organized, it's bad code.

  4. Folding can hide deficiencies in your editor. The presence of so-called "standard" boilerplate regions like "Public Constructors" and "Public Properties" and "Events" is not a feature. It's a bug. The editor should automatically offer to fold up these common structural blocks for you! I'm continually amazed that programmers spend time doing this scutwork when they could be writing useful code. Or at least demanding a smarter code editor.

I urge developers to write code that doesn't need folding to be readable, clear, and concise. I'm sure there are sane uses for code folding out there somewhere, but I rarely see them.

Discussion

Investing in a Quality Programming Chair

In A Developer's Second Most Important Asset, I described how buying a quality chair may be one of the smartest investments you can make as a software developer.

In fact, after browsing chairs for the last few years of my career, I've come to one conclusion: you can't expect to get a decent chair for less than $500. If you are spending less than that on seating – unless you are getting the deal of the century on dot-bomb bankruptcy auctions – you're probably making a mistake.

I still believe this to be true, and I urge any programmers reading this to seriously consider the value of what you're sitting in while you're on the job. In our profession, seating matters:

  • Chairs are a primary part of the programming experience. Eight hours a day, every day, for the rest of your working life – you're sitting in one. Like it or not, whatever you're sitting in has a measurable impact on your work experience.
  • Cheap chairs suck. Maybe I've become spoiled, but I have yet to sit in a single good, cheap chair. In my experience, the difference between the really great chairs and the cheap stuff is enormous. A quality chair is so comfortable and accommodating it effortlessly melts into the background, so you can focus on your work. A cheesy, cheap chair constantly reminds you how many hours of work you have left.
  • Chairs last. As I write this, I'm still sitting my original Aeron chair, which I purchased in 1998. I can't think of any other piece of equipment I use in my job that has lasted me ten full years and beyond. While the initial sticker shock of a quality chair may turn you off, try to mentally amortize that cost across the next ten years or more.

Choice of seating is as fundamental and constant as it gets in a programming career otherwise marked by relentless change. They are long term investments. Why not take the same care and consideration in selecting a chair as you would with the other strategic directions that you'll carry with you for the rest of your career? Skimping yourself on a chair just doesn't make sense.

Although I've been quite happy with my Herman Miller Aeron chair over the last 10 years, I've always been a little disenchanted with the way it became associated with dot-com excess:

In the '90s, the Aeron became an emblem of the dot-com boom; it symbolized mobility, speed, efficiency, and 24/seven work weeks. The Aeron was a must-have for hot startups precisely because it looked the least like office furniture: It was more like a piece of machinery or unadorned engineering. The black Pellide webbing was durable, and hid whatever Jolt or Red Bull stains you might get on it. Held taut by an aluminum frame, the mesh allowed air to circulate and kept your body cool. What's more, the chair came in three sizes, like a personalized tool. Assorted knobs and levers allowed you to adjust the seat height, tilt tension, tilt range, forward tilt, arm height, arm width, arm angle, lumbar depth, and lumbar height. The Aeron was high-tech but sexy – which was how the dot-commers saw themselves.

aeron chair

But baby-faced CEOs weren't drawn to the Aeron only for the way it looked. The Aeron was a visual expression of the anti-corporate zeitgeist, a non-hierarchical philosophy about the workplace. An office full of Aerons implicitly rejected the Fortune 500, coat-and-tie, brick-and-mortar model in which the boss sinks back in an overpriced, oversized, leather dinosaur while his secretary perches on an Office Max toadstool taking notes.

I recently had the opportunity to sit in a newer Herman Miller Mirra chair on a trip, and I was surprised how much more comfortable it felt than my classic Aeron.

chair-mirra

The Mirra chair was an excellent recliner, too. I've been disappointed by how poorly the Aeron reclines. I actually broke my Aeron's recline pin once and had to replace it myself. So I've retrained myself not to recline, which is awkward, as I'm a natural recliner.

All this made me wonder if I should retire my Aeron and upgrade to something better. I liked the Mirra, but the comments to my original chair post have a lot of other good seating suggestions, too. Here are pictures and links to the chairs that were most frequently mentioned as contenders, in addition to the Mirra and Aeron pictured above:

Steelcase Think Chair

steelcase think chair

Steelcase Leap Chair

steelcase leap chair

Ergohuman Mesh Chair

ergohuman chair

HumanScale Freedom Chair

humanscale freedom chair

HumanScale Liberty Chair

humanscale liberty chair

There were also some lesser known recommendations, such as the Haworth Zody chair, Nightingale CXO chair, BodyBilt ergo chairs, Hag kneeling chair, NeutralPosture ergo, the Chadwick Chair from the original designer of the Aeron, and something called the swopper.

Chair fit is, of course, a subjective thing. If you're investing $500+ in a chair, you'd understandably want to be sure it's "the one". The thing to do is find a local store that sells all these chairs and try them all out. Well, good luck with that. Don't even bother with your local big-box office supply chain. Your best bet seems to be back stores, as they tend to stock many of the more exotic chairs. Apparently they have a clientele of people who are willing to spend for comfort.

Reviews of individual chairs are relatively easy to find, but aren't particularly helpful in isolation. What we need is a multi-chair review roundup. The only notable roundup I know of is Slate's late 2005 Sit Happens: The Search for the Best Desk Chair. It's not as comprehensive as I would like, but it does have most of the main contenders. Notably, Slate's winner was the HumanScale Liberty.

Some other helpful resources I've found, both in the comments to this post, and elsewhere:

If this is all a bit too much furniture porn for your tastes, I understand. As for me, I'm headed off to my local friendly neighborhood back store to figure out which of these chairs will best replace my aging Aeron. By my calculations, the Aeron cost me about $7 per month over its ten year lifetime; I figure my continued health and comfort while programming are worth at least that much.

Update: Since people have been asking, I ultimately decided the best fit and feel for me, personally, was the Herman Miller Mirra chair. It's a huge upgrade from my ten year old Aeron. It feels like three or four revisions better. For example, the front lip of the seat is adjustable, which addresses one of the major concerns I had with my Aeron – as well as the vastly improved reclining I mentioned above. The only unexpected downside is that the plastic back is a little rough on the skin if you sit, er... shirtless. Although I am very pleased with my new shadow Mirra with citron back, I urge you to do the research and try the chairs yourself before deciding.

Discussion