sentenz / convention

General articles, conventions, and guides.
https://sentenz.github.io/convention/
Apache License 2.0
4 stars 2 forks source link

Refactor article about `twelve-factor app` with ChatGPT #162

Closed sentenz closed 1 year ago

sentenz commented 1 year ago

Twelve-Factor App

The Twelve-Factor App methodology is a software design and development methodology that outlines best practices for building scalable, maintainable, and cloud-native software applications. It provides a theoretical framework for designing and building software systems that can run and scale Software-as-a-Service (SaaS) applications in modern cloud environments.

1. Category

The Twelve-Factor App principles are technology-agnostic, meaning that they can be applied with any programming language, framework, or infrastructure.

1.1. Codebase

The Codebase principle states that each application should have a single, version-controlled codebase that is used to deploy the application across all environments.

In practice, this means that developers should use a version control system, such as Git or SVN, to manage the source code for their application. The codebase should include all of the application's code, dependencies, and configuration files, and should be used to deploy the application across all environments, from development to production.

By using a single, version-controlled codebase, developers can more easily manage and maintain their applications, and can ensure that all changes to the application are tracked and versioned. This can also help to ensure that all environments are consistent, and can reduce the risk of issues that may arise from differences between environments.

Benefits to using a single, version-controlled codebase:

1.2. Dependencies

The Dependencies principle states that applications should explicitly declare and isolate their dependencies. In a modern software application, dependencies can include libraries, frameworks, and other third-party software components that your application relies on to function properly.

In practice, this means that developers should define their application's dependencies in a manifest or configuration file, such as a package.json file in Node.js or a requirements.txt file in Python. Dependencies should be declared explicitly, including the version numbers of each dependency, and should be isolated from the application's runtime environment. This can be achieved using tools such as virtual environments, Docker containers, or other containerization technologies.

By explicitly declaring and isolating dependencies, developers can ensure that their applications are consistent across all environments, and can avoid potential issues that may arise from differences in dependencies or dependency versions. This can also help to ensure that the application can be easily deployed and scaled across different environments, and can be easily updated or modified over time.

Benefits to explicitly declaring and isolating dependencies:

NOTE See package managers for details.

1.3. Config

The Config principle states that an application's configuration should be stored in environment variables, and should be separate from the application code.

In practice, this means that developers should store any configuration information that may change between environments, such as database credentials or API keys, in environment variables rather than hard-coding them into the application code. This can be achieved using a configuration management tool, such as Puppet or Chef, or by using a platform-as-a-service (PaaS) provider, such as Heroku, that provides built-in support for environment variables.

By storing configuration information in environment variables, developers can more easily manage and maintain their applications, and can ensure that the same application code can be used across different environments, without needing to modify the code for each environment. This can also help to ensure that sensitive information, such as passwords or API keys, are not hard-coded into the application code, and are instead stored securely in environment variables.

Benefits to storing configuration information as environment variables:

NOTE Avoid storing sensitive information such as secrets and credentials in environment variables, as any process running on the same machine can access them and they can be accidentally exposed through logs or debugging messages. Instead use a secure secret manager such as Hashicorp Vault or Dapr to store sensitive information. However, in cases where environment variables are used, it is recommended to avoid global variables and instead use an .env file to store configuration information. The .env file should be kept secure and not accessible to unauthorized users.

1.4. Backing Services

The Backing Services principle states that any external resources that the application depends on, such as databases, message queues, or caching systems, should be treated as attached resources, and accessed via a well-defined API.

In practice, this means that applications should not depend on specific instances of backing services, but rather should be designed to work with a variety of different providers, and to use service discovery and configuration management tools to locate and connect to the appropriate services.

By treating backing services as attached resources, and accessing them via a well-defined API, applications can be more easily scaled, deployed, and updated, since the dependencies on the underlying services are managed separately from the application code.

Benefits to treating backing services as attached resources:

1.5. Building, Release, Run

The Building, Release, Run principle states that an application should be built, released, and run as discrete stages, with each stage having a distinct responsibility. It is a process-oriented approach that emphasizes the separation of concerns between the development, deployment, and operation of the application.

In practice, this means that the development process should be broken down into three stages:

By breaking down the development process into these discrete stages, developers can more easily manage and maintain their applications, and can ensure that each stage has a distinct responsibility. This can also help to ensure that the application is easily deployable and scalable across different environments, and that changes can be made to the application without affecting the runtime environment.

Benefits of stages:

NOTE See continuous pipelines for details.

1.6. Processes

The Processes principle states that applications should be designed as stateless processes, which do not maintain any application state in memory.

In practice, this means that applications should avoid storing session state, caches, or any other kind of stateful data in memory. Instead, applications should store all stateful data in external services, such as databases or caching systems, and use stateless processes to access this data via well-defined APIs.

By designing applications as stateless processes, developers can make them more easily scalable, deployable, and fault-tolerant. Stateless processes can be scaled horizontally by running multiple instances of the same codebase, and can be deployed and updated independently of each other. Since each process does not maintain any application state in memory, it can be easily replaced or restarted without affecting the overall application state.

