Here’s 5 easy steps for you to write better code today


It is my firm belief that the most important aspect of writing code is writing code that’s easy to read, easy to understand, and easy to maintain. Here are 5 very easy things you can start doing today to write clean code.

How many times have you come across a confusing and complex “algorithm” that you have no clue what it’s supposed to be doing? You mumble in your head “who the F*** wrote this piece of S***?!?”

You check the git log only to find out that you actually checked in this module 6 months ago…

Programmer Meme: When it's been 7 hours and you still can't understand your own code

(Hopefully you work in a high-performing team where code reviews are a natural part of your development process and this type of thing never [or at least rarely] occurs)

The ugly truth about software development is that this type of thing happens all too often. A bunch of highly-motivated programmers just crushing code to meet deadlines. We get so wrapped up on one piece at a time that we accept the pre-mature finish line the first time our code works. As Robert “Uncle Bob” Martin says,

Getting software to work and making software clean are two very different activities.

Robert “Uncle Bob” Martin – Clean Code: A Handbook of Agile Software Craftsmanship

Writing clean code is no easy task. Good software engineers write code that works. Great software engineers write code that’s clean.

Below we’ll dive into 5 very simple things you can start implementing NOW to start writing cleaner code. However, if you want to really transform your clean code writing abilities and impress all your co-workers during code review, I’d highly recommend you pick up a copy of Robert Martin’s book on “Clean Code: A Handbook of Agile Software Craftsmanship

Every software engineer must read this book >>

Focus on creating descriptive variable name

This is one of the easiest yet most beneficial things you can do, start using more descriptive and meaningful variable names.

// Code Snippet #1
public void print(int user) {
  if (user >= a) {
    System.out.println("User can drive.");
  }

  if (user >= b) {
    System.out.println("User can vote.");
  }

  if (user >= c) {
    System.out.println("User can drink.");
  }
}

The example above shows an example function with a poor choice of variable names. What are a, b, and c supposed to represent? What is the argument user supposed to indicate?

From the context of reading the whole function, it probably wouldn’t take entirely too long to decipher that this function is checking the user’s age and printing out the user’s capabilities, such as if they can drive, vote, or drink a beer. But the use of poor variable names made us have to read and think about what’s actually happening.

These variable names make us have to think. When reading code, we shouldn’t have to think. We should be striving to make code as readable as possible, as if we are just reading a story.

Before we refactor this code to make it more readable, let’s take this same example to look at a worse example.

// Code Snippet #2
public void print(int user) {
  if (user >= 16) {
    System.out.println("User can drive.");
  }

  if (user >= 18) {
    System.out.println("User can vote.");
  }

  if (user >= 21) {
    System.out.println("User can drink.");
  }
}

What do you think? At the surface, it may appear that replacing the ambiguous named variables of a, b, and c with hard-coded integers may make the code easier to understand. However, this is actually another poor practice called the Magic Number Code Smell.

Avoid using “magic numbers”

Magic numbers are hard-coded numbers inside the code. This is bad for a couple of reasons.

For starters, just like before, it makes the user have to think. Why are we checking for the int 16? Why are we checking for the int 18, or 21? Using a number alone gives no context to its meaning without needing to read the surrounding code to fully understand its intent.

Further more, how many other places is this number used? What happens if we need to change this number, we’d need to find all the hard-coded instances of this number. It becomes very easy to miss, even with a “Find and Replace” feature built into your IDE or text editor. It’s very easy for a 16 to be hidden inside of a larger number elsewhere in the code, and you unintentionally update this instance too!

Instead of magic numbers, you should create a named constant instead. Let’s refactor both examples above by creating named constants with descriptive names.

// Code Snippet #3
static final int MIN_DRIVING_AGE = 16;
static final int MIN_VOTING_AGE = 18;
static final int MIN_DRINKING_AGE = 21;

