CS326 Style Guide

Table Of Contents

Overview

Coding style is an important part of good software engineering practice. The goal is to write code that is clear and easy to understand, reducing the effort required to make future extensions or modifications.

In CS 326 we do not specify a detailed coding style that you must follow. However, we expect your code to be clear and easy to understand. This handout provides overall guidelines within which you should develop your own effective coding style.

Many other style guides are available. For example, you can see Apple’s design guidelines, Oracle’s Code Conventions for the Java Programming Language, or Google’s Style Guides for various languages. We do not require you to follow those guidelines slavishly — they are just one way to write your code in a comprehensible fashion — but you might consider them while developing your own style. Even more valuable than coding style guides are descriptions of good ways to design and write code. For Java programmers, Josh Bloch's book Effective Java is an excellent choice. Don’t let the name deceive you: much of that book carries over to all programming languages, including Swift.

Descriptive Names

Names for modules, types, variables, and branch labels should document their meaning and/or use. This does not mean that names need to be very long. For example, names like i and j are fine for indices in short loops, since programmers understand the meanings and uses of these variables by convention.

You should follow the standard Swift convention of capitalizing names of classes and modules, but starting method, property, variables with a lower case letter. Constants and enumeration value follow the same rules as properties. The Apple’s design guidelines provides some common naming conventions that you may want to use when naming your classes, methods, etc.

Consistent Indentation and Spacing

Indenting code consistently makes it easy to see where if statements and while loops end, etc. You should choose consistent strategies; for example, be consistent about whether you put the open curly brace on the same line as the if or on the next line, or what your if-let blocks look like. Examine the staff-supplied code, and the code in the resources, for sample styles; feel free to develop your own if it makes you more comfortable.

In XCode, Ctrl-I will indent your code, according to its built-in indentation rules. As a general rule, I frequently do Command-A followed by Ctrl-I to select all code and then format it.

Consistent (and adequate) horizontal spacing also helps the reader. There is no reason to try to save a column or two by eliminating spaces. You should leave a space after the comma that separates method arguments. You should leave a space between for, if, or while and the following open parenthesis; otherwise, the statement looks too much like a method call, which is confusing. In general, you should place only one statement on each line. Remember that people reading your code may have a monitor of a different width, or may choose to print your code for a code review (the TAs may do this!). 80 columns is a commonly-accepted width in industry, and we require this for the convenience of the TAs when marking up your printouts. A longer line once in a while is acceptable, but not as a general rule. When you break lines, do so at logical, not arbitrary, locations. (You can turn on an 80-column guide marker in XCode’s Preferences under Text Editing…)

Code files should never contain tab characters. They format differently in different IDEs, when printed, etc. A decent IDE should not insert tab characters in code files, or at least should have a setting that uses spaces instead. XCode by default does not use tabs.

Local variables

Local variables should have the smallest possible scope. Don’t declare it at the beginning of the method and then have a lot of unrelated code intervening between its declaration and its first use. As another example, if you have a variable that is used within each loop iteration, declare it inside the loop to make clear to readers that there are no loop-carried dependencies.

Also see Effective Java item #45.

Types

Inferred Types

You may let Swift infer the types of local variables in most cases, but feel free to add explicit type declarations whenever it improves readability. (And remember: XCode will most likely be able to tell you the type of a name whenever you hover the mouse over it.)

Declared Types

There are multiple ways to write some types. For example, Array<Int> and [Int] both represent an array of integers. In general, we’ll adopt the following forms for consistency:

  • [T] for an array of type T
  • [K: V] for a dictionary of type K to type V (as opposed to Dictionary<K,V>)
  • Set<T> for a set of type T

Type Casts

As a general rule, you should never have type casts in code you write (especially for CS 326). Casts are a work-around that hides information from the type system and prevents the compiler from flagging real bugs in your code.

