I've been in this phase in my career where I am doing all it takes to become a better software engineer. A few books I’ve read recently The Practice of Programming and Clean Code have made me reevaluate some of the bad programming habits that I have.
The bottom line is for those who are in the TL;DR mode is:
Software Engineers who develop software with clean code are those who care about their craft.
Put it this way: If a Software Engineer is being mindful about how they are coding for their current project, they are putting thought and effort into creating something that is correct, maintainable and as clean as possible. Someone who doesn't care wouldn't put that much energy into something like that. Makes sense, right?
Here are some few tips.
No magic numbers.
A magic number is basically a number that symbolizes some sort of entity or state. In mathematical terms, 3.1415 can be a magic number when it can be defined as PI
.
final double PI = 3.1415;
Sqrt(-1) is another one when it can just defined as constant IMAGINARY.
My recent TicTacToe project had a magic number in it. 9. What's 9? It's actually the maximum amount of moves we can make in a game. It would have been better to actually define 9 as a constant. Something like MAX_NUMBER_TURNS in order to make that distinguishable.
final int MAX_NUMBER_TURNS = 9;
Data Structures are for... Data
I used to be lazy and combine data structures and methods that operate on them into a single class. This is not great practice. Data structures should be used for abstracting a record, data or structure.
Take for example a linked list implementation. Something not-so-great would be where we have a Node
implementation and methods which operate on the list as a whole in the same implementation:
public class Node {
public int key;
public Node next;
}
public class LinkedList {
private Node head;
public void add(int x) {}
public bool contains(int x) {}
}
It would be better if we actually separated the two into a Node
class and a LinkedList
class where the latter makes use of the former.
public class Node {
public int key;
public Node next;
}
public class LinkedList {
public void add(Node head, Node x) {}
public bool contains(Node head, Node x) {}
}
public class Driver {
public static void main(String[] args) {
Node head = new Node();
head.key = 1;
Node n = new Node();
n.key = 2;
LinkedList list = new LinkedList();
list.add(head, n);
}
}
Keep the number of arguments to a function or method as minimal as possible. Ideally, zero to two arguments is good. Three is okay some of the time. At a certain point when the number of arguments for a method increases, the harder it is to recall the usage from a programmer's memory. As a result, it forces one to unnecessarily sift through API documentation to figure out what data members corresponds to what.
Personally, another thing I found from my personal experience is that a method which has too many parameters ends up being more difficult to implement. For myself, it is a warning sign of an overcomplicated algorithm, and that it should be broken up into separate methods.
Of course, nowadays we have IDEs that autocomplete such things -- but keep this in mind: The more arguments you have, the more likely your method is going to be doing more than one thing. The mindset should probably be to have your method perform a single task, and have it perform well.
If we get to the point where we need to pass in multiple arguments to a method, it may be useful to actually group the arguments into a data structure so that they are combined. Take for example:
int calcArea(int x, int y, int radius)
We can express the same method signature by wrapping the x- and y-values to a Point
object.
int calcArea(Point center, int radius)
We achieve the same result with the more concise method above.
I would say be careful though as arguments should only be grouped if it makes sense logically.
Lastly, try to adhere to the Single Responsibility Principle and keep the method as short as possible. Again, have it do one thing, and do it well.
Keep Related Code Grouped Together
This applies to class instance variables and methods. Instance variables should always be declared at the top. Imagine finding a random declaration of a class variable in the middle of an implementation of a class. That is sure to be misleading and turn a few heads!
Methods that access each other quite often should be near each other. Don’t be afraid to rearrange with some cut-and-paste action.
Comment Conservatively
Don't write useless comments. Your code should be self-explanatory. Classes, variables, methods and members should all be short, concise and self-explanatory. Avoid redundancy in comments.
Don't comment just to comment. For example:
int sum = a + b; // calculate the sum of a and b
Yeah, I can definitely see that I am adding a and b to the sum variable. Comments like the above are unnecessary.
Too many comments can create clutter in code. Also, when was the last time we were all on top of keeping our comments up to date?
Also, you know the saying: "If you don’t have anything nice to say, don’t say it."? It applies to comments too.
Comments that detail random standards and historical things aren't particularly useful. Just put the document ID, standard name, etc and be done with it. If something needs to be referenced, just a quick Google search of said standard can be enough.
Indentation
Indentation can make all the difference. Indenting your code properly to the point where it corresponds to the correct nested level makes it more readable.
One thing to note is that if you find yourself making multiple indentations due to nesting, it may be worth seeing if some of the conditionals and loops causing these indentations can be refactored into smaller methods.
Prefer Exception Handling than Error Codes
Favor error handling through exceptions rather than returning error codes. It is easier to add a new exception type usually by creating a new type of Exception class.
When handling exceptions, treat executing code as a transaction. When an exception has been caught and handled, your program should still function in a consistent manner.
Conclusion
I am still learning more each and everyday. The most important thing is to not know about good practices, but to actually be able to apply them mindfully in everyday development.