Showing posts with label hype. Show all posts
Showing posts with label hype. Show all posts

Monday, March 4, 2013

The Great Programming Debate

Over the last few years, I've noticed a pattern that keeps repeating in Internet forums and blogs:
  1. Someone asserts that programming is or should be for everybody, usually misrepresenting it as a glamorous and easy job full of benefits and no drawbacks, like this.
  2. Someone else posts an emphatic counterpoint, like this.
  3. A third person tries to point out that a certain degree of "programming literacy" should be required and goes on to point out that everyone knows (or should know) how to read and write, like this.
  4. The discussion devolves into polite bickering, because at this point most people are usually past their threshold for completely unbiased discussion and are invested in their own point of view.
I happen to have my own point of view, but it usually gets lost in the noise, since I invariably try to contribute it in a discussion during step 4. Looking back, I can see I've weighed in on both sides of the debate and I think it might be interesting to try to share a more balanced view and, perhaps, bring the people from the two extremes a bit closer.

Why Programming Should Be For Everybody


As I pointed out recently, in a similar discussion, I got into computers when I was 7 years old. Back then, "getting into computers", at least in my country, meant getting something like ZX Spectrum or Commodore and either playing games or learning how to code. Games were in English and a lot uglier than their arcade counterparts, so it should come as no surprise that most kids who had computers went on to learn how to make them "do tricks". That's the real allure of programming for a kid: you're typing in seemingly incomprehensible stuff and the machine does something. It's magic!

Not everyone had a computer, mainly because we were living in Communist Yugoslavia and it was not easy to import one, but nobody told us that computers are "not for everyone" and that only trained professionals can program. It was a slightly weird hobby to have, but it grew into some of the best years of my life: I grew up believing that the world would be transformed and that I would be part of it. Some days I felt like a combination of a wizard, secret agent and super-hero; other times I felt like a rebel straight out of Mentor's Hacker Manifesto.

At no point did I feel like a kid who's playing at being adult and should leave it to professionals.

You might think I'm attacking a straw man here, but this sort of elitism seems to be increasingly more prominent in discussions nowadays:
Sorry, but those things are technology and should be worked on by technologists because there's literally no on else who can work on them
That choice quote came from a discussion on Hacker News and it's the kind of extreme point of view that provokes people to react with very emphatic counterpoints.

Why Programming Isn't For Everybody


The problem with emphatic counterpoints is that they tend to go to the opposite, equally unhealthy extreme. If you've watched "What Most Schools Don't Teach", then you know that the intentions and goals behind the video are laudable. You probably also noticed that programming has been grossly misrepresented.

If you didn't know better, if you were the stereotypical impressionable youth, you might have walked away from that video believing that programming is easy if you know addition, subtraction and multiplication tables and that working as a programmer is a lot of fun, where you get free gourmet food, play video games and jam with a band inside an awesome office. You might also get an impression that you would be programming flying robots, self-driving cars and medical equipment.

As much as I agree that programming should be taught in schools -- and I'll get back to that later -- the fact is that programming is not easy and good programming is damn hard. To make things harder, good programming is a lot more important today, precisely because computers are so widespread. Bad code does have the power to ruin lives. I'm not talking about medical equipment or space shuttle systems here: a bad credit report won't kill you, but it might ruin your life for years; a badly programmed system might incorrectly match you as a terrorist.

When conveying a positive message in service of a good cause, you must take care not to go to extremes. Nobody tells kids that being a doctor is as easy as putting on a stethoscope and learning which side of the body holds the heart and which the liver. By simultaneously downplaying the difficulties of programming too much and painting a glamorous picture of it, you're setting kids up for disappointment:
We’ve all been raised on television to believe that one day we’d all be millionaires, and movie gods, and rock stars. But we won’t. And we’re slowly learning that fact. And we’re very, very pissed off.
-- Chuck Palahniuk, "Fight Club"

Incidentally, you're also bruising the egos of everyone who has worked their ass off for more than ten years trying to get closer to being a master programmer. While that's the most forgivable mistake, it still merits being mentioned, because you're alienating the very people you need the most.

