Style Guides

From NoskeWiki
Jump to navigation Jump to search

About

One of the unexpected things I discovered when I joined Google in 2013... is that (almost) every programming language has an official Google style guide which defines how you should format, structure and name your code files.

In this document I link to the public version of all our style guides, and have written my own quick summaries / file examples, to remind me how to do things; useful for when you switch between languages and forget the conventions! :)


Here are some of the public Google style guides:

  • Google C++ Style Guide
    • Summary: use clang-tidy ... 80 col limit... variable_name, class_member_variable_, ClassName, FunctionName(), set_class_mutator_name(), class_accessor_name(), kConstantName, EnumName, ENUM_VALUE_NAME, namespace_name, g_globals_discouraged!... my_class.h, my_class.cc, my_class_test.cc
  • Google JavaScript Style Guide
    • Summary: 80 col limit... functionName(), variableName, ClassName, EnumName, methodName(), CONSTANT_VALUE, foo.namespaceNames.bar... filenameslikethis.js
  • Google Java Style Guide
    • Summary: (80 or 100 col limit... variableNameCamelCase, ClassName, methodName(), CONSTANT_NAME, com.example.deeppackagename... FileNamesNotWellAgreedOn.java
  • Google Python Style Guide
    • Summary: use pylint ... 80 col limit... local_var_name, global_var_name, instance_var_name, function_parameter_name, ClassName, method_name:, ExceptionName, function_name:, GLOBAL_CONSTANT_NAME, module_name, package_name ... file_names.py, class_name.py, class_name_test.py
  • Google Objective-C Style Guide
    • Summary: 100 col limit... numVariableName, methodName(), ClassName, ...no_specific_file_convention.m


Why are Style Guides Important

Style guides are terrific!


Style guides ensure that no matter who's code at Google you are reading, the consistent structure means you understand it much more quickly... and it means that reviewers can't argue over the "best" way to do things, because the best way has been decided for us. No more arguing over variable name conventions! Better yet, people have written "lint" tools which check your document doesn't break the style and, in some cases, tools (eg: "clang-tidy") which will automatically fix a lot of formatting issues for you.

One of the most controversial rules is line length. For most of our language we enforce 80 character (C++, JavaScript, Python) or 100 character (Java, Objective-C) width on every line. At first it feels very limiting, but it really does come with huge advantages, such as encouraging good coding practices (not too much code per line) and allows you to have many files displayed side-by-side on your screen at once... which makes you look bad-ass.

The best news? Most of our style guides are public, so even if you don't work at Google, if you spend hours deciding how to format each file, let Google make the decision for you! Each decision is carefully weighed, and when you tell someone you use "Google style guide" convention for your project you get instant points with any employee, because they know your not one of those engineers that does his own really weird thing. If you teach programming, make sure your students use these style guides because it gives them points in programming interviews if they start the interview by saying "is it okay if I use the Google style guide naming conventions".

I regret that all the code I wrote before Google had my own weird convention... and on many of my wiki pages I've gone back and changed it. Style guides rule!


Google Code Readability

Within Google, we have an interesting concept where you can earn "readability" in a particular language, after you prove you have mastered it. If you don't have readability in a program language (for example C++), and write a file in that language, it means you must get "readability" approval from an engineer who does have readability in that language, as part of your code review.... which can be a pain. A good Google engineer should try to get readability in at least 1 language (although hopefully more) as soon as possible to put on their internal (and external) resume. To get readability, you must submit a file which is at least 300 lines of "non boiler plate" (useful) code + hopefully tests for this class to one of Google's volunteer "readability reviewers" who will nitpick the *crap* out of every line (not just for readability, but other good practices). If you're lucky you get a responsive reviewer, and then you have a good shot at getting readability within a month.... although some languages you'll find are far slower than others)! :)


C++ Style Guide

guide.h

         1         2         3        4          5         6         7         80 chars
<--------+---------+---------+-- 80 chars wide --+---------+---------+--------->


// Copyright 2013 Institute Name. All Rights Reserved.
// Author: andrew.noske@gmail.com (Andrew Noske)