public void printUserCapabilities(int userAge) {
  if (userAge >= MIN_DRIVING_AGE) {
    System.out.println("User can drive.");
  }

  if (userAge >= MIN_VOTING_AGE) {
    System.out.println("User can vote.");
  }

  if (userAge >= MIN_DRINKING_AGE) {
    System.out.println("User can drink.");
  }
}

The above example has updated the int user variable to int userAge, which gives a lot more context into its intent. In addition to the argument name improvement, it also has a more appropriate function name itself. The function name print was rather ambiguous, but printUserCapabilities helps tell this function’s story a little better.

It also has created named constants with meaningful variable names, which further helps with the readability of this function. It’s getting easier to actually read this function like a story. But there’s still more work we can do, by breaking this function up into more functions!

Focus on creating more functions

I’m a big fan of creating smaller, very targeted functions. In general, I aim to make sure my functions don’t go over 25 lines each. However, 25 lines is getting pretty long. Some people will feel that 25 lines is crazy for a function, and would rather see only 10 lines maximum per function. Others shoot for 5, or even 2-3.

There’s no right answer here. In general, functions should be as long as they need to be. But more often than not, you should be able to accomplish what you need to within 10-20 lines. To achieve this, it helps to break down large functions into smaller functions!

Replace conditionals with functions

If we look at Code Snippet #3 from above, we see that this function is really doing 3 things. It’s printing the user’s capability of being able to drive if the user is of driving age, then same for voting age, and same for drinking age.

This presents a great opportunity to extract out this logic into their own named functions (which will also help with readability).

Exacting conditional logic can help tremendously with readability, especially when there are multiple, complex conditionals within one if statement.

// too many conditionals in one if statement
if (userAge >= 16 && userHasSubmittedApplicationState.equals("submitted") && usersHoursOfDrivingEducation >= 30 && userKnowledgeTestScore > 79 && userRoadTestPoints > 89) {
  ...
}
// conditionals replaced with functions
if (userMetAllDrivingRequirements()) {
  ...
}

private boolean userMetAllDrivingRequirements() {
  return 
    userIsOfDrivingAge() &&
    userHasSubmittedApplication() &&
    userMetMinimumHoursOfDrivingEducation()
    userPassedKnowledgeTest() &&
    userPassedRoadTest();
}

private boolean userIsOfDrivingAge() {
  return userAge >= MIN_DRIVING_AGE; 
}

private boolean userHasSubmittedApplication() {
  return userHasSubmittedApplicationState.equals("submitted");
}

private boolean userMetMinimumHoursOfDrivingEducation() {
  final int MINIMUM_REQUIRED_HOURS = 30;
  return usersHoursOfDrivingEducation >= MINIMUM_REQUIRED_HOURS;
}

private boolean userPassedKnowledgeTest() {
  final int MINIMUM_REQUIRED_SCORE = 80;
  return userKnowledgeTestScore >= MINIMUM_REQUIRED_SCORE;
}

private boolean userPassedRoadTest() {
  final int MINIMUM_REQUIRED_SCORE = 90;
  return userRoadTestPoints >= MINIMUM_REQUIRED_SCORE;
}

Replace comments with functions

Many times, we will see comments in the code explaining the purpose of the code. Let’s look at back at Code Snippet #1 and assume that the developer at least added a comment to help explain it’s intent

// Code Snippet #1.1
public void print(int user) {
  // check if the users age is at least the minimum driving age
  if (user >= a) {
    System.out.println("User can drive.");
  }

  // check if the users age is at least the minimum voting age
  if (user >= b) {
    System.out.println("User can vote.");
  }

  // check if the users age is at least the minimum drinking age
  if (user >= c) {
    System.out.println("User can drink.");
  }
}

Comments like this, while with good intentions, end up just polluting the entire program. Why even go through the trouble of making this long comment when a properly named function would’ve done just as well, if not better.

// Code Snippet #4
static final int MIN_DRIVING_AGE = 16;
static final int MIN_VOTING_AGE = 18;
static final int MIN_DRINKING_AGE = 21;

