Laboratory Rubric

CSC 213 - Operating Systems and Parallel Algorithms - Weinman



Summary:
Information on how each laboratory exercise involving programing will be graded.

Overview

This course features weekly laboratory exercises, as listed on the course schedule. While each exercise will have its own rubric based on the elements of the assignment, all programming-based laboratories will feature several rubric elements in common. Each of these items are measured on the College's four point scale:
Excellent (Always)
Four points; A. Demonstrates the criterion either without fail or a minor indiscretion; a complete or superlative exemplar.
Good (Usually)
Three points; B. Misses one notable or two lesser instances of the criterion yet still demonstrates sound grasp of the practice.
Satisfactory (Sometimes)
Two points; C. Features at least two positive demonstrations of the criterion or misses several important opportunities.
Passing (Rarely)
One point; D. May have token, incomplete, or incorrect demonstrations of the criterion.
Failing (Never)
Zero points; F. Completely overlooks the criterion.
Some of the criteria below may seem artificial and superfluous in a context where you are writing relatively small programs that an instructor has largely specified for you. You might even sensibly argue that, in following the composition principle of "writing for the reader," it is is petty or excessive to deduct for omissions that can easily be understood by the instructor. However, that argument overlooks the larger purpose, which is to develop good habits that translate to practice in systems of much larger scale. The importance of these elements increases exponentially with program complexity, and we hope to enhance your ability to "write for the reader" in that context as well.
In summary, these metrics represent important skills in the broader world of software development and maintenance. We measure them to help ensure you learn their primary characteristics.

Contents

1  Structure
    1.1  Program factored into appropriate functions
    1.2  Functions have appropriate length and complexity
    1.3  Clear function, parameter, and variable names used
    1.4  Program is easy to read and understand
2  Comments
    2.1  Files and functions begin with appropriate comments
    2.2  Comments describe main computations
3  Verification
    3.1  Programs verify user input
    3.2  Programs check success of system/library calls
    3.3  Programs handle errors appropriately
    3.4  Transcripts test and demonstrate functionality

1  Structure

1.1  Program factored into appropriate functions

Strunk and White emphasize that the paragraph is the unit of composition in writing, "one paragraph to each topic" [SW]. Functions are a basic unit of programs, so: one (abstracted) task to each function.
Maintainable code is well designed. Good design means using appropriate levels of abstraction that allow a decomposition into functions that accomplish singular tasks comprehensibly. Moreover, functionality factored into separate functions makes it easier to reuse, avoiding redundant code. Because they are not repeated, bugs are easier to fix. Because functions perform a singular task, they are easier to unit test.

1.2  Functions have appropriate length and complexity

A corollary of factoring means your functions are not too long or overly complex. One guideline is to make an entire function to fit on one "screen" or less (about 50-65 lines). This scale makes interpreting a function's action more readable by forcing you to break it down into named steps that can be seen at once. This criterion also promotes easier debugging and unit testing.

1.3  Clear function, parameter, and variable names used

Speaking of named steps, those names should be sensible to your reader. We also remind you that you need to think beyond the scale of the program due this week. Your reader might be a programmer from another division, but it might also be yourself at a later time. Do not expect to remember what SMRHSHSCRTCH [KP, p. 5] means more than an hour after you write that down.
Like good writing, function names should indicate an action "verb" to promote understanding. An accompanying noun "object" in the name will also help.
In another analogy to writing, functions, parameters, or variables operating in analogous roles should be named analogously, the programmer's version of, "express co-ordinate ideas in similar form" [SW].
Clear parameter names are critical because they function as a bridge between two different scopes of execution. Avoid single letters; parameter names should suggest the role and semantics of each value entering the function. As the system scale grows larger, other programmers may only be familiar with a different corner of the system. Along with documentation, the published interface (parameter names) of your code will be key to understanding how to use it.
Generally variables are values local to a small function (cf 1.1 and 1.2). Yet these names too should clearly indicate their corresponding values. While no code is "self documenting," good names go hand-in-hand with understanding. They are the programmer's version of the writer's injunction to "avoid pronouns with unclear antecedents."
Global names of functions and variables should be descriptive. Because they might occur anywhere in a program, their names need to completely evoke their meaning, regardless of context.
Loop variables representing an array index are often named i; that's not intrinsically bad. In fact, such conventional uses often benefit from short names, where long names would adversely impact brevity. However, nested loop variable names should indicate more about the meaning of each index. For example, r and c for rows and columns in a two-dimensional structure, t for a thread index, etc. When the corresponding array has a clear name, using mnemonic index variables can help you avoid errors.

1.4  Program is easy to read and understand