Shades of Grey


In the end, it all boils down to recognizing that things aren't black and white. Putting the programming on a pedestal, unreachable by all but a select few, is as wrong as representing it as a glamorous job where anyone can be a rock star if they only try.

I agree that all schools should teach a certain level of programming, just as they teach literacy and a certain level of mathematics. In our society, computers are everywhere and everyone should be brought up to understand them and be reasonably proficient at manipulating them.

On the other hand, even though everyone is taught reading and writing, not everyone ends up being a writer. Even those who do are not necessarily successful. Likewise:
  • Knowing basic biology does not make you a doctor.
  • Learning how to play an instrument well enough to play it professionally requires constant practice and even then you might never get to be famous and hugely successful.
  • Getting into NBA requires more than being able to run the length of the basketball court.
  • Being able to prepare scrambled eggs or even come up with a recipe or two of your own doesn't make you Jamie Oliver.
Are these analogies exaggerated? Yes, but so are some messages we offer about programming. Let's face it, not everyone will have the aptitude to take make programming more than a hobby.

The important thing is that people of all ages need to be free to try it and we need to welcome them. However, it's equally important to make sure it's okay for them to fail. Our industry needs all the good hands it can get, but it's hard enough as it is without adding tons of bad, unhappy programmers who were promised the world and tricked into believing they could get it by wishing upon a star.

Thursday, July 24, 2008

"Encryption Chip" Will Not End Piracy

Nolan Bushnell is full of it. There, I finally got that off my chest. It's arguably acerbic and rather rude, but it needed to be said. You have no idea how hard I've tried to avoid saying it. After all, he's the founder of Atari and a historical figure unto his own. He deserves certain respect for that.

The first time Nolan Bushnell claimed that the "encryption chip" will end piracy, I exercised due restraint. His statement reverberated all over the Internet, causing reactions that ranged from mild skepticism on one end of the spectrum to derision and disgust on the other.

So why am I writing now, more than two months later, if nobody believed him in the first place? In other words, why am I beating a dead horse? Partly, it's because he did it again and it pisses me off. Mostly, though, it's because I'm rather interested in copy protections and security; it's sort of a hobby of mine.

The most important lessons you learn in those two fields is that no protection is perfect and every solution spawns a new class of problems. This means that there will never be one final (technical) solution to the issue of piracy; there is no silver bullet. The experts from both fields are locked in an arms race with their adversaries. Once you've learned that, you'll have no problem recognizing that Nolan Bushnell is really just flogging his merch.

However, the issue runs deeper than that.

Copy Protection and Security

When I referred to copy protections and security I said "two fields", even though one can be considered a subset of the other; after all, copy protections are supposed to prevent the unauthorized use of software. Even though this is technically true, there are some drastic differences between the two.

An important difference is the level of cooperation from the users. When it comes to information security, the users actively cooperate with the protection systems, because it's in their best interest. You don't give access to your bank account to all your friends, do you?

On the other hand, copy protections often clash with the users' interests. Some of these interests are illegal, such as downloading a commercial game for free. But other interests are quite legal and legitimate. You added more memory to your computer? Odds are you might have to reactivate your Windows.

Another important difference is that a copy protection has to protect an application that lives on the user's computer. Unless we're talking about an MMOG, there's no server counterpart that executes a critical piece of code, without which the game can't work.

When you put those two things together, it becomes obvious why you can't make a perfect copy protection: you're relying on cooperation from a user that has complete control over his copy of your content or software. If that user doesn't want to cooperate, the best you can do is delay him. Even unbreakable ciphers won't help you, because sooner or later you'll have to decrypt the content and, when you do, the user will nab it.

But what if you could alter these conditions? You could make sure that there's a critical part of an application that executes somewhere where the user doesn't have control over it: that's what MMOGs do. The other option is to take away the control from the user.

Trust Controversy

Enter the "trusted computing". The first time I heard of it was back when Microsoft was touting Palladium. Back then, it sounded like a bad pun: a company found guilty in an antitrust lawsuit is proposing to build a "trusted computing platform" for its users. The irony was not lost on anyone and it provoked some enlightening responses from security experts.