public void printUserCapabilities(int userAge) {
  if (userIsOfDrivingAge(userAge)) {
    System.out.println("User can drive.");
  }
  
  if (userIsOfVotingAge(userAge)) {
    System.out.println("User can vote.");
  }

  if (userIsOfDrinkingAge(userAge)) {
    System.out.println("User can drink.");
  }
}

private boolean userIsOfDrivingAge(int userAge) {
  return userAge >= MIN_DRIVING_AGE;
}

private boolean userIsOfVotingAge(int userAge) {
  return userAge >= MIN_DRIVING_AGE;
}

private boolean userIsOfDrinkingAge(int userAge) {
  return userAge >= MIN_DRIVING_AGE;
}

By creating these functions, we’re further abstracting the implementation logic from the higher-level printUserCapabilities function, making it that much easier to read it like a story. Further more, the function name is descriptive enough to not even need a comment to explain its intent.

Create functions with less arguments

Here’s a bold statement that I firmly stand by: functions should have no more than 2 arguments.

Thinking back to my grade-school algebra class, I learned that functions take in an input, do something to that input, and produce an output.

f(x) = 2x + 2

It’s a simple concept really, and it should stay as such. Yet somehow I continue to come across functions with 4 or more arguments.

public void logMessage(String message, String errorDescription, int requestTypeCode, Date dateRequested, int userId) {
  ..
} 

The more arguments a function has, the more confusing it tends to be. It requires more mental mapping. It requires the person who needs to implement that function to remember which position to include which parameter in. Given the above logMessage example, the developer needs to remember that the general logging message is the first position and the error description is in the second position. But why, what happens if the developer accidentally switches the two, and puts the error message first?

If you’re creating a function that needs to perform some sort of logic on more than 2 variables, then those variable would probably benefit by being grouped together in their own class.

public class LoggerMessage {
  private String message;
  private String errorDescription;
  private int requestTypeCode;
  private Date dateRequested;
  private int userId;

  // getters and setters
  ...
}

...

  public void logMessage(LoggerMessage message) {
    ...
  }

By grouping the 5 variables together in a LoggerMessage class, we eliminate the need for the mental mapping when using the logMessage function now. Now it’s just a matter of creating an instance of LogMessage and passing in 1 argument to the function.

Create small classes

I think we’re seeing a trend here. Smaller is better when it comes to code. Smaller functions, smaller parameter lists, smaller classes, smaller file sizes, etc. Smaller code equals a smaller chance of bugs. It equals a smaller amount of time to debug and maintain. All of this “smallness” equates to a dollar amount. Smaller code essentially equals smaller cost for the company.

Every class you create should handle one responsibility and one responsibility only (are you familiar with the Single Responsibility Principle?)

Another way to think about it, create a class that will only have one reason to change in the future.

I just recently refactored a project that contains a very large questionnaire. There several different features to this questionnaire, such as the ability to start a new blank questionnaire, copy an old questionnaire into a new questionnaire, save a questionnaire, submit the questionnaire for review, etc.

The reason I wanted to refactor this project is because all of these features were contained inside 1 giant super class that handled all of this logic, called QuestionnaireService. This class was over 1000 lines long!

If we take into account our Single Responsibility Principle, this QuestionnaireService class essentially has many reasons to change. If the process to submit a questionnaire needs to change, we need to update the class. If the process to copy a questionnaire ever changes, we’d again need to update this class. etc. etc.

So during refactoring, I broke up this class into separate, smaller classes with more targeted scopes, such as NewBlankQuestionnaireService, CopyOldQuestionnaireService, SaveQuestionnaireService, SubmitQuestionnaireService.

All of these classes do still share similar functions though, so instead of duplicating all of this logic, they all extend and inherit from the common QuestionnaireService class to share this common logic.

This allows the main QuestionnaireService class to contain all the shared functions. But functions that are only relevant to submitting a questionnaire were contained inside the SubmitQuestionnaireService class. Functions that were only relevant to copying a questionnaire were only inside of the CopyOldQuestionnaireService class.

