Well-named methods are better than comments

Today I’m going to discuss a benefit of one of the greatest inventions in the history of computer science - the method. One reason I love writing software so much is that the field is so young. We haven’t even had a century to consider the best alternatives for writing robust software and methods are only 50 years old or so, depending on your definition. Compare that with other fields like construction. Imagine how much we had to learn about construction 50 years after the first building was ever made. I bet there was ample room for improvement! That’s the state we’re in today in software.

Methods are normally taught as a way to remove duplication and that is undoubtedly one of their benefits. However, I want to discuss another benefit of methods that is often overlooked - their ability to improve program readability.

Consider a program I’ve written that converts from National Hockey League (NHL) depth charts on RotoWorld into a comma-separated values (CSV) player file suitable for use in another program I’ve written than runs the draft for an NHL pool.

At its heart, the depth chart parser reads from an input file (the depth charts saved as a text file) and writes to an output file (the CSV file). Therefore, the first time I wrote the program’s main method, it looked like this:

public static void main(final String[] cmdLineArgs) throws IOException
{
  final String inputFileSpec = "C:/data/hockeypool/rotoWorldDepthCharts.txt";
  System.out.println("Reading players from " + inputFileSpec + "...");
  final List<Player> players = new PlayersSupplier(
        new FileReader(inputFileSpec)).get();

  final String outputFileSpec = "C:/data/hockeypool/regularSeasonPlayers.csv.txt";
  System.out.println("Writing players to " + outputFileSpec + "...");
  new PlayersWriter(new FileWriter(outputFileSpec)).write(players);
}

(Ignore the hard-coded paths for now. I haven’t yet made the program flexible enough to accept configuration as to where to read and write data.)

At first glance, there’s nothing wrong with this method. It’s pretty short. There is only one path for readers to comprehend (in other words, there are no conditional statements). However, I still think it can be improved because the lines that read from and write to files are hard to digest. There’s a lot going on there that obscures the real purpose of those lines.

Some literature encourages the use of comments in a situation like this:

public static void main(final String[] cmdLineArgs) throws IOException
{
  // Read players from the input file.
  //
  final String inputFileSpec = "C:/data/hockeypool/rotoWorldDepthCharts.txt";
  System.out.println("Reading players from " + inputFileSpec + "...");
  final List<Player> players = new PlayersSupplier(
        new FileReader(inputFileSpec)).get();

  // Write players to the output file.
  //
  final String outputFileSpec = "C:/data/hockeypool/regularSeasonPlayers.csv.txt";
  System.out.println("Writing players to " + outputFileSpec + "...");
  new PlayersWriter(new FileWriter(outputFileSpec)).write(players);
}

Don’t do this! As Robert Martin so eloquently puts it in Clean Code, comments are failures; they indicate that we have failed to express ourselves in code. We should not be proud when we write a comment. Rather, we should use comments only as a last resort when all other mechanisms for expression have failed.

As Martin recommends, let’s try to express ourselves in code by using well-named methods:

public static void main(final String[] cmdLineArgs) throws IOException
{
  final List<Player> players = readPlayers(
        "C:/data/hockeypool/rotoWorldDepthCharts.txt");
  writePlayers(players, "C:/data/hockeypool/regularSeasonPlayers.csv.txt");
}

private static List<Player> readPlayers(final String fileSpec) throws IOException
{
  System.out.println("Reading players from " + fileSpec + "...");
  return new PlayersSupplier(new FileReader(fileSpec)).get();
}

private static void writePlayers(final List<Player> players, final String fileSpec)
      throws IOException
{
  System.out.println("Writing players to " + fileSpec + "...");
  new PlayersWriter(new FileWriter(fileSpec)).write(players);
}

Doesn’t this make the main method easier to understand? The method now clearly does two things - read players and write players. If you want to know the implementation details of those operations, you can look at the private methods, but if you don’t care about those details, you can ignore them. For example, if your team took over responsibility for maintaining this class, the first time you looked at the code you should not have to burden your mind with the minutiae of how the files are read and written. Note that by introducing private methods we were also able to forego the use of the inputFileSpec and outputFileSpec variables, further condensing the main method and making it even easier to understand. Additionally, the implementation details of reading and writing players can now more easily be refactored into their own classes, should we choose to do so.

In summary, use well-named methods liberally, even if they’re only called once. They make it easier to understand your code and they’re so much better than comments!