However, there are some specific instances in which casting will be needed, primarily when accessing objects within the UIKit application framework. Use casts only as a last resort, and document why they are used in every case. If the cast will always succeed, use the as! cast operator and indicate what. If the cast may possibly fail, use the as? operator, document the conditions under which it may fail, and gracefully handle the case when it does.

Informative Comments

All code should be commented properly, but don’t make the mistake of writing comments everywhere — a bad or useless comment is worse than no comment. If information is obvious from the code, adding a comment merely creates extra work for the reader. For example, this is a useless comment that would only help someone who does not know the programming language:

i++;    // increment

Good comments add information to the code in a concise and clear way. For example, comments are informative if they:

  • Enable the reader to avoid reading some code. The following comment saves the reader the effort of figuring out the effect of some complicated formulas, and states the programmer’s intention so the formulas can be checked later on.

    // Compute the standard deviation of list elements that are
    // less than the cutoff value.
    for element in list {
        ...
    }

    An important application of this type of comment is to document the arguments and return values of methods so clients need not read the implementation to understand how to use the method.

  • Explain an obscure step or algorithm. This is especially important when the effects of some step are not immediately obvious in the code itself. You should explain tricky algorithms, operations with side effects, magic numbers in the code, etc.

    // Signal that a new transfer request is complete and ready
    // to process.  The manager thread will begin the disk transfer
    // the next time it wakes up and notices that this variable has changed.
    buffer_manager.active_requests = buffer_manager.active_requests + 1;
  • Record assumptions. Under what assumptions does a piece of code work properly?

    // The buffer contains at least one character.
    // (If the buffer is empty, the interrupt handler returns without
    // invoking this function.)
    c = buffer.nextCharacter();
  • Record limitations and incomplete code. Frequently, the first version of code is incomplete; it is important to record which code is known to be incorrect. If you run out of time on an assignment and turn in a program that does not function correctly on all inputs, we will expect your code to show that you understand its limitations.

    if (n > 0) {
        average = sum / n;
    } else {
        // FIXME: Need to use decayed average from previous iteration.
        // For now, just use an arbitrary value.
        average = 15;
    }

Hints:

  • Don’t write code first and then comment it — comment it as you go along. It is easier to comment it while you are thinking about it and still remember its details, and you are unlikely to go back and do it later. In fact, it’s best to comment the code before you write it — doing so may expose weaknesses in your ideas and save you time on the implementation.
  • We do not require you to write comments on every program piece. However, your grade depends substantially on the clarity of your code, and some piece of the program that seems clear to you may not be clear to the reader. Therefore, we strongly recommend that you add explanatory comments to all classes, properties, and methods, especially public ones — it will likely be to your advantage to do so.

SwiftDoc Comments

Every class, every interface, every public method and property, and every nontrivial non-public method and field, should have an explanatory SwiftDoc comment. The format and style of these comments can be found in Apple’s Markup Formatting Reference

SwiftDocs are useful even on non-public members, for two reasons. First, SwiftDoc has a special option that causes it to output documentation for all members, including private ones. You would never supply this to your clients, but it sure is helpful for the development team to have this handy. Second, an IDE such as XCode displays the SwiftDoc for a member when you hover over a use, and this is as useful for private and public members.

Comments should be grammatical English. If more than a few words, a comment should consist of complete sentences that start with a capital letter and end with a period.

