Skip to content

Coding Style Guide

Jennifer Marx edited this page Apr 17, 2019 · 118 revisions

Meta

  • Code is read much more often than it is written
  • If possible, always provide a source for recommendations and rules
  • Specify exact version numbers for dependencies
  • Delete unnecessary code (don't comment it out)
  • If implementation is hard to explain, it's probably a bad idea. Usually you can make a hard-to-explain function easier-to-explain via "divide and conquer" -- split it into several functions.
  • No HTTP 500 (internal server) errors should be displayed to the users
  • Don't use "fetch": standard nomenclature is "get" for code and "retrieve" for comments and log messages
  • Aim for Minimum Valuable Product (MVP) with each milestone:

Minimum Valuable Product description

Source formatting

  • use only lowercase for URLs and file system paths (to avoid HTTP 404 errors)
    • use underscores in Django URL pattern names rather than dashes (Two Scoops 1.6.2)
    • dashes in actual URLs are fine (Two Scoops 1.6.2)
  • PEP 0008 for Python
  • The Puppet Language Style Guide
  • line length limits:
  • 79 chars for Python (as per PEP 0008)
  • 100 chars for JavaScript
  • soft limit of 100 chars for HTML/CSS
  • 140 characters for Puppet manifests
  • all file types and languages
  • indent with spaces, not tabs
  • 4 for Python code (as per PEP 0008)
  • 2 for everything else (JavaScript, JSON, HTML, CSS, etc.)
  • naming conventions
  • PEP 0008 for Python
  • camelCase for JavaScript and JSON
  • command line style checking tool
  • Hanging indentation and brackets
      # preferred
          { 'mouse': 'abc'
            'dog': 'cbd' }
      # followed-by
          {
            'mouse': 'abc', 
            'dog': 'cbd'
          }

Design patterns

  1. One codebase tracked in revision control, many deploys
  2. Explicitly declare and isolate dependencies
  3. Store config in the environment
  4. Treat backing services as attached resources
  5. Strictly separate build and run stages
  6. Execute the app as one or more stateless processes
  7. Export services via port binding
  8. Scale out via the process model
  9. Maximize robustness with fast startup and graceful shutdown
  10. Keep development, staging, and production as similar as possible
  11. Treat logs as event streams
  12. Run admin/management tasks as one-off processes
  • File structure (Django and JavaScript)
  • organize by feature
  • keep tests with features

Deployment

  • Puppet
  • Parametrize classes

REST API

  • URLs should include nouns, not verbs.
  • Use plural nouns only for consistency (no singular nouns).
  • Use HTTP verbs (GET, POST, PUT, DELETE) to operate on the collections and elements.
  • You shouldn’t need to go deeper than resource/identifier/resource.
  • URL v. header:
    • If it changes the logic you write to handle the response, put it in the URL.
    • If it doesn’t change the logic for each response, like OAuth info, put it in the header.
  • Query params and form data field names should be snake_case
  • View set names should be singular

Resources:

Python

  1. imports from __future__
  2. standard library imports
  3. imports from core Django
  4. imports from third party libraries and apps
  5. imports from the apps that you created as part of your Django project
  6. explicit relative imports from the current app
  • For example:
from __future__ import absolute_import

from core.views import FoodMixin
from .forms import WaffleConeForm
from .models import WaffleCone
  • Avoid using 'import *'. It can cause name collisions that are tedious to track down.

  • Why Import Structuring is Important!

  • Naming rules:

    • Class names: CamelCase, and capitalize acronyms: HTTPWriter, not HttpWriter.
    • Variable names: lower_with_underscores.
    • Method and function names: lower_with_underscores.
    • Modules: lower_with_underscores.py. (But, prefer names that don't need underscores!)
    • Constants: UPPER_WITH_UNDERSCORES.
    • Precompiled regular expressions: name_re.
    • Don't be afraid to pick a slightly longer name because it's more descriptive. No one wins any points for shortening "response" to "rsp".
  • Avoid using print. Use the logging module and in Django management commands self.stdout and self.stderr.

  • Formatting strings:

"Value: {}".format(value)

Note: Simple string concatenation can be favorable in some cases when readability isn't affected

logger.info("%s went %s wrong", 42, 'very')
# bad
from itertools import groupby, chain, \
                      izip, islice
# good
from itertools import (groupby, chain,
                       izip, islice)
  • Documentation strings in Python:
    • write docstrings for all public modules, functions, classes, and methods
    • follow PEP 0257 -- Docstring Conventions:
      • the """ that ends a multiline docstring should be on a line by itself
      • for one liner docstrings, please keep the closing """ on the same line
  • If an opening parenthesis or square bracket is at the end of a line then the closing one should be on a line by itself
  • List Comprehensions:
    • List comprehensions are an elegant way to define and create lists based on existing lists.
    • It is generally more compact and faster than normal functions and loops for creating lists.
    • We should avoid writing very long list comprehensions in one line to ensure that code is user-friendly and easy to debug.
    • Every list comprehension can be rewritten in for loop, but every for loop can’t be rewritten in the form of list comprehension.

AngularJS and JavaScript

  • Use components instead of directives whenever possible
  • Define 1 component per file, keeps files under 400 lines of code.
  • Small functions (single responsibilities), less than 75 lines of code.
  • Wrap Angular components in a Immediately Invoked Function Expression (IIFE)
  • Ordering Dependencies (alphabetize with groups)
    • 3rd parties
    • Custom dependencies
module.factory('Service', function ($rootScope, $timeout, MyCustomDependency1, MyCustomDependency2) {
  return {
    //Something
  };
});
/* recommended */
angular
    .module('app')
    .controller('DashboardController', DashboardController);

DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice'];

function DashboardController($location, $routeParams, common, dataservice) {
}

For code documentation, use [js doc] (http://usejsdoc.org/).

'use strict';
  • avoid global variables
  • wrap custom code into an IIFE
(function (window, undefined) { <CODE> })(window);
  • In the feature directory, keep all relevant files (partials, controllers, directives, services, etc).
  • For filenames, use hyphens for filenames. (example-file-name.js)
  • For filenames, use . notation for file types or other identifies which are not the actual name. (example-file-name.spec.js)
  • For filenames, refrain from using the .tpls.html notation instead use .html.

HTML/CSS

  • Separate words in ID and class names by a hyphen
  • Avoid qualifying ID and class names with type selectors
  • When quoting attributes values, use double quotation marks.

Version control

  • We follow Vincent Driessen's branching model
  • Smaller (more fine grained) commits are better than large ones (makes debugging and understanding changes easier)
  • Commit messages should always reference issue numbers but do not close issues via commit messages
  • Create pull requests to merge feature branches into develop branch (merge only after changes were reviewed by at least one other team member)
  • Merge feature branches back into develop often (they should not exist for longer than a week or two at most)
  • Keep pull requests as small as practical, ideally under 100 lines of code (10 tips for better Pull Requests)
  • Name feature branches as follows: <your_github_username>/<feature_branch_name>
  • Provide commit message summary under 50 chars (Writing good commit messages)
  • Do not create more than one release branch at a time

Merging via command line:

Step 1: From your project repository, bring in the changes and test.

git fetch origin
git checkout -b <your_github_username>/<feature_branch_name> origin/<your_github_username>/<feature_branch_name>
git merge develop

Step 2: Merge the changes and update on GitHub.

git checkout develop
git merge --no-ff <your_github_username>/<feature_branch_name>
git push origin develop

Remember to delete feature, release and hotfix branches after they are merged - locally and on GitHub.

Issues and Milestones

  • The Next milestone should contain issues only temporarily (until the next dev meeting: issues should go into the backlog or the current milestone).
  • New issues should never go directly into the current milestone without team approval - only into the Next or the backlog.
  • Issues describing bugs or enhancements should contain the following sections: Steps to reproduce, Observed results, Expected results.
  • Milestones are named using following this pattern: "Release major.minor.patch" where major.minor.patch is a version number like 1.2.3

Exceptions:

References