Monday, April 5, 2010

Code Standards - Why we need them

It might be a bit far fetched to call this a gem, at the same time for those who have never worked in a team before or are trying to organize one this might be life saving. I also feel that I need to add some more articles that cater more to the beginning programmer, while these sorts of articles won't become the standard for the site they will appear from time to time.

So what is important about code standards you may ask ? There are two main cases, one is when working with other people and the other is when you are working with code you haven't touched in a along time. The problem with a lot of code you find on the net and tutorials are that they are inconsistent in how the code looks or hard to read, obviously that lats part is subjective when you are used to it any way of coding might be easy to read. But when you are working with code you haven't touched in a long time you are unlikely to remember all the details of that code so any code standard that makes the code easier to read and understand helps.


It might be a bit far fetched to call this a gem, at the same time for those who have never worked in a team before or are trying to organize one this might be life saving. I also feel that I need to add some more articles that cater more to the beginning programmer, while these sorts of articles won't become the standard for the site they will appear from time to time.

So what is important about code standards you may ask ? There are two main cases, one is when working with other people and the other is when you are working with code you haven't touched in a along time. The problem with a lot of code you find on the net and tutorials are that they are inconsistent in how the code looks or hard to read, obviously that lats part is subjective when you are used to it any way of coding might be easy to read. But when you are working with code you haven't touched in a long time you are unlikely to remember all the details of that code so any code standard that makes the code easier to read and understand helps.

And when working in a team this is absolutely essential, imagine if everyone on the team had their own way of writing code. This would mean that every member would have a hard time reading the code the other members produce since they are written in their style. This leads to segregation of the code base with the different members working only on code within a certain area that they feel familiar with and can read easily (because it follows their standards). This is contra productive as ideally you want every member of the team to be able to read and maintain any part of the code so that you aren't tied down by the loss or absence of a key member.

It also is important then looking for bugs that you can easily read and understand all the code that interacts with the code you are currently writing. Having a single unified code standard for the entire team makes this a lot easier as no matter who wrote the code it looks the same way and all you have to work with is actually understanding how the code works not the actual code itself. This tie neatly in with having a solid architecture in helping everyone to be familiar everywhere in the code because the same patterns are followed.

I hope by now you can see the need of having a code standard as anything that helps readability of code lessens the chance of anyone misunderstanding the code and creating a bug. Of course they will still happen but the more you understand the code you are working with the fewer bugs you will produce.

And if you opt to use a code standard it makes sense to try to create a code standard that makes as much sense as possible when reading the code, you could say that this is the job of comments. But the problem is comments easily get out of date when someone changes the code and not the comments, if the code could be clear enough that comments are unnecessary you have taken a huge step in the direction of code readability.

So what should a good code standard contain ? Well that is a very open question  for the rest of this gem we will look at the code standard used at thegameassembly which is based on the one we used at massive entertainment. We will look at which part we standardized and why.

First out is variable naming one key point of our standard is to have describing names for everything without takign into consideration how long those names gets, the reason for this is that the time spent writing the code is incredibly small comapred to the time we spend thinkign about how to write it and debugging it. So any time we loose on writing those longer variable names are easily amde back on the shorter debugging time we gain by amkign the code more readable. We also enforce some standard on how the names should be written to make it easier to differer between data types and functions etc.

Functions is written with a big letter first and after that every new word starts with a new big letter.

Good Examples


LoadNormalMap();
GetYAndNormal();
GenerateHeightfield();

Bad Examples

getOwned();
readyfiringAnimation()

A class name also begins with a large letter for example

CollisionObject_Sphere
CollisionObject_Box

A variable always start with a small letter and after that every new world starts with a large letter, but this is not all depending on the variable it might have a prefix. As readability is our goal here and I think you can see how we strive for the text to read as plain English the m_,s_ etc from Hungarian notation isn't really going to fit in well so we are going to use prefixes that makes sense from an English perspective (Btw all of these was taught to the founders at Massive from people at Ericsson so it is not just a standard we made up even though we have modified it). So the standard works as follows an member of a class is prefixed with my a argument to a function is prefixed a,an or some depending on what makes most sense grammatically. Static members of a class is prefixed our instead of my, and global variables local to the file is prefixed local while truly global variables are prefixed global

Example

void SetHealth(int aHealth)
{
     myHealth=aHealth;
}

You can clearly se on the variable name if it is the internal variable or the argument by just looking at the prefix this helps with avoiding name collisions while making the code nice and readable.


Factory* GetInstance()
{
     return(ourInstance);
}

By prefixing the variable with our it is clear that it's a static member this way we can easily make sure that we are only trying to access static variables from static functions.

The beauty of all of this is that by a simple look at the variable name I can easily see where it comes from and act accordingly and since we don't mix any weird signs or short codes the code still feels easy readable as long as you get used to the amount of text(which goes quickly, and you will be happy for it because your code will read like comments).

I will not say that these are the only way to flag the differences between variables but it is a good way, the important part is that you have a way to see from where the variable is coming this way it's easier if you have to look at a piece of code to quickly figure out what it is doing.

