I’ll be talking on Thursday, July 24, at 3:25pm, in either room E143 or E144 at the Oregon Convention Center.
I’ll tell you basically everything I know and have learned about software design, and then how it applies to Open Source software, all in about 45 minutes. Should be fun and interesting!
Bugs most commonly come from somebody’s failure to reduce complexity. Less commonly, they come from the programmer’s misunderstanding of something that was actually simple.
Other than typos, I’m pretty sure that those two things are the source of all bugs, though I haven’t yet done extensive research to prove it.
When something is complex, it’s far too easy to misuse it. If there’s a black box with millions of unlabeled buttons on it, and 16 of them blow up the world, somebody’s going to blow up the world. Similarly, in programming, if you can’t easily understand the documentation of a language, or the actual language itself, you’re going to mis-use it somehow.
There’s no right way to use a box with millions of unlabeled buttons, really. You could never figure it out, and even if you wanted to read the 1000-page manual, you probably couldn’t remember the whole thing well enough to use the box correctly. Similarly, if you make anything complex enough, people are more likely to use it wrongly than to use it correctly. If you have 50, 100, or 1000 of these complex parts all put together, they’ll never work right, no matter how brilliant an engineer puts them together.
So do you start to see here where bugs come from? Every time you added some complexity, somebody (and “somebody” could even be you, yourself) was more likely to mis-use your complex code. Every time it wasn’t crystal clear exactly what should be done and how your code should be used, somebody could have made a mistake. Then you put your code together with some other code, and there was another chance for mistakes or mis-use. Then we put more pieces together, etc.
Often, this sort of situation happens: the hardware designer made the hardware really complicated. So it had to have a complicated assembly language. This made the programming language and the compiler really complicated. By the time you got on the scene, you had no hope of writing bug-free code without ingenious testing and design. And if your design was less than perfect, well…suddenly you have lots of bugs.
This is also a matter of understanding the viewpoint of other programmers. After all, something might be simple to you, but it might be complex to somebody who isn’t you.
If you want to understand the viewpoint of somebody who doesn’t know anything about your code, find the documentation of a library that you’ve never used, and read it.
Also, find some code you’ve never read, and read it. Try to understand not just the individual lines, but what the whole program is doing and how you would modify it if you had to. That’s the same experience people are having reading your code. You might notice that the complexity doesn’t have to get very high before it becomes frustrating to read other people’s code.
Now, once in a while, something is really simple, and the programmer just misunderstood it. That’s another thing to watch for. If you catch a programmer explaining something to you in a way that makes no sense, perhaps that programmer misunderstood something somewhere along the line. Of course, if the thing he was studying was extremely complex, he had basically no hope of fully understanding it without a PhD in that thing.
So these two things are very closely related. When you write code, it’s partially your responsibility that the programmer who reads your code in the future understands it, and understands it easily. Now, he could have some critical misunderstanding—maybe he never understood what “if” meant. That’s not your responsibility. Your responsibility is writing clear code, with the expectation that the future programmer reading your code understands the basics of programming and the language you’re using.
So, there are a few interesting rules that you can get out of this one:
The simpler your code is, the fewer bugs you will have.
Always work to simplify everything about your program.
Okay, most programmers know the story—way back when, somebody found an actual insect inside a computer that was causing a problem. (Actually, apparently engineers have been calling problems “bugs” since earlier than that, but that story is fun.)
But really, when we say “bug” what exactly do we mean?
Here’s the precise definition of what constitutes a bug. Either:
In The Never-Shipping Product, I mentioned seven ways to add complexity, and one of them was “Lock-In To Bad Technologies.” But what’s a “bad” technology? Is it all just based on opinion? Should we throw our hands up in the air and give in to the whims of our junior developer who thinks writing the application in BASIC is a great idea?
Well, okay, maybe it’s not all opinion. There must be some way to tell a good technology from a bad one (besides looking back after five years of development and saying, “Wow, we really shouldn’t have decided to base our product off of Microsoft Bob.”)
When I’m evaluating a technology for inclusion in one of my projects, I look particularly at the technology’s survival potential, interoperability, and attention to quality. Keep Reading
One of the most important things to know about any kind of engineering is:
There are some things about the future that you do not know.
Obviously it’d be ideal if we were all-knowing and could perfectly predict every consequence of every decision we’ll ever make. But that’s impossible. In fact, it’s so far from possible that if you predict more than half of the consequences of your engineering decisions, you’re godlike or just really lucky.
Let’s take an example outside of the realm of programming: Keep Reading
If you’re going to be in Portland, Oregon or attending OSCON and you want to hear me talk, I’ve proposed a session for FOSSCoach called Code Simplicity: Software Design In Open Source Projects.
Registration for FOSSCoach is free, but is limited to 100 people, so sign up if you want to come.
When you work as a professional programmer, you almost always know somebody (or are somebody) who’s going through one of the most common development horror stories in the book:
“We started working on this project five years ago, and the technology we were using/making was modern then, but it’s obsolete now. Things keep getting more and more complex with this obsolete technology, so it keeps getting less and less likely that we’ll ever finish the project. But if we re-write, we could be here for another five years!”
Another popular one is: “We can’t develop fast enough to keep up with modern user needs.” Or, “While we were developing, Google wrote a product better than ours much faster than us.”
When I hear things like that, the first thing I ask myself is “How did that happen? Why did it take so long for them to finish their product that they ran into that problem?”
The answer lies in complexity. Keep Reading
So, I’m a huge Kyle XY fan, and I was entertaining myself this morning by watching the various “behind the scenes” clips that they have on the website. Of course, before each clip was an ad–the same ad every time–for The Sims. No matter how silly the ad may be, being forced to watch it over and over did eventually get me thinking–although not about buying The Sims:
Why has The Sims sold 100 million copies, while Second Life, an ostensibly much more flexible and powerful universe, only has about 2 million active users? They look pretty similar, and you might guess at first glance that they’d have somewhat similar audiences. But, although 2 million users is nothing to scoff at, 100 million absolutely trounces it. So why the big difference?
Well, of course, The Sims has EA Games behind them, who have a massive distribution channel and a lot of marketing power, but the Internet buzz and general promotion of Second Life is pretty good too, so although EA has the edge, that doesn’t explain a 50-to-1 difference in sales. There must be something actually different about the products themselves.
Well, at first glance, The Sims is very user-friendly and Second Life is (from what I’ve heard) hard to use. The Sims does a limited scope of things very well, and Second Life does an unlimited number of things through a difficult interface, with mediocre results.
But fundamentally, why is it that products like The Sims succeed so much more than things like Second Life? And why does The Sims have a better interface, why do people want to play The Sims more than they want to play Second Life? Keep Reading
Often, if something is getting very complex, that means that there is an error somewhere far below the level that things are getting complex on.
For example, it’s very difficult to make a car move if it has square wheels. You’re going to be spending lots and lots of time figuring out how to make the car work, when really it should just have round wheels.
Any time there’s an “unsolvable complexity” in your program, it’s because there’s something fundamentally wrong with it. If the problem is “unsolvable” at one level, maybe you should back up and look at what’s underlying the problem. Maybe you put square wheels on the car, and now you’re trying to figure out how to make it go fast.
Programmers actually do this quite often. For example, “I have this terribly messy code, now it’s really complex to add a new feature!” Well, your fundamental problem there is the that code is messy. Clean it up, make the already-existing code simple, and suddenly adding the new feature will be simple.
What Problem Are You Trying To Solve?
If somebody comes up to you and says something like, “How do I make this pony fly to the moon?”, the question you need to ask is, “What problem are you trying to solve?” You’ll find out that they really need to collect gray rocks. Why they thought they had to fly to the moon, and use a pony to do it, only they know. People do get confused like this.
So when things get complex, back up and you look at the problem that you’re trying to solve. Take a really big step back. You are allowed to question everything. Maybe you thought that adding 2 and 2 was the only way to get 4, and you didn’t think about adding 1 and 3 instead, or just skipping the addition entirely and just putting “4” there. The “problem” is “How do I get 4?” Any method of solving that problem is acceptable, so figure out what the best method would be, for the situation that you’re in.
Discard your assumptions. Really look at the problem that you’re trying to solve, and think about the simplest way to solve that problem. Not “How do I solve this problem using my current code?” Not “How did Professor Bob solve this problem in his program?” No, just how, in general, in a perfect world, should that problem be solved? From there, you might see how your code needs to be re-worked. Then you can re-work your code. Then you can solve the problem.