Then, since nothing really seemed to happen and we didn't all suddenly wake up in some digital equivalent of 1984, I lost track of this topic for a while. I forgot about it until Nolan Bushnell started his TPM hype. A quick search engine query revealed that TPM stands for "Trusted Platform Module" and that it's the central component of "trusted computing".

What, then, is the so-called "trusted computing"? It's a technology that encompasses the following concepts:
  1. Endorsement key is a cryptographic key pair unique to one computer. The chief use for it is to prove the computer's identity.
  2. Secure I/O makes sure that the communication between the user and their software is secure and cannot be intercepted or altered.
  3. Memory curtaining protects those parts of memory that contain sensitive data (such as cryptographic keys) from unauthorized access, even by the operating system itself.
  4. Sealed storage binds data to the specific platform -- both hardware and software -- so that you cannot access it from any other platform.
  5. Remote attestation allows authorized parties to detect changes to the platform configuration in order to make sure that they meet the expected parameters; in other words, to prove that nobody tampered with the platform.
That's just a brief summary, to give you an idea of what we're talking about here. If you want more information, I recommend that you start at Wikipedia and then go on directly to the Trusted Computing Group site.

So, the core idea is to make computers more secure, by ensuring that no "untrusted" code has access to your stuff. At least, that's supposed to be the core idea. Unfortunately, there has been a great deal of confusion about the word "trust" in "trusted computing". Specifically, who is supposed to trust whom?

If you read Bruce Schneier's essay on "trusted computing", you'll notice that there's a good deal of controversy and confusion surrounding the issue. As one commenter so aptly put it, the only one not trusted seems to be the owner of the computer.

All Your Base

Each of the five concepts of "trusted computing" addresses a real security problem:
  1. Endorsement keys would be used to mitigate spoofing concerns in secure transactions by establishing the identity of each party involved.
  2. Secure I/O is supposed to avoid security breaches through techniques such as keylogging.
  3. Memory curtaining would make sure that sensitive information, such as cryptographic keys, is not allowed to "leak" somewhere where it could be extracted by malicious parties.
  4. Sealed storage would do a similar thing for sensitive information in non-volatile storage.
  5. Remote attestation could help network administrators easily detect intrusions and attacks on their machines.
Yet, after a closer look at them, it becomes evident that there's plenty of room for abuse. Imagine, for example, a system that enforces specific usage policies on your data:
  • It would use sealed storage to bind that data to a particular application or set of applications that you're allowed to use on that data.
  • It would employ memory curtaining to make sure you cannot extract that data directly from memory.
  • It would use secure I/O to make sure you cannot intercept it on its way somewhere else.
  • It would use remote attestation to report if you tamper with any part of the system.
  • And it would clearly identify you as a "culprit" to whoever is interested in enforcing those policies, if it possessed both your personal information and your endorsement key.
Is there any kind of usage policy that springs immediately to mind? There are two, actually: DRM and vendor lock-in. Ross Anderson describes several ways to abuse TC in his FAQ. Richard Stallman dedicates a whole chapter to this topic, in his book "Free Software, Free Society"; although slightly reminiscent of Book of Revelations in tone, it offers some very interesting insights.

Another interesting aspect of "trusted computing" is that it actually raises the stakes when it comes to information security: imagine a worm that successfully exploits a bug in the supposedly secure OS code to install a "trusted" rootkit? Talk about irony.

Pirates vs. Ninjas

Getting back to the original topic, does this mean that Nolan Bushnell is right? Is his "stealth encryption chip" really going to send all the pirates to the Davy Jones's Locker? Not by a long shot! Remember, if the software in question does not have some critical code running on some computer under control of some "authority", you can eventually break its copy protection.

When it comes to policy enforcement, the most important part of the "trusted computing" is the remote attestation. This is the way to ensure you won't tamper with the policy enforcement code. Incidentally, it requires you to be online. Now back up a couple of months and remember what happened when BioWare tried to pull that trick on its players.

