Upgrade to Pro — share decks privately, control downloads, hide ads and more …

12 Factor App

12 Factor App

Rafael Pazini

August 01, 2024
Tweet

More Decks by Rafael Pazini

Other Decks in Programming

Transcript

  1. Rafael Pazini Pós-graduando em IA & Big Data - USP

    Formado em Ciência da Computação - UNIP Engenheiro de software com mais de 10 anos de experiência em desenvolvimento de aplicações backend de alta performance. Atualmente na PlutoTV como Senior Software Engineer.
  2. O que é 12 Factor App? Conjunto de práticas para

    construir aplicações SaaS escaláveis e mantíveis Criado por desenvolvedores da Heroku Promove melhores práticas de desenvolvimento e operação de software
  3. 1. Codebase 2. Dependencies 3. Config 4. Backing Services 5.

    Build, Release, Run 6. Processes 7. Port Binding 8. Concurrency 9. Disposability 10. Dev/Prod Parity 11. Logs 12. Admin Processes Os 12 Fatores
  4. 1. Codebase Uma base de código rastreada em controle de

    versão, muitas implantações Todo o código de uma aplicação deve estar em um único repositório de controle de versão. Cada commit pode ser implantado em múltiplos ambientes (desenvolvimento, teste, produção). Facilita a gestão de versões e a colaboração entre desenvolvedores. git init git add . git commit -m "Initial commit"
  5. 2. Dependencies Declare e isole dependências Todas as dependências da

    aplicação devem ser explicitamente declaradas e gerenciadas. Facilita a replicação do ambiente de desenvolvimento e produção, garantindo consistência. // go.mod module myapp go 1.22 require ( github.com/labstack/echo/v4 v4.6.3 ) go mod tidy
  6. 3. Config Armazene a configuração no ambiente Configurações, como credenciais

    de banco de dados ou chaves de API, devem ser armazenadas em variáveis de ambiente. Aumenta a segurança e facilita a mudança de configurações entre diferentes ambientes. package main import ( "fmt" "os" ) func main() { dbURL := os.Getenv("DATABASE_URL") fmt.Println("Database URL:", dbURL) } export DATABASE_URL="postgres://user:password@localhost/db" go run main.go
  7. 4. Backing Services Trate serviços de apoio como recursos anexados

    Serviços de apoio, como bancos de dados, sistemas de fila, cache, etc., devem ser tratados como recursos anexados que podem ser facilmente substituídos. Aumenta a portabilidade e facilita a troca de serviços de apoio. package main import ( "fmt" "os" ) func main() { redisURL := os.Getenv("REDIS_URL") fmt.Println("Redis URL:", redisURL) } export REDIS_URL="redis://localhost:6379" go run main.go
  8. 5. Build, Release, Run Separe estritamente os estágios de construção

    e execução O processo de implantação deve ser dividido em três estágios: build, release, e run. Build: Converte o código-fonte em um executável. Release: Combina o executável com a configuração específica do ambiente. Run: Executa a aplicação no ambiente. Garante que a mesma versão do código seja testada e implantada em produção. # Build stage FROM golang:1.22 AS builder WORKDIR /app COPY . . RUN go build -o myapp # Run stage FROM alpine:latest WORKDIR /root/ COPY --from=builder /app/myapp . CMD ["./myapp"]
  9. 6. Processes Execute a aplicação como um ou mais processos

    sem estado A aplicação deve ser executada como um ou mais processos que não mantêm estado persistente entre as execuções. Qualquer estado deve ser armazenado em um serviço de apoio. Facilita a escalabilidade e a recuperação de falhas. package main import ( "github.com/labstack/echo/v4" ) func main() { e := echo.New() e.GET("/ping", func(c echo.Context) error { return c.String(200, "pong") }) e.Start(":8080") }
  10. 7. Port Binding Exporte serviços via ligação de porta A

    aplicação deve ser auto-suficiente e expor seus serviços via ligação de porta, sem depender de servidores web externos. Simplifica a configuração e a implantação. package main import ( "github.com/labstack/echo/v4" ) func main() { e := echo.New() e.GET("/ping", func(c echo.Context) error { return c.String(200, "pong") }) e.Start(":8080") }
  11. 8. Concurrency Escale por meio do modelo de processo A

    aplicação deve ser projetada para escalar horizontalmente, executando múltiplas instâncias (processos) em paralelo. Facilita a escalabilidade e melhora a performance. apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: replicas: 3 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers:
  12. 9. Disposability Maximize a robustez com inicialização rápida e encerramento

    gracioso A aplicação deve iniciar e parar rapidamente, suportando encerramento gracioso (graceful shutdown) para evitar perda de dados. Melhora a robustez e facilita o gerenciamento de processos. package main import ( "context" "log" "net/http" "os" "os/signal" "time" "github.com/labstack/echo/v4" ) func main() { e := echo.New()
  13. 10. Dev/Prod Parity Mantenha o desenvolvimento, o teste e a

    produção o mais semelhante possível Os ambientes de desenvolvimento, teste e produção devem ser o mais semelhantes possível para evitar "surpresas" na produção. Reduz riscos e inconsistências entre ambientes. # Dockerfile para desenvolvimento e produção FROM golang:1.22 WORKDIR /app COPY . . RUN go build -o myapp CMD ["./myapp"]
  14. 11. Logs Trate logs como fluxos de eventos A aplicação

    deve tratar logs como fluxos de eventos contínuos e não se preocupar com o armazenamento ou a roteamento dos logs. Facilita a centralização e a análise de logs. package main import ( "log" "net/http" "github.com/labstack/echo/v4" ) func main() { e := echo.New() e.GET("/ping", func(c echo.Context) error { log.Println("Received request for /ping") return c.String(200, "pong") })
  15. 12. Admin Processes Execute tarefas de administração/gerenciamento como processos únicos

    Tarefas administrativas (como migrações de banco de dados) devem ser executadas como processos únicos e independentes do ciclo de vida da aplicação. Facilita a execução de tarefas administrativas sem interromper o serviço principal. package main import ( "database/sql" "fmt" _ "github.com/lib/pq" ) func main() { db, err := sql.Open("postgres", "user=username dbname=mydb sslmode=disable") if err != nil { panic(err) } defer db.Close()
  16. Test-Driven Development (TDD) Abordagem de desenvolvimento onde os testes são

    escritos antes do código funcional Reduz bugs e melhora a qualidade do código Facilita a manutenção e refatoração do código
  17. O Ciclo TDD Escreva um teste que falhe Escreva o

    código mínimo necessário para passar o teste Refatore o código para padrões aceitáveis
  18. Exemplo de TDD em Go Vamos criar uma função simples

    para adicionar dois números e escrever testes para ela. package main import "fmt" // Add function func Add(a, b int) int { return a + b } func main() { fmt.Println(Add(2, 3)) }
  19. Escrevendo Testes package main import "testing" // TestAdd function func

    TestAdd(t *testing.T) { result := Add(2, 3) if result != 5 { t.Errorf("Add(2, 3) = %d; want 5", result) } }
  20. Table-Driven Tests Usando Table-Driven Tests para melhorar a legibilidade e

    manutenção dos testes package main import "testing" // TestAdd function with Table-Driven Tests func TestAdd(t *testing.T) { var tests = []struct { a, b, expected int }{ {1, 1, 2}, {2, 3, 5}, {10, 20, 30}, } for _, tt := range tests {
  21. Benefícios do TDD Maior confiança no código Facilidade na refatoração

    Documentação viva Leia mais sobre TDD em Go no meu . artigo