---
url: /java/12-Factor十二要素应用宣言.md
---
# 十二要素应用宣言 (12-Factor App) 深度总结

> **来源**: [12factor.net](https://12factor.net/zh_cn/) | 作者：Adam Wiggins | 更新：2017

***

## 1. 概述

### 1.1 什么是 12-Factor App？

**十二要素应用宣言（The Twelve-Factor App）** 是一套构建现代 **SaaS 应用** 的方法论，由 Heroku 的开发者们在 2012 年提出。这套方法论基于作者在超过一百个 SaaS 项目开发中的实践经验，并通过 Heroku 平台见证了数十万应用程序的开发、部署和扩展过程。

### 1.2 核心价值

| 价值维度 | 说明 |
|----------|------|
| **标准化** | 自动配置，降低新人学习成本 |
| **可移植性** | 与操作系统划清界限，实现环境无关 |
| **云部署** | 专为现代云计算平台设计 |
| **持续交付** | 缩小开发/生产环境差异 |
| **扩展性** | 无需明显改变工具或架构即可横向扩展 |

### 1.3 目标读者

* **开发人员**: 任何 SaaS 应用的开发团队成员
* **运维工程师**: 负责部署和管理应用的运维人员
* **架构师**: 设计云原生系统架构的技术领导者

### 1.4 适用场景

* ✅ 新建的 SaaS 应用项目
* ✅ 计划迁移到云端的传统应用
* ✅ 需要改善 DevOps 流程的团队
* ✅ 微服务架构的设计与实施

***

## 2. 核心概念详解

### 2.1 术语定义

| 术语 | 定义 |
|------|------|
| **基准代码 (Codebase)** | 使用版本控制系统跟踪的一份代码库 |
| **部署 (Deploy)** | 运行应用的一个实例，对应一个发布版本 |
| **发布 (Release)** | 代码 + 配置的合并产物，可直接运行 |
| **进程 (Process)** | 应用中运行的一个工作单元 |
| **后端服务 (Backing Services)** | 应用通过连接使用的辅助服务（数据库、队列等） |
| **环境 (Environment)** | 应用运行的上下文，包括环境变量和资源 |

***

## 3. 十二要素完整解析

### Ⅰ. 基准代码 (Codebase)

> **"一份基准代码，多份部署"**

#### 核心原则

* 一个应用必须对应 **一个版本控制仓库**
* 每次部署都从同一代码库检出
* **多个应用不能共享同一代码库**（违反则成为分布式系统而非单一应用）
* 共享代码应拆分为独立的类库，通过依赖管理加载

#### 实践方法

```
✅ 正确做法:
- GitHub/GitLab 上每个应用一个仓库
- 分支是轻量级的，仍属于同一代码库

❌ 错误做法:
- 多个应用放在同一个 Git 仓库
- 不同服务共享同一套业务代码
```

#### 解决痛点

* 确保多台机器上的应用程序源代码一致
* 提升排查问题的效率
* 简化版本管理和追踪

***

### Ⅱ. 依赖 (Dependencies)

> **"显式声明依赖关系"**

#### 核心原则

* **绝不依赖**隐式存在的系统全局包
* 所有依赖必须 **显式声明** 并 **隔离管理**
* 使用包管理工具进行依赖控制

#### 实践方法

```bash
# Node.js - package.json
{
  "dependencies": {
    "express": "^4.18.0",
    "lodash": "^4.17.21"
  }
}
npm install

# Python - requirements.txt + Virtualenv
requests==2.28.0
flask==2.2.0
pip install -r requirements.txt

# Java - Maven pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.20</version>
    </dependency>
</dependencies>

# Go - go.mod
module myapp
go 1.21

require github.com/gin-gonic/gin v1.9.0
```

#### 解决痛点

* 避免"在我机器上能跑"的问题
* 明确项目依赖关系，便于维护和升级
* 防止依赖冲突和版本不一致

***

### Ⅲ. 配置 (Config)

> **"在环境中存储配置"**

#### 核心原则

* 配置必须在不同部署间 **可以变化**
* 配置与代码 **严格分离**
* 使用 **环境变量** 存储配置

#### 什么是配置？

* 数据库连接字符串
* API 密钥和令牌
* 第三方服务凭证
* 功能开关
* 环境特定的 URL

#### 什么不是配置？

* `config/routes.rb` (内部路由配置)
* 依赖注入关系 (框架内部配置)
* 业务逻辑相关的常量

#### 实践方法

```bash
# ❌ 错误：硬编码在代码中
const DB_HOST = "localhost";
const API_KEY = "sk-123456";

# ❌ 错误：配置文件签入代码库
# config/database.yml
database: production
  host: localhost
  password: secret123

# ✅ 正确：使用环境变量
const DB_HOST = process.env.DB_HOST;
const API_KEY = process.env.API_KEY;

# 部署时设置
export DB_HOST="prod-db.example.com"
export API_KEY="sk-prod-key"
```

#### 优势对比

| 方式 | 安全性 | 灵活性 | 跨语言 |
|------|--------|--------|--------|
| 代码常量 | ❌ 低 | ❌ 差 | ❌ 无 |
| 配置文件 | ⚠️ 中 | ⚠️ 中 | ❌ 无 |
| 环境变量 | ✅ 高 | ✅ 好 | ✅ 通用 |

#### 解决痛点

* 安全：代码开源不会泄露敏感信息
* 灵活：修改配置无需重新编译
* 通用：与语言和系统无关

***

### Ⅳ. 后端服务 (Backing Services)

> **"把后端服务当作附加资源"**

#### 核心原则

* 将数据库、消息队列、缓存等视为 **附加资源**
* 通过 **配置** 连接这些服务
* 服务应该 **易于替换**

#### 典型后端服务

* 数据库 (MySQL, PostgreSQL, MongoDB)
* 缓存 (Redis, Memcached)
* 消息队列 (RabbitMQ, Kafka, ActiveMQ)
* 邮件服务 (SMTP, SendGrid)
* 对象存储 (S3, Azure Blob)

#### 实践方法

```java
// ❌ 错误：硬编码服务地址
public class Database {
    private static final String URL = "jdbc:mysql://localhost:3306/mydb";
}

// ✅ 正确：通过配置获取
public class Database {
    private static final String URL =
        System.getenv("DATABASE_URL");
}

// 切换服务只需修改配置
// 从 MySQL 切换到 RDS
export DATABASE_URL="jdbc:mysql://rds-endpoint.rds.amazonaws.com:3306/mydb"
```

#### 解决痛点

* 服务变更对代码透明
* 开发环境和生产环境使用相同类型的服务
* 支持快速迁移和故障转移

***

### Ⅴ. 构建，发布，运行 (Build, Release, Run)

> **"严格分离构建、发布和运行阶段"**

#### 核心原则

* **构建阶段**: 编译代码、打包依赖
* **发布阶段**: 代码 + 配置 = 发布版
* **运行阶段**: 在环境中执行发布版

#### 流程图解

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Build     │  →  │   Release   │  →  │    Run      │
│  (构建)      │     │  (发布)      │     │  (运行)      │
└─────────────┘     └─────────────┘     └─────────────┘
      │                   │                   │
   代码 + 依赖          代码 + 配置          执行实例
```

#### 实践方法

```bash
# 构建阶段
git clone https://github.com/example/app.git
cd app
npm install           # 安装依赖
npm run build         # 编译/打包

# 发布阶段
# 创建包含代码和配置的发布版本
RELEASE_ID=$(date +%Y%m%d-%H%M%S)
deploy --release $RELEASE_ID

# 运行阶段
# 在特定环境中启动发布版本
run --release $RELEASE_ID --env production
```

#### CI/CD 示例

```yaml
# .github/workflows/deploy.yml
name: Deploy
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm ci
      - run: npm run build
      - run: npm test

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh ${{ github.sha }}
```

#### 解决痛点

* 构建产物不可变，保证一致性
* 回滚只需切换发布版本
* 明确各阶段职责，便于调试

***

### Ⅵ. 进程 (Processes)

> **"以一个或多个无状态进程运行应用"**

#### 核心原则

* 应用以 **一个或多个进程** 运行
* 进程必须是 **无状态** 的
* 不存储任何会话数据到内存或磁盘

#### 什么是无状态？

```
✅ 无状态进程:
- 请求处理不依赖本地存储的状态
- 用户会话数据存储到数据库/Redis
- 任意进程可以处理任意请求

❌ 有状态进程:
- 将会话数据存储在本地内存
- 依赖本地文件存储用户上传
- 特定用户总是被路由到特定进程
```

#### 实践方法

```python
# ❌ 错误：本地内存存储会话
class SessionStore:
    def __init__(self):
        self.sessions = {}  # 内存字典

# ✅ 正确：外部存储
import redis
class SessionStore:
    def __init__(self):
        self.redis = redis.Redis(host='redis-host')

    def get(self, session_id):
        return self.redis.get(session_id)
```

#### 允许的"状态"

* **本地缓存**: 允许，但必须可丢弃且可不同步
* **预热数据**: 允许，但必须有预热机制
* **计算中间结果**: 允许，但不能跨请求持久化

#### 解决痛点

* 天然支持水平扩展
* 进程失败不影响整体服务
* 简化负载均衡策略

***

### Ⅶ. 端口绑定 (Port Binding)

> **"通过端口绑定提供服务"**

#### 核心原则

* 应用 **自己监听端口**
* 不依赖外部 Web 服务器转发
* 完全自包含的运行单元

#### 实践方法

```javascript
// Node.js 示例
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

// Spring Boot 示例
// application.properties
server.port=${SERVER_PORT:8080}

// Go 示例
port := os.Getenv("PORT")
if port == "" {
    port = "8080"
}
http.ListenAndServe(":" + port, nil)
```

#### 架构对比

```
❌ 旧架构:
用户 → Apache/Nginx → 应用 (不监听端口)
                    ↓
              通过 FastCGI/SCGI 通信

✅ 新架构:
用户 → Nginx(仅反向代理) → 应用 (监听端口)
                         ↓
                   直接 HTTP 通信
```

#### 解决痛点

* 应用完全自包含
* 更容易容器化和部署
* 支持灵活的拓扑结构

***

### Ⅷ. 并发 (Concurrency)

> **"通过进程模型进行扩展"**

#### 核心原则

* 通过 **增加进程数量** 来扩展
* 而不是增加单个进程的資源
* 使用进程类型区分工作负载

#### 进程类型示例

```
Web 进程: 处理 HTTP 请求
Worker 进程: 处理后台任务（邮件发送、数据处理）
Scheduler 进程: 定时任务调度
```

#### 实践方法

```yaml
# Kubernetes 部署示例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 5  # 扩缩容只需修改这个值
  selector:
    matchLabels:
      app: myapp
  template:
    spec:
      containers:
      - name: web
        image: myapp:latest
        ports:
        - containerPort: 8080
      - name: worker
        image: myapp:latest
        command: ["npm", "run", "worker"]
```

```bash
# Procfile 示例 (Heroku)
web: node server.js
worker: node worker.js
scheduler: node scheduler.js
```

#### 扩展类比

> "就像便利商店，忙的时候多开几个柜台，而不是训练一个超人店员"

#### 解决痛点

* 线性扩展能力
* 根据负载类型独立扩展
* 适应云原生环境

***

### Ⅸ. 易处理 (Disposability)

> **"快速启动和优雅终止可最大化健壮性"**

#### 核心原则

* 进程可以随时 **启动** 或 **关闭**
* **快速启动**: 理想情况 < 10 秒
* **优雅关闭**: 完成当前请求后再退出

#### 快速启动技巧

```
✅ 减少启动时的重型操作
✅ 懒加载非必需组件
✅ 连接池预warm而非全量初始化
✅ 避免启动时同步下载大量数据
```

#### 优雅关闭实现

```java
// Spring Boot 优雅关闭示例
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setAddShutdownHook(false); // 手动管理
        ConfigurableApplicationContext context = app.run(args);

        // 注册关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("收到关闭信号，开始优雅关闭...");

            // 1. 停止接收新请求
            webServer.stopAcceptingRequests();

            // 2. 等待现有请求完成
            awaitActiveRequestsComplete();

            // 3. 关闭连接池
            dataSource.close();

            // 4. 刷新日志
            flushLogs();

            // 5. 关闭应用上下文
            context.close();

            System.out.println("应用已优雅关闭");
        }));
    }
}
```

```go
// Go 优雅关闭示例
func main() {
    srv := &http.Server{Addr: ":8080", Handler: handler}

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    go func() {
        sig := make(chan os.Signal, 1)
        signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
        <-sig

        // 优雅关闭
        if err := srv.Shutdown(ctx); err != nil {
            log.Fatal("强制关闭:", err)
        }
    }()

    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatal(err)
    }
}
```

#### 解决痛点

* 快速扩容应对流量高峰
* 滚动更新零停机
* 提高系统健壮性

***

### Ⅹ. 开发环境与线上环境等价 (Dev/Prod Parity)

> **"尽可能的保持开发、预发布、线上环境相同"**

#### 核心原则

* 三种差距要 **最小化**:
  1. **时间差距**: 快速部署，今天写的代码明天就能上线
  2. **人员差距**: 开发者也参与部署流程
  3. **工具差距**: 开发用 PostgreSQL，正式环境也用

#### 环境对比表

| 维度 | ❌ 传统做法 | ✅ 12-Factor 做法 |
|------|------------|------------------|
| 数据库 | 开发 SQLite，生产 MySQL | 全程 PostgreSQL |
| 缓存 | 开发不用，生产 Redis | 全程 Redis |
| 消息队列 | 开发用模拟，生产 RabbitMQ | 全程 RabbitMQ |
| 部署方式 | 手动 FTP 上传 | Docker 容器 |
| 部署频率 | 每月一次 | 每天多次 |

#### Docker 实践

```dockerfile
# Dockerfile - 所有环境统一
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
```

```yaml
# docker-compose.yml - 本地开发环境
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    depends_on:
      - db
      - redis

  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb

  redis:
    image: redis:7-alpine