This refactoring effort of breaking up the large super class into separate smaller classes made the code a lot easier to read, understand, and maintain. Each class is now only about 200 lines long each!

It may seem like overkill in a sense, I have 5 classes all dealing with the same questionnaire. I suppose that’s just a school of thought though. I’m going to paraphrase one of Robert Martin’s thoughts on this concept of many small classes instead of a few large classes:

If you have a collection of tools, would you prefer to have one large drawer where you throw all your tools in, or would you rather have your tools organized into many small drawers each containing well-defined labels?

Delete all dead (or commented out) code

I believe Jeff Atwood says it best:

Ruthlessly delete code that isn’t being used. That’s why we have source control systems!

Jeff Atwood – Co-founder of Stack Overflow and Discourse and Blogger of Coding Horror

Dead code lives in the form of not only code that’s not being used, but also as commented out code.

I’ve seen the later a lot so far in my career (and to be honest, I used to do it a lot myself too!).

During a refactoring or technology migration effort, we write the new functions but comment out the old code to A) keep as a reference to ensure we coded it correctly and B) in case we need to roll back the changes, we could easily just un-comment out the code.

I learned this practice from a (at the time) more senior developer than me, when I was still in my junior developer days. (I have since banned myself from the practice, and mentor others to follow along).

Like Jeff Atwood mentions, we use source/ version control systems for this exact reason! Commented out code and dead code just clutters the files, making them unnecessarily longer.

Future developers of the project become too scared to delete the code in fear that it will break something if they do. Or they may see a function commented out and begin to question if it’s supposed to be commented out in the first place.

“Why is this commented out?” “Did someone forget to uncomment it?” “Should I uncomment it?” “Is it needed?” “Why am I here?”

During the major refactoring effort I mentioned before, I came across a very, very large SQL constants file. I hated this file. This file was over 6000 lines long, consisting of nothing but public final static String queries. This one file consisted of probably 80% of all the queries for the entire application. In one file. Where probably 50% of the queries in here were old, obsolete, dead code that was no longer being used!

It took me a good while to scrub through this file, removing the queries that were no longer needed, and more importantly removing these queries from this file into a more appropriate file.

What I did was moved the queries into their appropriate DAO interfaces instead of private final static String queries. By doing this, it is now tremendously easier to tell when one of these queries has been re-written into a new function and the old query is no longer needed. Since it’s a private variable, the IDE now informs us when the variable isn’t being used anymore. This gives any future programmer the absolute confidence that they may safely delete this function if it is in fact dead code.

Conclusion

Writing better code that’s clean and easy to understand is different than writing code that works. There are some quick and easy things you can start doing right now to write better code that will impress your team.

  1. Take your time to think of very descriptive variable names, function names, and class names. Don’t use ambiguous names that have no context. It should be very simple to understand what a variable/function/class is used for by its name alone.
  2. Break down large functions into smaller functions with descriptive names. Replace complex conditionals and comments with functions when appropriate.
  3. Limit function signatures to no more than 2 arguments. If a function needs 3 or more parameters, consider grouping those variables into their own class.
  4. Create small and targeted classes with one specific responsibility. The smaller the class, the better.
  5. Delete all dead code and commented out code. Make it a habit.

Where to go from here?

This blog really just stratches of surface of learning how to write better code that’s clean and easy to understand and maintain. If you really want to take your clean code skills to the next level and impress your development team during code reviews, then I’d highly suggest you get Robert Martin’s book on Clean Code. You can use my affiliate link to purchase a copy for yourself here:

Every software engineer must read this book >>

Adam Allard

Father of 2, husband of 1, developer of many. I'm a Software Engineer for Northrop Grumman creating web applications for the DoD. Currently I'm primarily working with Java and Angular based applications, although I have some years of experience in various languages and frameworks, including Python & Django, C# & Xamarin, PHP, and Bootstrap. My hobbies include time with my family, wondering when the Green Bay Packers will win their next Super Bowl, drinking over-priced beer, and of course learning and teaching.

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Content