CowGreeter.swift from Lab 1 has several SwiftDoc comments: at the top of the file, before the class declaration, before the greeting field, and before each method. You can tell that these are SwiftDoc comments because of where they appear in the code and because they start with /** instead of /*.

It is important to use this syntax to document your code so that your comments will appear properly within XCode and in the HTML documentation generated by the Jazzy SwiftDoc generation tool. There are a number of SwiftDoc Tags that get formatted in a special way when the HTML documentation is generated. You can identify these tags because they start with the - sign, such as -Parameter and -Returns.

For CSE 326, we will use a few additional tags that are not standard. We’ll cover how to use these elsewhere.

When someone else (such as your TA) is trying to understand your code, he or she will often first look at the generated SwiftDoc to figure out what it does. Thus, it is important that you check the generated HTML yourself to ensure that it clearly and accurately communicates the contracts of your code.

TODO, MARK, and FIXME Comments

XCode handles a few types of comments specially. For example, if you want to leave yourself a note about a piece of code that you need to fix, preface the comment with TODO:

// FIXME: Arrays with negative numbers are not handled properly yet.

You can also leave general notes about tasks that remain:

// TODO: Add support for scaling and shifting the view.

These special comments appear help you find what still needs to be done, and also appear in XCode’s Jump Bar (the bar above the editor area, where you might see something to the effect of “Project Name > Classes > Filename > No Selection.” Click on “No Selection” to see all declarations and special comments in the current file. You can also group declarations in titled sections in the list that pops up in the Jump Bar by inserting comments like:

// MARK: - Control Logic

Use these features to help organize your code.

Commenting Out Code

Sometimes, you want to temporarily or permanently comment out some code.

Swift has two ways to write comments: /*...*/ comments out everything between the comment delimiters, and // starts a comment that ends at the end of the line. You should use // to comment out code. Reserve /*...*/ comments for SwiftDoc comments, in which case the opening tag should have an additional asterisk: /**.

In Swift, you can comment out a block of code by highlighting the region and pressing Ctrl+/. Pressing the combination a second time uncomments the code.

The rest of this section explains why you should use line comments such as // instead of block comments such as /*...*/: because block comments do not nest. For example, if you already did:

let a = "Purple "
let b = "Cow"
/* let b = "CS 326" */
let c = "Moo."
let d = 42

But then you wanted to comment out the creation of variables b and c using a block comment, you would have:

let a = "Purple "
/* let b = "Cow"
/* let b = "CS 326" */
let c = "Moo." */
let d = 42

Notice that this failed to comment out the statement where c is created because the comment ends on the previous line. Also, this code will no longer compile because there is a */ dangling by itself after the definition of c. This may seem easy to fix now, but if you have commented a large block of code, it may be a pain to find the nested block comment that is causing the compilation error. You can avoid this mess entirely by using the // comment:

let a = "Purple "
// let b = "Cow"
// // let b = "CS 326"
// let c = "Moo." 
let d = 42

This also makes it easier to uncomment smaller blocks of commented regions.

Documenting Code

Specification-level Comments

Abstract data types. Every abstract data type (class, struct, or protocol) should have:

  1. An overview section that gives a one or two line explanation of what objects of the type represent and whether they are mutable.
  2. A list of specification fields. There might be only one; for example, a set may have the field elems representing the set of elements. Each field should have a name, a type, and a short explanation. You may find it useful to define extra derived fields that make it easier to write the specifications of methods; for each of these, you should indicate that it is derived and say how it is obtained from the other fields. There may be specification invariants that constrain the possible values of the specification fields; if so, you should specify them.

Method Specifications. All public methods of classes should have specifications; tricky private methods should also be specified. Method specifications should follow the requires, modifies, throws, effects, returns structure described in the course material on specifications.

Implementation-level Comments

Implementation notes. Class and struct comments should include the following elements:

  1. An abstraction function that defines each specification field in terms of the representation fields. Abstraction functions are only required for classes and structs that are abstract data types, and not for classes like a GUI component.
  2. A representation invariant. RIs are required for any class that has a representation. We strongly recommend that you test invariants in a checkRep() method where feasible.
  3. For classes and structs with complex representations, a note explaining the choice of representation (also called the representation rationale): what tradeoffs were made and what alternatives were considered and rejected (and why).

Runtime assertions. These should be used judiciously, as explained in lecture. For a longer discussion of the how runtime assertions can improve the quality of your code, see Writing Solid Code by Steve Maguire, Microsoft Press, 1995.