Style
Some Style Facts:
- Good style is...
- Both an art and a science.
- Open to some debate, yet mostly agreed upon.
- The product of years of suffering due to bad style.
- Good style reduces time spent debugging, and...
- Around 75% of a developer's time is spent debugging (source)
- Fixing a bug takes 30 times longer than writing a line of code (source)
- Good style reduces errors, and...
- Software errors cost just the US economy over $2 trillion annually in 2020! (source)
- Software errors have resulted in many spectacular failures (source)
- Some Style Guides (which are not OUR style guide):
15-112 Style Overview
See below for some of the most important high-level rules to keep in mind while writing your code. If you adhere to these, you will generally avoid losing any style points.
Within a few hours of the Saturday homework deadline, we will run our style grader and provide you with a style score and a list of violations (if any). You may earn back any lost style points by (thoughtfully) modifying your code to address the errors before we re-run the script after Monday at 8pm.
- Clarity Rules
- Ownership
Except for work submitted in CS Academy, you must include your name, andrewId, and section in a comment at the top of every file you submit.
- Comments
You should write concise, clear, and informative comments that
make your code even easier to understand.
- Comments should be included with any piece of code that is not self-documenting.
- Comments should not be written where they are not needed.
- Helper Functions (Top-Down Design)
You should use top-down design to break down large programs into helper functions where appropriate.
- This also means that no function should become too long (and therefore unclear). You should generally
avoid writing functions longer than about 30 lines.
- Exceptions: blank lines and comments do not count towards this line limit.
- Variable Names
Use meaningful variable and function names (whenever possible).
- Variables and functions should be written in the camelCase format. In this format, the first letter is in lowercase, and all
following words are uppercased (eg: tetrisPiece). Do not use snake_case (eg: tetris_piece).
- For example, some good, meaningful variable names for a graphics problem with a flag might include numStars, numStripes,
stripeColor, or stripeWidth.
- On the other hand, it can be hard to know what variables named a, b, and c do, especially if you haven't written any comments
either!
- Short variable names are ok if it is relatively obvious what
they do. For example, cx and cy are commonly use for the center
point of a circle, and r is probably the circle's radius.
- Variable names should not overwrite built-in function names; for example, str is a bad name for a string variable.
- Common built-in keywords to avoid include dict, dir, id, input, int, len, list, map, max, min, next, object, set, str, sum, and type.
- Exceptions: i/j for index/iterator, c for character, s for string, and n/x/y for number.
- Unused Code
Your code should not include any dead code (code that will never be executed).
- Additionally, all debugging code should be removed once your program is complete, even if it has been commented out.
- Formatting
Your code formatting should make your code readable. This includes:
- Not exceeding 80 characters in any one line (including comments!).
- Pro tip: put long expressions in parentheses, because
parenthesized expressions can include newlines.
- Indenting consistently. Use spaces, not tabs, with 4 spaces per indent level (most editors let you map tabs to spaces automatically).
- Using consistent whitespace throughout your code.
- Robustness Rules
- Test Functions
You should always include test functions for each function that is reasonably testable.
- We will not
require that you add test cases to any test
functions that we have supplied for you in the starter file.
However, you should still write test functions for your own
(non-graphical, non-interactive)
helper functions
- Exceptions: you do not need to test interactive, random, graphics, data initialization, or event functions.
- Efficiency
In general, your code should be reasonably efficient.
- As this is an introductory course, we do not expect you to write the most efficient solutions generally. However, your code may
not be "grossly" inefficient, especially if you had simple, clear, and reasonably efficient options to choose from. By this, we mean
that most of your functions (with a few exceptions later in the course) should run in under 5 seconds.
- Repetitive Code
In general, you should not have repetitive code.
- One example of this is duplicate code, code that has been copy-pasted. This should instead be put into a helper function.
- Another example is "numbered"-code, where there are multiple variables like foo0, foo1, foo2. These should be represented by a
loop or list instead.
- Magic Numbers
Your code should not include magic numbers.
- A magic number is a number used outside of a variable assignment in code that does not have an immediate and clear meaning.
- In general, every number besides -1, 0, 0.5, 1, 2, and 10 is magic.
- If you must use a magic number, store the number or an expression using the number in a variable, then use that variable.
- Global Variables
Global variables can be useful, but they can also be dangerous,
and often lead to complicated bugs. Avoid global variables.
- Other Rules
- Prohibited tokens/names
In general, you should only use concepts from the 15-112 notes and lectures in your assignments.
- Certain modules, functions, data types, etc. may bypass the learning objectives of the course. You should avoid using these
until if and when we cover them in the notes.
- Note that some things will never be allowed in 112 (for example, eval). Typically CS Academy will give you an error for
prohibited concepts, but the style grading script may catch additional ones.
- The list of Python tokens and syntax that we do not teach is too long to put here, so you should instead just be aware of what we
have taught, and avoid using anything we have not. If the grading script deducts points due to using a prohibited token or name,
don't panic; just rewrite the function in a way that does not use it and resubmit it along with your other style corrections. If you
find it difficult to do so, this may be an indication that you are relying too much on external help or resources like AI.
- Clarity / Best Practice violations
Many of the error codes listed below are in place because they either hinder your ability to write clear and robust code. Note that AI
tools and certain code you find online may technically pass the test cases but will likely violate one or more of these.
- Broken or misformatted code
If your code has any syntax errors or cannot be analyzed by our style grader, you might not receive any style feedback except for one
or more codes indicating major errors.
- Without this feedback, it will be difficult to correct your style and resubmit, so be sure to correct all syntax errors before
the grading deadlines!
- If your functions appear to be passing all test cases and you are receiving error codes like K201, K202, or K203, please see the
course faculty so that we can investigate what's wrong.
Style Violation Codes
See below for a list of the error codes that the style grading script will enforce. We do
not expect you to memorize this list, and
if you mindfully follow the rules above, few (if any) of these will apply to your submissions, especially if your code is passing the test
cases. Note that no script can catch
all style violations, so we may also perform manual style grading from time to time.
Aside from the 15-112-specific codes, these rules are applied from a few commonly-used style enforcement tools (flake8 and pycodestyle,
linked below) so if you receive a style error and you are unsure what it means, try googling it. If you still do not understand, please
post the relevant parts of your submission and the style error to Ed.
- 15-112 rules (see above):
K101: 'Line width is >80 characters'
K102: 'You may not use top-level code that is not import, def, or class.'
K103: 'Disallowed token. You may not use this: {the piece of banned syntax you used}'
K104: 'Do not use builtin "round" in Python 3. Use CS Academy "rounded" instead.'
K111: 'Disallowed variable name: {the banned variable name you used}'
K112: 'Do not begin variable names with uppercase letters: {the banned variable name you used}'
K113: 'Disallowed underscore in variable name: {the banned variable name you used}'
K121: 'Disallowed function name: {the banned function name you used}'
K122: 'Do not begin function names with uppercase letters: {the banned function name you used}'
K123: 'Disallowed underscore in function name: {the banned function name you used}'
K131: 'Disallowed class name: {the banned class name you used}'
K132: 'Do not begin class names with lowercase letters: {the banned class name you used}'
K133: 'Disallowed underscore in class name: {the banned class name you used}'
K201: 'Unexpected style grading error. See the course faculty for assistance.'
K202: 'Error when trying to read file. See the course faculty for assistance.'
K203: 'Unexpected style grading error. See the course faculty for assistance.'
- pycodestyle (see here):
E101: 'Indentation contains mixed spaces and tabs'
E111: 'Indentation is not a multiple of four'
E112: 'Expected an indented block'
E113: 'Unexpected indentation'
E117: 'Over-indented'
E223: 'Tab before operator'
E224: 'Tab after operator'
E242: "Tab after ''"
E304: 'Blank lines found after function decorator'
E402: 'Module level import not at top of file'
E502: 'The backslash is redundant between brackets'
E702: 'Multiple statements on one line (semicolon)'
E703: 'Statement ends with a semicolon'
E704: 'Multiple statements on one line (def)'
E713: "Test for membership should be 'not in'"
E714: "Test for object identity should be 'is not'"
E721: "Do not compare types, use 'isinstance()'"
E731: 'Do not assign a lambda expression, use a def'
E741: "Do not use variables named 'I', 'O', or 'l'"
E742: "Do not define classes named 'I', 'O', or 'l'"
E743: "Do not define functions named 'I', 'O', or 'l'"
E901: 'SyntaxError or IndentationError'
E902: 'IOError'
E999: 'SyntaxError'
W191: 'Indentation contains tabs'
W503: 'Line break occurred before a binary operator'
W504: 'Line break occurred after a binary operator'
W601: ".has_key() is deprecated, use 'in'"
W602: 'Deprecated form of raising exception'
W603: "'<>' is deprecated, use '!='"
W604: "Backticks are deprecated, use 'repr()'"
W605: "Invalid escape sequence 'x'"
- flake8 (see here):
F401: 'module imported but unused'
F402: 'import module from line N shadowed by loop variable'
F404: 'future import(s) name after other statements'
F406: '"from module import *" only allowed at module level'
F407: 'an undefined __future__ feature name was imported'
F501: 'invalid % format literal'
F502: '% format expected mapping but got sequence'
F503: '% format expected sequence but got mapping'
F504: '% format unused named arguments'
F505: '% format missing named arguments'
F506: '% format mixed positional and named arguments'
F507: '% format mismatch of placeholder and argument count'
F508: '% format with * specifier requires a sequence'
F509: '% format with unsupported format character'
F521: '.format(...) invalid format string'
F522: '.format(...) unused named arguments'
F523: '.format(...) unused positional arguments'
F524: '.format(...) missing argument'
F525: '.format(...) mixing automatic and manual numbering'
F541: 'f-string without any placeholders'
F601: 'dictionary key name repeated with different values'
F602: 'dictionary key variable name repeated with different values'
F621: 'too many expressions in an assignment with star-unpacking'
F622: 'two or more starred expressions in an assignment (a, *b, *c = d)'
F631: 'assertion test is a tuple, which is always True'
F632: 'use ==/!= to compare str, bytes, and int literals'
F633: 'use of >> is invalid with print function'
F634: 'if test is a tuple, which is always True'
F701: 'a break statement outside of a while or for loop'
F702: 'a continue statement outside of a while or for loop'
F704: 'a yield or yield from statement outside of a function'
F706: 'a return statement outside of a function/method'
F707: 'an except: block as not the last exception handler'
F721: 'syntax error in doctest'
F722: 'syntax error in forward annotation'
F723: 'syntax error in type comment'
F811: 'redefinition of unused name from line N'
F821: 'undefined name name'
F822: 'undefined name {name} in __all__'
F823: 'local variable name {name} referenced before assignment'
F831: 'duplicate argument name in function definition'
F841: 'local variable name is assigned to but never used'
F901: 'raise NotImplemented should be raise NotImplementedError'
C901: 'Function is too complex'
S001: 'invalid escape sequence'