If you believe that pirates can't hide behind this forever, think again. There are numerous valid reasons to resist the attempts to introduce an artificial dependency on Internet connection into software and all those reasons boil down to one: the connection is not always available, yet the artificial nature of the dependency means that the software doesn't actually need it to work properly.

Besides, people are not yet convinced that "trusted computing" will actually make things better. Plus, there are all sorts of concerns about privacy and also about practicality of the whole approach. Still, the Trusted Computing Group has been formed, the commercial motivation is there and "trusted computing" will keep rolling, until all doubts and concerns have been dealt with, one way or another.

What, then, is the worst blow "trusted computing" could deal to pirates? Indulge me a bit, as I let my imagination run wild and explore a "what-if" future.

Crack Dealers

Back when I was a little kid, learning what makes the cute, little Spectrum 48 tick, pirates were selling games on audio tapes. Today, pirated games are free. They are cracked for free, by enthusiasts; they are uploaded for free, on sites that survive on advertising or donations; and they are downloaded for free. I can still see some pirates in the streets, selling CDs and DVDs, but I'm sure they won't be buying any Ferraris with that money.

Fast forward to the time when "trusted computing" is in its full swing. To crack protections, pirates need very specialized software, maybe even some hardware, and a lot more effort than before. More than ever, piracy is something that only the select few can do.

However, it is also more lucrative than ever. As the usage policies are enforced more rigorously, the multitudes who used to obtain their entertainment for free now have to go and buy it. The big companies take advantage of that and the prices are even higher than before. You can buy an overpriced game directly from its publisher; or you can take a chance and go buy yourself a pirated copy from a local "software crack dealer". It's illegal, sure, but it's a lot cheaper and you can afford to buy a lot more.

Suddenly, pirates are not your everyday enthusiasts anymore; instead, they're rich criminals. They have bodyguards with guns. They have shady lawyers. They have money laundering enterprises and fake fronts and lots of connections. They know powerful people. In your efforts to eradicate a problem, you managed to make it mutate into something worse.

If this seems improbable and exaggerated, that's okay: I don't believe it's likely to happen. My point is that you should always be on the lookout for unintended consequences. It would be nice if, just for once, we asked ourselves where we're going before we get there.

Tuesday, June 17, 2008

Firefox 3: A Five Act Tragedy

Act I: The Hype

I first heard of the Firefox Download Day at the office, from a co-worker. It wasn't a dark and stormy night, however much we all wished it to be -- the A/C was on the blink yet again.

Naturally, my curiosity was piqued. I've been using Firefox for years now and I've grown used to it and a bunch of add-ons for it. Sure, it had a couple of annoying bugs, but I could live with those. Of course, a new and improved version was likely to get rid of them, which would have been incentive enough to check it out.

So I went to the "Spread Firefox!" site to find out what the fuss was all about. When I found that Mozilla not only planned to give us all a new version of my browser of choice, but also establish a Guiness World Record, I was hooked. I pledged right away and also scheduled a reminder in my Google Calendar, to make sure I wouldn't forget.

Act II: The Outage

It seems childish in retrospect, but I got really excited. Not only was I going to get my hands on the hottest piece of software on the Web, but I was also going to be a part of the effort to set a Guiness World Record. And I wasn't the only one around: a few other co-workers were as excited about this as I was.

When the big day finally came, I made sure I was busy working to keep my mind off the wait. The time passed quickly enough and when the clock was finally done ticking its way to 1 pm, I went back to the Firefox site. That's when I got my first surprise: the site was down.

It's not unusual for a site to be brought down to its knees by overwhelming interest, but I would have expected Mozilla to be prepared. After all, they were the ones who did their best to attract all that attention. Why make a fuss if you can't cope with the results?

After a couple of hours of doing a Wile E. Coyote impression, Mozilla finally got its stuff together and I was finally able to download my copy. Once it was on my disk, I wasted no more time and clicked through the install impatiently. At last, the moment of truth was at hand: I launched Firefox 3!

Act III: The Incompatibility

The first thing my new Firefox did was to check my add-ons. It then proceeded to notify me that less than half of them had updates compatible with version 3 of Firefox. That left more than half of my add-ons disabled.