Benefits to designing applications as stateless processes:

1.7. Port Binding

The Port Binding principle states that applications should be self-contained and should not rely on the availability of any external dependencies to run.

In practice, this means that applications should listen on a designated port and bind to it when started. The application should not assume anything about the host environment, such as the availability of a specific port or network interface, but should instead use environment variables or configuration files to specify the port number and other network settings.

By adhering to the Port Binding principle, applications can be more easily deployed and scaled across different environments, since the application code does not rely on any external dependencies or assumptions about the host environment.

Benefits of Port Binding:

1.8. Concurrency

The Concurrency principle states that applications should be designed to take advantage of concurrency and parallelism in order to maximize resource utilization and responsiveness.

In practice, this means that applications should be designed to handle multiple requests and processes concurrently, rather than relying on a single thread or process to handle all incoming requests. This can be achieved through the use of asynchronous programming models, such as event-driven architectures or reactive programming, or by using techniques such as thread pooling and load balancing to distribute requests across multiple threads or processes.

By designing applications to take advantage of concurrency, developers can improve the performance, scalability, and responsiveness of their applications, especially in high-traffic or high-load scenarios. Concurrency allows multiple requests to be processed simultaneously, reducing latency and increasing throughput.

Benefits to designing applications to take advantage of concurrency:

1.9. Disposability

The Disposability principle states that applications should be designed to be easily disposable and replaceable, and should be able to start up and shut down quickly without causing any data loss or corruption.

In practice, this means that applications should be designed to handle graceful shutdowns and restarts, and should be able to recover quickly from crashes or other failures. Applications should not rely on long-lived processes or in-memory state, but should instead use external storage systems, such as databases or file systems, to store application data.

By designing applications to be disposable, developers can ensure that their applications can be easily updated, scaled, and replaced without causing any disruptions to the user experience or data integrity. Disposable applications are also more fault-tolerant, since they can recover quickly from crashes or other failures without losing data or requiring manual intervention.

Benefits to designing applications to be disposable:

1.10. Dev/Prod Parity

The Dev/Prod Parity principle states that the development, testing, and production environments should be as similar as possible in order to minimize differences and avoid issues that may arise from environment-specific configuration or dependencies.

In practice, this means that developers should strive to use the same tools, libraries, and configurations in all environments, and should avoid making changes to the production environment that are not tested and approved in the development and testing environments. This can be achieved through the use of automated build and deployment processes, version control, and configuration management tools.

By ensuring that the development, testing, and production environments are as similar as possible, developers can reduce the risk of issues arising from environment-specific differences, such as incompatible dependencies, configuration mismatches, or differences in hardware or software. This can help to minimize the time and effort required to deploy and maintain applications, and can improve the overall quality and reliability of the application.

Benefits to ensuring dev/prod parity:

1.11. Logs

The Logs principle states that applications should treat logs as event streams, and should generate logs in a standardized format that can be easily aggregated and analyzed.

In practice, this means that developers should design their applications to generate logs in a standardized format, such as JSON or syslog, and should use logging frameworks and libraries that support structured logging. Logs should be generated for all significant events, such as requests, errors, and warnings, and should be sent to a centralized logging service, such as Splunk, Logstash or Elasticsearch, where they can be aggregated, analyzed, and searched.

By treating logs as event streams, developers can gain valuable insights into the behavior and performance of their applications, and can quickly identify and diagnose issues that may arise. Centralized logging also makes it easier to manage and maintain logs, and can provide a centralized source of truth for debugging and troubleshooting.

Benefits to treating logs as event streams:

Example of conform Logs principle:

package main

import (
    "os"
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
)

func main() {
    // Set up the logger to output JSON format
    log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})

    // Set the logging level to debug
    zerolog.SetGlobalLevel(zerolog.DebugLevel)

    // Log some events
    log.Debug().Msg("This is a debug message")
    log.Info().Msg("This is an info message")
    log.Warn().Msg("This is a warning message")
    log.Error().Msg("This is an error message")
}

1.12. Admin Processes

The Admin Processes principle states that applications should provide administrative processes as one-off processes that can be run independently of the application's main processes.

In practice, this means that developers should design their applications to provide a set of administrative processes that can be used to perform tasks such as database migrations, backups, and other maintenance tasks. These administrative processes should be run independently of the application's main processes, and should be designed to run in a single execution environment, rather than being part of the application's ongoing runtime.

By providing administrative processes as one-off processes, developers can more easily manage and maintain their applications, and can avoid potential issues that may arise from running administrative tasks as part of the application's ongoing runtime. This can also help to ensure that administrative tasks are performed in a consistent and repeatable manner, regardless of the underlying environment.

Benefits to providing administrative processes as one-off processes:

2. References

github-actions[bot] commented 1 year ago

:tada: This issue has been resolved in version 1.20.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket: