Bloom Code Guidelines

Attitude

Your code should be easy redable & understandable for the other developers.
Code should be written to minimize the time it would take for someone else to understand it.
Try to keep same code style everywhere over the code to prevent confuse of the reader.

* Most of below examples are relevant to Java, TypeScript, JavaScript languages

General

  • Follow standard conventions.
  • Keep it simple stupid. Simpler is always better. Reduce complexity as much as possible.
  • Boy scout rule. Leave the campground cleaner than you found it.
  • Always find root cause. Always look for the root cause of a problem.
  • Be consistent. If you do something a certain way, do all similar things in the same way.

Design rules

  • Keep configurable data at high levels.
  • Prefer polymorphism to if/else or switch/case.
  • Prevent over-configurability.
  • Use dependency injection.
  • Follow Law of Demeter. A class should know only its direct dependencies.
  • Avoid logical dependency. Don't write methods which works correctly depending on something else in the same class.
  • Avoid side-effects
  • Prefer immutability (*.ts wrap types with Readonly<>)
  • Everything should be relevantly close to it’s usage...

Data structures

  • Do one thing.
  • Prefer fewer arguments.
  • Avoid hybrids structures (half object and half data).
  • Prefer Types over Interfaces for pure data structures.
  • Base class should know nothing about their derivatives.
  • Better to have many functions than to pass some code into a function to select a behavior.
  • Avoid Circular Dependencies.

Code smells

  • Rigidity. The software is difficult to change. A small change causes a cascade of subsequent changes.
  • Fragility. The software breaks in many places due to a single change.
  • Immobility. You cannot reuse parts of the code in other projects because of involved risks and high effort.
  • Needless Complexity.
  • Needless Repetition.
  • Opacity. The code is hard to understand.
  • Added multifunctional third-party library for just one use-case.
  • Unnecessary type casts (e.g. *.ts name: key as SomeName) – use explicit typequards for it.

Naming

In Short

  • Choose descriptive and unambiguous names.
  • Use PascalCase for type names.
  • Do not use I as a prefix for interface names.
  • Use camelCase for function names.
  • Use camelCase for property names and local variables.
  • Do not use _ as a prefix for private properties.
  • Use whole words in names when possible.
  • Use explanatory constants (avoid magic numbers).

Pack information into your names (Think of names as a tiny comment)

  • Make meaningful distinction – try to pick self-commentary name.

  • Avoid generic names (or know when to use them).
    But don't forget that any extra info you squeeze into a name will be seen every time the variable is seen.

  • Prefer concrete names instead of abstract names (give the names that directly describe what the method will do). Attach an extra info to a name by using suffix especially in the places where a bug can easily sneak if someone mistakes that the variable is: elapsed_ms, members[mi], html_utf8, plained_password, data_urlenc, model_onDataReceived, etc.

  • Find more "Colorful" words:

      send -> deliver, dispatch, announce, distribute, route, sendBets;
      find -> search, extract, locale, recover, findLetter;
      start -> create, launch, begin, open, startRound;
      make -> create, setUp, build, generate, compose, add.
    
  • If you're going to use a generic name like tmp, it or retval, have a good reason for doing so.

  • Use longer names for larger scope's (shorter names are better for variables that span only few lines).

  • The clearest way to name a limit is to add max_ or min_ in front of the thing being limited: MAX_ITEM_IN_CARD, minRange, maxRange.

  • Prefer first/last and min/max instead of start/stop for inclusive ranges (where the range should include both end points).

  • The typical convention for naming inclusive/exclusive ranges (where the range should include the begin point and exclude the end point) is begin/end.
    For example: All events that happened on October 20, it's easier to write:

      showEventsInRange("OCT 20 12:00am", 
                        "OCT 21 12:00am");
    

    than it's to write:

      showEventsInRange("OCT 20 12:00am", 
                    "OCT 21 11:59:59:9999am");
    
  • Use the compute/calculate prefixes in 'get' methods that should iterate over past data before returning some value (cause calling such method may be very expensive if there a lot of data, but unsuspecting programmer might call it carelessly).


Abbreviations

Avoid them as a general rule. For example, calculateOptimalValue() is better method name than calcOptVal().

Being clear is more important than minimizing keystrokes. And if you don't abbreviate, developers won't have to remember whether you shortened a word like qualified to qual or qlfd.
However, the are few standardized abbreviations:

  • acc for accessibility, as ButtonAccImpl;
  • auto for automatic, as in autoLayout
  • eval for evaluate, as in EvalBindingResponder
  • impl for implementation, as in ButtonAccImpl
  • info for information, as in GridRowInfo
  • num for number of, as in numChildren
  • min for minimum, as in minWidth
  • max for maximum, as in maxHeight
  • nav for navigation, as NavBar
  • regexp for regular expression, as in RegExpValidator
  • util for utility, as StringUtil
  • enum for enumerator, as GameType