Being a coder means that an essential part of your job is to imagine worst cases for your code, so
I generally try to be an optimist about everything else, just to balance things out. Therefore I surmised that the Mozilla site was still experiencing difficulties. Did I mention that my optimism is rarely founded?

I spent some time fumbling with the new user interface, only to find out that the developers didn't stop at adding the add-on search into Firefox itself. They also decided to remove the link that took the users directly to the add-on page. I ended up checking manually and the add-on page was working quite well, thank you.

That's where I felt Mozilla managed to set a record, although it wasn't the one they were hoping for: I went from enthusiastic to disappointed in less than 5 minutes since I installed their software. What were they thinking?

Pretend you're in charge of developing a rather successful software product. One of the key advantages it has over the competition is its extensibility. There's a huge variety of add-ons for it and they do lots of different things, ranging from silly to extremely useful. Even though your users might be dissatisfied with some aspects of your software, they would still be reluctant to change to a product that doesn't have the add-ons they use. So what do you do when you decide to develop a shiny new version of your product?

The fashionable answer seems to be "break the add-ons that worked nicely with the older versions", even though the history teaches us the opposite.

Okay, I understand that you can't always maintain compatibility. Sometimes, preserving compatibility is in direct conflict with a higher-priority design goal and you just have to go ahead and break stuff. What you do in those situations is to try to minimize the damage, which is what Mozilla tried to do. But what do you do about the stuff the remains broken?

Here's what you do: you make your installer check the compatibility and report to user before installing the new version and breaking your user's stuff.

Act IV: The Retreat

Once again life conspired to remind me why I'm a late adopter. After a minute or two of staring at the list of things I wouldn't be able to use with Firefox 3, I decided to go back to version 2. A routine check of my download folder revealed that I apparently got rid of the installer at some point in time. No big deal, I can download it from the Firefox site, right?

Wrong! There is no link to Firefox 2 anywhere on the whole page. No matter how hard you try, you won't be offered a chance to turn back. And I tried very hard, trust me. Among other things, I tried typing "firefox 2" into the search box. That particular attempt rewarded me with zero results. I didn't even get a link to a press release or release notes for Firefox 2. Zilch. Firefox 2 has been exterminated.

Despite all its pictures of birds and balloons and a smiling Sun, the message Mozilla is sending its users is loud and clear: "Assume the position!"

In the end, I found the download link on www.firefox2.com, a site that features a prominent notice that it isn't associated with Mozilla in any way. It worked for me, but it probably won't work for you. As I'm writing, I'm also looking at the new content of the site, where every occurence of "Firefox 2" has been replaced with "Firefox 3", except the site URL, of course.

Just in case you really need to go back, the solution is to grab the Firefox 3 download link and change it manually to request the version 2.0.0.14. For example, the correct link to download the English (US) version of Firefox 2.0.0.14 for Windows is:
http://download.mozilla.org/?product=firefox-2.0.0.14&os=win&lang=en-US


Act V: The Repose

Having successfully overcome the unexpected difficulties, I decided it was time to wrap things up and go back to work. I installed Firefox 2 over its younger sibling and launched it. My optimism was proven unfounded yet again: Firefox neatly crashed when it tried to load my saved session.

I tried a few things before I stopped fighting the inevitable and uninstalled Firefox. Hoping fervently that the problem would be solved this time, I reinstalled it and launched it.

By this time I was more than a mite miffed or a tad testy: I was positively pissed off. In the spirit of their decision that I won't need version 2 ever again, Mozilla obviously didn't bother to test the downgrade process.

Fortunately, it turned out that no drastic measures were necessary. I was back to Firefox 2 and all my add-ons worked again.

Epilogue

While writing this post, I stumbled upon this article that explains how to make older add-ons work with new Firefox. In short, it describes a quick hack that makes Firefox skip the step where it checks for add-on compatibility. The article was about version 3.0rc1, so I don't know whether this will work in the final release. I didn't try it and I don't intend to. I prefer to wait until all the add-ons I use are updated. Having a new browser that's shiny and sleek is cool, but being able to use it my way is way cooler.

