Software Development Styleguide

From aptrust
Jump to: navigation, search

This is a brief document to cover some best practice guidance for development or coding styleguide information for anyone working with code that is part of APTrust.  It is provided as a general guideline for how to write code or encourage practices that promote re-usability, readability and sustainability of our technical infrastructure.  Traditional software engineering best practices for things like encapsulation, DRY and the use of design patterns is of primary importance, this guide only attempts to add a few other specifics over normal best practices.

Testing Requirements

As a general rule all non-private methods should be covered with unit tests.   Use coveralls where possible to express unit test coverage to we can more easily assess high level code quality.

Mocking Services

Where possible you should mock services in unit tests or use a test environment option that can be easily mimic a service (for instance using sqlite for tests over a larger database).  Various languages and frameworks provide options for mocking such as 'mock' and 'nose' in python.

Fixtures

Where possible try to generate fixtures within unit tests rather than depending on an import file.  FactorGirl in ruby is a good example and there are equivalents in Python too.

Continuous Integration Testing

We use TravisCI with hooks to our GitHub codebases for continuous integration and testing.  By default you should configure it so only the master and develop branches are tested.

ReadMe Files

The top level of each codebase should always contain a README or README.md file that contains at least a quick summary of what that code is for and any other important information about the code.  They should use the markdown regardless of other internal documentation tools like rDoc or Sphinx documentation for consistency.

Coding Styleguides

Below are some best practices for writing code in a similar style to maintain readability and consistency across codebases.

Ruby

Unless otherwise specified below, please follow the recommendations on the GitHub Ruby Style Guide when coding in Ruby.

Documentation

Use rDoc spec for docstring comments in your ruby codbase if there is a question.

Hashes

Use the Hash Literal method introduced in Ruby 1.9.  For example:
# good 1.9+ style

robot = {

   name:  "Optimus Prime",

   type:  "Autobot",

}

# bad

robot = {

   :name => "Megatron",

   :type => "Decepticon",

}

Python

Unless otherwise specified below, please follow the PEP-8 recommendation.  We are striving to use python3 wherever possible to take advantage of the improved async libraries and native unicode support.

Documentation

Please use Restructured Text format for docstring comments and the Sphinx documentation library for expanded documentation if needed.

Go

When creating go code please follow the recommendations in the Effective Go documentation.

Git Revision Control

By default we use the Git version control system hosted under the APTrust GitHub Organization.  Because of highly dependent nature of libraries and code being used in the project we will generally manage code under a single Git repository  In cases where it makes sense like prototype or with completely disconnected codebases we may pursue separate repositories but for now we will us a single primary repository to be noted and linked here after it is created.

Currently all GitHub repositories are publicly accessible so please keep this in mind while coding. Refrain from committing any sensitive information like API or Access Keys. These are to be stored securely in encrypted Ansible files and populated in the codebase at deployment time.

Git Branching and Tagging Strategy

Overall we recommend a standard Master/Develop/Release/Tag strategy as explained in This Post at Nive.com.  In a nutshell:

  • Master: Should contain the most resent stable codebase.  Tags should be created from code merged back to Master from Develop to
  • Develop:  Should be for stable development code and generally use for integration and merging from release branches.  Develop should see frequent merges coming from release branches as needed.
  • Release:  Generally a non-stable local release branch.
  • Tags:  Serve as the formal version-ed releases of a codebase.

GitFlow is a great Git extension that will essentially help mange the branching and tagging strategy for you.  I've gotten a lot of use out it and would recommend it.

Twelve Factor Apps

We strive to design, build and deploy applications by adhering to the 12-factor principles :

  • Use declarative formats for setup automation, to minimize time and cost for new developers joining the project;
  • Have a clean contract with the underlying operating system, offering maximum portability between execution environments;
  • Are suitable for deployment on modern cloud platforms, obviating the need for servers and systems administration;
  • Minimize divergence between development and production, enabling continuous deployment for maximum agility;
  • And can scale up without significant changes to tooling, architecture, or development practices.

Namely these are:

I. Codebase

One codebase tracked in revision control, many deploys
Keeping each APTrust app in a single repository, but there can be many deploys per app (staging, demo, production)

II. Dependencies

Explicitly declare and isolate dependencies
APTrust uses different dependency management tooling with each app. For Golang apps we use Glide, for Rails app we use Bundler

III. Config

Store config in the environment
APTrust app configuration is largely stored (encrypted) within the configuration management tooling Ansible. Envrionments are setup with the appropriate variables at deployment time. All APTrust app's keep non-sensitive default config variables within their repository to allow for local development setups.

IV. Backing services

Treat backing services as attached resources
Backing services like databases are accessed per URI as attached resources. A change to environment variables at deployment, can point to different resources. There are no hardcoded values (unless local development defaults).

V. Build, release, run

Strictly separate build and run stages

VI. Processes

Execute the app as one or more stateless processes

VII. Port binding

Export services via port binding

VIII. Concurrency

Scale out via the process model

IX. Disposability

Maximize robustness with fast startup and graceful shutdown

X. Dev/prod parity

Keep development, staging, and production as similar as possible

XI. Logs

Treat logs as event streams

XII. Admin processes

Run admin/management tasks as one-off processes