Coding and Testing Standards and Best Programming Practices For PHP WEB DEVELOPERS
Introduction
Anybody can write code. With a few months of programming experience, you can write ‘working applications’. Making it work is easy, but doing it the right way requires more work, than just making it work.
Believe it, majority of the programmers write ‘working code’, but not ‘good code’. Writing ‘good code’ is an art and you must learn and practice it.
Everyone may have different definitions for the term ‘good code’. In my definition, the following are the characteristics of good code.
Reliable
Maintainable
Efficient
1.Purpose of coding standards and best practices
To develop reliable and maintainable applications, you must follow coding standards and best practices.
The naming conventions, coding standards and best practices described in this document are compiled from our own experience and by referring to various Microsoft and non Microsoft guidelines.
2. Naming Conventions and Standards
Use Pascal casing for Class names
public class HelloWorld
{
…
}
Use Pascal casing for Method names
function SayHello($name)
{
…
}
Use Camel casing for variables and method parameters
$totalCount = 0;
function SayHello($name){
$fullMessage = “Hello “.$name;
…
}
Use the prefix “I” with Camel Casing for interfaces ( Example: IEntity )
Variable Names
a. Use all lower case
b. Use ‘_’ as the word separator
c. Global variables should be prepended with a ‘g’
d. Global constants should be all caps with ‘_’ separators
e. Static variables may be prepended with ‘s’.
Use Meaningful, descriptive words to name variables. Do not use abbreviations.
Good: $address,$salary .
Not Good: $nam,$addr,$sal .
Do not use single character variable names like $i, $n, $s etc. Use names like $index, $temp
One exception in this case would be variables used for iterations in loops:
for ( $i = 0; $i < $count; $i++ )
{
…
}
If the variable is used only as a counter for iteration and is not used anywhere else in the loop, many people still like to use a single char variable (i) instead of inventing a different suitable name.
Do not use underscores (_) for local variable names.
All member variables must be prefixed with underscore (_) so that they can be identified from other local variables.
Do not use variable names that resemble keywords.
Always echo in double quotes For Ex:
$myVar = “xyz”; // assign a value to $myVar
echo $myVar; // writes ‘xyz’
echo “abc to $myVar”; // writes ‘abc to xyz’// but with single quotes
echo ‘abc to $myVar’; // writes ‘abc to $myVar’
Try to avoid “include” and “require” in including files instead of this you can use “include_once” and “require_once”
Prefix boolean variables, properties and methods with “is” or similar prefixes.
Ex: private $_isFinished=true
File name should match with class name.
For example, for the class HelloWorld, the file name should be helloworld.php.
Use Pascal Case for file names.
3.Indentation and Spacing
Comments should be in the same level as the code (use the same level of indentation).
Good:// Format a message and display
$fullMessage = “Hello ” . $name;
$currentTime = date();
$message = $fullMessage . “, the time is : ” . $currentTime;
Not Good:// Format a message and display
$fullMessage = “Hello ” . $name;
$currentTime = date();
$message = $fullMessage . “, the time is : ” . $currentTime;
Curly braces ( {} ) should be in the same level as the code outside the braces.
Keep private member variables, properties and methods in the top of the file and public members in the bottom.
4.General Good Programming practices
Always use multi-tier structure where you have different frontend, business and database layer.
Method name should tell what it does. Do not use mis-leading names. If the method name is obvious, there is no need of documentation explaining what the method does.
Good:
function SavePhoneNumber ( $phoneNumber )
{
// Save the phone number.
}
Not Good: // This method will save the phone number.
function SaveDetails ( $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.
function SaveAddress ( $address );
// Send an email to the supervisor to inform that the address is updated.
function SendEmail ( $address, $email );
function SaveAddress ( $address )
{
// Save the address.
// …
}
function SendEmail ( $address, $email )
{
// Send an email to inform the supervisor that the address is changed.
// …
}
Not Good: // Save address and send an email to the supervisor to inform that
// the address is updated.
function SaveAddress ( $address, $email );function SaveAddress ( $address, $email )
{
// Job 1.
// Save the address.
// …// Job 2.
// Send an email to inform the supervisor that the address is changed.
// …
}
Always watch for unexpected values. For example, if you are using a parameter with 2 possible values, never assume that if one is not matching then the only possibility is the other value.
Good:
if ( $memberType == $eMemberTypes_Registered )
{
// Registered user… do something…
}
else if ( $memberType == $eMemberTypes_Guest )
{
// Guest user… do something…
}
else
{
// Un expected user type. Throw an exception
throw new Exception (“Un expected value”);// If we introduce a new user type in future, we can easily find
// the problem here.
}
Not Good:
if ( $memberType == $eMemberTypes_Registered )
{
// Registered user… do something…
}
else
{
// Guest user… do something…// If we introduce another user type in future, this code will
// fail and will not be noticed.
}
Do not hardcode numbers. Use constants instead. Declare constant in the top of the file and use it in your code.
However, using constants are also not recommended. You should use the constants in the config file or database so that you can change it later. Declare them as constants only if you are sure this value will never need to be changed.
Convert strings to lowercase or upper case before comparing. This will ensure the string will match even if the string being compared has a different case.
if (strtolower($name) == “john” )
{
//…
}
Use empty instead of “”
Good:
if ( empty($name) )
{
// do something
}
Not Good:
if ( $name == “” )
{
// do something
}
Avoid using member variables. Declare local variables wherever necessary and pass it to other 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.
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.
In the application start up, do some kind of “self check” and ensure all required files and dependancies 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 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 should the user do to solve the problem. Instead of message like “Failed to update database.”, suggest what should the user 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.
If you have a method returning a collection, return an empty collection instead of null, if you have no data to return. For example, if you have a method returning an ArrayList, always return a valid ArrayList. If you have no items to return, then return a valid ArrayList with 0 items. This will make it easy for the calling application to just check for the “count” rather than doing an additional check for “null”.
Declare variables as close as possible to where it is first used. Use one variable declaration per line.
Do not use session variables throughout the code.
Do not store large objects in session. Storing large objects in session may consume lot of server memory depending on the number of users.
Always use style sheet to control the look and feel of the pages. Never specify font name and font size in any of the pages. Use appropriate style class. This will help you to change the UI of your application easily in future. Also, if you like to support customizing the UI for each customer, it is just a matter of developing another style sheet for them.
Do not include too many js and css files in a page, it will make page heavier and the load time will increase.
5.Comments
Do not write comments for every line of code and every variable declared.
Use // for comments. Avoid using /* … */. And Use of Perl/shell style comments (#) is discouraged.
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.
Do not write comments if the code is easily understandable without comment. The drawback of having lot of comments is, if you change the code and forget to change the comment, it will lead to more confusion.
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 initialize 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 doesn’t need any comments to understand.
Perform spelling check on comments and also make sure proper grammar and punctuation is used.
6.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. Lot of developers uses this handy method to ignore non significant errors. You should always try to avoid exceptions by checking all the error conditions programmatically. In any case, catching an exception and doing nothing is not allowed. In the worst case, you should log the exception and proceed.
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.
Good:
void ReadFromFile ( string fileName )
{
try
{
// read from file.
}
catch (FileIOException ex)
{
// log error.
// re-throw exception depending on your case.
throw;
}
}
Not Good:
void ReadFromFile ( string $fileName )
{
try
{
// read from file.
}
catch (Exception $ex)
{
// Catching general exception is bad… we will never know whether
// it was a file error or some other error.
// Here you are hiding an exception.
// In this case no one will ever know that an exception happened.
return “”;
}
}
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) 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’.
When you re throw an exception, use the throw statement without specifying the original exception. This way, the original call stack is preserved.
Good:
catch
{
// do whatever you want to handle the exceptionthrow;
}
Not Good:
catch (Exception $ex)
{
// do whatever you want to handle the exceptionthrow $ex;
}
Do not write try-catch in all your methods. Use it only if there is a possibility that a specific exception may occur and it cannot be prevented by any other means. For example, if you want to insert a record if it does not already exists in database, you should try to select record using the key. Some developers try to insert a record without checking if it already exists. If an exception occurs, they will assume that the record already exists. This is strictly not allowed. You should always explicitly check for errors rather than waiting for exceptions to occur. On the other hand, you should always use exception handlers while you communicate with external systems like network, hardware devices etc. Such systems are subject to failure anytime and error checking is not usually reliable. In those cases, you should use exception handlers and try to recover from error.
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.
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.
7.MY SQL
Never forget to close db connections.
Do not prefix stored procedures with sp, which is a prefix reserved for identifying system stored procedures.
Do not prefix user-defined functions with fn_, which is a prefix reserved for identifying built-in functions.
Do not prefix extended stored procedures with xp_, which is a prefix reserved for identifying system extended stored procedures.
Do not use prefix in table name and filed name.
Prefix “view_” for View, “pr_” for stored procedure, “rul_” for User Rule, “dft_” for User Default, “fn_” for User Function, “udt_” for User Data Type, “trg_” for Trigger.
SQL keyword upper case, such as SELECT, INSERT, WHERE,
As for SQL statements dynamically generated, please also try to keep this style for the convenience of reading and debugging.
Make sure you normalize your data at least to the 3rd normal form. At the same time, do not compromise on query performance. A little bit of denormalization helps queries perform faster.
Write comments in your stored procedures, triggers and SQL batches generously, whenever something is not very obvious. This helps other programmers understand your code clearly. Don’t worry about the length of the comments, as it won’t impact the performance, unlike interpreted languages like ASP 2.0.
Do not use SELECT * in your queries. Always write the required column names after the SELECT statement, like: SELECT CustomerID, CustomerFirstName, City This technique results in reduced disk I/O and better performance.
Try to avoid server side cursors as much as possible. Always stick to a ‘set-based approach’ instead of a ‘procedural approach’ for accessing and manipulating data. Cursors can often be avoided by using SELECT statements instead. If a cursor is unavoidable, use a WHILE loop instead. I have personally tested and concluded that a WHILE loop is always faster than a cursor. But for a WHILE loop to replace a cursor you need a column (primary key or unique key) to identify each row uniquely. I personally believe every table must have a primary or unique key.
Avoid the creation of temporary tables while processing data as much as possible, as creating a temporary table means more disk I/O. Consider using advanced SQL, views, SQL Server 2000 table variable, or derived tables, instead of temporary tables.
Try to avoid wildcard characters at the beginning of a word while searching using the LIKE keyword, as that results in an index scan, which defeats the purpose of an index. The following statement results in an index scan, while the second statement results in an index seek:SELECT LocationID FROM Locations WHERE Specialities LIKE ‘%pples’,SELECT LocationID FROM Locations WHERE Specialities LIKE ‘A%s’ .Also avoid searching using not equals operators (<> and NOT) as they result in table and index scans.
Use ‘Derived tables’ wherever possible, as they perform better. Consider the following query to find the second highest salary from the Employees table:
SELECT MIN(Salary)
FROM Employees
WHERE EmpID IN
(
SELECT TOP 2 EmpID
FROM Employees
ORDER BY Salary Desc
)The same query can be re-written using a derived table, as shown below, and it performs twice as fast as the above query:SELECT MIN(Salary)
FROM
(
SELECT TOP 2 Salary
FROM Employees
ORDER BY Salary DESC
) AS A
This is just an example, and your results might differ in different scenarios depending on the database design, indexes, volume of data, etc. So, test all the possible ways a query could be written and go with the most efficient one.
While designing your database, design it keeping “performance” in mind. You can’t really tune performance later, when your database is in production, as it involves rebuilding tables andindexes, re-writing queries, etc. Use the graphical execution plan in Query Analyzer or SHOWPLAN_TEXT or SHOWPLAN_ALL commands to analyze your queries. Make sure your queries do an “Index seek” instead of an “Index scan” or a “Table scan.” A table scan or an index scan is a very bad thing and should be avoided where possible. Choose the right indexes on the right columns.
Prefix the table names with the owner’s name, as this improves readability and avoids any unnecessary confusion. Microsoft SQL Server Books Online even states that qualifying table names with owner names helps in execution plan reuse, further boosting performance.
Use SET NOCOUNT ON at the beginning of your SQL batches, stored procedures and triggers in production environments, as this suppresses messages like ‘(1 row(s) affected)’ after executing INSERT, UPDATE, DELETE and SELECT statements. This improves the performance of stored procedures by reducing network traffic.
Never use [dbo] in stored procedures and SQL queries until and unless told.
Leave a Reply
Be the First to Comment!