Skip to content

Latest commit

 

History

History
417 lines (347 loc) · 13.8 KB

STYLEGUIDE.md

File metadata and controls

417 lines (347 loc) · 13.8 KB

Style Guide

Prelude

We try to maintain a consistent style for two reasons:

  • It makes it easier for any dev to jump into any part of the codebase.
  • Following the guide can help avoid common mistakes and language gotchas.

Set up your editor to do the heavy lifting. If your editor is capable, configure it to align with the standards listed below. This makes mistakenly introducing style errors less likely.

Some lint and style rules are enforced by Rubocop. You can check your changes locally by running rake lint.

All Languages

  • Be consistent with surrounding code. When working in a new file, take a bit of time to get a feel for the style. The intent is to keep the code readable and not interrupt the flow.
    • If there are conflicting styles in the surrounding code, go with the one closest to the standards listed below.
    • When possible, clean up the less preferred style when it's simple to do so. -If it's a quick clean-up, modify the file to align with standards.
  • Prefer helpers from already-included libraries—i.e., do a quick search to make sure you're not reinventing a wheel. Lodash, Google Closure tools, and standard Ruby libraries often have well-tested helpers for common operations.
  • Prefer extracting descriptively-named methods and variables over introducing redundant comments.
  • Remove anything Git's diff view complains about: No trailing whitespace at the ends of lines, single newline at end of document. Configure your editor to do this automatically.
  • Stick to an 80 character line length when possible, except for long URLs in comments or long regular expressions. Occasionally wiggling between 80 and 120 characters is OK in cases where it greatly improves the readability over a multiple line split.
  • Space after comment start. // Use capitalization and punctuation in comments.

Ruby

Default: https://github.com/bbatsov/ruby-style-guide

Fallback: https://github.com/styleguide/ruby

  • Use YARD (jsdoc style @tag annotations) when documenting parameter types and return values. http://www.rubydoc.info/gems/yard/file/docs/GettingStarted.md

  • Prefer Ruby 1.9 hash syntax. [link]

    # bad
    {:a => 1}
    
    # good
    {a: 1}
  • Prefer single quotes for non-interpolated strings. [link]

    # bad
    "Double-quoted string without interpolation"
    
    # good
    'Single quotes for normal strings'
  • When breaking lines while method chaining, prefer trailing dot on first line to leading dot on second line. [link]

    # bad
    my_object
      .first_method
      .second_method
    
    # good
    my_object.
      first_method.
      second_method

Rails

Prefer skinny controllers. Leverage the framework where possible and write as little custom code as possible to implement the feature. Guidelines here are not set in stone: when in doubt prefer readable code over strict adherence to the style guide.

Default: https://github.com/bbatsov/rails-style-guide

Fallback: https://github.com/thoughtbot/guides/tree/master/style/rails

Fallback: http://matthewpaulmoore.com/post/5190436725/ruby-on-rails-code-quality-checklist

  • Helpers shouldn't set instance variables. Prefer directly returning a value from the helper. [link]

JavaScript

