/bestpractices

Best practices and coding conventions for the Amnesty International Digital team

Amnesty International logotype and logo banner

Best Practices and Coding Conventions

This repository provides development guidelines for Amnesty International digital projects.

The contents of this repository are released under a Creative Commons CC BY 3.0 License and were adapted from the NPR Apps Best Practices Guide.

Contents

  1. Repositories
  2. Sites and Apps
  3. HTML and CSS
  4. Javascript
  5. Version control
  6. Ruby
  7. Python
  8. Servers
  9. Databases

Repositories

READMEs

Every README should include:

  1. A quick project overview
  2. Information about the directory structure
  3. How to get started developing
  4. Code promotion workflow
  5. Environments
  6. Links to where to find more information.

Plus:

  • Document steps to setup the project from a blank slate. (Data loading, etc.) This should include paths to files stored in Dropbox when relevant.
  • Document any required environment variables. If these are secrets they should also be stored in the team Dropbox.
  • Document any cron jobs that must be installed on the servers. In the app-template this just means using the crontab file in the project root.
  • Document any server dependencies that are not part of our standard stack. This includes documenting how to install them. Whenever feasible this documentation should be in the form of fab commands.

Sites and Apps

Naming conventions

Naming things (variables, files, classes, etc.) consistently and intuitively is one of the hardest problems in computer science. To make it easier, follow these conventions:

  • Always proceed from more general to more specific. For example, widget-skinny is better than skinny-widget.
  • Strive for parallelism. If you have a begin() function, then have an end() function (not stop() or done()).
  • Group related names with common prefixes, e.g. search_query and search_address.
  • Prefer more specific terms to more vague ones. If it's an address call it address, not location.
  • When a function operates on a variable, their naming should be consistent. If working with updates then process_updates(), don't process_changes().
  • Maintain naming conventions between iterables and their iterators: for update in updates, not for record in updates.
Prefer... To...
create insert, add, new
update change, edit
delete remove, purge
setup init
make build
wrapper wrap

(Note: sometimes these words don't mean the same thing, but when they do, prefer the former.)

HTML and CSS

  • Element IDs and class names should always be lowercase-with-dashes.
  • Multi-part IDs and class names should always proceed from more general to more specific. For example, electris-skinny is better than skinny-electris.
  • Put modals and JST templates in their own files. In the app-template these belong in the templates and jst directories, respectively. When this isn't feasible, put modals in the page footer first, followed by inline javascript templates.
  • Prefer HAML over ERB for HTML templates
  • Prefer SCSS over LESS for CSS templates
  • KSS for documenting and generating CSS styleguides

Javascript

General

  • Use 4-spaces for indentation (because it's easier to be consistent with Python than it is to switch your editor back and forth).
  • Javascript variables names should always be lowercase_with_underscores.
  • Static variables and configuration parameters should be in TITLECASE_WITH_UNDERSCORES.
  • All global variables should be defined at the top of the file.
  • All variables should be constrained to the current scope with var.
  • Declare only a single variable on one line.
  • End all statements with a semicolon.
  • Use spaces after opening and before closing braces and brackets in array and object definitions, i.e. { foo: [ 1, 2, 3 ] } not {foo:[1,2,3]}.
  • Do not use spaces after opening or before closing parentheses, i.e. if (foo == true) { and not if ( foo == true ) {.
  • When accessing properties of a data structure (such as one retrieved using getJSON) prefer bracket syntax (data["property"]) to attribute syntax (data.property).
  • Very frequent property references should be cached, i.e. var array_length = array.length;.
  • Use === rather than ==. (Why?)
  • Use single-quotes for strings.

Libraries

For consistency, prefer the following libraries to others that perform the same tasks:

  • jQuery for DOM manipulation
  • Underscore.js for functional programming (where Underscore and jQuery overlap, i.e. each(), prefer Underscore)
  • Bootstrap for responsiveness
  • Moment.js for datetime handling
  • jPlayer for audio/video playback

jQuery-specific

  • jQuery references that are used more than once should be cached. Prefix these references with $, i.e. var $electris = $("#electris");.
  • Whenever possible constrain jQuery DOM lookups within the scope of a cached element. For example, $electris.find(".candidate") is preferable to $(".candidate").
  • Always use on, never bind, delegate or live. on should also be preferred to "verb events", such as click.

Ruby

Baseline

Libraries

For consistency, prefer the following libraries to others that perform the same tasks:

Python

Baseline

Libraries

For consistency, prefer the following libraries to others that perform the same tasks:

  • Fabric for project tasks
  • Flask for light web apps or Django for heavy web apps
  • boto for accessing AWS programmatically
  • pytz for manipulating timezones
  • psycopg2 for accessing Postgres
  • lxml for XML/DOM manipulation
  • Requests for working over HTTP

Specifics

  • When testing for nulls, always use if foo is None rather than if !foo so foo == 0 does not cause bugs.
  • Always initialize and store datetimes in the UTC timezone. Never use naive datetimes for any reason.
  • Always use with when accessing resources that need to be closed.
  • Always access blocking resources (files, databases) as little as possible.
  • When accessing a dictionary element that may not exist, use get(). For example, os.environ.get('DEPLOYMENT_TARGET', None).
  • Project settings that will be used by both fabric and other code should be isolated in app_config.py. fabfile.py and Django's settings.py should import from this file to prevent duplication.
  • Imports should be organized into three blocks: stdlib modules, third-party modules and our own modules. Each group should be alphabetized.
  • Avoid from foo import *. It is the mindkiller.

Version control

Git

  • Don't store binary files (comps, databases) in the repository.
  • If a binary object needs to be shared then store it in Dropbox or on S3. If it is part of the setup process (e.g. a database backup) then use fabric commands to read and write it.
  • Never, ever store passwords, keys or credentials in any repository. (Use environment variables instead and .gitignore for configuration files.)

Github

Use Github Flow:

  1. Anything in the master branch is deployable
  2. To work on something new, create a descriptively named branch off of master (ie: new-oauth2-scopes)
  3. Commit to that branch locally and regularly push your work to the same named branch on the server
  4. When you need feedback or help, or you think the branch is ready for merging, open a pull request
  5. After someone else has reviewed and signed off on the feature, you can merge it into master
  6. Once it is merged and pushed to ‘master’, you can and should deploy immediately

Github flow in the browser:

  1. Create a branch right from the repository.
  2. Create, edit, and delete files, rename them, or move them around.
  3. Send a pull request from your branch with your changes to kick off a discussion.
  4. Continue making changes on your branch as needed, updating the pull request automatically.
  5. Once the branch is ready to go, the pull request can be merged using the big green button.
  6. Branches can then be tidied up using the delete buttons in the pull request, or on the branches page.
  7. Repeat.

Servers

  • Environment variables belong in /etc/environment. This file should be sourced by cron jobs. (This happens automatically when using cron.sh.)
  • Data for services provided by this system belong in /srv.

Databases

Naming conventions

General

  • Tables, columns, variables and view names should always be lowercase_with_underscores.
  • Naming order should be general_specific for instance, tweets_canadian.

Tables

  • Tables should have tbl_ prefix.
  • Tables should be plurals of contents, for instance people not person.

Views

  • Views should not have a prefix.

Reports

  • Reports should have standard English conventions for capitalisation and spacing.
  • Reports should be named team, general to specific, for instance Monthly giving campaign code details

Queries

  • Use 2-spaces for indentation.
  • Keywords, clauses, and operators should always be uppercase.