This criterion may encompass many abstract properties, but we can list a few concretely. Indentation, white space, and style consistency.
First, your program indentation should match the program flow. While most modern programming languages (Python excluded) care not a whit where your code appears because whitespace is irrelevant, the compiler is not the primary audience for your program. Human readers rely on indentation to help understand program flow with respect to functions, conditionals, and loops. Fortunately, your text editor will mostly do this for you automatically.
White space should help the human reader understand your programs at the expression, block, function, and source code level. After all, "white space is free", at least on screen. For grading, your programs will be printed (on paper, where white space is decidedly not free), so we take the comment with a grain of salt, admitting a measure of truth. Help your reader understand your code, but do not add separation beyond what is needed to aid clarity.
Practice a consistent style. When you write code from scratch, you may wish to practice one of the many existing "standard" coding styles. This course will not dictate a specific style, though your employers or open source projects may demand adherence to a particular style. When you are adding to or modifying an existing piece of software, match its style.
Finally, Kernighan and Pike [KP, pp. 6-8] give us a few additional style injunctions We add one further concrete style guideline: Line should be no wider than 80 characters. This makes them easier to read both on screen and on paper. (Note: If running enscript(1) on your program code produces the output "X lines were wrapped," where X is some number, then your lines are wider than 80 characters and you should re-format your code with additional linebreaks.

2  Comments

2.1  Files and functions begin with appropriate comments

Generally, each source code file should name its author(s) and their contact information and give a brief description of the file's contents. This contextualization helps the reader to assess provenance and purpose. See the course laboratory guidelines for specific details.
Documenting before you write a procedure will help you plan and clarify the requirements of your implementation; it may even help you assess whether you need to refactor 1.1. Good programs will comment every function definition, describing and contextualizing the purpose of the program unit in English. Consider writing complete 6P documentation. Excellent programs will include preconditions and postconditions in function comments.

2.2  Comments describe main computations

Kernighan and Pike [KP] remind us:
Comments are meant to help the reader of a program. They do not help by saying things the code already plainly says, or by contradicting the code, or by distracting the reader with elaborate typographical displays. The best comments aid the understanding of a program by briefly pointing out salient details or by providng a larger-scale view of the proceedings. [emphasis added][KP, p. 23]
They also give us the following injunctions:

3  Verification

3.1  Programs verify user input

When functions verify their preconditions, they can immediately isolate the detected violation, making debugging much easier. While not all functions need to do so, programs that receive input from users are less likely to have their input carefully crafted or guarded. Thus, you must enable helpful feedback to a user in the form of input verification. The most common forms of input verification include type (i.e., for numbers given as command line parameter character arrays) and ranges.

3.2  Programs check success of system/library calls

Many programming languages provide robust exception handling mechanisms. Because C is a low-level programming language, one should develop the necessary habit of detecting and handling errors. Often this means simply printing a helpful message (cf. 3.3) and exiting the program.
When programming with the C library and particularly when making operating system calls, you must check the return code value (if one is provided) so that appropriate action can be taken. For example, see errno(3) for the plethora of possible failures.

3.3  Programs handle errors appropriately

Good design accommodates failures. When things go wrong, it is important to deliver the unfortunate news to the right audience. When you write library code (code linked to other programs), the correct approach is usually an appropriate return code (cf. 3.2). After all, your client program likely wants to decide how to deal with your failure, rather than have your library print output to the user that is likely to befuddle them.
When writing programs directly for the end user, the correct approach to an unrecoverable error is usually to print an informative error message to the unbuffered stderr file stream. If the failure is due to a system or library call (cf. 3.2), the standard library functions perror(3) and strerror(3) can assist in providing an even more informative message.
When verifying user input (cf. 3.1), the error message should state the valid form of input [KP, p. 114].

3.4  Transcripts test and demonstrate functionality

Every program you submit must be accompanied by a transcript documenting the program's compilation and execution. Many large scale projects feature automated builds and tests as built-in progress checks. In the absence of these automated tools, we still want to promote the idea that the code you submit has demonstrated behaviors. Moreover, these demonstrations help facilitate expedited grading. (Though you are asked to submit your source code in compilable form to enable manual verification.)
In addition, demonstration runs should span a complete range of possible (valid) inputs, and enough invalid inputs to confirm error handling (cf. 3.3). Occasionally, additional commands (e.g., cat(1), ls(1), diff(1), etc.) may be needed to verify correct behavior.

Acknowledgments

In broad strokes, the contents of this rubric have been influenced by many sources. Many of the criteria are adapted from an earlier version of Henry Walker's Program Style Summary and Checklist. Marge Coahran influenced some of the style considerations and John Stone suggested expressing the learning objective as forming habits. Although cited when possible, many uncited ideas no doubt have been influenced by Kernighan and Pike [KP].

References

[KP]
Brian W. Kernighan and Rob Pike. The Practice of Programming. Addison-Wesley, 1999.
[SW]
William Strunk, Jr. and E.B. White. The Elements of Style. 1959.
Copyright © 2012, 2014 Jerod Weinman.
ccbyncsa.png
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.