Friday, June 1, 2007

Dark Side of Design Patterns

Every now and then I overhear someone excitedly talking to someone else about Design Patterns. They're so excited about it, you can hear the capital D and capital P quite clearly. And they use that tone: you know, the one usually reserved for great sports achievements. And, invariably, I can't resist the temptation to get involved. Things usually go downhill from there.

I can understand their chagrin. How would you like it if you just discovered something cool only to have some spoilsport tell you that roughly half of it is crap? Yet I just can't keep quiet; partly because I'm mean and love seeing their reactions, but mostly because I'm sick of the hype.

Patternitis
"The whole problem with the world is that fools and fanatics are always so certain of themselves, but wiser people so full of doubts."
Bertrand Russell

Don't get me wrong, I think design patterns are a great idea. Hardly a new one, but great nevertheless. The problem is that every Great Idea attracts its share of fanatics who think it's the coolest thing since sliced bread. It gets paraded around as a Silver Bullet and a Golden Hammer until its limitations percolate through the layers of hype. Inevitably, the general hype dies down -- if we're talking about a genuine Great Idea, not glossyware stuff like SOA -- and all that's left are pockets of hype that form about enthusiastic newbies or incompetent know-it-alls.

I personally love stamping out those little pockets and that's what I want to do with this blog post. I'll start by asserting the following:
  1. If you think that developing software can be reduced to applying and combining design patterns, you are wrong.
  2. If you think that a design pattern you don't completely understand must nevertheless be good, you are wrong.
  3. If you think that design patterns are a new invention made possible by OOP, you are wrong.
  4. If you think that someone is a good programmer just because they know design patterns, you are wrong.
  5. If you think that you must know design patterns to be a good programmer, you are wrong.
Now that the most common misconceptions are out of our way, we can get to separating the wheat from the chaff.

Gold vs. Other Glittery Stuff
"Any man whose errors take ten years to correct is quite a man."
J. Robert Oppenheimer

What is a design pattern? The original book by Gamma, Helm, Johnson and Vlissides (popularly known as GoF) takes a page and a half to answer this question. I prefer a shorter and simpler answer, even if it lacks all the accuracy of the original. Wikipedia has pretty good definition:
In software engineering (or computer science), a design pattern is a general repeatable solution to a commonly occurring problem in software design. A design pattern is not a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations. Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved. Algorithms are not thought of as design patterns, since they solve computational problems rather than design problems.
Still a bit long for my taste, but it's quite usable. As a matter of fact, the first sentence is the most important; the rest just helps cement the scope.

The GoF book defines 23 design patterns: 5 creational, 7 structural and 11 behavioral. It took me a lot of conscious effort and quite a few years of working experience to admit that not all of those patterns are gold. In fact, the wheat-to-chaff ratio is quite close to 50 percent: there are 12 that I could describe as "okay", "good" or "great" and 11 that I see as "hack", "poor" or even "rubbish".


The Good
"The remarkable thing about Shakespeare is that he really is very good, in spite of all the people who say he is very good."
Robert Graves

If I learned something from my previous managers, it's to always dangle the carrot before using the stick. Seriously, though, the original GoF book has some really good patterns in it.

Take Bridge, for example. The best thing about this pattern is that it teaches you about the separation of concerns. You can adapt it to classless object-oriented languages, you can even apply it in structural programming languages. The main point is to keep the implementation primitives separate from the operations that use them.

Builder is another example of a highly useful pattern. Just look at SAX: they provide the director and you have to write the builder. Highly efficient and very elegant. By the way, I really recommend reading the original GoF book if you want to understand this pattern. Some of the other books attempt to explain it by applying it to convoluted problems such as constructing a MIME message or building a user interface. A word of advice to the authors: KISS.

A few other great patterns include Command, Composite and Template Method. These really make the best out of OOP and it shows. These are all nice examples of taking advantage of the benefits of OOP.

Then there is a whole range of patterns that have been around for a long time, long before OOP and long before GoF invented design patterns as we know them: Adapter, Façade, Flyweight, Chain of Responsibility and Observer.

