Monday, June 22, 2015

The Twelve-Factor App

In the modern era, software is commonly delivered as a service: called web apps, or software-as-a-service. The twelve-factor app is a methodology for building software-as-a-service apps proposed by Heroku PaaS.

1. Codebase. One codebase tracked in revision control, many deploys.

One-to-one correlation between the codebase and the app:
  • If multiple codebases - it's not an app but distributed system. And each component in a distributed system is an app.
  • Multiple apps sharing the same code is violation of this principle. The solution is to factor code into libraries and include them via dependency management.
A deploy is the running instance of the app.

2. Dependencies. Explicitly declare and isolate dependencies.

Declare all dependencies completely and exactly via a dependency declaration manifest  and use dependency isolation tool during execution.

3. Config. Store config in the environment.

An app's config is everything that is likely to vary between deploys:
  • Resource handles to the database, Memcached, and other backing services
  • Credentials to external services such as Amazon S3 or Twitter
  • Per-deploy values such as canonical hostname for the deploy
Need strict separation of config from code. Config varies substantially across deploys, code doesn't. The twelve-factor app stores config in environment variables.

4. Backing Services. Treat backing services as attached resources.

It's an any service which app consumes over the network as part of its normal operation. The code of app makes no distinction between local (means deployed to localhost) and third party services.

5. Build, release, run. Strictly separate build and run stages.

For example, it is impossible to make changes to the code in the runtime. Release can not mutate once it is created and any changes must create a new release.

6. Processes. Execute the app as one or more stateless processes.

Twelve-factor processes are stateless and share-nothing. Any data that needs to persist must be stored in a stateful backing service, typically a database. Sticky sessions are violation of twelve-factor and should never be used or relied upon. Session state data is a good candidate for a datastore that offers time-expiration, such as Memcached or Redis.

7. Port binding. Export services via port binding.

The twelve-factor app is completely self-contained and does not rely on runtime injection of a webserver into the execution environment. The web app exports HTTP as a service by binding to a port, and listening to requests coming in on that port.

8. Concurrency. Scale out via the process model.

In the twelve-factor app, processes are a first class citizen. Developer can architect their app to handle diverse workloads by assigning each type of work to a process type. This does not exclude individual processes from handling their own internal multiplexing, via threads inside the runtime VM, or the async/evented model. But an individual VM can only grow so large (vertical scale), so the application must also be able to span multiple processes running on multiple physical machines.

The share-nothing, horizontally partitionable nature of twelve-factor app processes means that adding more concurrency is a simple and reliable operation.

9. Disposability. Maximize robustness with fast startup and graceful shutdown.

The twelve-factor app's processes are disposable, meaning they can be started or stopped at a moment's notice. Processes should strive to minimize startup time.

10. Dev/prod parity. Keep development, staging, and production as similar as possible.

Minimize gaps between development and production environments:
  • Make time gap small. A developer may write code and have it deployed hours or even just minutes later.
  • Make personnel gap small: developers involved in deploying app and watching its behavior in production.
  • Make the tools gap small: keep dev and prod as similar as possible.
The twelve-factor developer resists the urge to use different backing services between development and production.

11. Logs. Treat logs as event streams.

Logs are the stream of aggregated, time-ordered events collected from the output streams of all running processes and backing services.

A twelve-factor app never concerns itself with routing or storage of its output stream. Most significantly, the stream can be sent to a log indexing and analysis system, or a general-purpose data warehousing system such as Hadoop/Hive.

12. Admin Processes. Run admin/management tasks as one-off processes.

In a local deploy, developers invoke one-off admin processes by a direct shell command inside the app’s checkout directory. In a production deploy, developers can use ssh or other remote command execution mechanism provided by that deploy’s execution environment to run such a process.

References:
  1. The Twelve-Factor App

Friday, June 19, 2015

Criteria for Decomposing Systems into Modules

Benefits from decomposing system into modules:
  • Managerial - independent development
  • Flexibility - easier to change product
  • Comprehensibility - easier to understand product
Criteria for splitting system into modules:
  • BAD: Each module corresponds to separate subroutine
  • GOOD: Module hides ("chunk") information
What module should hide:
  • Important design decisions
  • Design decisions which is likely to change
What else:
  • Data structures
  • Sequence of instructions to call a given routine and the routine itself
  • The sequence of items processing
etc.

Hierarchical structure of dependencies and "clean" decomposition into modules are two desirable but independent properties of the system.

It is almost always incorrect to do decomposition of the system based on the flow chart (each block in the flow chart is a separate module). Instead begin with the list of difficult design decisions or decisions which is likely to change. Each module then designed to hide such decisions from others. So subroutines and programs (blocks in flow chart) is an assembled collection of code from various modules.

References:
  1. On the Criteria To Be Used in Decomposing Systems into Modules (1972)

Thursday, June 18, 2015

Fallacies of Distributed Computing

Above described assumptions which architects and designers of distributed systems are likely to make and which prove to be wrong in the long run:

  • The network is reliable. The network is unreliable and we need to address possible failure scenarios.
  • Latency is zero. Try to make few as possible network calls since latency is not zero and huge comparing to in-memory calls.
  • Bandwidth is infinite. Try to simulate real production environment.
  • The network is secure. You need to build security into your solution from Day 1. You need to be aware about security concerns and its implications even so the architect of the system should not be a security expert.
  • Topology doesn't change. In real environment topology can change. And you need to be aware regards it (e.g. use “Next Hop” routing or specify address by DNS name). 
  • There is one administrator. Administrators can constraint your options and you need to help them to manage your application.
  • Transport cost is zero. There are costs associated with both computational resources and money spent on network maintenance.
  • The network is homogeneous. Interoperability will be needed sooner or later. Do not rely on proprietary protocols but use standard technologies that are widely accepted.

References:
  1. Fallacies of Distributed Computing Explained

Tuesday, June 16, 2015

Distributed vs. Non-Distributed Computing

Distributed and non-distributed computing has conceptual differences:
  • Latency
  • Memory access
  • Partial failure
  • Concurrency
Major problems in distributed computing correspond to this differences:
  • Ensuring adequate performance
  • Dealing with differences in memory models between local and distributed entities
  • Dealing with partial failures and lack of a central resource manager
  • Dealing with problems of concurrency
Distributed application interface should reflect its distributed nature. Merging this two models leads to one of the following problems:
  • Making local computing looks like distributed makes local computing unnecessary difficult.
  • Making distributed computing looks like local leads to the unreliable system.

References:
  1. A Note on Distributed Computing (1994)