Introduction¶
This is a style guide for CSE 123 and contains a list of guidelines we expect you to follow when writing code. This document covers the entire quarter, so it is not meant to be something that you memorize and understand entirely through one read-through. Rather, it is a reference guide that you will be able to use to look up rules and examples (hint: Ctrl/Cmd-F is your friend here!). Using this in combination with the the spec is a great way to be careful with your internal code details.
Note that this guide is not 100% comprehensive. It cannot replace feedback your TAs give or specific instructions from the spec. This guide lists the general rules to follow, but your TAs will be able to give you specific feedback on the code you write and how they fit into issues of style.
Motivation (and what’s in these boxes?)
Here is a summary of why code quality matters:
-
Programming is a collaborative activity, so it is important to know how to write code easily understandable by other humans.
-
Many programmers will distrust and judge you if you routinely churn out unreadable and ugly code.
-
Writing clean code reduces the chances that our code is buggy.
-
Pragmatically, many people in industry and in open source projects will conduct code reviews on your code and will and block changes that do not match their internal standards. The way we grade assessments mimics this industry best practice.
Many people believe that when writing code, the only thing that matters is if your code is working or not. This is false—while it’s important to get your code working, it is just as important to make sure your code is understandable to other people.
Programming is a highly collaborative activity—it’s fairly common to have to work together with other people, sometimes even hundreds of them at a time! As a result, it’s very important to write code that is easy to work with to allow others to integrate seamlessly with your work. Another benefit is that since clean code is easier to understand, you’ll work much faster and introduce fewer bugs. While you will not work in groups on assignments in CSE 123 unless otherwise specified, it’s still important to practice programming with good style—it’s the core difference between whether a program is pleasant or impossible to work with.
In general, focusing on writing clean code forces you to acquire an eye for detail. Programming, and writing correct (externally and internally) programs requires careful attention, so it’s better to acquire that habit sooner, rather then later. After all, code is now a part of everyday life—we use code to control cars, control medical equipment, moderate our infrastructure, perform legal analysis, hold private data, and so forth. It’s possible you’ll go on to help create the software that ends up becoming part of others’ lives. If that’s the case, we have a responsibility to write as clean and well-designed code as possible to minimize the chances of bugs and accidents that might sneak in due to carelessness. As a result, the sooner you can get into the habit of caring about detail and correctness, the better.
Note that there is not just one way to write code. A code quality guide outlines a common set of standards that a community of programmers would like everyone to use. Different communities may use completely different rules than one another, and that is totally fine! Having a common set of standards for code quality allows for those in the community to better edit and read that code. So just because there is something in our code quality guide doesn’t make it the “one right way to write” something. But you will still need to follow our guidelines, just like if you were to go work at a tech company, you would have to follow theirs.
Naming/Formatting¶
Line length¶
No line should exceed 100 characters in length. A line of code that would otherwise exceed 100 characters should be divided into multiple lines and indented following line-wrapping indentation, as described below.
Indentation¶
Code blocks between curly braces should move to the next indent level. Each time a curly brace is opened, move to the next line and increase the indent level. When reaching the end of the code block, the closing curly brace should occur on the next line and the indent should return to the previous indent level. The indent level applies to both code and comments throughout the block defined by the curly braces. Keep the number of spaces between each indent level consistent!
if (condition) {
// Code block on increased indent level
...
}
// Return to previous indent level
...
If dividing a line of code into multiple lines (line-wrapping), each line after the first should be indented at least one level.
// The continuation lines should be indented at least 1 level.
public static void method1(String param1, String param2, String param3,
String param4, String param5) {
System.out.println("This is a really really really really really long " +
"print statement.");
}
// The second line of the method header is now indented properly
// (by 2 levels).
public static void method1(String param1, String param2, String param3,
String param4, String param5) {
...
}
// Continuation lines can also be indented more than 1 level.
public static void method1(String param1, String param2, String param3,
String param4, String param5) {
...
}
// Second line of the print statement is now indented 2 levels (but
// could also be indented more).
System.out.println("This is a really really really really really long " +
"print statement.");
Casing¶
- Local variables, methods, and fields should be
camelCase(each word after the first begins with capitalization). - Class names should be
UpperCamelCase(each word begins with capitalization). - Class constants (variables declared with
public static final) should beSCREAMING_CASE(each word uppercase and separated by underscores).
public class MyClassName {
private int myFieldName;
public static final int MY_CLASS_CONSTANT = 1;
public void myMethodName(int myParamName) {
int myLocalVariable = 1;
...
}
}
Descriptiveness¶
Use descriptive names that will help out another reader of your code. The name and type of a variable or method should be sufficient to communicate their purpose in the program.
For example, suppose we’re trying to name a List<String> that will keep track of the names of each chapter in a book. We want to avoid names that simply restate the type of the variable (e.g. listOfStrings) or are overly vague (e.g. values). Instead, we might use chapterNames: this name gives some context for what the list will store and what it represents at a high-level.
Loop variable names¶
Descriptive names for loop variables are preferred, but i, j, and k are allowed as names for loop variables. If used, they must always be used in order (i then j then k), whether nested or at the same level.
Avoid magic numbers¶
Avoid using “magic numbers” where the meaning of the value is not immediately apparent. Prefer to store the value in a variable or constant to help make code more readable.
Streamline print statements¶
Ensure print statements make the format and structure of the output clear by avoiding unnecessary splits, over-aggressive combinations, or meaningless empty strings.
System.out.print("This is some output!");
System.out.println();
System.out.println("This is some output!");
System.out.println("This is some output!\nThis is more output!");
System.out.println("This is some output!");
System.out.println("This is more output!");
System.out.println("");
System.out.println();
One statement per line¶
Limit each line of code to one statement unless structurally necessary (e.g. in a for loop). In most cases, a semi-colon should end a line.
Use of Concepts¶
Use wildcard import statements¶
Use wildcard import statements instead of specific imports. e.g., use statements like import java.util.*; over import java.util.Scanner;
Utilize functional decomposition¶
Split significant and/or repeated tasks into methods to keep programs easily readable and comprehensible. The main method should be a clear, concise, high-level summary of the program, with details found in other methods called from main.
Consider the example program below. Its functionality can be summarized as simulating a walk from an initial position, prompting the user to move forward or backward until they reach position 0. Refer to the comments in the code to get an idea of how this program was functionally decomposed!
public class Walker {
// The main method reflects the summary of the entire
// program and calls the moveForward and moveBackward
// methods to execute those actions.
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
System.out.print("Initial position? ");
int pos = console.nextInt();
while (pos > 0) {
System.out.print("(F)orward or (B)ackward? ");
String choice = console.next();
if (choice.equals("F")) {
pos = moveForward(console, pos);
} else {
pos = moveBackward(console, pos);
}
}
System.out.println("Reached position 0.");
}
public static int moveForward(Scanner console, int pos) {
System.out.print("How many steps forward? ");
// Calling the getNumSteps method, just like moveBackward
// does.
int steps = getNumSteps(console);
int newPos = pos + steps;
System.out.println("You are taking " + steps + " steps forward to " +
"position " + newPos + ".");
return newPos;
}
public static int moveBackward(Scanner console, int pos) {
System.out.print("How many steps backward? ");
int steps = getNumSteps(console);
// Calling the getNumSteps method, just like moveForward
// does.
int newPos = pos - steps;
System.out.println("You are taking " + steps + " steps backward to " +
"position " + newPos + ".");
return newPos;
}
// Both moveForward and moveBackward call this method
// since they share the functionality of re-prompting
// the user for the number of steps until the user
// enters a non-negative number.
public static int getNumSteps(Scanner console) {
int steps = console.nextInt();
while (steps < 0) {
System.out.println("Number of steps must be non-negative!");
System.out.print("Please re-enter the number of steps: ");
steps = console.nextInt();
}
return steps;
}
}
Avoid trivial methods¶
Ensure methods represent a significant task and, in general, consist of at least two meaninful steps. If replacing a method call with the code inside would make the program appear cleaner, or if the code inside is replaceable by any single statement, the method may be trivial.
Avoid unnecessary parameters and fields¶
Avoid unnecessary parameters or fields including ones that are unused or whose values can be easily derived from other parameters or fields.
Avoid redundant code¶
Avoid redundancy whenever possible. This includes in conditionals and loops, methods, and constructors.
- In this example, code that occurs in both branches of the conditional can be moved outside of the conditional.
// Note that some lines of code happen unconditionally.
if (x % 2 == 0) {
System.out.println("The number is " + x + ".");
System.out.println("I love even numbers too.");
System.out.println("See you later!");
} else {
System.out.println("The number is " + x + ".");
System.out.println("I don't like even numbers either.");
System.out.println("See you later!");
}
System.out.println("The number is " + x + ".");
if (x % 2 == 0) {
System.out.println("I love even numbers too.");
} else {
System.out.println("I don't like even numbers either.");
}
System.out.println("See you later!");
- In this example, the behavior of one constructor is a subset of the other.
public MyClass(int x, String s) {
if (x % 2 == 0) {
method1();
} else {
method2();
}
method3(s);
}
public MyClass(int x) {
if (x % 2 == 0) {
method1();
} else {
method2();
}
}
public MyClass(int x, String s) {
this(x);
method3(s);
}
public MyClass(int x) {
if (x % 2 == 0) {
method1();
} else {
method2();
}
}
Use proper conditional structures¶
Use the most appropriate conditional structure to reflect the logic of the program. Avoid empty branches, combinable branches, and illogical structures.
- Empty conditional branches: a conditional branch containing no code within.
if (max < 0) {
// Instead of including an empty branch,
// flip the conditional to have if (max >= 0)
// and no else.
} else {
System.out.println(max);
}
- Combinable conditional code: two or more conditional branches containing exactly the same code.
if (max < result) {
return max;
}
if (max == 0) {
// Instead of having two branches with the same code,
// combine the two conditions with ||.
return max;
}
- Illogical conditional structure: conditional branches that do not clearly reflect code logic.
if (condition) {
System.out.println("hello!");
} else if (!condition) {
// Instead of making an additional check to
// condition, replace the else if with an else
// branch.
System.out.println("goodbye");
}
if (num == 122) {
System.out.println("This is great number");
}
if (num == 123) {
// Instead of making a disconnected if branch,
// attach an else if branch to the first check.
System.out.println("This is a pretty good number");
}
- Unnecessary
elsebranch when throwing exceptions: Use an if statement to throw an exception under certain conditions, then have the normal behavior of the method outside of any attached conditional structure. If the exception is thrown, the method will not continue executing, so theelsebranch is unnecessary.
public static void isPrime(int num) {
if (num < 0) {
throw new IllegalArgumentException();
} else {
// Non-exception code should not be attached to the
// exception branch
int factors = 0;
...
}
}
public static void isPrime(int num) {
if (num < 0) {
throw new IllegalArgumentException();
}
int factors = 0;
...
}
Use proper conditional tests¶
Avoid writing redundant or unnecessary conditions that do not affect the program’s behavior.
- If a condition is guaranteed to be true when a statement is reached, there is no need to test the condition.
if (condition) {
return false;
}
if (!condition) {
...
}
if (condition) {
return false;
}
...
- There is no need to compare
booleanvalues totrueorfalse, you can isntead just use thebooleanvalue as the test itself.
if (condition == true) {
...
}
if (condition) {
...
}
if (condition == false) {
...
}
if (!condition) {
...
}
Do not test a condition and then return a boolean value directly reliant on it - you can just return the boolean value itself.
if (condition) {
return true;
} else {
return false
}
return condition;
Use interface types¶
Declare variables using their interface type rather than the implementation type when applicable.
ArrayList<String> names = new ArrayList<String>();
List<String> names = new ArrayList<String>();
Specify type parameters¶
Include type parameters when dealing with classes like List, Set, etc. that can be initialized to hold different data types. The type must be specified in the <> operator on the left-hand side (when declaring the variable), but the right-hand side (when initializing the variable) can leave the operator empty.
List names = new ArrayList();
List<String> names = new ArrayList<String>();
List<String> names = new ArrayList<>();
Use appropriate types¶
Use the most appropriate type for all variables and data structures to best represent the intent of the code.
For example:
- Rather than an
intthat is only ever set to one fo two values, consider aboolean. - Rather than a
Stringthat only ever contains exactly one character, consider achar. - Rather than a
Listthat is only ever accessed from the front, consider aStack.
Use appropriate access modifiers¶
Declare all fields and methods with an appropriate access modifier to properly encapsulate code. In particular:
- Fields should always be declared
private. - Methods and constructors should be declared
publicif they are expected to be used by a client, andprivateotherwise.
Why would I write a method that clients can’t use?
There are many different reasons why we might want to declare a method to be private. When we make a method private, we prevent clients from calling it in ways that might corrupt our fields or otherwise break the program functionality. Furthermore, private methods reduce and simplify the functionality a client has to deal with since those methods can only be called internally.
Initialize fields appropriately¶
Initialize fields in constructors rather than at declaration, even if the initial value is not depending on constructor parameters.
Prefer local scope¶
Declare variables in as local a scope as possible to keep code well-organized. This includes preferring local variables to fields when the variable is only needed in one method.
public static void printTenScores(Scanner console) {
int nextScore;
for (int i = 1; i <= 10; i++) {
System.out.print("next score? ");
nextScore = console.nextInt();
System.out.println("score " + i + " = " + nextScore);
}
}
public static void printTenScores(Scanner console) {
for (int i = 1; i <= 10; i++) {
System.out.print("next score? ");
int nextScore = console.nextInt();
System.out.println("score " + i + " = " + nextScore);
}
}
Prefer simplicity and avoid unnecessary computation¶
Avoid performing unnecessary “expensive” operations or processing as much as possible. This does not mean that code must be written in the most efficient way possible. However, code should ensure work is not performed until needed and not needlessly duplicated.
- Do not create unnecessary objects
public static void printLongWords(List<String> words) {
List<String> longWords = new ArrayList<String>();
for (String word : words) {
if (word.length() > 5) {
longWords.add(word);
}
}
for (String word : longWords) {
System.out.println(word);
}
}
public static void printLongWords(List<String> words) {
for (String word : words) {
if (word.length() > 5) {
System.out.println(word);
}
}
}
- Do not create objects earlier than needed
// nums is unnecessary if our program enters
// the if case.
int[] nums = new int[5];
if (condition) {
System.out.println("Not using the array here!");
} else {
for (int i = 0; i < nums.length; i++) {
nums[i] = i;
}
}
if (condition) {
System.out.println("Not using the array here!");
} else {
int[] nums = new int[5];
for (int i = 0; i < nums.length; i++) {
nums[i] = i;
}
}
public static void increasingArray(int length) {
// res goes to waste if we throw an
// exception immediately!
int[] res = new int[length];
if (length <= 1) {
throw new IllegalArgumentException();
}
...
}
public static void increasingArray(int length) {
if (length <= 1) {
throw new IllegalArgumentException();
}
int[] res = new int[length];
...
}
- Do not perform computations until needed
public static void method(List<String> words) {
method2(words);
if (words.isEmpty()) {
throw new IllegalArgumentException();
}
}
public static void method(List<String> words) {
if (words.isEmpty()) {
throw new IllegalArgumentException();
}
method2(words);
}
public static String method(List<String> words) {
String result = method2(words);
if (words.size() < 5) {
return words.get(0);
}
return result;
}
public static String method(List<String> words) {
if (words.size() < 5) {
return words.get(0);
}
String result = method2(words);
return result;
}
- Do not repeat complex computations unnecessarily
public static void main(String[] args) {
String s1 = "hello";
System.out.println("Our result is " + method1(s1));
System.out.println("The length is " + method1(s1).length());
}
public static String method1(String s) {
for (int i = 0; i < 1000; i++) {
s += "*";
}
return s;
}
public static void main(String[] args) {
String s1 = "hello";
String res = method1(s1);
System.out.println("Our result is " + res);
System.out.println("The length is " + res.length());
}
public static String method1(String s) {
for (int i = 0; i < 1000; i++) {
s += "*";
}
return s;
}
Commenting/Documentation¶
See the Commenting Guide.
Forbidden Features¶
The following features should NEVER be used in any graded CSE 123 work (unless otherwise specified by the assignment or problem):
- Global variables (i.e. non-
finalstatic fields) breakinside of a loop or conditionalcontinuetry/catch- the
System.exitmethod - the
vartype identifier and local variable type inference - lambdas, streams, and method references
- package declarations
System.console
Acknowledgements¶
Thanks to:
- Ava Doan
- Cady Xia
- Colin Lim
- Connor Sun
- Dani Ballisteros
- Diya Yaga
- Elba Garza
- Hanna Pan
- Ivy Wang
- Nicole Ham
- Rohan Boinpally
- Wesley Li
- Yang Wu
and the rest of the CSE 12X team, who all provided feedback that shaped this guide!
Finally, thanks to all the students reading. Surely you’ll now contribute to writing stylish code that exists in the world.
Note: If you find any typos or otherwise have suggestions or clarifications, feel free to email the head TA to suggest a fix (and optionally have your name listed in the acknowledgements).