Future-proof systems#
Preparation#
Read the following:
Introduction#
- Future-proof system
- system that is designed to remain functional, adaptable, and maintainable over time. Change of new hardware and maintaining personal should not significantly affect its operation. 
Exercise 8
Look at Designing a future-proof health monitoring system. Which guidelines would you have for this system?
Exercise 9
Do you think mainframe computers are future-proof? Why?
 
Fig. 5 A pair of IBM mainframes. On the left is the IBM z Systems z13. On the right is the IBM LinuxONE Rockhopper.
 CC BY-SA 4.0. By Agiorgio. Source: Wikimedia Commons#
The twelve factors#
- Twelve-Factor App methodology
- a methodology for building software-as-a-service applications with portability and resilience in mind. 
Following comments based on Twelve Factor commit fcb9858
Codebase#
- codebase
- any single repository, or any set of repositories that share a root commit. 
 
Fig. 6 One codebase can be deployed in multiple environments#
- one codebase corresponds to a single app. 
- two apps require two codebases - here we have a distributed system 
 
Exercise 10
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Dependencies#
- app declares all dependencies, which results in a deterministic environment, e.g., - Python - pip install
- Ruby - bundle installusing- Gemfile
 
- app does not rely on implicit existence of system-wide packages. Dependencies are isolated if possible, e.g., - Python - virtualenv
 
- app does not rely on the implicit existence of any tools, e.g., - ImageMagickor- curlshould be vendored into the app
 
Exercise 11
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Config#
- config
- everything that is likely to vary between deploys 
- separation - strict separation of config from code 
- different deployments differ in their configuration 
 
- environment variables are better than - config.yml, because- files can be accidentally committed to the repository 
- files can be scattered in different places 
- environment variables are language- or framework-agnostic 
 
- treat environment variables as granular controls, never group them by environment, i.e., instead of a single - config.jsthat contains all the environments, reference environment variables in- config.js.
Exercise 12
Imagine a software-as-a-service application that serves multiple customers. Each gets a dedicated instance of the app, and each instance is deployed separately.
Does the following approach follow the config guideline of the twelve-factors methodology? If not, improve it. If yes, explain.
environments:
  staging:
    DATABASE_URL: postgres://staging-db-url
    API_KEY: staging-api-key
  production:
    DATABASE_URL: postgres://prod-db-url
    API_KEY: prod-api-key
Backing services#
- backing service
- any service the app consumes over the network as part of its normal operation 
e.g., PostgreSQL, RabbitMQ, Dovecot, Memcached, OpenStreetMap etc
- each backing service is treated as an attachable resource.   - Fig. 7 Backend services are attached using URLs. This makes their replacement convenient.# 
- Every service can be used as an URL. From the perspective of the deployment, there is no distinction between a self- and externally-managed backend service. 
Exercise 13
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Build, release, run#
- Distinct stages - build: transforms into an executable bundle using the dependencies
- release: deploys the executable using the config
- run: runs the app in the execution environment
 
- each release is a unique, immutable snapshot that can be referenced 
- keep - runminimal, push complexity to the- build
Exercise 14
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Processes#
- app is executed as one or more processes. 
- app’s processes are stateless and share nothing. This enables convenient horizontal scaling. Each request can be served by any node. The sessions can be stored in Memcached or Redis. 
Exercise 15
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Port binding#
- an app is completely self-contained, instead of being a module of another service. Example: a PHP app must be started by Nginx. Solution: integrate a web server in your app, e.g., Tornado in Python 
- Use port binding to route requests to your app. Example your app runs on - localhost:5000which you use for development. In deployment, the webserver routes the requests to this port.
Exercise 16
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Concurrency#
- an app is executed as one or more processes, e.g., PHP as child processes of the webserver started by demand, Java executes a single process, but starts individual threads 
- apps use unix process model. If the app needs many processes, then all the processes are started using a single point. 
- Separate different workload types to different processes for scaling 
- Execution environment manages processes, e.g., systemd, foreman (during development) 
 
Fig. 8 An app divided into three different process types. This allows scaling dependent on the workload type#
Exercise 17
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Disposability#
- Processes can be started or stopped easily without impacting the whole applications - e.g., a web server process can be replaced on demand without disrupting the system after an update 
 
- Minimize start-up time - minimizes down-time after an update or failover 
 
- Graceful shutdown - Web process: - cease to listen on the service port, or send a maintenance message 
- allow current requests to finish 
 - Worker process: - Return the current job to the work queue, e.g., in RabbitMQ 
 
- Tolerance against sudden termination, e.g., hardware failure - e.g., return the requests to the queue and retry, if the service times out 
 
Exercise 18
- Does this factor make the system future-proof? 
- If yes, give a counter example which shows that the system is not future-proof if this factor is not taken into account. 
Development/production parity#
minimize the gaps between development, staging and production. Gap examples
- time gap: developer’s work takes a long time and is not deployed 
- personnel gap: developers are not involved in deployment (but ops engineers) 
- tools gap: developers use local tools, e.g., SQLite instead of PostgreSQL. 
Solutions:
- use CI/CD 
- use the same tools as in the production 
Logs#
- app produces logs as a stream of time-ordered events, e.g., - text format 
- one event per line typically 
- exceptions may span over many lines 
- unbuffered 
- use - stdoutor- stderr
 
- app is not responsible for routing or storage of logs. - use Splunk for log indexing and analysis 
- Hadoop/Hive as a general-purpose data warehousing system 
- these tools can implement anomaly detection 
 
Admin processes#
- administrative task processes are separate from business logic processes 
- admin processes are part of the same environment, i.e., use the config file 
Example:
- bundle exec thin startfor a web process
- bundle exec rake db:migratefor an administration task
Criticism#
Note that the criticism may be directed on a previous version of the methodology. We covered the live version.
- this methodology does not mean that monolithic applications are always bad 
Further resources#
- Patterns of distributed systems - First chapter summarizes general problems of distributed systems. Designing the system with these problems in mind could make the system more resilient and future-proof 
 
- API versioning: strategies & best practices - design the API so that it can evolve with backwards compatibility