UW Logo

CS246: Object-Oriented Software Development

Supported by the Instructional Support Group

University of Waterloo : Faculty of Mathematics : Cheriton School of Computer Science : CS 246 Home Page : Link to CS246e

Assignment Coding Guidelines


Above all

Coding quality is a judgment call. There isn't one right way to do things. Every "rule" can potentially be broken under the right circumstances. So do not turn your brain off, but be constantly evaluating the code you write, and asking yourself what the right choice is, and whether you have a good reason to deviate from the guidelines given below.

In this course, we prescribe a "golden rule" for coding quality: write the kind of code you would like to see in someone else's code if you were asked to work on it.

Appearance

We don't enforce a specific look and feel for your code, but we expect you to be consistent. Choose an appropriate indentation style for your code and stick with it. Choose an appropriate and visually pleasing policy for use of whitespace and stick with it. Choose an appropriate form of initialization syntax (the new uniform initialization syntax, the older forms, or a disciplined mixture of these) and stick with it.

The appearance of your code is one aspect of what we look for during handmarking, but it is only the tip of the iceberg. Read on ...

Documentation

Your code must be readable, but this does not necessarily imply that you must write copious amounts of documentation. Well-written code is self-documenting. It is possible for code to be of excellent quality, and easy to follow, without any documentation at all, though in many circumstances, a few well-placed comments would be quite appropriate. On the other hand, needless inline documentation can be a distraction.

To make your code readable, choose meaningful names for variables, functions, classes, etc. Consider using additional parentheses in expressions, rather than relying on the user to remember the entire C++ operator precedence hierarchy. Consider using typedef to give a descriptive name to a complex type expression. Write functions that do a single thing, with an intuitive interface. Write classes that have a single responsibility. Split up complex tasks. Avoid "tricks".

Inline documentation should be used to explain those aspects of your code that would not be apparent to a casual observer. For example, if there is some subtlety that requires this statement to occur before that statement, you might wish to make this a comment. You should also use inline documentation to explain your reasons in those cases where you feel you should "break the rules".

Code that is "commented out" should be removed from your programs before you hand them in, as it may confuse the markers. Debugging statements controlled by preprocessor directives do not have to be removed.

Avoid premature optimization

Your first responsibility is to make your code readable. Efficiency is a secondary concern. Before you decide to optimize part of your code for efficiency, make sure the gain in efficiency is worth the effort and the potential loss in readability. For example, it does not matter what algorithm you use to sort a list if the list is guaranteed (or expected) to be short. You will not improve performance, but you will cost yourself time and readability.

Focus on efficiency for the parts of the program that truly matter: for inputs whose size could be very large, or for situations where the most intuitive solution (or your solution) would be especially slow. Programs that are unreasonably inefficient on unbounded inputs will be penalized.

Don't make inefficiency a bad habit

There are habits you can develop that lead to more efficient code (even if only modestly so) without costing you in time or readability. For example, pass arguments by const ref whenever possible, if they are larger than an int. Use const as much as possible, as it helps the compiler generate better code, and helps you to avoid errors. Use the member initialization list (MIL), as it avoids double initialization. Use prefix ++ instead of postfix ++, as it avoids having to remember the previous value of the variable. Use stack-allocated objects, unless there is a good reason to use the heap. These are not premature optimizations. These are simply good habits that avoid needless inefficiency.

Defend against errors

Let the compiler help you avoid errors. Write explicit constructors. Avoid casting. Use const as much as possible. Declare overriding methods override. If a class is not meant to be subclassed, declare it final. Use references instead of pointers, unless you'll need to change where they "point", or unless they could be null. Use assertions to help detect coding errors. Keep your code properly encapsulated. Prefer non-member functions over member functions. Minimize friendships.

Write high-quality code

Less is more: a simple problem should have a simple solution. If you find yourself writing a long program to solve what seems to be a simple problem, ask yourself if you are approaching the problem correctly. We do not expect that your solutions will be the same length as ours, but if they are significantly longer, without a good reason, this may be an indicator that you haven't solved the problem well, and your grade may reflect this.

Never write the same code twice. If you find yourself having to do the same thing more than once, or if you find yourself cutting and pasting code, you may be missing out on an opportunity to build an abstraction. Perhaps you need a helper function. Perhaps you can factor repeated code outside of an if statement. If two functions do similar things, maybe one function should be calling the other. If two classes have methods that do similar things, perhaps there should be a third class that does that thing, that the other two classes could then use.

Think about the structure of your algorithms. There are situations where goto is useful, but they are rare. Avoid "flag" variables. They are sometimes useful, but they are often an indication of poorly-structured control flow. They are hard to keep up-to-date, they create code that is hard to read, and they are an abundant source of bugs. Try to avoid deeply-nested control structures (though, obviously, the nature of the problem will, to some extent, dictate a certain minimum amount of needed nesting).

Think about your solution before you write your program, and come up with a good design. We will accept a variety of approaches to most problems, but if your approach is truly bad, even if it seems to work, we will deduct marks.


[an error occurred while processing this directive]

Valid XHTML 1.0 Strict