While naming standards is one of the more important parts of the code standard it isn't everything there are still other parts that will help you when developing. I went with those first because they are relatively uncontroversial (Get 6 programmers in a room and start talking about code standards and blood will most likely fly after a while) these other are a bit more discusses but also really important.

For if cases always uses the brackets, this way you can easily place breakpoints inside the statement.,

Good example

if(myHealth<10)
{
    myScore-=10;
}


Bad example

if(myHealth<10) myScore-=10;


While the code example is silly the huge difference is that in the first version I can put a break point inside the brackets to see when it happens and in the second version I can't.

So always use the brackets even when it feels silly because one day you will want to put that breakpoint there and if you use brackets somewhere and not elsewhere your code will be inconsistent.

A topic that is often under heated discussing is where to declare your variables, there are basically two schools. One says that you should declare all your variables at the top of your function. That way you wont create any variable naming conflicts unknowingly and if a person wants to see what data you are using he can find it all safely in one place. It's actually a hard case to argument against the major argument is most of the time that it is bothersome to scroll to the top of your function to see all the variables, of course here is a hint if your functions are so long that you are having trouble scrolling in them then they are too long and should be divided into smaller parts. However all of this is subjective and some people really hate having to scroll around.

The goes argument that can be said for declaring your variables when you use them is that you can see the variables easier and if you are working on a small fix you can add a new variable really quickly (and I absolutely love anything that speeds up development) , but the big part is that when declaring variables inside for loops etc the compiler knows that they wont be changed outside of it and can perform various optimizations. If you unroll a loop once to use booth the execution pipes on your processor this is essential.

So there are good cases for booth sides. For me personally I leave most of my variables at the top of the function but I also add them in the middle of the function where it makes sense in an attempt to get the best from booth worlds (But then also the worst but ohh well you can't win this one)

A code standard also normally talks about how you use enums, constants and #defines

We used the standard that everything we defined should be CAPS only to make certain that we know that this is a define and those dangerous.

Lets define the same data as the different type

#define MAX_NR_OF_CLIENTS 16
#define RANDOMMACRO() ;

Clear caps and stands out amongst the rest, this is most important for macros as they are inherently dangerous.

const int localMaxNrOfClients = 16;

Observe the local prefix it means that it is restricted to this file so it is defined in a .cpp file (In some cases we have opted to sue CAPS after local for consts too to make them stand out more


const int globalMaxNrOfClients = 16;

Since this is declared as global it is declared in a header file so everyone can read it, the caps issue is the same for this one.

For enums we also decided to have all them as CAPS.

enum classType
{
RANGER,
WARRIOR,
MAGE
};

This is so that people that see them knows they are not normal variables and don't try to treat them as such.

Finally we are down on the last part the project/file/class naming conventions.(Yep we did class once but now we look at a possible prefix to put before the class definition) a problem for C++ development is name collisions and also knowing in what project a certain class resides, as with many things there are two major schools of this, one that I will call the old school and one which tries to rely on namespaces. Having worked with booth they booth have their advantages and disadvantages.

Common to booth is that you are trying to create a short acronym to use to represent your project (2-4 letters are the standard)

Examples

WIC_Player   becomes WICP_
WIC_Common becomes WICO_

Msound becomes MS_
Nemesis3D becomes N3_

etc

When using the old school method every file is prefixed with this tag and so is the class name. This has several really neat advantages, you minimize the risk of naming collisions which makes it perfectly ok for you to have several classes with the same name except for the prefix and you avoid any possible file collisions by changing the file names too. When you see a class or a file you know directly what project it belongs too. Of course the problem arises if someone else uses the same prefix as you and also your classes are permanently renamed. In C++ a feature has been added called namespaces, this is intended to be used to remove naming collisions by grouping your classes under a namespace. This can be used on a per project bases and it has a very important feature which allows you to  create a short name for the namespace dynamically when you are using it. So your Namespace could be Called Nemesis3D and then in the project using it you just declare that N3 = Nemesis3D the beauty of this is that you aren't changing the name of your class and in the case of a collision you will only have to change what short name you are using for your namespace, you don’t have to physically change the code.

Of course nothing is perfect and the big hassle with namespaces is that they don't help with file collisions therefore you are often forced to use a prefix on the files still which kind of goes against the idea. I have worked with booth and would have a hard time deciding which one to use for the moment. The flexibility and correctness of using namespaces is really nice especially that I can sue a using declaration when working inside of the project to remove the prefixes and cut down on code amount. But the hack with prefixes son files are still bothering me so I would say it depends on project length but I will definitely look to see if there isn't any nice way to solve the file prefix issue first.

This has been a brief look at a coding standard, a real standard needs to contain allot more for example how we handle switch cases, how we handle errors, what we are allowed to do in a  constructor, What design patterns we commonly use and why, the list can go on. But the big point here is that what is important is to have a standard not the exact standard I have tried to show you an example of a part of a standard and I have also tried to show and discuss some of the issues that might appear when trying to select a standard but the rest is up to you.

1 comment:

  1. This comment has been removed by the author.

    ReplyDelete