Default: https://google.github.io/styleguide/javascriptguide.xml

  • Use 2 spaces per indentation level. Line continuations should be indented at 4 spaces (including function arguments). Wrap at 80 characters. [link]

    StudioApps.prototype.reallyLongFunctionName = function (argument1,
        argument2, argument3) {
      // ...
    };
  • Always use braces for blocks. [link]

    // bad
    if (test) return true;
    
    // good
    if (test) {
      return true;
    }
  • Parentheses adjacent to name, but not to a keyword. [link]

    function () { ... }
    function test() { ... }
    while (n < 1) { ... }
  • Unlike Google's JavaScript styleguide, we don't require padding spaces inside object literals. [link]

    var obj = {x: 1}; // This is okay
    var obj = { x: 1 }; // This is okay too

    See ESLint rule object-curly-spacing and this section of Google's styleguide for more information.

  • Avoid this in functions where callers might not bind the correct scope. Generally this means not using this in static methods and bare functions. [link]

  • Prefer IE9-compatible native JavaScript collection operators (such as .forEach, .map, .filter) over library equivalents. [link]

  • Separate event handlers from markup. [link]

    -# bad
    .header_popup{onclick: '/* do something... */'}
    
    -# good
    .header_popup
    :javascript
    $('.header_popup').click(function () {
      /* do something... */
    });
  • Binary and Ternary line continuations should have the operator at the beginning of the line (note this is different from Google's JavaScript style guide). [link]

    // bad
    myObject.
        doSomething().
        doSomethingElse();
    
    // good
    myObject.doSomething()
        .doSomethingElse();
  • We have a few different patterns for how we export things in our JS files currently. There's not necessarily an expectation that we'll go fix these all, but new code should try to follow these patterns. [link]

    // good
    module.exports.foo = function foo() { }
    // elsewhere
    foo();
    
    // okay, unless you're calling this method locally
    module.exports.foo = function () {
    }
    // elsewhere - bad
    module.exports.foo();
    
    // good
    var Foo = module.exports = function () {
    };
    Foo.prototype.bar = function () {
    }
    
    // bad
    module.exports = {
      foo: function () {
      }
    }
    
    // bad
    function foo() {
    }
    module.exports = {
      foo: foo
    }
  • Avoid inline Javacript in HAML and ERB views. Inline Javascript is hard to lint, test, and reuse, and tends to build in lots of global interdependencies between code and views.

    Here are some hints and guidelines.

    • New JS code in our Rails apps should go in a .js file, not inline in the view; this will be enforced by code review.

    • If you modify inline JS code in a template, please move it out of the file as part of the same CL. (Exceptions can be granted on a case by case basis.)

    • Server-side configuration information that needs to be shared with Javascript code should be put in app_options. Our templates include a script tag which assigns app_options to a Javascript variable so that it as accessible from JS.

ES6

Our default style if not mentioned here should be that mentioned in the AirBnb guide https://github.com/airbnb/javascript. Exceptions that we'd like to make should be noted here.

React

Our default style if not mentioned here should be that mentioned in the AirBnb guide https://github.com/airbnb/javascript/tree/master/react

https://github.com/airbnb/javascript/tree/master/react#spacing We're okay with no space in self-closing tags, i.e. <MyComponent/> and <MyComponent /> are both valid.

  • Components with many attributes should have one per line, with 2 spaces of indentation. Child components should have 2 spaces of indentation. Paritally linted by jsx-first-prop-new-line and jsx-indent-props.
// Bad
var component = (
  <MyComponent param1={1} param2={2} param3={3} param4={4} param5={5}>
    <ChildComponent/>
  </MyComponent>
);

// Good
var component = (
  <MyComponent
    param1={1}
    param2={2}
    param3={3}
    param4={4}
    param5={5}
  >
    <ChildComponent/>
  </MyComponent>
);
// good
<Component
  prop1="prop1"
  prop2="prop2"
>
  textContent
</Component>


// bad
<Component
  prop1="prop1"
  prop2="prop2">textContent</Component>

// good - fine to put content on same line if the tag opens & closes on that line
<Component>textContent</Component>
// Bad
var component = (<MyComponent
    foo="bar"
    onClose={this.handleClose}
  >
    <ChildComponent/>
  </MyComponent>);

// Good
var component = (
  <MyComponent
    foo="bar"
    onClose={this.handleClose}
  >
    <ChildComponent/>
  </MyComponent>
);
var selfClosing = (
  <MyComponent
    foo="bar"
    onClose={this.handleClose}
  />
);

CSS-in-JS

We prefer SCSS modules over CSS-in-JS (see CSS for more). If you are working in a file that has already-existing CSS-in-JS and cannot migrate the styling over to an SCSS module, please follow these guidelines:

  • Prefer single object for all styles vs. inlined style objects. Define static styles below the component, and only dynamic styles in the render method.
// Bad
var component = (
  <div style={{color: 'red', display: 'block'}}>
    <div style={{color: 'blue', fontSize: 10}}>I'm a child</div>
  </div>
);

// Good
var component = (
  <div style={styles.root}>
    <div style={styles.child}>I'm a child</div>
  </div>
);
...
var styles = {
  root: {
    color: 'red',
    display: 'block'
  },
  child: {
    color: 'blue',
    fontSize: 10
  }
};

// Example of defining static and dynamic styles separately
var Component = function (props) {
  var styles = _.merge({}, staticStyles, {
    root: {
      color: this.props.color
    }
  });
  return (
    <div style={styles.root}>
      <div style={styles.child}>I'm a child</div>
    </div>
  );
};
var staticStyles = {
  root: {
    display: 'block'
  },
  child: {
    color: 'blue',
    fontSize: 10
  }
};
  • Prefer numbers vs strings for pixel values
// Bad
var styles = {
  root: {
    width: '100px',
    height: '100px'
  }
}

// Good
var styles = {
  root: {
    width: 100,
    height: 100
  }
};

In /apps

Use lodash and jQuery libraries in /apps.

CSS

On the frontend, some lint and style rules are enforced by Stylelint. You can check your changes locally by running yarn lint:scss from the apps directory. Some rules have been disabled for now, but we are moving towards enabling all of the rules that are part of the Stylelint standard configuration.

Some key points:

  • Use SCSS modules over CSS-in-JS. The module file takes the name "my-component.module.scss" and lives in the same directory as the component.

  • Use kebab-case (not camelCase nor snake_case) for separating words in IDs, classes, mixins and filenames.

  • Use px for small values (e.g. less than 4 px) and specific values (e.g. 87px).

  • Use rem or em for other values like font-size, margin, and padding.

    • Default to rem, but to quote this article "use em units in places where the influence of nearby parent elements would make sense".
    • To convert between px and em (or rem) for our site, use a Pixel to Em converter with 16 as the default pixel size.
  • Avoid inline styles in markup.

  • Use names that are as short as possible but as long as necessary.

  • Use SCSS helpers for vendor prefixing.

  • Extract colors into named, re-used variables.

  • Extract magic numbers into named variables.

  • Use ID selectors only when there is, and only ever will be, one item on a page.

  • Nest selectors a maximum of three levels deep.

  • Prefix scss partials with _, e.g., _header.scss.

  • Use alphabetical order for new declarations in already-alphabetically-ordered files. Place @extends and @includes at the top of lists.

    Refer to the Sass style guide, and to CSS Tricks' Sass and CSS style guides for more.

HTML

Default: https://google.github.io/styleguide/htmlcssguide.xml

  • Avoid inline event handlers in markup.
  • Avoid inline styles in markup.
  • Prefer double quotes for attributes.
  • Use dashes instead of underscores, camel casing, etc for separating words in IDs and classes.