Wednesday, October 18, 2006

http://www.gotapi.com/

http://www.gotapi.com/ is a very nice AJAXED API reference for html, css, js, php and many others. Very very handy

Tuesday, October 17, 2006

Common sense programming

In this post I will talk a little about some common sense in organizing your code. There is nothing brilliant in this post, yet common sense seems to be the first thing we lose whenever coding something a little complicated. First, any code structure falls in one of the following:

  • sequential structures
  • logical structures (if, case)
  • repetitive structures (for, while, do)
  • others (goto, recursion, return )

I'll talk a little about each

1. Sequential structures

Most of your code is executed once in a while. Yet even this can be confusing.

First, make sure that whenever the order of the statements counts, this is obvious. Let's take an example:

calculateMarketingIncomes();
calculateSalesIncomes();
getCustomerData();

Now, suppose the sales incomes can be calculated only after the marketing ones (probably, the first procedure initializes some variables that are needed for the second). More, the customer data cannot be accessed until the sales incomes are calculated. It is obvious that the code sequence does not give a clue about this. There are a few options you can consider in cases like this:

  • give better names: you can rename calculateMarketingIncomes to InitializeDataAndCalculateMarketingIncomes. Clumsy name? Maybe you should break your code into 2 procedures, InitiailzeMarketingData and CalcultateMarketingIncomes
  • have some parameters that make the sequence logical. Have a value returned and use it in the next procedure. That should make clear that the order is important
  • if and only if you cannot change the structure of the code, have comments that explain the importance of ordering the code that way

In other cases, the order does not matter for a certain step. Yet,the statements should be grouped logically. Consider the following bad example:

object a1=new object();
object a2=new object();
object a3=new object();
a1.doStuff();
a2.doStuff();
a3.doStuff();
a1.clean();
a2.clean();
a3.clean();

The bad thing is that the code for each object is interlaced with the others. When debugging a1, you have to look at 3 times the number of lines that you normally should. A better version is:

object a1=new object();
a1.doStuff();
a1.clean(;
object a2=new object();
a2.doStuff();
a2.clean();
object a3=new object();
a3.doStuff();
a3.clean();

This way, you will have to deal with less lines when interested in one object and it will be also easy to separate the code into routines if needed.

Logical structures

Logical structures are needed whenever one needs to execute a certain piece of code depending on certain factors. First, consider using routines for checking. Instead of

if ((ch>="a" && ch<="z") ||(ch>="A" && ch<="Z"))

consider

if(isChar(ch))

where isChar is a routine defined elsewhere.

Another thing to watch for is testing for the logical condition. Instead of testing for not not false, test for true.

Whenever you have nested if conditions, make sure the normal path is easy to follow. Try to put the usual case in the if clause and the unusual in the else. This will improve both readability and performance.
Repetitive structures

First of all, make sure you use the logically right structure. It is known that in languages such as C++ you can achieve with a for everything you can achieve with a while or do...while. Yet there is no reason not to use while or do...while if that improves readability.

Try not to nest more than 3 repeatitive structures. Besides the big performance penalty, it is known that our brains have troubles in understanding them.

Beware of cross talk. It is common to use variables as i,j,k to walk through repetitive statements. Yet it is common to manipulate j instead of i. Try to give the variables more meaningful names whenever you have nested control structures.

Others

Beware of gotos. gotos are not evil, but they really decrease your code readability. Use them only if no other options are available. And think it through before deciding that you have no more options. Cases when you really need them are very, very rare.

Beware of recursion. It is an elegant solution, yet very resource intensive. First, make sure you have an exit condition. Then, make sure that there will be no stack overflow. And, most important, consider not using it. The usual examples, like computing a factorial using recursion, are plain wrong. You can do these operation with a loop and you will save lots of resources (by eliminating the overhead of a function call) in so doing.

If your language supports foreach or any other kind of looping through array structures, use it! It increases readability.

Not that everything in this post may be obvious. Yet remember these advices when you are deep into your source code.

About Routines

*By routine, I understand a function, method or a procedure.

Programming always comes down to writing routines. So doing it better might help :). Here are some steps that may improve their quality

  1. Start from a good design. Know in advance what routines your program needs and be sure that every routine does one thing. A routine that initialises the form, calculates default values and displays a welcome message is really bad.
  2. Give the routine a good name. A good name describes specifically what a routine does, For a function, the name should represent the returned value(e.g. getFont, sin). The name of a procedure should be a verb followed by an object that clearly defines what the procedure does. PrintReport, LogError, SaveCustomerData are good routine names. HandleStuff, SaveData, InitializeData are bad names. If you cannot find a good name for your routine, there is a problem with your design. Either the routine does more than one logical action, either less than one, either you understanding of the problem is wrong. Bad names=Bad Code.
  3. Start by writing pseudo-code. Pseudo-code is written in plain English. Pseudo-code should be clearly understood by someone unfamiliar with the specific programming language you use. After you are finished, revise it and write it in more detail. Repeat until it is harder to revise the pseudo-code than writing the code itself.
  4. Comment your pseudo-code, and after each line write the correspondent line of program code. Programming should be an almost mechanical action by now. The technique of writing pseudo-code helps by unleashing you from the programming language details and by providing the code comments even before writing the code
  5. Compile only at the end. Challenge yourself to write code that compiles. Compiling often distracts your attention and tempts you into using hacks just to see your code compiled.
  6. No hacking! Using hacks is bad practice. Think of the right solution. A hack has the chance to remain there even if you say you will change it ASAP. Hacks pile up and soon the project gets out of control. It is better to go slower than hack your way through