Finally, I reserve a special place for Mediator, not because it's better than the rest, but because it's a perfect example of a good pattern that can be horribly abused. One word: Delphi. The "RAD paradigm" promoted by Delphi is to add objects to their containers and then write all the logic in a few huge Mediator classes. Delphi is based on an object-oriented language, has an excellent core framework and class library and yet it teaches its users to forget OOP. Bottom line: no matter how cool the pattern, don't overdo it.

The Ugly
"Beauty is the first test; there is no permanent place in the world for ugly mathematics."
G.H. Hardy

We have all, at some point in our lives, had to choose the lesser of two evils. That's most likely what you will be doing when you decide to apply one of the following: Proxy, Memento, Visitor, Decorator or State. All of these are a cumbersome solution for something that should have been supported or aided by the language itself.

Proxy, for example, is a necessary and useful pattern. The problem with it is that a lot of the languages require you to implement it by writing tons of boilerplate code. With some good metaprogramming facilities, Proxy becomes trivial.

Likewise, the motivation behind Memento is valid, but it should be solved by the language or its core library. Serializing and deserializing objects should be trivial, which means you shouldn't have to write gobs of code to do it. Look at the way you do it in Java:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(oos);
try {
  oos.writeObject(originator);
  oos.close();
} catch (IOException e) {
  // won't happen but we _still_ 
  // have to write code to ignore it
}
byte[] memento = baos.toByteArray();
Now compare it to Ruby:
memento = Marshal.dump(originator)
I rest my case.

Visitor is another example of hacking your way around language limitations. Most of the object-oriented code anyone writes needs only single dispatch. You can argue that supporting double or multiple dispatch in the language is wasteful, although there are ways around that. But when your language leaves you with no other choice and you need double dispatch, you will have to use Visitor. If you happen to need multiple dispatch, good luck writing and maintaining that code.

Finally, we come to Decorator and State. Both of these patterns are useful and both deal with the same issue: sometimes we want to adapt the behavior of individual objects depending on their state or some other trait we want to manipulate during run time. I believe that it should be possible without writing all the boilerplate code that you need for Decorator and State. I've learned and experimented with Self and Io languages and they both allow you to solve these needs very elegantly. Again, look at a sample implementation of State pattern in Java:
public class Bot {
  
  private int health;
  private BotState state;
  
  protected void changeState(BotState newState) {
    state = newState;
  }
  
  protected static final BotState HEALTHY = new HealthyState();
  protected static final BotState INJURED = new InjuredState();
  
  protected abstract static class BotState {
    public void takeDamage(Bot bot, int damage) {
      bot.health -= damage;
    }
    
    public void heal(Bot bot, int delta) {
      bot.health += delta;
    }
    
    public void enemySpotted(Bot bot, Object enemy) {}
  }
  
  protected static class HealthyState extends BotState {
    public void takeDamage(Bot bot, int damage) {
      super.takeDamage(bot, damage);
      if (bot.health <= 40) {
        bot.changeState(INJURED);
      }
    }
    
    public void enemySpotted(Bot bot, Object enemy) {
      bot.chase(enemy);
    }
  }
  
  protected static class InjuredState extends BotState {
    public void heal(Bot bot, int delta) {
      super.heal(bot, delta);
      if (bot.health >= 50) {
        bot.changeState(HEALTHY);
      }
    }
    
    public void enemySpotted(Bot bot, Object enemy) {
      bot.avoid(enemy);
    }
  }
  
  public void chase(Object enemy) {
    System.out.print("Chasing " + enemy);
  }
  
  public void avoid(Object enemy) {
    System.out.print("Avoiding " + enemy);
  }
  
  public void takeDamage(int damage) {
    state.takeDamage(this, damage);
  }
  
  public void heal(int delta) {
    state.heal(this, delta);
  }
  
  public void enemySpotted(Object enemy) {
    state.enemySpotted(this, enemy);
  }
}
Now compare it with the same thing in Io:
Bot := Object clone do (
  init := method(self health := 100)
  chase := method(enemy, ("Chasing " .. enemy) println)
  avoid := method(enemy, ("Avoiding " .. enemy) println)
  takeDamage := method(damage, health = health - damage)
  heal := method(delta, health = health + delta)
  enemySpotted := method(enemy, nil)
  changeState := method(newState, self setProto(newState))
)

