Coding Standards, Naming Conventions and Formatting Styles
Written by Stéphane Richard (Mystikshadows)
INTRODUCTION:
AS you've read from the title of this article, it is all about coding standards, naming conventions and style of your source code module. The mean reason I've created this article isn't to brainwash you into my way of doing things. This article is not the first to talk about such a subject. But what i have elaborated here have proven themselves worth my time on all my projects both professional and personal. There's more than one reason to do so as you'll read here and to the best of my knowledge, following these standards and conventions, that you created yourself, might take just a bit more time in the programming phase of your project, but it can easily save you alot of time in the debugging phase. Likewise, because these are your standards made to follow your coding methods, it doesn't really take any of the fun out of your programming project.
The main thing to remember in this article isn't the standard itself (although you're more than welcome use it if you like it) but rather to give you the reasons why coding standards exist, why they can bring benefits to your projects, even the smallest ones. It takes a little mental training to motivate yourself to create this standard, take the extra time to name your variables, subs and other elements with clear and relevant names. But you'll be the first to reap the rewards of implementing these practices into your own programming endeavors. Note that I am using the standards I've elaborated in the 3rd part of my Graphical User Interfaces - A Complete Study So the standards might look familiar as it is the standards I follow on any of my projects (big or small). I will however elaborate on them in more details here. So then, Let's get right to it.
WHY CREATE A STANDARD AND STICK TO IT:
This is always the bigger question isn't it? Of course, if you can't find a reason to create a standard, chances are it will be hard for you to actually create one and even more so actually follow the standard you set yourself. What I will do here is give you the reasons why I created my own standards, and what benefits they've given me so that you can see for yourself what standards and conventions are really all about.
- Code Readability:
Let's face it, it doesn't matter how you look at it, if you can understand what you are reading even before you are actually reading and following code, it makes the whole learning curve much quicker. I like to keep my code as easy to read as it is to understand the concepts within the code. When I write code, I think of two things. The first thing is I make an effort to present the code to myself the way I would want to see any code I'd look at and how clear I need things to be for myself. The other thing I think about is detailed in the next reason. A project's development starts at the coding of the many modules, it doesn't end with it. The code will need to be maintained, sometimes for months to maybe a year, in other times it could be for a decade, it really depends on the project you are working on. No matter how long, when the code is readable, it just makes the whole process go smooth. - Code Maintainability:
This really has 2 distinct roles. The first is while you are working on your current project. When a standard is follows, it allows you to almost instinctively know where things are and what possible problems have occured. In other words, understanding the standard used quickly helps you to understand the code. Hence, when you think of a change you need to make a change to your project, you can know where that change goes. Likewise, if you think of a new feature you want added to your project, you can instantly know where it will go. If you document your code clearly enough you can even know the impact that change will have. The second maintainability reason is of course when you think of a change or addition that you want to make to a project you finished a couple years ago. Or if you want to take an old project and port it to a new language. Knowing your standards and conventions will really quicken the process of porting part or all of your project to that new language. As I mentionned before, the coding phase doesn't stop when you coded your project, far from it. - Quicker and Shorter Debugging Phase:
When you think about it, this is one of the best reasons to implement a standard. From my own experience, I've found that when I follow a standard, one I set myself, or one set by my employers (if they are well designed of course). It seems that I automatically change the way I code. Not just in the way I present the code, but also in the way I think about the code before I write it down. This often results in a bit more thinking about the code I'm about to write which means that the resulting code seems to automagically become not only clearer, but more apt to do the task. I'm not exactly sure how that happens per se. But from what I've seen, because you have to think of the way your routines will need to be coded to follow the standard, you are in fact manipulating the whole logical structure of your code, not just it's presentation. As such, you are "subliminally" debugging more of your code before you put it in your text editor. What this means is at the end of a coding process, your code is more apt to do it's designated task than it would have been if you'd just sit down and throw the code in the editor as your brain spits it out. In my own experience the debugging phase was reduced anywhere from 20% to even 70% depending on what I was coding. To me, even at 20%, it represents a big chunk of time especially if your project size is bigger than usual. We could easily be talking weeks faster all the way to months faster. Imagine that. - Going Back To An Old Project:
Sure when you think about it, the project you're work on currently is easy to remember right? You have almost all the routines in your head, you have a good idea where everything is in all the 1000s of lines of code that make up that project. In your next project or in your previous project you might have worked on another game that uses 3D or other techniques similar to the ones you are using now. In that case, it can be somewhat easier to go back to your previous project. Think about this scenario. 2 years ago you finished making your own version of Word Perfect and right now, you're working on an OpenGL game, you've been working at it for 2 years now, so you're pretty good with OpenGL and 3D. That's all great. Now close your project, and reopen that Word Processing project you were working on 2 years before. Think you can locate things as quickly as you did or even understand what you were doing 2 years ago or longer? Don't you wish you documented all those lines of code better back then? This is, by far, the most common thing that happens with coding projects. But if you created standards, and later get back to them, it cuts off the adaptation period quite substantially. - Group Projects:
Whether it's a personal project that you and a couple of friends are working on or whether your business hired 20 or more programmers to work on one single project. It's clear to see how good standards can really benefit the whole group. Let's face it, everyone that programs has his or her own way of doing so. From the way they indent the code to the fact that might prefer to use binary files wherever possible where another programmer might prefer random access files. If you put 10 people on a project, and tell them all to create a routine that performs a task without talking to each other, chances are no two routines will be the same. That's how different programmers are. All the background experience of each programmer are responsible for the code they produce today. So, on a big group project, if all programmers can be made to follow a standard, suddenly everyone speak closer to the same language and that makes the whole project more consistent to follow and understand.
I don't know about you, but by reading these advantages, I'm sure just those got you thinking all of a sudden. It's clear to see that good standards and naming conventions really helps make things more prepared for the unevitable future of programming, changing existing code, adding new code in and remembering what you did 5 years ago. If you could find a way to give you all these "easier things in a programmer's life", wouldn't you want to alteast give it a good try? It doesn't take a bachelor's degree to create a standard and you don't need rocket science to be able to stick to it. All you need is the time to create it. In my case it took about 2 good hours of thinking and writing. So it's not that much time. But the time it will save you in the long run can be alot more than you would think possible.
So then, with all this in mind, the next logical step is to simply go right ahead and elaborate these standards. It's very important for you to remember that these are the standards I created and like to work by. They might sound interesting to you and as such you can definitaly used them if you want. Some of your might feel that only part of this elaboration wight be usable in your case. Again, feel free to use this in whole or in parts. The real idea behind this whole article is to help you find a way to organize your code in a way that will help you work on projects without being overwhelmed by the code you have done or the code you are about to undertake. Simple organization prior to coding as well as some common sense is really all it takes. So het's talk about these standards.
CODING STANDARDS:
The standard I'll be using is broken down into three workable standard groups. These are the "Source File Documentation", "Indentation and Spacing" and "Naming Conventions". As I mentionned, coding standards are there because when the code gets big, it's clarity will have better chances at staying as clear as possible across the thousands of lines of codes there will be. Let's take the time to review these standards right here.
- Source File Documentation:
When creating a source file, often, documentation of this source file is quickly neglected usually because of lack of time, some think time is better spent actually coding the intended purpose and say to themselves: "I'll document it when it works!". I myself often used that technique as well because when there's no time there's no point. However, this project has no deadlines. Like everyone else, I want my things done yesterday but this is suicidal on any project that you deem a big project. I'd rather wait (as impatient as I am), as long as it takes, to be sure that what I get is both readable and understandable. As such I try to give a special effort to comply with the following standards.
- Module Level comments:
This is usually done at the beginning of a module, it should have the module name, project to which it belongs, version number, creation date, author name, and other standard information of this nature. It should also have a paragraph that explains it's purpose clearly. It could be a sentence, sometimes a sentence is enough, other times more information is needed. Common sense will play a role to determine how much information is needed. Right after this I leave a comment section to add "things to do" so I don't lose track of thing or forget something that should have been done. Finally, at the end of the module, I also like to have a history section, at the end of the source module, so that I can track the work done. Here is a sample Module header comment, taken from my ErgonOS GUI project:' =================================================================================== ' NAME: Ergon O.S. (Ergonomic Operating System) ' FILE NAME: ErgonOS.bas ' FILE TYPE: Main Program Module ' VERSION: 1.00.000a Build 0001 ' DATE CREATED: Wednesday, October 26th, 2005 ' ----------------------------------------------------------------------------------- ' DESCRIPTION: This module calls upon the functionality of other modules in order ' to validate the command line parameters and act upon them. Then it ' will load the configuration file, initialize all sub systems and ' present the Ergon O.S. GUI to the user. ' =================================================================================== ' PROJECT NAME: Ergon O.S. ' PROJECT TYPE: Graphical User Interface ' VERSION: 1.00.000a Build 0001 ' PROJECT URL: Coming Soon ' LANGUAGE: FreeBasic Version 0.15b and higher ' TARGET: DOS 32 Bits ' ----------------------------------------------------------------------------------- ' DESCRIPTION: Ergon O.S. is a Graphical User Interface Project designed to be a ' document based User Interface. To best describe this project, you ' simply need to think of the GUI as a document based interface ' rather than an application based interface as commonly seen these ' days. ' =================================================================================== ' AUTHOR NAME: Stephane Richard ' NICKNAME: MystikShadows ' EMAIL: srichard@adaworld.com ' WEBSITE: Coming Soon ' ----------------------------------------------------------------------------------- ' COPYRIGHT: Copyright (c) January 2005 - Stephane Richard ' LICENSE: Licenced under G.P.L. Version 2 for OpenSource Projects ' Licenced under B.S.D. for freeware/shareware projects ' DISTRIBUTION: Allowed in concordance to the G.P.L. Version 2 ' ALTERATIONS: Allowed as under the terms of the G.P.L. Version 2 ' =================================================================================== - Procedure Level comments:
This is usually done at the beginning of a procedure or function. It should have the name of the function or procedure, a list of parameters and their types and an indication on whether a parameter is optional or defaulted to a value somehow. It should state what the function returns, if any, what it assumes, what valid values are acceptable for parameters in the case of dates for example, perhaps a date can't be earlier than a certain date. And a paragraph on the role of the procedure or function. Once again, here's an example to illustrate this:' =============================================================== ' NAME: LoadConfiguration() ' PARAMETERS: ConfigFile AS STRING ' RETURNS: 0 if no errors, Err number if anything happened ' ASSUMES: ConfigFile is valid path and file name. ' CALLED FROM: ErgonOS.bas Main Program Section ' --------------------------------------------------------------- ' DESCRIPTION: This Sub takes the passed configuration file ' name, opens it and loads up the values into the ' appropriate User Defined Types so that they are ' available to the rest of the application. ' =============================================================== FUNCTION LoadConfiguration(ConfigFile AS STRING) AS INTEGER ... END FUNCTION - Source Level comments:
This is done just before a certain block of code within a procedure or function. usually one or two sentences to explain the code to come when the developer knows it's a tricky part to understand. Again, logic and common sense should guide this level of documentation, some code are clear enough as is, some require some explanation. Before you decide that no comments are needed, think about the long term for a bit and picture youself going back to this non commented code in a couple of years. If you're very active in a very different type of programming project, you might not even need a couple years but only a couple of months to see how looking at the same code can get confusing. Every bit of clarity helps in this case.
- Module Level comments:
- Indentation and Spacing:
Indentation, to me, helps to clearly determine where a block of code begins and where it ends. For the number of spaces to use, that's irrelevent, whatever the preference may be just as long as it's clear enough to visually see the beginning and end of a language contruct. I believe at least 2 characters should be used, but aside that, any indentation style is left up to the developer. Me I usually align to the next Item. like so:
The main reason for this is that to me Aligning the CALL ThisSub under ThisVariable and ThatSub under Counter like I did is visually cleaner in my opinion. That's just my opinion however and if I was to receive code that wasn't aligned this way I wouldn't refuse it. Everyone has their own style of coding. Me I just happen to like it that way.IF ThisVariable = 1 THEN CALL ThisSub END IF FOR Counter = 1 TO 10 CALL ThatSub NEXT Counter
Usually indentation is used for Class definitions, If Then Else constructs, Loops, Procedure and Function declaration and the rest of the code is then indented to the level where it should be to visually show that it is part of a given construct. Many editors even have an Auto-Indent feature that can do a good job at indenting code for your in a more than acceptable means. In other cases, most language have a "code beautifier" or code formatter program that can take a source file and spruce it up visually speaking.
Common sense is really what it's all about here. If the code looks clear enough to understand then it should be perfect just the way it is. It's all visual aids that help other understand the source file, so it should be used as a documentation tool.
- Naming Conventions:
As far as naming conventions goes, well common sense and logic will play a very big role here as well. When naming a source file, a procedure or function, a class definition or a variable, one has to think of others that haven't seen the code yet. What can they say about the names you've chosen determines the quality of the naming convention used.
- File Names:
When naming a source file, a form, a database, whatever the case may be, it's important to keep the name of the file as clear as possible as far as it's intended purpose is concerned. Basically, I name my module based on what they pertain to. For example, if a module has user defined types and is at a global (read shared) module I would typically call it "SharedUserTypes.bi". However, if the module has specific string parsing module for a scripting engines I would more than likely call it "ScriptParserFunctions.bi". Now that the OS doesn't limit us to 8 characters and 3 for the file extension, we can more freely use wise naming conventions like these making the whole project that much clearer. - Class Names:
Well a class is an object and therefore should be named according to how you would name any other objects. If you are defining a class for animals for example, simply call it AnimalClass, it needs to speak for itself. Common sense should once again help here. When other people look at your code, they will know, by reading "AnimalClass" that what you are refering to is a class definition that defines an animal data type. Since all classes are created to define an object, they can all follow this type of naming convention regardless of the class type. Attention to these little details help clear things up a lot. Imagine if you called it just Animal. What would tell them that this is a class definition? They would have to go to it's definition to see what it is and that is just a waste of time. In my case, Classes are named <ClassName>Class, User defined types are <TypeName>Type this helps clearly define what the data type is. One of the very wide spread naming convention is called The Hungarian Notation and has existed for quite some time already. It has advantages but to me, in many ways, the means by which it gives details to a variable name also makes the code a bit more cryptic in my opinion, but go ahead and read about it, you might like it enough to use it all the time, which is why I provided the URL to it. It's very wide spread and if you plan on working as a programmer for a living, you should probably get familiar with it if you haven't done so already. - Procedure/Function Names:
A procedure or a function usually perform an action or a calculation, therefore, it is recommended that the procedure or function name start with a verb stating that action followed by a name of what that action is perform on. For example a function that would caculate the tax on a given amount should be called CalcTax or CalculateTax. By always making sure the verb used clearly identifies the action taken, there will never be a surprise as to what the function should be doing. The parameters of the procedure should follow your variable naming convention again to keep your code as compliant to your standards as possible. - Variable and DataType Names:
A variable stores information to be used later or elsewhere in the module or a given project (depending on their scope). A name, aside standard variable naming conventions (start with a letter, followed by numbers or other letters etc etc..) should tell, by its name, what information it will store at the very least for the sake of clarity of code. A DataType (User Defined Type that is) is a structure that will more than one piece of information It's name will need to represent the data it will hold clearly as well as end with Type to clearly see it's a TYPE definition. For example, CoordinateType. A variable declared as that type will omit the Type ending. Hence <scope> Coordinate AS CoordinateType. Depending on the situation, an array of a given TYPE definition will be TypeNameList or the plural form of the TypeName. Finally, Global variables declaration will start with Global and the name of the variable. Again, you could use the Hungarian Notation system if you'd like, the choice is up to you. Note that I like to align my type definitions (as you'll see here), to me, it helps make then easier to read and to identify when scrolling quickly through the code. Here's an example of all these:
' -------------------------------------------------------- ' NAME: WindowType ' DESCRIPTION: This User Defined Type holds the needed ' information to display and manage a ' window on the screen. ' -------------------------------------------------------- TYPE WindowType WindowNumber AS INTEGER WindowTitle AS STRING WindowTop AS INTEGER WindowLeft AS INTEGER WindowHeight AS INTEGER WindowWidth AS INTEGER ForeColor AS INTEGER BackColor AS INTEGER END TYPE DIM Window AS WindowType DIM WindowList() AS WindowType ' OR DIM Windows() AS WindowType DIM SHARED GlobalWindows() AS WindowType
- File Names:
ERROR MANAGEMENT:
In big projects, if you don't implement error management right from the start, it will be a major headache to add it later in the project or at the end after you're done coding everything else. For this reason alone I talk about error management as part of a convention because like the rest of your standards and conventions, they need to be implemented from the start so they don't quickly become quite an overwhelming task. However, doing it from the start like this, you can integrated it easily to your coding routine and almost not have to worry about it. At the end you'll end up with all your error management done and be ready to move on to the next step of your project right away.
Throughout all the modules, each sub and function, when implementing the error management code, you might want to think about the reasons why you are implementing error management. As far as I'm concerned One of the main reason I implement error management is because of the debugging phase and the other errors that might occur afterwards as users actually use my program. As such, I need a system that can point me as close as possible to the erroneous code. This way I don't have to spend too much time locating where the error is actually happening. For that to successfully occur, I need a certain list of information to help me in my quest. Whether the bug is reported on screen or logged to a file this information will still help locating bugs quickly. Here's that information: (note that this is also in my 3rd part of my GUI series).
Error Information | Description |
Engine Name Module Name Sub/Function Name Parameter List Error Number Error Description |
The Engine name is displayed The Name of the .bas or .bi file The Sub or Function Name The values of the parameters passed The error number as returned by ERR A human readable description of what the error means |
This essentially means that whenever an error is returned by the system, it will give me back these specific pieces of information. By knowing right from the start, the Engine where the error occured, the module name, the sub or function name, the values of the parameters that were passed to that sub or function (if any) and the error that occured, imagine how fast the debugging process will suddenly become for me, or anyone else correcting the error. Here is an example of a typical function and how I will provide the needed information. Let's assume that this function will be in a module called MouseControl.bas and that the project is called DrawingPad:
' ============================================= ' NAME: MouseOverControl() ' PARAMETERS: XPos AS INTEGER ' YPos AS INTEGER ' Width AS INTEGER ' Height AS INTEGER ' MouseX AS INTEGER ' MouseY AS INTEGER ' ASSUMES: Parameters are positive values ' RETURNS: True if mouse is on top of the ' control, False if it is not. ' CALLED FROM: EVRIL's main logic loop. ' --------------------------------------------- ' DESCRIPTION: This function will take the ' position and dimensions of a ' control, do the math to get ' the range of coordinates that ' the mouse need to be in to be ' considered to be on top of the ' control and compare the mouse ' coordinates to see if it falls ' within the control's range. If ' it does, True will be returned ' to the calling sub. If it's ' not on top of it, False will ' be returned instead. ' ============================================= FUNCTION MouseOverControl(XPos AS INTEGER, YPos AS INTEGER, _ Height AS INTEGER, Width AS INTEGER, _ MouseX AS INTEGER, MouseY AS INTEGER) AS INTEGER ' ------------------------------------------------ ' We create a variable to hold the error message ' ------------------------------------------------ DIM ErrorMessage AS STRING DIM BottomRightX AS INTEGER DIM BottomRightY AS INTEGER DIM WorkResult AS INTEGER ' -------------------------- ' Turn in Error management ' -------------------------- ON LOCAL ERROR GOTO ErrorManager ' --------------------------------------- ' Evaluate the bottom right coordinates ' --------------------------------------- BottomRightX = XPos + Width - 1 BottomRightX = YPos + Height - 1 ' ------------------------------------------------------------ ' Compare Mouse coordinates to control's area and return the ' proper value of the function based on if the mouse is over ' the control or not. ' ------------------------------------------------------------ IF (MouseX >= XPos AND MouseX <= BottomRightX) AND (MouseY >= YPos AND MouseY <= BottomRightY) THEN WorkResult = True ELSE WorkResult = False END IF ' ------------------------------------------------------------- ' Return the value and exit the function right here before ' execution of the error management section can take place ' if no error occured. ' ------------------------------------------------------------- MouseOverControl = WorkResult EXIT FUNCTION ' ------------------------------------------------------------------- ' This is where the error management message is created and printed ' ------------------------------------------------------------------- ErrorManager: ' ------------------------------------------------------- ' First step is to formulate the complete error message ' ------------------------------------------------------- ErrorMessage = "Engine: DrawingPad" + CHR$(13) + _ "Module: MouseControl.bas" + CHR$(13) + _ "Function: MouseOverControl" + CHR$(13) + _ "Parameters: (" + STR$(XPos) + ", " + _ STR$(YPos) + ", " + _ STR$(Height) + ", " + _ STR$(Width) + ")" + CHR$(13) + _ "Error: " + STR$(Err) + ":" + Err$ ' ------------------------------------------------------------- ' Next we either display the error or append it to a log file ' ------------------------------------------------------------- IF ErrorLogging = True THEN LogError(ErrorMessage) ELSE DisplayError(ErrorMessage) END IF ' --------------------------------------------------------------- ' In this case, we resume execution loging or showing the error ' --------------------------------------------------------------- RESUME NEXT END FUNCTION
IN CONCLUSION:
And there you have it, the coding standard documents comes to an end. If you've paid attention, you can see that the major factor you need to remember when creating your coding standards is that they have to make sense to you now and in a year or 2 if you need to get back to that same code. If just that stays in your mind after reading this, I will be able to say mission accomplished. Too many times I've seen people code away for months, leave to work on another project and be baffled when they need to get back to that project. Too many times I've joined projects that didn't follow any standard imaginable. It's really amazing what a couple of hours (maybe a days work) defining a standard can do to the overall coding process. How much it can help during and after the programming phase. This is the major reason why I wrote this article. Is to make you think about it.
What's to say next? Are you going to create your standards or keep working the way you are now? That decision is up to you some might have the natural gift to code so clear that not understanding what's happening is simply not an option. Others could benefit from such a standard being in place. But that's just as far as code readability goes. For maintainability purposes however, I strongly believe that having a standard in place can only have benefits. Sure when deadlines are concerned people tend to think that standards just waste too much time, but that's usually because they didn't know what I've shared with you right here. When it comes to deadlines, there's one thing you need to think about. Do you want to spend a day creating a better standard or delay your release 3 or 4 weeks because you are lost in 1000s of lines code somewhere thinking (maybe I should have created a standard)? That answer is simple to me, isn't it to you? Until I write again, feel free to email me with comments or questions. Happy coding.
MystikShadows
Stéphane Richard
srichard@adaworld.com