This will apply to C# mostly (since I'm a C# dev by trade it will be what I'll focus on), but most of these principles stay the same, regardless of the language you're using.
Naming conventions
Use Pascal casing for class, type, method and constant names:
public class SomeClass
{
public const int SomeConstant = 1;
public void SomeMethod()
{
//...
}
}
Use Camel casing for variables and method parameters:
private int totalCount;
public void SayHello(string firstName)
{
string message = "Hello " + firstName;
//...
}
Do not use Hungarian notation to name variables:
private int m_totalCount; - Incorrect
private int nTotalCount; - Incorrect
private int _totalCount; - Incorrect
private int totalCount; - Correct
- Prefix Interfaces with I:
public interface ISomeInterface
{
//...
}
Suffix custom attribute classes with Attribute
public class ToolBoxConfigAttribute : Attribute
{
//...
}
Suffix delegates related to events with EventHandler
public delegate CloseEventHandler(object sender, EventArgs arguments);
Suffix delegates related to callback methods with Callback
public delegate AsyncIOFinishedCallback(string message);
Suffix custom exception classes with Exception
public class CustomException : ApplicationException
{
//...
}
- Name methods using verb-object pair, such as SaveProduct()
- Methods with returning values should have a name describing the value returned, such as GetPageExpiryDate()
- Use meaningful, descriptive words to name variables.
- And remember: a good variable name describes the semantic not the type.
An exception to this rule is GUI code. All fields and variable names that contain GUI elements like button should be prefixed with their abbreviated type name. For example:
System.Web.UI.Forms.Button btnSubmit;
System.Windows.Forms.TextBox nameTextBox;
- Do not use abbreviations. Use name, address, salary etc instead of nam, addr, sal
- Do not use single character variable names like i, n, x etc. Use names like index, temp.
One exception in this case would be variables used for iterations in loops:
for (int i=0; i < 10; i++)
{
//...
}
Capitalisation summary
Type Case Notes
Class/Struct Pascal Casing
Interface Pascal Casing Starts with I
Enum values Pascal Casing
Enum type Pascal Casing
Events Pascal Casing
Exception class Pascal Casing End with Exception
Public fields Pascal Casing
Methods Pascal Casing
Namespace Pascal Casing
Property Pascal Casing
Protected/Private fields Camel Casing
Parameters Camel Casing
Indentation
- An indentation standard using spaces never was achieved. Some people like two spaces, some prefer four and others die for eight, or even more spaces. Better use tabs. Tab characters have some advantages:
• Everyone can set their own preferred indentation level
• It is only 1 character and not 2, 4, 8 … therefore it will reduce typing (even with smart indenting you have to set the indentation manually sometimes, or take it back)
• If you want to increase the indentation (or decrease), mark one block and increase the indent level with Tab with Shift-Tab you decrease the indentation. This is true for almost any text editor
• Here, we define the Tab as the standard indentation character
- When an expression will not fit on a single line, break it up according to these general principles:
• Break after a comma
longMethodCall(expr1, expr2,
expr3, expr4, expr5);
• Break after an operator
• Prefer higher-level breaks to lower-level breaks
PREFER:
var = a * b / (c - g + f) +
4 * z;
BAD STYLE – AVOID:
var = a * b / (c - g +
f) + 4 * z;
The first is preferred, since the break occurs outside the parenthesized expression (higher level rule). Note that you indent with tabs to the indentation level and then with spaces to the breaking position in our example this would be:
• Align the new line with the beginning of the expression at the same level on the previous line
Spacing
- Spaces improve readability by decreasing code density. Here are some guidelines for the use of space characters within code:
• Do use a single space after a comma between function arguments.
Right: Console.In.Read(myChar, 0, 1);
Wrong: Console.In.Read(myChar,0,1);
• Do not use a space after the parenthesis and function arguments
Right: CreateFoo(myChar, 0, 1)
Wrong: CreateFoo( myChar, 0, 1 )
• Do not use spaces between a function name and parenthesis.
Right: CreateFoo()
Wrong: CreateFoo ()
• Do not use spaces inside brackets.
Right: x = dataArray[index];
Wrong: x = dataArray[ index ];
• Do use a single space before flow control statements
Right: while (x == y)
Wrong: while(x==y)
Do use a single space before and after comparison operators
Right: if (x == y)
Wrong: if (x==y)
- Blank lines improve readability. They set off blocks of code which are in themselves logically related. Two blank lines should always be used between:
- Logical sections of a source file
- Class and interface definitions (try one class/interface per file to prevent this case) One blank line should always be used between:
- Methods
- Properties
- Local variables in a method and its first statement
- Logical sections inside a method to improve readability. Note that blank lines must be indented as they would contain a statement this makes insertion in these lines much easier.
Exception handling
- Never do a 'catch exception and do nothing'. If you hide an exception, you will never know if the exception happened or not.
- In case of exceptions, give a friendly message to the user, but log the actual error with all possible details about the error, including the time it occurred, method and class name etc.
- Always catch only the specific exception, not generic exception.
- No need to catch the general exception in all your methods. Leave it open and let the application crash. This will help you find most of the errors during development cycle.
- You can have an application level (thread level or global.asax) error handler where you can handle all general exceptions. In case of an 'unexpected general error', this error handler should catch the exception and should log the error in addition to giving a friendly message to the user before closing the application, or allowing the user to 'ignore and proceed'.
- Do not write try-catch in all your methods. Use it only if there is a possibility that a a specific exception may occur. For example, if you are writing into a file, handle only FileIOException.
- Do not write very large try-catch blocks. If required, write separate try-catch for each task you perform and enclose only the specific piece of code inside the try-catch. This will help you find which piece of code generated the exception and you can give specific error message to the user.
- You may write your own custom exception classes, if required in your application. Do not derive your custom exceptions from the base class SystemException. Instead, inherit from ApplicationException.
Comments
- The // (two slashes) style of comment tags should be used in most situations. Where ever possible, place comments above the code instead of beside it. Here are some examples:
// This is required for WebClient to work through the proxy
GlobalProxySelection.Select = new WebProxy("http://itgproxy");
// Create object to access Internet resources
//
WebClient myClient = new WebClient();
- Comments can be placed at the end of a line when space allows:
public class SomethingUseful
{
private int itemHash; // instance member
private static bool hasDoneSomething; // static member
}
- Do not write comments for every line of code and every variable declared.
- Write comments wherever required. But good readable code will require very less comments. If all variables and method names are meaningful, that would make the code very readable and will not need many comments.
- Fewer lines of comments will make the code more elegant. But if the code is not clean/readable and there are less comments, that is worse.
- If you have to use some complex or weird logic for any reason, document it very well with sufficient comments.
- If you initialise a numeric variable to a special number other than 0, -1 etc, document the reason for choosing that value.
- The bottom line is, write clean, readable code such a way that it does not need any comments to understand.
- Do a spell check on comments and also make sure proper grammar and punctuation is used.
Documentation comments
- All public and protected types, methods, fields, events, delegates, etc. should be documented using XML tags. Using these tags will allow IntelliSense to provide useful details while using the types. Also, automatic documentation generation tooling relies on these tags.
public class Foo
{
/// <summary>Public stuff about the method</summary>
/// <param name=”bar”>What a neat parameter!</param>
/// <devdoc>Internal stuff!</devdoc>
///
public void MyMethod(int bar) { … }
}
- However, it is common that you would want to move the XML documentation to an external file – for that, use the <include> tag.
public class Foo
{
/// <include file='doc\Foo.uex' path='docs/doc[@for="Foo.MyMethod"]/*' />
///
public void MyMethod(int bar) { … }
}
Good programming practices
- Avoid having too large files. If a file has more than 300~400 lines of code, you must consider refactoring code into helper classes.
- Avoid writing very long methods. A method should typically have 1~25 lines of code. If a method has more than 25 lines of code, you must consider re factoring into separate methods.
- Method name should tell what it does. Do not use misleading names. If the method name is obvious, there is no need of documentation explaining what the method does.
Good:
void SavePhoneNumber ( string phoneNumber )
{
// Save the phone number.
}
Not good:
// This method will save the phone number.
void SaveData ( string phoneNumber )
{
// Save the phone number.
}
- A method should do only 'one job'. Do not combine more than one job in a single method, even if those jobs are very small.
Good:
// Save the address.
SaveAddress ( address );
// Send an email to inform that the address is updated.
SendEmail ( address, email );
void SaveAddress ( string address )
{
// Save the address.
// ...
}
void SendEmail ( string address, string email )
{
// Send an email to inform that the address is changed.
// ...
}
Not good:
// Save address and send an email
SaveAddress ( address, email );
void SaveAddress ( string address, string email )
{
// Job 1.
// Save the address.
// ...
// Job 2.
// Send an email to inform that the address is changed.
// ...
}
- Use the C# or VB.NET specific types, rather than the alias types defined in System namespace.
Good:
int age;
string name;
object contactInfo;
Not good:
Int16 age;
String name;
Object contactInfo;
- Do not hardcode numbers. Use constants instead.
- Avoid using many member variables. Declare local variables and pass it to methods instead of sharing a member variable between methods. If you share a member variable between methods, it will be difficult to track which method changed the value and when.
- Use enum wherever required. Do not use numbers or strings to indicate discrete values.
- Do not make the member variables public or protected. Keep them private and expose public/protected properties.
- Never hardcode a path or drive name in code. Get the application path programmatically and use relative path.
- Never assume that your code will run from drive ‘C:’. You may never know, some users may run it from network or from a ‘Z:’.
- In the application start up, do some kind of ‘self check’ and ensure all required files and dependencies are available in the expected locations. Check for database connection in start up, if required. Give a friendly message to the user in case of any problems.
- If the required configuration file is not found, application should be able to create one with default values.
- If a wrong value found in the configuration file, application should throw an error or give a message and also should tell the user what are the correct values.
- Error messages should help the user to solve the problem. Never give error messages like ‘Error in application’, ‘There is an error’ etc. Instead give specific messages like ‘Failed to update database. Please make sure the login ID and password are correct.’
- When displaying error messages, in addition to telling what is wrong, the message should also tell what the user should do to solve the problem. Instead of message like ‘Failed to update database’ suggest what the user should do: ‘Failed to update database. Please make sure the login ID and password are correct.’
- Show short and friendly message to the user, but log the actual error with all possible information. This will help a lot in diagnosing problems.
I'm sure there will be some points that you don't agree with, which is absolutely fine by me, as long as you disagree consistently, there is nothing worse than working on inconsistent code.