HealthyBot := Bot cloneWithoutInit do (
  takeDamage := method(damage,
    resend
    if (health <= 40, changeState(InjuredBot))
  )
  enemySpotted := method(enemy, chase(enemy))
)

InjuredBot := Bot cloneWithoutInit do (
  heal := method(delta,
    resend
    if (health >= 50, changeState(HealthyBot))
  )
  enemySpotted := method(enemy, avoid(enemy))
)
I would even settle for hard, specific syntax supported by the language itself, like in UnrealScript.

Bottom line? Our languages really need to evolve. Combine prototype-based OOP that you can find in Self and Io with excellent metaprogramming offered by Lisp and you will have a language that kicks ass and doesn't need ugly patterns.

The Bad
"There is no stigma attached to recognizing a bad decision in time to install a better one."
Laurence J. Peter

Thus we come to the place where the bad things dwell. There's a variety of patterns that fall into this group and they do so for diverse reasons. Let's start with a couple that gave me nightmares for a long while.

Factory Method sounds like a good idea. Upon close examination, Abstract Factory is revealed to be Factory Method applied to Factory Method, which makes it sound even better. Only those who have been fooled by this pattern can appreciate the despair and the subsequent numbness brought on by writing tons and tons of the boilerplate code required by it. The worst thing is that it seems to be the only solution. Until you find out about Dependency Injection. Try it and you'll see the difference.

Another pattern that really fouls things up is Singleton. For a long time I couldn't find one good reason why I should have a class with only one instance, instead of making all that code static. Then someone asked what I would do if I had to pass an interface to it. Okay, that justifies it, I can even think of examples other than boilerplate Abstract Factory code. Another valid point is that you might want to switch from one instance to a pool and you don't want to rewrite the bulk of your code. Then there is the case mentioned in the GoF book that talks about subclassing the singleton and creating a registry of singletons. If you think that things are getting out of hand, you're right. The solution is the same as above: use Dependency Injection.

Then there's Iterator pattern. That one should be easy: use the foreach statement. And if your language doesn't have it, ask for your time back. It's simply unacceptable to have to write reams of ugly code for something we use so often. It deserves its own syntax.

What about Strategy pattern? According to the GoF and to all subsequent authors, you should use it to encapsulate a family of algorithms and make them interchangeable. That's about one thing for which I would not use it. If you want to do that use functions. Again, if you don't have them, ask for your time back. On the other hand, you could use Strategy pattern when your implementation strategy has to maintain state over time. But then it's not really Strategy, is it? Slippery things, these patterns.

Finally, there's Interpreter. I don't have any beef with its motivation: just look at how things have been evolving and you'll see several examples of minilanguages, scripting languages and XML-based stuff, all getting interpreted in the normal course of execution of your code. From printf format string of olden to XML configuration files of today, the idea has been in use for a loooong time. Thing is, this isn't a design pattern. It doesn't fit into the scope of a design pattern. And you won't make it fit by sketching out your favorite implementation of an interpreter for your favorite minilanguage. It just does not belong.


Patternitis Revisited
"When people are free to do as they please, they usually imitate each other."
Eric Hoffer

I have to give it to GoF though, they had some great ideas and the only time their scope slipped was with the Interpreter pattern. Sadly, the same couldn't be said about the later authors. I'll say it out loud: interface is not a design pattern. There are many things you could say about an interface. From a conceptual point of view, it's the definition of the contract your code will have to satisfy. From a language-specific point of view, it's a syntactic and semantic construct. But it is definitely not a design pattern. Neither is a symbolic constant name or an accessor method name.

It might seem that I'm beating a dead horse here, but I want to make it absolutely clear that not everything in software development is a design pattern. It's too easy to lose focus in enthusiasm, so do yourself a favor: learn the design patterns, keep using the ones you like (where applicable) and move on. There's a lot more to do.