Is it easy to intentionally write bad code? When you look at a lot of the software out in the industry is seems that the answer has to be 'yes.' We're surrounded by bad code, but that isn't the same thing as writing it intentionally.
In trainings and workshops over the past couple of months, I've asked people to sit down and intentionally write the worst code that they can imagine. A lot of it ends up being obtuse, with redundant and obscure calculations, but somehow it always seems to miss the mark - it's not like the "bad code" that we typically encounter, and it's worth wondering why.
Anyway, I offer this as a challenge to you. Try to intentionally write the worst code that you can and see how you feel about it. Notice how that code is different from bad code that you tend to see.
There's a psychologist named Fritz Perls who coined something he called the 'Law of Paradoxical Intention.' If you are blocked on something, try to do the exact opposite of what you are trying to do. It turns out that there's a lot you can learn about good design by intentionally trying to write bad code. Just, please, don't ever check it in.
If you do this kata, please post your results and reflections online. I'll be blogging reflections about what I've learned so far soon.
Edit: I may have gotten this idea transitively from Jason Gorman. I heard that he ran a 'bad code kata' at a QCON conference last year.
Eno famously used his "Oblique Strategies" http://en.wikipedia.org/wiki/Oblique_Strategies to engender greater creativity or circumvent creative blocks.
--johnt
Posted by: Jdtangney | July 14, 2010 at 09:08 AM
That would be pretty easy for me, I could just leave out all refactoring efforts and it would quickly become a complete mess!
Posted by: SStone | July 14, 2010 at 05:26 PM
... Clemens Szyperski mentioned the notion of "design in the negative space", i.e. think about what features you do *not* want.
The code level may be a bit different though - bad code is probably not the code that fails to implement the right feature(s), but rather code that successfully blurs the association between features and code lines. In other words, it does not communicate, or (un)intentionally miscommunicates.
I encountered a nice C++ example today: A piece of legacy code defines an iterator type that has two methods:
T *current();
returns the current element, and
bool next();
moves to the next element *and* returns whether there is actually a next element. This allows for loops like this:
while(i->next()) {
...
}
The assumption is that you have to call next() before accessing the first element (so the internal reference pointer does not point to index 0 but to index -1 initially!).
So next() does go to the next element as you would assume from the name, but you have to remember to go to the "next" element initially to arrive at the first element.
Posted by: Bob Lauer | July 15, 2010 at 05:23 AM
@Bob that design is actually correct... starting at -1 is the only way for the iterator to tell us if it is empty, without having to add a third member to the contract.
Posted by: Rexm | July 15, 2010 at 07:37 AM
http://gist.github.com/371740
Binary search on an ordered queue. The code itself isn't that bad, but the algorithm is.
Posted by: Florian Junker | July 16, 2010 at 01:46 PM
@Rexm: The above design is indeed correct, but it does not communicate well. The "next()" method is used to move forward *and* to signal an end of the iterable container.
A clean design would be the C++ STL iterators or the Java iterators (with methods hasNext() and next() separating the two concerns).
Another kind of bad code is overly clever code. At university the following one was a common joke, but some people actually write code like that in production:
#define swap(x, y) x^=y;y^=x;x^=y;
Posted by: Bob Lauer | July 19, 2010 at 02:41 AM
An interesting idea. I would guess that it's hard to write intentionally bad code because bad code is usually as a result of some misunderstanding. There is a paradox here because to deliberately create something that is the result of a misunderstanding you must first understand it!
Posted by: Steve Knight | July 26, 2010 at 04:45 AM
The bad code that I see in the wild is mostly the result of well-intentioned (or, at least, not malicious) people working under great pressure in difficult circumstances. That seems as if it would be a hard thing to simulate.
And the badness accumulates over time, as the naked options created by bad code get called (see http://www.m3p.co.uk/blog/2010/07/23/bad-code-isnt-technical-debt-its-an-unhedged-call-option/)
To simulate this we would have to write some code under circumstances where we are under pressure, distracted, maybe frightened, rather than by deliberately making mistakes. Write some code while, quite literally, not giving yourself time to think, and see what happens.
Posted by: Keith Braithwaite | July 26, 2010 at 10:56 PM
I immediately thought of two possibilities of bad code and it's hard to know which one is actually worse:
1) Code that is hard to understand
2) Code that looks understandable but has a subtle yet devastating fault in it, like an unintuitive API or a missing character that makes the code do something completely different.
Posted by: Heikki Naski | August 01, 2010 at 01:06 PM
Late reply, but this is interesting as I noticed this as well when trying to create code that is violating a certain principle (in order to demonstrate the principle) and that can be very hard to make something real and wrong when you are focused on it so much. Then then you are not, i.e. in regular code, you see the examples much easier.
So in order to achieve this task we should just write 'normal' code, then as a rule this will be bad code, except for some of use of course :)
And finally, just let someone else write the code as it is always sooo easy to find flaws in other peoples code ;)
Posted by: Mark Nijhof | August 11, 2010 at 10:46 AM
Helping students to intentionally write bad code might be easier if you set them a problem that is closer to the things that go awry in professional work.
A kata that includes a few overlapping/interlocking business rules that allow you to make your code fester could suffice. Think about the simple things like:
* Ignore obvious encapsulation or inheritance.
* Repeat code (redundant code).
* Deeply nest your code.
* Construct complex boolean conditionals.
* Use conditions instead of procedures where possible.
* No/Not enough new classes.
* Let's have this all in the 'main' method shall we?
Would something along the lines of:
http://codekata.pragprog.com/2007/01/kata_nine_back_.html or
http://codekata.pragprog.com/2007/01/kata_sixteen_bu.html work perhaps?
Posted by: Eric | August 17, 2010 at 01:28 AM