If you're considering using an abbreviation that isn't listed here, please search the source code to determine whether it is already in use. If you don't find it, think twice about whether abbreviating is really appropriate.

Whatever u can spell out “horizontal” and “vertical” in most places, such as horizontalScroll and verticalScroll but abbreviate them to H and V in the very-commonly-used container names HBox and VBox.


Acronyms

Various acronyms are common, such as CSS, HLOC, IME, RX, MXML, RPC, RSL, UI, UID, URL, WSDL, XML, JSON.

An acronym is always all-uppercase or all-lowercase (e.g., URL or url, but never Url). The only time that all-lowercase is used is when the acronym is used by itself as an identifier, or at the beginning of an identifier, and the identifier should start with a lowercase letter.

Examples of identifiers with acronyms are CSSStyleDeclaration, IUID, ui, id, itemID, url, pictureURL.Follow the acronym-casing rules even in the rare case that two acronyms must be adjacent. An example (which isn't actually in use) would be something like loadCSSURL(). But try to avoid such names. Better will be loadCSSfromURL() or loadExternalCSS().


Type-specifying names

If you want to incorporate the type into the name, make it the last “word”. Don't use the old conventions of concatenating abbreviated type suffixes such as _btn to indicate type. For example, name a border Shape border, borderSkin, or borderShape, but not border_ui.
Often, the best name for an object is simply the same as its type, with different casing:

Button button = new Button();
// or more specific:
Button quoteButton = new Button();

Package names

Start them with a lowercase letter and use underscores for subsequent words: controls, list_classes.

The package that contains a lot of similar things should have name in plural form: controls, filters, commands, events, managers, resources, skins, states, styles, utils, validators.

It is common to use a gerund for the name of a package which implements a concept: binding, logging, messaging, printing, mapping.Otherwise, they are generally "concept nouns": core, graphics, api.


Namespace names

Start them with a lowercase letter and use underscores between words: fx_internal, obxect_proxy.


Interface names

Just give a proper general name, that corresponds to api e.g. List, Set, Repository

There was old convention to start them with I and use intercaps (camelCase) for subsequent words: IList, IFocusManager. But, developer works with an object and should not care whether it's an interface or actual implementation.

There is also wide popular to add Impl to the end of the implementation, but better to give your implementation more specific name instead e.g. ArrayList, HashSet, PropertiesRepository


Class names

Start them with uppercase and use camelCase: Button, FocusManager, UIComponent, FooBarEvent, FooBarError, FooBarInstance, FooBarValidator, FooBarBorder, FooBarSkin, FooBarIcon, FooBarUtil, etc.

There is also holly war about names, that end on er and or.
I'm sure, that's is more than fine to use such in natural entities like Error, Border, Actor


Constant names

Use all uppercase letters and use underscores between words: DEFAULT_WIDTH, OFF. The words in the identifier must match the words in the constant value if it is a String:

Java
public static final String FOO_BAR = "fooBar";

Instance names

Do not use "_" as a prefix for private properties.


Getter/Setter & Argument names

Give the storage variable for the getter/setter getFoo/setFoo the name foo.


Type declarations

Use the narrowest type that is appropriate. For example, a loop index should be a int, not a long*. e.g mouseEventHandler should declare its argument type as MouseEvent, not Event.

Java
// Do this:
public String setLabel(String value) {...}
// or this
public String setLabel(String label) {...}

// Not this:
public String setLabel(String labelValue) {...}
// or this:
public String setLabel(String val) {...}

Use event (not e, evt, or eventObj) for the argument of every event handler:

Java
public void onApplicationEvent(BalanceUpdateEvent event) {...}

Event handler names

Event handlers should be named by concatenating "Handler" to the type of the event: mouseEventHandler(). If the handler is for events dispatched by a subcomponent (i.e., not this), prefix the handler with the subcomponent name and underscore: textInput_focusInHandler(), model_betConfirmHandler()

Anyway nobody likes to write extra code, so on prefix works properly in any project onMouseEvent().

* Whichever style you choose, be consistent so that it does not become an overhead for your team to process multiple styles in related pieces of code.


Statements

Despite of some advantages of Allman indentation style Egyptian braces style is used more widely in java world and especially in script languages like JavaScript and TypeScript.

  • The { (opening brace) should be at the end of the line that begins the compound statement;
  • The } closing brace should begin a line and be indented to the beginning of the compound statement.

* "Compound statements" - are statements that contain lists of statements enclosed in braces { statements }


Import statements

Avoid using .* (group imports) if you don't use whole group in your class.


if, if-else, if-else-if-else, for, while, do statements

Braces should be used around all statements, even single statements, when those are part of a control structure, such as an if or for statement. This makes it easier to add statements without accidentally introducing bugs to forgetting to add braces.

Follow the if, if else, for, while, switch statements with a single space ' ' before the left parenthesis:

Java
// Do this:
if (a < b) {...}

// And this:
for (int i = 0; i < n; i++) {...}

// Not these:
if(a<b) {...}
if(a < b) {...}
if( a < b ) {...}

Try to not use more than 2 nested if statements inside each other:

Java
// Do this:
if (!condition1) {
  return false;
}
...
if (!condition2) {
  return false;
}
...
if (!condition3) {
  return false;
}
...
return true;

// Not this:
if (condition1) {
  ...
  if (condition2) {
    ...
    if (condition3) {
      ...
      return true
    }
  }
}
return false;

switch statements

Every switch statement should include a default case. The break in the default case is redundant, but it prevents a fall-through error if later another case is added.

Java
switch (condition) {
  case A: // falls through B
  case B:
    statements;
    /* falls through C*/
  case C:
    statements;
    break;
  default:
    statements;
    break;
}

White space


Blank Lines

Blank lines improve readability by setting off sections of code that are logically related.

Two blank lines should always be used in the following circumstances:

  • Between sections of a source file
  • Between class and interface definitions

One blank line should always be used in the following circumstances:

  • Between methods

  • Between the local variables in a method and its first statement

  • Before a block or single-line comment

    Java
    if (condition) {
    
      /* Handle the condition. */
      ...
    }
  • Between logical sections inside a method to improve readability


Blank Spaces

  • A keyword followed by a parenthesis should be separated by a space:

    Java
    while (true) {...}
  • A blank space should not be used between a method name and its openings parenthesis, but should appear after commas in argument lists:

    Java
    doSomething(String foo, String bar) {...}
  • The expressions in a for statement should be separated by blank spaces:

    Java
    for (expr1; expr2; expr3) {...}
  • All binary operators except . should be separated from their operands by spaces:

    Java
    a += c + d;
    a = (a + b) / (c * d);
    
    while (d++ = s++) {
      n++;
    }

Indentation


Block indentation: +2 spaces

Each time a new block or block-like construct is opened, the indent increases by two spaces.
When the block ends, the indent returns to the previous indent level.
The indent level applies to both code and comments throughout the block.

* All code samples here formatted in this way.


Indent continuation lines at least +4 spaces

When line-wrapping, each line after the first (each continuation line) is indented at least +4 from the original line.
In general, two continuation lines use the same indentation level if and only if they begin with syntactically parallel elements.


Do things in more natural way!


Comparison

Write comparisons in the order that they read most naturally:

Java
// Do this:
if (n == 3) {...}   // "if n is 3"

// Not this:
if (3 == n) {...}   // "if 3 is n"

++ and -- operators

Prefer postfix form in ++ and -- operators:

Java
// Do this:
for (int i = 0; i < n; i++)

// Not this:
for (int i = 0; i < n; ++i)

Ternary operator

Use a ternary operator in place of a simple if/else statement, especially for null checks:

Java
// Do this:
return (item != null) ? item.label : defaultLabel;

// Not this:
if (item == null) {
   return defaultLabel;
}
return item.label;

// But Don't use nested ternary operators!
// Don't do this:
return a < b ? -1 : (a > b ? 1 : 0);

// Do this:
if (a < b) {
   return -1;
} else if (a > b)
   return 1;
}
return 0;

If an expression containing a binary operator appears before the ? in the ternary ?: operator, it should be parenthesized:

Java
// Do this:
return (item != null) ? item.label : defaultLabel;

// And this:
return flag ? "foo" : "bar";

// Not this:
return item != null ? item.label : defaultLabel;

Coercion

Don't compare a Boolean value to true or false; it already is one or the other:

Java
// Do this:
if (flag) {...}

// Not this:
if (flag == true) {...}

// And this:
if (a && b) {...}

// Not this:
if ((a && b) != false) {...}

Miscelanious


Initialization

Try to initialize local variables where they’re declared. The only reason not to initialize a variable where it’s declared is if the initial value depends on some computation occurring first.

Java
// Do this:
String foo = "Bar";

// Not this:
String foo;
foo = "Bar";

One statement per line

Each statement is followed by a line break.

Java
argv++; argc--; // Avoid this!

see Also

** Many of concepts above were applied from “The Clean Code” — Robert C. Martin


No comments:

Post a Comment