Skip to content

Instantly share code, notes, and snippets.

@dubrowgn
Last active July 28, 2022 07:48
Show Gist options
  • Save dubrowgn/3fefb65310327879b986bb82ce842f29 to your computer and use it in GitHub Desktop.
Save dubrowgn/3fefb65310327879b986bb82ce842f29 to your computer and use it in GitHub Desktop.
Results Oriented Style Guide

Results Oriented Style Guide

For the Improvement of Our Software Craft and the Reduction of Stress

Style Guide Goal

The only goal of this style guide is the collective improvement of our professional craft as software engineers.

Non-Goals

This style guide is not about enforcing consistency, unless that consistency provides some tangible benefit to the professional craft. It is also not about absolutes, or rules that “shall not be broken.” As such, this style guide contains only guidelines.

Style Guide Guidelines

The following points are meant to help steer guidelines in a direction that accomplishes the style guide’s goal. They make the assumption that a traditional style guide rule is not inherently free. That is, there is a cost to maintaining and enforcing any rule, even rules that “don’t really matter.” It also makes the assumption that consistency is not inherently a good thing. Perfect consistency removes opportunity for the development of new techniques and different rules of thought. As such, it is not interested in rules that pick one of several equally valid options in the interest of consistency.

  • Guidelines should provide some tangible benefit.

Guidelines which have no measurable impact on actual code or products hold no value. We seek to reduce time wasted by focusing on things that actually make a difference. Beware of guidelines that make us feel good.

  • Guidelines should foster learning and help share lessons learned the hard way.

Learning is key to our continual improvement as engineers. Most worthwhile lessons are learned the hard way, but we should strive to share that knowledge as best we can. This way, we can avoid repeatedly making the same mistakes, and at the very least, we might recognize the signs faster when we do inevitably make mistakes.

  • Guidelines should invoke thoughtful contemplation on the part of the reader.

Guidelines which stimulate introspection and objective review of the facts help facilitate learning.

  • Guidelines should come with rationale.

Guidelines that can’t be justified hold no value. If something really is a good idea, we should be able to express why.

  • Guidelines should be understandable after a single reading.

Guidelines which are difficult to understand have reduced usefulness. Simple guidelines, which are free from exceptions, are easier to learn and remember.

  • Guidelines should be remember-able after a single reading.

Guidelines which are difficult to remember are less likely to be used. Simple guidelines, which are free from exceptions, are easier to remember and employ.

  • Guidelines should not be absolute.

As engineers, we recognize our capacity to make sound and reasoned decisions, but we also recognize that we don’t know everything. No matter how good a guideline may be, there exists circumstances which are unforeseen, and may benefit from an alternative approach or frame of mind. We seek to empower individual engineers to make reasoned decisions based on the information they have available.

Core Tenets

There are many design considerations in any software project, and many are not compatible with others. Thus, we declare our core tenets here. These are design considerations we consider to be most important, sometimes to the detriment of other considerations.

Correctness

Software that doesn’t do what it was designed to do is not very useful. Thus, the first tenet is correctness. Does the software do what it is supposed to do? The other tenets are generally useful in that they support this objective.

Simplicity

Simplicity is at the heart of many desirable characteristics, and is thus arguably more important than correctness. From simplicity flows correctness, readability, maintainability, and many, many other benefits.

Readability

Real world software is read many times more than it is written, often by individuals who are not the original author. Being able to read an understand written code is important to its long term viability.

Maintainability

Real world software is dynamic, and evolves over time. Technical and social requirements change drastically over time. The rate of change in the environments software runs is astounding. In order for software to adapt to these changes, it must be maintainable. That is, changeable, repairable, inspectable, and readable.

Language Agnostic

  • Prefer simple over complex

  • Prefer concrete over abstract

  • Avoid unnecessary indirection

  • If, “You aren’t going to need it” (YAGNI), avoid writing it. Instead, prefer patterns that leave the option open in the future, in the unlikely event you really did need it.

Functions