Design tehniques

Design, unlike coding itself, is a heuristic process. There is no "perfect" way. You have to know what, how, when to use and yet you will still be wrong sometimes. It is a wicked problem. You cannot find a solution till you've solved it. You return to revising your design several times in a project(hopefully, fewer and fewer times as your experience increases). There is no advice in design to be taken for granted. Everything should be weighted against your project and you should always remember that your primary goal is to Manage Complexity(for more about this, read Code Complete 2nd edition, it is the source for many of my posts). These being said, here are some guidelines for problem solving.

1) Seeing the problem form multiple points of view

Very often I see programmers writing lots of heavy code just because they overlooked a much simpler solution. Never stop at your first idea. Probably the next one will be better. Learn to think from multiple point of view. Be creative. Design is heuristic, so it benefits from creativity.

2) Divide and Conquer

Learn to break the problem in smaller subsets, then think about each one separately. This way you reduce complexity

3) Top down

Start with a very abstract model of the system, then work your way down. It is easier to understand the puzzle when you have the big picture. Yet there are times when the big picture is not obvious...

4) Bottom up

Start with concrete cases, then try to work your way to the big picture.

5) Prototyping

Very often, you need to test your theory. For example, if the client asks for a database than supports 1000 transactions per second, be sure you create a quick and dirty model that tests just that on the database engine of your choice. Lots of design errors come from untested theories. Think of your prototype code as throwaway code. This helps you ensuring that you do not go into unnecessary detail. Prototype only what it is needed.

6) Team work

Often, someone else can see something you are overlooking. Be sure to ask for opinions as often as you can, it works wonders.

It is clear that the techniques I described should be used in a mixed way.

Kinds of names to avoid

  1. Misleading name or abbreviations. Some time ago, I thought it was cool to name a variable that represents a Fuel Action Low Sequence Enzyme as FALSE. It is so easy to remember! Yet so confusing for the others or even for you after a while. The bottom line is: Never use this kind of abbreviations
  2. Names with similar meanings. To have fileIndex and fileNumber in the same place is a bad idea. There is no logical way to tell which is which.
  3. Different meanings, similar names. clientReps and clientRecs are hard to distinguish. They will probably create lots of confusion. Try to abbreviate differently or use synonyms.
  4. Similar-sounding names. Goal_Donor and Gold_Owner are tricky. It is hard to distinguish them in speech, so consider changing them.
  5. Numerals in names. There are few times when file1 and file2 are the most appropriate names. Almost in any case, you can do better than that.
  6. Names that are easily misspelled.I find myself typing cleint instead of client very often. This may not be a problem in a strong typed language used with a IDE that has auto-complete features, but it is a big problem in a weakly typed one. Because you never know in what program language you will program in 10 years, it is better to build healthy habits. Most English handbooks contain lists of commonly misspelled words.
  7. Do not differentiate only by letter capitalization. Even if most programming languages support this, it is probably a bad idea to do so. The "psychological" difference is too small.
  8. Avoid multiple languages. Decide on the natural language your program is written in. It is common to find names in many languages in multinational programs. And this is a big problem.
  9. Avoid using keyword names. Even if it sounds obvious, sometimes it happens. I had problems with SQL statements that used field names which where reserved keywords multiple times.
  10. Unrelated names. It is fun to see variable names like little_johnny, pookie, lex_luther. But this guarantees that nobody will understand what you mean. So express your humor in other ways.

Managing Complexity

Software is complex. It is normal for you not to be able to know all the ins and outs of your program. Yet, you have to if you plan to be a successful programmer. The primary imperative in any kind of design is Managing Complexity. So, whenever the program it's close to get out of control, you probably need more design. You do not need design when you can understand it. In any moment of your development process, remember that your main goal is to Manage the complexity. Everything is secondary to this.