// The purpose of this "Guide" class is to provide an minimal example of how
// to write files which satisfy Google's C++ style guide / conventions.
// As you can see, this header comment explain the purpose of these files
// (guide.h and guide.cc in this case), and includes a link to relevant
// documentation down the bottom. Files can move around, so the two optional
// lines up top remind people who wrote it (and how to contact them) plus
// copyright info. No need to include the filename.
// 
// See: http://andrewnoske.com/Style_Guides.

#ifndef PATHFROMROOT_SUBPROJECT_GUIDE_H_
#define PATHFROMROOT_SUBPROJECT_GUIDE_H_

#include <string>                       
#include <unique_ptr>

#include "//root/subproject/company.h" 
#include "//root/subproject/rule.h"

namespace subprojectname {
const int kPreferredCharWidth = 80;  // Keep your lines <= 80 chars wide.
                                     // and align overflow comments like this.

enum CommentType {
  kLineComment = 0,  // We prefer line comments ("//").
  kAreaComment,      // We generally use "/**/" only when bug hunting.
};

// Guide class is used to specify and apply rules representing a style guide.
// Example usage:
// 
//   string my_code_fragment = "... <your code file contents> ...";
//   Guide *cpp_guide = new Guide();
//   cpp_guide->indent(2);
//   cpp_guide->AdjustCodeMatchGuide(my_code_fragment, true);
class Guide {
 public:
  Guide();
  ~Guide();
  
  // Returns a string representing what the first line of your files should
  // look like. Output format:
  //    "// Copyright 2013 Institute Name. All Rights Reserved."
  string GetFirstHeaderLine();
  
  // Adds a new formatting rule to the guide.
  string AddRule(const Rule rule);
  
  // Inputs the string contents of code file and returns true if it passes
  // all the guideline rules set by this file.
  bool DoesCodeMatchGuide(const string code);

  // Changes the input code to match the guide by inserting and deleting
  // white space characters where needed and returns the number of changes
  // made. If 'change_comments' is true it also modifies comments where 
  // needed, but never alters the meaning of the code itself.
  int ChangeCodeToMatchGuide(string *code, bool change_comments);

  int default_indent() { return default_indent_; }

  void set_default_indent (int default_indent_spaces) {
    default_indent_ = default_indent_spaces;
  }

 private:
  int default_indent_;    // Number of spaces to indent - never tabs.
  vector<Rule> rule_;     // Always private variables end with an underscore.
  
  CommentType preferred_comment_type_;

  std::unique_ptr<Company> company_;
}
}  // namespace subprojectname

#endif  // PATHFROMROOT_SUBPROJECT_GUIDE_H_

THINGS TO NOTE:

  • Naming:
    • Normal variables: (lowercase_underscore)
    • Class: (UpperCase)
    • Private class variables: (trailing_underscore_)
    • Function: (titleCase))
    • Function arguments: (lowercase_underscored)
    • Getter/setter function names: (match_variable))
    • Comments: ( // Sentence with period.)
  • Comments:
    • A comment explaining what each class does is always helpful, as is a brief example usage to show how it works.
    • Always prefer // over /* */, and should be in format: // Starts with capital ends with period.
    • Every non-trivial method in your .h should have a comment to explain what it does in terms of inputs and outputs.
  • Other:
    • #ifndef and #endif (always): immediately after the header comment is the "#ifndef" which represents the full filepath to ensure its uniqueness. The #endif" should have a comment matching it ("#endif // MATCHING_LABEL_").
    • namespace: production files should use their own project namespace and a comment to show where it ends ("} // namespace projectname").
    • #includes: are in alphabetic order with a blank line after STL includes.
    • constants and enums: are unique in that they start with a lowercase k followed by title case.
    • const (liberally): use "const" keyword wherever possible, including all applicable function inputs.
    • default method values (no!): in production code we never set default method values since changing this default later could ruin other code!
    • function arguments (in order): Arguments are generally ordered with inputs first and outputs last, except in cases like "code" which is both an input and output.
    • getters and setters: for getters and setters we match the lowercase name of the private variable minus the last underscore. To save chars we usually prefer "x()" to "get_x()", but "set_x()" is as you'd expect. For really simple getters we can we can reduce to 1 line as per example, and these don't warrant commenting.
  • unique_ptr: is popular to use as it deletes automatically when it go out of scope.


guide.cc

<------------------------------- 80 chars wide -------------------------------->


// Copyright 2013 Institute Name. All Rights Reserved.
// Author: andrew.noske@gmail.com (Andrew Noske)

#include "//root/subproject/guide.h"

#include <math>                   

#include "//root/differentproject/stuff.h"
#include "//root/util.h"

using subprojectname::Company;
using util::GetCurrentYear;


namespace subprojectname {
Guide::Guide() : {
  default_indent_ = 2;
  preferred_comment_type_ = kAreaComment;
}

string Guide::GetFirstHeaderLine() {
  string line = "// Copyright " + GetCurrentYear() + " ";
  
  if (company_.get()) {                       
    line.append(company_.get()->name(), ".");
  } else {
    line.append("Private code. ");
  }
  line.append("All Rights Reserved.");
  return line;  // Line comment at least 2 spaces after code.
}

void Guide::AddRule(Rule rule) {  // "{" is never on new line.
  rule_.pushback(rule);           // No redundant newline before first block.
}

bool Guide::DoesCodeMatchGuide(const string code) {
  string expected_first_line = GetFirstHeaderLine();
  if (code.find_first_of(expected_first_line) != 0)
    return false;  // NOTE: Small ifs we don't need braces.
  
  // Check the code against each line:
  for (int i = 0; i < rule_.size(); ++i) {
    if (!rule_[i].DoesCodeSatisfyRule(code))
      return false;
  }
  return true;
}

// ChangeCodeToMatchGuide works by inputting the code into the "ApplyRule" 
// method of each rule we have in the "rule_" vector in order and tallying 
// the number of changes made.
int Guide::ChangeCodeToMatchGuide(string *code, bool change_comments) {
  <...>
}


// Macros are discouraged, but here's on anyway.
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

// Returns a value between 0 and 1 showing how well the code matches the 
// guide... basically an excuse here to show arithmetic and code which
// overflows one line.
float Guide::CalculateCodeAgreement(int num_lines,  // Non-empty lines.
                                    int num_lines_too_long,
                                    int num_rule_style_errors,
                                    int num_rule_warnings) {
  if (!num_lines_too_long &&    // Leave the && at the end.
      !num_rule_style_errors &&
      !num_rule_warnings) {
    return 1.0;
  }
  if (num_lines == 0) return 0;  // Avoid divide by zero.
  float weighted_penalty_per_line =
      (num_lines_too_long * kLineTooLongPenalty +
      num_rule_style_errors * kStyleErrPenalty +
      num_rule_warnings * kWarningPenalty) / num_lines;

  return MIN(1, 1 - weighted_penalty_per_line);
}


}  // namespace subprojectname

THINGS TO NOTE:

  • function comments (better in .h): if you want a comment explaining how to use a function lookup the .h - only put a comment in the .cc if it explains technical details (stuff the user of the class doesn't need to know and isn't info duplicated in the .h), and even then they are probably better inside the function.
  • spaces (one): all loop and conditional statements have a space before the "(", but no redundant spaces inside brackets, ever. For mathematical operators, space each of them out by one space (eg: a * b + c.
  • "{" is never on new line.
  • empty lines (seldom): never >1 between blocks and never redundant newline before 1st block inside braces.
  • comment location (before or same line): comments always appear before a code block, or on the same line - never after the code!
  • macros (discouraged): macros are discouraged (inline functions are recommended instead), but if you must, then use all cap letters for macro.
  • line overflow (indent 4 spaces or align) if code overflows 1 line, then either: align with the bracket (as above) or give 4 spaces extra indent (as below). Leave each operator (&&,||,/,+) at the end of the previous line (not the start of the overflow line).
  • return (discouraged brackets): is no need for redundant brackets on return.
  • arithmetic: use brackets only where necessary and spaces either side of any arithmetic operator.
  • trailing newline (compulsory): add one blank line at the end of each file (although no idea why!)

Links