Functions are the cornerstone of any software. Functions are simple, composable, testable, and stateless.

  • Prefer functions which do one thing, rather than many.

  • Prefer pure functions (functions without side effects) over functions with side effects.

  • Prefer shorter functions over longer ones.

  • Prefer functions with fewer parameters.

  • Avoid immediately branching off a parameter value.

If a function does largely different things depending on the value of a parameter, chances are the logic would be better expressed as multiple functions instead. It’s not uncommon for each branch to fall naturally into its own function. This branching pattern is most common with boolean flag parameters, or switch-casing off of an enumerated value.

Organization

  • Prefer linear control flow.

The more linear that logic flows, the more readable it tends to be. Many common patterns inherently lead to non-linear control flow, but the two most prevalent are probably indirection and excessive nesting of conditionals. Avoid indirection when possible, and employ guard statements to avoid excessive nesting.

  • Try to propagate errors when possible.

  • Prefer functions over classes.

  • Prefer pure data over classes.

  • Prefer files which contain logically congruent code.

  • Avoid global variables and state when possible.

Formatting

  • Prefer short lines over long lines.

  • Avoid excessive wrapping.

  • Group code into logical chucks surrounded by white space, similar to a paragraph in a novel.

  • Wrap between logical parts when possible, such as parameters, instead of in the middle of them.

Documentation

  • Document what a thing promises to do, not how it does it.

  • Prefer self-documenting functions over actual documentation.

  • If you find yourself wanting to document a chunk of logic, consider splitting it into a function instead.

Naming

  • Prefer succinct names over verbose ones.

Naming is one of the more difficult aspects of writing quality software. One may be tempted to err on the side of excessively verbose or explicit naming conventions. Taken to the extreme, this reduces the signal-to-noise ratio of a name. If two things have names containing several words, it can become difficult to tell them apart. How two things differ can often be very important, but excessively verbose or explicit names hide this important signal among the noise of filler words.

In the other extreme, excessive use of acronyms or one character names can make it difficult to understand what a name represents.

The word succinct means, “briefly and clearly expressed,” which is a succinct way to describe a good name. If adding words to a name doesn’t enhance understanding, it’s probably best to leave them out. A longer name isn’t inherently better than a shorter one.

  • Prefer to reuse existing nomenclature when possible.

Original ideas are generally not that common. When using an existing idea, it’s generally a good idea to use the existing nomenclature, even if it might stand out a bit. This helps reduce the time it takes to onboard new engineers, and it aids in communication with outside teams.

  • Avoid encoding semantic meaning into names.

Historically, patterns like so called Hungarian notation have been very popular. The industry has largely moved away from some of these patterns, but many are still quite prevalent. The problem with encoding semantics into a name, is you have to change the name when the semantics change. In a way, they represent a leaky abstraction, because consumers of the name are made aware of something they probably shouldn’t. Here are a few examples:

  1. capitalizing macros → have to rename when function call turns into function-like macro

Writing macro names in full caps means the caller is made aware of the mechanics of how the thing is implemented. It’s not uncommon to wrap a function in a macro, for example. Using this naming conention means you have to rename all call sites when this happens, even if the caller doesn’t care. Generally speaking, callers shouldn’t care whether a function is actually a function or a macro, so this rule creates a lot of otherwise unnecessary code churn, with very little to no tangible benefit in return.

  1. _name or name_ → have to rename when private member becomes public

Languages that don’t have a mechanism to hide private details tend to use this pattern out of necessity, but otherwise it's generally a good idea to avoid this pattern.

  1. ‘k’ prefix → have to rename when variable changes constness

  2. Hungarian notation → have to rename when a variable changes type

  3. <something>list → have to rename when data structure changes

Red Flags

  • Any type with “manager” in the name.

  • Directories, files, or types called “utils.”

Locking

  • Lock data, not code

Locking inherently exists to protect some piece of data from concurrent access of some kind. Locking designs that keep this in mind tend to shorter, simpler, and more correct.

  • Avoid re-entrant locks

Locking designs that require re-entrant locks are inherently at odds with the “lock data not code” guidance. Additionally, re-entrant locking code is very difficult to fully reason about, and much more likely to have deadlock issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment