I'm not going to claim that programming is easy, but I am going to say that it is not hard for the reasons people usually assume it is. Programming is not a deeply theoretical subject like Chemistry or Physics; you don't need an advanced degree to do well at it. (There are important principles of Computer Science, but it's possible to get a degree after studying them and to have only vague ideas of how to apply them to practical programming. Contrariwise, we'll experience many important Computer Science lessons by the seat of our pants, without bewildering ourselves with abstract notation; there are plenty of successful programmers who don't have Computer Science degrees.)
Comparing programming to some physical tasks, programming does not require some innate talent or skill, like gymnastics or painting or singing. You don't have to be strong or coordinated or graceful or have perfect pitch. Programming does, however, require care and craftsmanship, like carpentry or metalworking. If you've ever taken a shop class, you may remember that some students seemed to be able to turn out beautiful projects effortlessly, while other students were all thumbs and made the exact mistakes that the teacher told them not to make. What distinguished the successful students was not that they were better or smarter, but just that they paid more attention to what was going on and were more careful and deliberate about what they were doing. (Perhaps care and attention are innate skills too, like gymnastic ability; I don't know.)
Some things you do need are (1) attention to detail, (2) stupidity, (3) good memory, and (4) an ability to think abstractly, and on several levels. Let's look at these qualities in a bit more detail:
Comparing programming to some physical tasks, programming does not require some innate talent or skill, like gymnastics or painting or singing. You don't have to be strong or coordinated or graceful or have perfect pitch. Programming does, however, require care and craftsmanship, like carpentry or metalworking. If you've ever taken a shop class, you may remember that some students seemed to be able to turn out beautiful projects effortlessly, while other students were all thumbs and made the exact mistakes that the teacher told them not to make. What distinguished the successful students was not that they were better or smarter, but just that they paid more attention to what was going on and were more careful and deliberate about what they were doing. (Perhaps care and attention are innate skills too, like gymnastic ability; I don't know.)
Some things you do need are (1) attention to detail, (2) stupidity, (3) good memory, and (4) an ability to think abstractly, and on several levels. Let's look at these qualities in a bit more detail:
- attention to detail
In programming, the details matter. Computers are incredibly stupid (more on this in a minute). You can't be vague; you can't describe your program 3/4 of the way and then say ``Ya know what I mean?'' and have the compiler figure out the rest. You have to dot your i's and cross your t's. If the language says you have to declare variables before using them, you have to. If the language says you have to use parentheses here and square brackets there and squiggly braces some third place, you have to. - stupidity
Computers are incredibly stupid. They do exactly what you tell them to do: no more, no less. If you gave a computer a bottle of shampoo and told it to read the directions and wash its hair, you'd better be sure it was a big bottle of shampoo, because the computer is going to wet hair, lather, rinse, repeat, wet hair, lather, rinse, repeat, wet hair, lather, rinse, repeat, wet hair, lather, rinse, repeat, ...
I saw an ad by a microprocessor manufacturer suggesting the ``smart'' kinds of appliances we'd have in the future and comparing them to ``dumb'' appliances like toasters. I believe they had it backwards. A toaster (an old-fashioned one, anyway) has two controls, and one of them is optional: if you don't set the darkness control, it'll do the best it can. You don't have to tell it how many slices of bread you're toasting, or what kind. (``Modern'' toasters have begun to reverse this trend...) Compare this user interface to most microwave ovens: they won't even let you enter the cooking time until you've entered the power level.
When you're programming, it helps to be able to ``think'' as stupidly as the computer does, so that you're in the right frame of mind for specifying everything in minute detail, and not assuming that the right thing will happen unless you tell it to. (This is not to say that you have to specify everything; the whole point of a high-level programming language like C is to take some of the busiwork burden off the programmer. A C compiler is willing to intuit a few things--for example, if you assign an integer variable to a floating-point variable, it will supply a conversion automatically. But you have to know the rules for what the compiler will assume and what things you must specify explicitly.) - good memory
There are a lot of things to remember while programming: the syntax of the language, the set of prewritten functions that are available for you to call and what parameters they take, what variables and functions you've defined in your program and how you're using them, techniques you've used or seen in the past which you can apply to new problems, bugs you've had in the past which you can either try to avoid or at least recognize by their symptoms. The more of these details you can keep in your head at one time (as opposed to looking them up all the time), the more successful you'll be at programming. - ability to abstract, think on several levels
This is probably the most important skill in programming. Computers are some of the most complex systems we've ever built, and if while programming you had to keep in mind every aspect of the functioning of the computer at all levels, it would be a Herculean task to write even a simple program.
One of the most powerful techniques for managing the complexity of a software system (or any complex system) is to compartmentalize it into little ``black box'' processes which perform useful tasks but which hide some details so you don't have to think about them all the time.
We compartmentalize tasks all the time, without even thinking about it. If I tell you to go to the store and pick up some milk, I don't tell you to walk to the door, open the door, go outside, open the car door, get in the car, drive to the store, get out of the car, walk into the store, etc. I especially don't tell you, and you don't even think about, lifting each leg as you walk, grasping door handles as you open them, etc. You never (unless perhaps if you're gravely ill) have to worry about breathing and pumping your blood to enable you to perform all of these tasks and subtasks.
We can carry this little example in the other direction, as well. If I ask you to make some ice cream, you might realize that we're out of milk and go and get some without my asking you to. If I ask you to help put on a party for our friends, you might decide to make ice cream as part of that larger task. And so on.
Compartmentalization, or abstraction, is a vital skill in programming, or in managing any complex system. Despite what I said in point 3 above, we can only keep a small number of things in our head at one time. A large program might have 100,000 or 1,000,000 or 10,000,000 lines of code. If it were necessary to understand all of the lines together and at once to understand the program, the program would be impossible to write or understand. Only if it is possible to think about small pieces in isolation will it ever be possible to work with a large program.
Compartmentalization, powerful though it is, is not automatic, and not necessarily an instant cure for all of our organizational problems. We carry a lot of assumptions around about how various things work, and things work well only as long as these assumptions hold. To return to the previous example, if I ask you to go the store and get some milk, I'm assuming that you know which kind to get, where the store is, how to get there, how to drive if you need to, etc. If some of these assumptions weren't valid, or if there were several options for any of them, we might have to modify the way I gave you instructions. I might have to tell you to drive to the store, or to go to Safeway, or to get some two percent milk.
Therefore, we can't simply compartmentalize all of our processes and subprocesses and forget about complexity problems forever. We have to remember at least some of the assumptions surrounding the compartmentalization scheme. We have to remember what we can and can't expect from the processes (people, computer programs, etc.) which we call on to do tasks for us. We have to make sure that we keep our end of the bargain and don't fall down on any of the commitments and promises we've made on the tasks we've been asked to do and which others are assuming we'll keep.
Thinking about the mechanics of a design hierarchy, while also using that hierarchy to avoid having to think about every detail of it at every level all of the time, is one of the things I mean by ``thinking on several levels.'' It's tricky to do (obviously, it's tricky even to describe), but it's the only way to cut through large, complex problems.