```

#### 解决痛点

* 让故障提前在测试环境发生
* 减少"在我机器上是好的"问题
* 提高部署可靠性

***

### Ⅺ. 日志 (Logs)

> **"把日志当作事件流"**

#### 核心原则

* 应用只管 **输出到 stdout/stderr**
* **不管理** 日志文件
* 日志由外部系统收集和处理

#### 架构流程

```
应用 → stdout → Log Agent → 集中式日志系统
                     ↓
              ELK Stack / CloudWatch / Datadog
```

#### 实践方法

```javascript
// ❌ 错误：写入日志文件
const fs = require('fs');
fs.appendFileSync('app.log', 'User logged in\n');

// ✅ 正确：输出到 stdout
console.log('User logged in');
console.error('Database connection failed');
```

```python
# Python 示例
import logging
import sys

logger = logging.getLogger(__name__)
handler = logging.StreamHandler(sys.stdout)
logger.addHandler(handler)
logger.info('Application started')
```

#### 日志消费示例

```bash
# Docker 日志查看
docker logs -f myapp

# Kubernetes 日志查看
kubectl logs -f deployment/myapp

# 集中式日志查询 (ELK)
# Kibana 中搜索: service_name:myapp AND level:error
```

#### 现代演进：可观测性三支柱

| 支柱 | 作用 | 代表工具 |
|------|------|---------|
| **日志 (Logs)** | 记录具体事件 | ELK, Loki |
| **追踪 (Tracing)** | 请求链路追踪 | Jaeger, Zipkin |
| **指标 (Metrics)** | 性能数据统计 | Prometheus, Grafana |

#### 解决痛点

* 不用担心硬盘被塞爆
* 统一收集所有容器的日志
* 方便全文检索和分析
* 即使应用崩溃，日志依然保留

***

### Ⅻ. 管理进程 (Admin Processes)

> **"后台管理任务当作一次性进程运行"**

#### 核心原则

* 数据库 migration、数据清理等作为 **一次性进程** 运行
* 与主应用使用 **相同的代码库、配置和环境**
* 纳入版本控制和发布流程

#### 典型场景

* 数据库迁移 (migrate)
* 数据种子填充 (seed)
* 数据清理脚本
* 报表生成
* 批量数据处理

#### 实践方法

```json
// package.json 脚本
{
  "scripts": {
    "migrate": "node scripts/migrate.js",
    "seed": "node scripts/seed.js",
    "cleanup": "node scripts/cleanup.js",
    "report": "node scripts/report.js"
  }
}
```

```bash
# 执行管理任务
npm run migrate
npm run seed
npm run cleanup
```

```yaml
# Kubernetes Job 示例
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
spec:
  template:
    spec:
      restartPolicy: Never  # 一次性任务
      containers:
      - name: migrator
        image: myapp:latest
        command: ["npm", "run", "migrate"]
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: database-url
```

```yaml
# Procfile 示例
web: node server.js
worker: node worker.js
release: node migrate.js && node seed.js
```

#### 解决痛点

* 避免误操作
* 提高可追溯性
* 确保脚本与代码版本一致
* 自动化执行流程

***

## 4. 面试高频题库

### 基础概念题

**Q1: 什么是 12-Factor App？它的核心目标是什么？**

> **参考答案**: 12-Factor App 是一套构建现代 SaaS 应用的方法论，包含 12 个最佳实践原则。核心目标是实现应用的标准化、可移植性、云原生支持和无缝扩展能力。

**Q2: 为什么配置应该存储在环境变量中而不是配置文件中？**

> **参考答案**:
>
> 1. 安全性：代码开源不会泄露敏感信息
> 2. 灵活性：修改配置无需重新编译代码
> 3. 通用性：与编程语言和框架无关
> 4. 防错：不易意外提交到版本控制系统

**Q3: 什么是有状态和无状态进程？为什么 12-Factor 要求无状态？**

> **参考答案**:
>
> * 有状态：在本地内存或磁盘存储会话/用户数据
> * 无状态：所有状态存储到外部服务（数据库、Redis）
> * 原因：无状态才能支持水平扩展，任意进程处理任意请求

### 原理理解题

**Q4: 解释构建、发布、运行三个阶段的区别和联系**

> **参考答案**:
>
> * 构建：源码 + 依赖 → 可执行产物
> * 发布：构建产物 + 配置 → 可运行版本
> * 运行：在特定环境中执行发布版本
> * 联系：三个阶段严格分离，每个阶段的输出是下一个阶段的输入
> * 好处：构建产物不可变，回滚简单，职责清晰

**Q5: 如何实现优雅关闭 (Graceful Shutdown)？**

> **参考答案**:
>
> 1. 停止接收新请求
> 2. 等待现有请求处理完成（设置超时）
> 3. 关闭数据库连接池等资源
> 4. 刷新日志缓冲区
> 5. 关闭应用上下文
>
> 关键信号：SIGTERM（Linux）、关闭钩子（Java）、defer（Go）

### 实战应用题

**Q6: 如何在一个微服务项目中实践 12-Factor？**

> **参考答案**:
>
> 1. 每个微服务独立仓库（Codebase）
> 2. 使用 Docker 管理依赖（Dependencies）
> 3. 配置中心统一管理配置（Config）
> 4. 服务发现替代硬编码地址（Backing Services）
> 5. CI/CD流水线分离构建发布（Build/Release/Run）
> 6. 无状态容器化部署（Processes）
> 7. 每个服务独立暴露端口（Port Binding）
> 8. HPA 自动扩缩容（Concurrency）
> 9. 快速启动 + 优雅关闭（Disposability）
> 10. 统一的 Helm Chart/Kustomize（Dev/Prod Parity）
> 11. 日志收集到 ELK/Loki（Logs）
> 12. Job/CronJob 执行迁移（Admin Processes）

**Q7: Docker 和 Kubernetes 如何帮助实践 12-Factor？**

> **参考答案**:
>
> * Docker: 封装依赖、端口绑定、环境一致性
> * Kubernetes: 进程模型、自动扩缩容、一次性任务 (Job)、配置管理 (ConfigMap/Secret)

### 进阶思考题

**Q8: 12-Factor App 是否适用于单体应用？**

> **参考答案**: 适用。12-Factor 的核心思想是构建可维护、可扩展的应用，无论单体还是微服务都可以受益。特别是配置分离、依赖管理、日志处理等原则，对任何规模的应用都有价值。

**Q9: 12-Factor App 有哪些局限性？**

> **参考答案**:
>
> 1. 主要针对 SaaS/Web 应用，不适用于桌面/移动端
> 2. 某些原则（如无状态）可能牺牲性能（无法利用本地缓存）
> 3. 对于遗留系统改造成本高
> 4. 未深入涉及安全、监控等话题

***

## 5. 最佳实践总结

### 5.1 渐进式导入建议

| 阶段 | 优先级 | 要素 | 理由 |
|------|--------|------|------|
| 第一 | ⭐⭐⭐ | Config, Dependencies, Stateless | 最核心，收益最大 |
| 第二 | ⭐⭐ | Logs, Dev/Prod Parity | 显著改善运维质量 |
| 第三 | ⭐ | 其他要素 | 逐步优化 |

### 5.2 推荐工具栈

| 领域 | 工具推荐 |
|------|---------|
| **容器化** | Docker, Podman |
| **编排** | Kubernetes, Docker Compose |
| **配置管理** | Consul, Etcd, AWS Parameter Store |
| **日志收集** | ELK Stack, Loki, Fluentd |
| **CI/CD** | GitHub Actions, GitLab CI, Jenkins |
| **基础设施即代码** | Terraform, Pulumi |

### 5.3 常见坑点提醒

```
⚠️ 不要为了遵循 12-Factor 而过度设计
⚠️ 本地缓存可以使用，但要确保可丢弃
⚠️ 开发环境尽量和生产使用同类型服务
⚠️ 日志不要写文件，永远输出到 stdout
⚠️ 一次性任务也要走版本控制和发布流程
⚠️ 启动时间控制在 10 秒内
⚠️ 优雅关闭要设置合理超时时间
```

***

## 6. 技术关联与扩展

### 6.1 与相关概念的对比

| 概念 | 与 12-Factor 的关系 |
|------|-------------------|
| **十二因子 vs 十二因子+** | 后者是针对云原生的扩展版本 |
| **12-Factor vs 微服务** | 12-Factor 是微服务的基石 |
| **12-Factor vs Serverless** | Serverless 天然符合 12-Factor |
| **12-Factor vs 六边形架构** | 两者互补，关注不同层面 |

### 6.2 现代演进

* **Cloud Native**: 云原生是 12-Factor 的自然延伸
* **Service Mesh**: 解决了 12-Factor 未覆盖的服务间通信问题
* **GitOps**: 实现了更严格的发布管理
* **Observability**: 扩展了日志为完整的可观测性体系

***

## 7. 一句话总结

> **12-Factor App 是一套指导构建云原生 SaaS 应用的方法论，通过 12 个核心原则（基准代码、依赖声明、配置分离、后端服务抽象、构建发布运行分离、无状态进程、端口绑定、并发扩展、快速启停、环境一致、日志流、一次性管理进程），帮助团队开发出可移植、可扩展、高可靠的应用程序。**

***

## 参考资料

1. [官方文档](https://12factor.net/zh_cn/) - The Twelve-Factor App (简体中文版)
2. [Heroku Blog](https://www.heroku.com/blog/heroku-open-sources-twelve-factor-app-definition) - Heroku Open Sources the Twelve-Factor App Definition
3. [Medium](https://medium.com/@ymwkac/什麼是-12-factor-app-c0a79a9a817f) - 什麼是 12-Factor App？
4. [阿里云开发者](https://developer.aliyun.com/article/1362327) - 技术阅读摘要 - 1.十二要素应用原则
5. [Red Hat](https://www.redhat.com/en/blog/12-factor-app) - An illustrated guide to 12 Factor Apps

***
