Os requisitos que o negócio não pede: o Capítulo 2 de DDIA (Parte 1)

Este post é um resumo da discussão do Clube do Livro da Craft Code Club sobre o Capítulo 2 de Designing Data-Intensive Applications.


O requisito que o negócio não te conta

O capítulo abre com uma provocação que a turma abraçou logo de cara: antes de entrar em requisitos não funcionais, o que são requisitos funcionais e não funcionais?

A definição que nasceu da conversa foi bem direta:

  • Requisito funcional é aquilo que vem do negócio. É o que está (ou deveria estar) no PRD, o que resolve um problema do cliente, o que vende. Se você não entrega, não entregou a funcionalidade.
  • Requisito não funcional é aquilo que o negócio não te pede, mas que você sabe que precisa existir para a feature ter sucesso. Ninguém abre um card pedindo "que aguente a Black Friday". Mas se não aguentar, a funcionalidade fracassa do mesmo jeito.

E aqui apareceu um padrão que muita gente reconheceu na própria carreira: no começo, a gente só enxerga o funcional. O requisito não funcional é aquele "efeito colateral" que só acontece depois que você implementa. Conforme você amadurece como engenheiro, o escopo do seu olhar vai abrindo: primeiro você só quer resolver o problema no código; depois começa a pensar no impacto daquele código na escalabilidade, na experiência do usuário, na capacidade de o sistema evoluir.

Quando a gente deixa de se preocupar com a capacidade de evolução do software, a gente empurra o problema para o eu do futuro. E ele sempre cobra a conta.

O perigo mora no "vamos deixar isso para depois". Naquele momento de "só entregar feature", adiar arquitetura, organização de código e escalabilidade parece barato. Não é. Em algum momento isso puxa o seu pé, e o software que deveria resolver o problema passa a não resolver mais.

O truque: amarrar o não funcional ao negócio

A parte difícil de requisito não funcional não é técnica, é vender ele como importante. Como justificar que o time vai gastar capacidade em algo que o negócio nem pediu?

O caminho que funcionou para a turma é fazer um gancho com o negócio. Se hoje é um MVP com poucos usuários, ótimo, mas qual é a visão de produto no longo prazo? Se isso aqui der certo, vira carro-chefe? Vira diferencial competitivo? A partir daí você amarra necessidade técnica (escalabilidade, tolerância a falhas, consistência, etc) a algo que o negócio realmente entende como importante. Quando a necessidade técnica se conecta com o valor de negócio, a conversa flui.

E há munição concreta para essa conversa: a relação entre performance e dinheiro. O livro cita estudos clássicos que cruzam requisito não funcional com ROI e conversão, do tipo "cada 100 ms a mais de lentidão derruba ~1% das compras no checkout". Quando você traduz milissegundos em receita perdida, gestão e produto passam a ouvir.

Alguns fios que apareceram na discussão:

  • Nem todo usuário pesa igual. Existe aquele cliente que compra com frequência e gasta muito na plataforma. Ele tem muitos dados, mas são dados "quentes": a jornada dele precisa ser rápida e fluida, porque é ele que sustenta o MRR. Vale priorizar a experiência dessas estrelas.
  • Picos sazonais são o teste real. Varejo em Black Friday, e-commerce na época de Natal ou Copa do Mundo, são momentos em que "não pensei em performance" vira aplicação caindo e venda perdida. É o oposto de otimização prematura: é conhecer o seu negócio.
  • O MVP traz o usuário; a experiência o mantém. A aplicação inicial atrai; o que segura o usuário dentro da plataforma (o lifetime dele) é a experiência. eE experiência, aqui, é justamente o conjunto de requisitos não funcionais que a gente vai lapidando depois que o sistema já está no ar.

Requisitos não funcionais são tão importantes quanto os funcionais, só que ficam escondidos. Não são visíveis para produto, e muitas vezes nem para pessoas técnicas a princípio. Leva anos de experiência para enxergá-los bem e fazer os julgamentos certos.


Estudo de caso: a rede social

Para dar concretude a tudo isso, o capítulo abre com um estudo de caso de uma rede social parecida com o X/Twitter. É o típico exemplo "gigante, meio fora da nossa realidade", mas é ótimo para exercitar trade-offs.

Os números que ancoram o exemplo:

  • Na casa de 500 milhões de posts por dia, uma média de ~5.800 posts por segundo.
  • Picos que passam de 150 mil posts por segundo.
  • Cada usuário segue ~200 pessoas e é seguido por ~200.

O primeiro rascunho é o óbvio: três tabelas num banco relacional (usuários, posts, quem-segue-quem) e, como o negócio quer algo "ao vivo", um polling a cada poucos segundos batendo na API para montar a timeline. Funciona no papel, e explode na escala. Fazendo a conta de padeiro (back of the envelope estimation), uma abordagem ingênua chega à casa de centenas de milhões de lookups por segundo (algo como 2 milhões de leituras de timeline por segundo, cada uma cruzando ~200 usuários que aquele user segue). Insustentável.

A métrica-chave: a proporção de leitura

Aqui entrou um conceito que a primeira edição do livro nomeava explicitamente como métrica-chave: entre as várias métricas do sistema, algumas puxam a decisão arquitetural inteira. Neste caso, a chave é a read ratio, a proporção entre leituras e escritas.

E ela é brutalmente desequilibrada: lê-se muito mais do que se escreve. Isso muda tudo. Se a leitura é o gargalo e a escrita é rara, faz todo sentido pagar mais caro na escrita para deixar a leitura barata. É o capacity planning guiando a arquitetura.

View materializada: pré-computar a timeline

A saída é a view materializada: em vez de montar a timeline na hora da leitura, você pré-computa a timeline de cada usuário. Cada vez que alguém acessa, a visualização dele já está pronta.

Um cuidado importante que a turma fez questão de frisar: aqui "view materializada" é um conceito guarda-chuva, não necessariamente aquela MATERIALIZED VIEW do banco relacional. É a ideia de pré-processar e guardar pronto para leitura. Onde você materializa (no próprio banco, num key-value em memória, no cache, onde for) depende dos seus outros trade-offs (escrita, disco, latência). Isso conecta direto com um conceito do Capítulo 1: quem define a natureza do dado é a aplicação, não a ferramenta. Você tem liberdade para escolher o instrumento certo para o problema.

O trade-off fica explícito:

  • Ganho: economiza um oceano de operações de leitura. A timeline chega pronta.
  • Custo: cada novo post obriga a recomputar a timeline de cada seguidor. Você penaliza a escrita.

E a pergunta que segue é: será que penalizar a escrita é mesmo um problema? Neste domínio, não muito. Escrita mais lenta é aceitável, e o fato de um seguidor não ver o post no mesmo milissegundo (uma consistência eventual) também é aceitável para uma timeline. Ou seja: o "trade-off" de penalizar a escrita, neste caso, quase não dói. Como diz o livro, tudo é trade-off, a graça é perceber qual lado você pode pagar sem sentir dor.

O problema da celebridade (e a solução híbrida)

A view materializada tem um calcanhar de Aquiles: e quando alguém tem 10 milhões de seguidores? Um único post viraria 10 milhões de escritas em 10 milhões de timelines. O barato saiu caro.

Mas a turma foi certeira: isso é exceção, não regra. A esmagadora maioria dos usuários não tem milhões de seguidores. A estratégia é trabalhar onde se ganha mais resultado, pré-computar para o caso comum é tratar a celebridade à parte, com uma abordagem híbrida: o post da celebridade não é distribuído por escrita; ele é buscado sob demanda (fan-out on read) e mesclado na hora. Quase todas as soluções boas aqui acabam híbridas.

Vale pensar também em regionalização: uma celebridade brasileira não precisa escalar para o mundo inteiro ver. Quem segue a Anita está aqui, não tem um chinês do outro lado do mundo esperando aquela timeline. Você distribui o esforço por região e, olhando esse mesmo conceito no agregado, escala a nível mundo sem desperdiçar recurso guardado à toa.

View materializada é a mesma coisa que cache?

Se a view materializada guarda algo pronto, qual a diferença para um cache?

O consenso: os dois são conceitos, não ferramentas, e têm interseção, mas não são a mesma coisa.

  • A view materializada é sobre pré-computar uma operação cara e deixá-la pronta para leitura. Você paga o custo antes, de propósito.
  • O cache normalmente atua no momento da leitura: o dado é processado, guardado, e a próxima leitura bate nele. É expor barato aquilo que é muito acessado.

E eles se complementam: você pré-computa a timeline (view materializada) e ainda coloca um cache na frente para não onerar o banco. Em camadas, fica mais rápido ainda. Aliás, "cache" também é palavra guarda-chuva, falamos de pelo menos quatro nesse encontro: cache de banco, cache da aplicação, CDN e browser. E há duas leituras do próprio termo: o cache "cru" (pré-computar uma operação cara e salvar) e o cache "implementado", com todas as suas estratégias (TTL, ordem de escrita, invalidação, eviction).

Um detalhe fino sobre por que, neste caso, a view materializada bate o cache puro: o cache pode ser pego de surpresa. Imagine 8h da manhã, todo mundo acordando e pegando o celular ao mesmo tempo, 20 milhões de requisições num piscar de olhos batendo num cache frio. Com a timeline já materializada, você não depende de "esquentar" nada na hora do pico. E o cache ainda te obrigaria a lidar com a dança da invalidação. Como sempre: cache tem ônus e bônus; comece pelo simples e vá complicando só quando precisar.

Fan-out: um input que se espalha

Toda essa distribuição de posts tem um nome: fan-out. De modo geral, é um input que se fragmenta e se distribui para vários pontos. Um usuário super famoso posta uma vez, e aquilo precisa chegar em milhões de timelines.

No domínio de redes sociais, as duas estratégias já apareceram implicitamente:

  • Fan-out on write: na hora em que o post é escrito, você já distribui (materializa) nas timelines de todo mundo.
  • Fan-out on read: você monta a timeline só quando alguém lê. O que, como vimos, não segura a escala.

Por que fan-out on write costuma vencer aqui? Controle de throughput. Você conhece a vazão que o seu sistema aguenta e consegue limitar a distribuição a esse teto. Se fizesse tudo na leitura, os picos ficariam impossíveis de controlar. É contraintuitivo na primeira vez, mas fazer o trabalho na escrita te devolve previsibilidade.

E o conceito de fan-out extrapola a rede social, vale a pena guardar (inclusive para entrevistas):

  • Comunicação síncrona: uma request na sua API que, para responder, dispara muitas requests internos.
  • Mensageria: uma mensagem chega num ponto (o exchange do RabbitMQ, por exemplo) e é roteada para vários consumidores.
  • Grafos: a interação de um nó dispara N interações para outros nós adiante.

Fechamos o estudo de caso com um link para o Capítulo 1: cada timeline pré-computada é um dado derivado da base principal (o system of record), onde os posts realmente moram. A view materializada é derivada; a fonte da verdade continua sendo o storage primário.

Performance: response time vs. throughput

Com o estudo de caso resolvido, o capítulo formaliza o vocabulário de performance em torno de duas métricas:

  • Response time (tempo de resposta): o tempo do fluxo inteiro, do ponto de vista do usuário. Da request sair do cliente até a resposta voltar.
  • Throughput (vazão): a quantidade de coisas que acontecem num intervalo: requisições por segundo, dados por segundo, operações por segundo, posts por segundo, etc.

As duas se relacionam. O throughput mostra a capacidade do sistema. Quando você atinge o limite de vazão, só há dois caminhos: reduzir o tempo de resposta de cada operação individual ou aumentar a capacidade de vazão.

A analogia que colou foi a do cano: se você precisa de mais água dentro de casa, ou aumenta o diâmetro do cano (throughput) ou faz a água passar mais rápido (response time). Outro ponto levantado foi também, colocar mais canos, que seria a escala horizontal, que no fim se encaixa em aumentar o throughput.

A anatomia do tempo de resposta

O ponto mais rico dessa parte é entender que response time não é um número atômico, ele é a soma de várias etapas. Pensando numa request web, dentro daquele tempo cabem: a conexão de rede, a espera até o escalonador do sistema operacional dar CPU para a aplicação, a execução da aplicação em si, a espera pelo banco de dados, a espera pelo disco...

E é aqui que entra um alerta de vocabulário que pega muita gente (confissão coletiva na sala): latência não é sinônimo de response time. O livro diferencia:

  • Response time é a experiência ponta a ponta, do cliente.
  • Latência é, especificamente, o tempo de rede, o tempo que a request leva para sair do cliente e chegar ao serviço.
  • No meio ainda tem o tempo de fila (esperando ser processada), o tempo de serviço (processando de fato) e mais uma fila na volta.

Falar "minha latência está em 1 segundo" quase sempre é impreciso. O problema costuma estar só numa delas. Muitas vezes a latência não é o problema (latência baixíssima) e o vilão real é a fila ou o serviço.

Ferramentas que ajudam a enxergar isso:

  • Distributed tracing e spans: dentro de um mesmo código você cria spans para ver exatamente onde a operação está lenta, se é network, se é espera, se é banco, se é lógica, se está travando no event loop do JavaScript, se é CPU.
  • A métrica certa para escalar é a fila, não a CPU nem a memória. Se a fila de requisições está crescendo, é sinal para escalar antes que o response time desande.

E os números certos guiam a solução: network alta? Talvez a infra esteja deployada na região errada, pense em CDN ou numa nova região. Fila subindo? Escala horizontal do serviço. Processamento alto? Talvez o problema seja algorítmico, e a otimização é no código. Talvez as dependências externas estejam sendo o problema.

Quando o throughput estoura: o retry storm

O que acontece quando você bate no teto da vazão? A fila dispara e tudo fica lento. Pior: você entra num ciclo vicioso, fila longa aumenta o tempo de resposta, tempo de resposta maior faz a fila crescer mais, e por aí vai. O livro chama isso de retry storm: uma tempestade de retentativas que alimenta a própria sobrecarga.

Um arsenal de estratégias apareceu para lidar com isso:

  • Circuit breaker: se o serviço caiu por sobrecarga, até subir mais instâncias o problema pode ir se agravando a um ponto que nem mesmo mais instancias resolveriam, a carga continua lá esperando. O circuit breaker vai recusando umas requisições e aceitando outras, dando fôlego para o serviço recuperar a saúde aos poucos.
  • Backoff exponencial: se você reenvia sempre a cada 1 segundo, continua martelando o sistema. A ideia é aumentar o intervalo a cada falha, 1s, 2s, 4s, 8s (idealmente com um componente aleatório), dando tempo para o sistema respirar.
  • Load shedding: o sistema detecta que a sobrecarga está chegando e rejeita requisições proativamente, "estou cheio, por enquanto não aceito".
  • Back pressure: o sistema responde ao cliente pedindo para diminuir o ritmo de envio.
  • Rate limiting: imagine um balde enchendo de fichas, com limite de, digamos, 10. O cliente gasta as fichas nas requisições; quando o balde esvazia, ele espera reencher. Distribui melhor a carga entre os usuários. Token bucket é só uma das várias estratégias de rate limit.
  • Lock de idempotência: antes de enfileirar, você "loca" o processamento (por exemplo, de um SKU). Se a mesma request chegar de novo enquanto a primeira está na fila, você descarta, já está sendo processada. Solta o lock ao terminar. Um relato de produção contou que isso levou a fila "da água para o vinho": de centenas de milhares para, no pior caso, algumas centenas.

E nada disso é novo, aliás. O TCP já implementa controle de tráfego e de carga internamente, sem a gente perceber. A internet inteira está calcada nisso, decisões que a galera dos anos 80 e 90 já tomou por nós.

A internet foi tão bem feita que ninguém tem noção da complexidade que ela é.

E, saindo um pouquinho do livro, tem a fila virtual (a "sala de espera"), aquela tela de "você está na posição X" em lançamento de Black Friday, ingresso de show, ou GTA 6. Não é user-friendly e não é recomendada como padrão, mas em casos raros de pico extremo é uma boa estratégia para cadenciar a galera e não derrubar tudo. (Bônus: costuma ter randomização, para não privilegiar quem está mais perto do servidor, e teve até quem lembrasse do sorteio de ingressos da Copa, que virou um fan-out só para os sorteados comprarem.)

A média mente: a história dos percentis

Se tem um trecho para levar tatuado dessa parte, é este. Como você mede o response time do seu sistema? A resposta instintiva, a média, é justamente a armadilha.

Pegue cinco requisições: 8, 10, 11, 11 e 50 segundos. A média dá 18 segundos. Só que 18 não representa quase ninguém: quatro das cinco requisições foram rápidas (perto de 10s), e o número foi todo puxado por um único outlier de 50s, provavelmente um usuário numa conexão 3G. A média suaviza os dados e cria uma máscara sobre o que de fato aconteceu.

A escadinha de métricas ajuda a entender por quê:

  • Média: soma tudo e divide. Some os dados num número só, e some, junto, a realidade.
  • Mediana (p50): ordena os valores e pega o do meio. Já é melhor, mas ainda é um ponto só.
  • Percentil: é uma mediana generalizada, você escolhe qual pedaço da distribuição quer olhar. O p50 é a mediana; o p95 te dá os 95% "de baixo" e joga fora a cauda de outliers; e assim por diante.

A grande sacada: percentil e mediana não achatam os dados. Eles dividem a distribuição em pacotes, mas os valores continuam originais, contando uma história real.

E que história? O p50 te diz que 50% das requisições foram mais rápidas que aquele valor (e 50% mais lentas). O p99 te diz que 1 em 100 requisições está mais lenta que aquilo, e 99 em 100 estão mais rápidas. Muito mais direto e honesto que uma média.

Vale a intuição das casas decimais que o livro passa de forma sutil:

  • p99 = 1 em 100.
  • p99,9 = 1 em 1.000.
  • p99,99 = 1 em 10.000.

E o percentil também informa até onde vale a pena otimizar. Uma Amazon da vida quer esses números baixos, mas ir de p99 para p99.9 pode ter um custo altíssimo para um ganho minúsculo. Será que compensa? Ter essa noção é parte do trabalho.

Observabilidade: histogramas e as três siglas

Para trabalhar percentil em produção, a ferramenta são os histogramas: eles acumulam as métricas em buckets por faixa, sem precisar recalcular média nenhuma. Aparecem o tempo todo nas ferramentas de observabilidade, e é o que permite ver o movimento dos percentis ao longo do tempo de forma isolada, não só o retrato agregado do dia inteiro. (Ficou combinado, inclusive, trazer um encontro dedicado a observabilidade e OpenTelemetry lá na frente.)

E tudo isso desemboca no trio que formaliza "o que é performance boa" para o seu sistema:

  • SLI (Service Level Indicator): o indicador que você mede, por exemplo, o próprio response time.
  • SLO (Service Level Objective): o objetivo que você define em cima do indicador. Por exemplo, "99% das requisições abaixo de 200 ms". Serve de guard rail para a evolução do software: te empurra a pensar em observabilidade, otimizar banco, escalar horizontal ou verticalmente.
  • SLA (Service Level Agreement): o acordo com o cliente, com dinheiro e obrigações no meio. Estourou o SLA? A fatura vem, pode virar crédito ou multa para o cliente.

Num provedor de cartões, por exemplo, esses números ditam o sucesso do serviço. E é exatamente essa ciência dos números que orienta a arquitetura, implementar um cache, partir para microsserviços, escalar horizontalmente, com um norte bem definido, em vez de no chute.

Confiabilidade: falta (fault) vs. falha (failure)

A turma começou aqui a última seção do dia, e a diferença que mais chamou atenção foi entre fault e failure.

A analogia que fixou o conceito (em plena época de Copa) foi a do futebol: uma falta (fault) não para o jogo, ele segue, tropeçando, mas segue. Já uma falha total (failure) é o jogo parar. No sistema é igual: um fault é um problema pontual que o sistema contorna e continua funcionando; um failure é quando ele não consegue se recuperar.

Daí vem o fault tolerant: um sistema que continua servindo o usuário mesmo com faults acontecendo. Um sistema tolerante a falhas é robusto e continua entregando valor; um que não tolera cai por inteiro ao menor problema, tem um ponto frágil. Mecanismos clássicos: RAID para discos (um disco falha, os outros seguem) e múltiplas instâncias para sistemas distribuídos (uma cai, as outras continuam).

(Parêntese divertido: ninguém lembrou de bons termos em português para fault e failure, em PT tudo vira "falha". A Novatec está traduzindo a 2ª edição, então em breve saberemos a escolha oficial. Até lá, ficamos no inglês mesmo, melhor do que arriscar traduzir "pipeline" para "tubo" ou "shorts" para "bermudas".)

SPOF: o ponto único de falha

Conceito central quando se desenha arquitetura: o SPOF (Single Point of Failure). É aquele elemento que, se cair, derruba tudo. Um banco de dados sem réplica nem segunda instância é o exemplo óbvio: ele falha, o sistema inteiro para. Identificar o SPOF na sua arquitetura é dever de casa.

E o SPOF não é só interno. Um gateway de pagamento único num e-commerce é um SPOF externo: se aquele provedor cai, e agora? Pensar em arquitetura é pensar também nos serviços externos que você integra, e no acoplamento que eles criam. Até um framework é uma forma de acoplamento: se o time do framework muda algo, sua manutenção sofre. Quanto mais você desacopla a aplicação de regras específicas de banco, de dependências externas e de frameworks, mais mitiga o ponto único de falha (e aqui o DDD ajuda bastante). Cuidado clássico: o "microsserviço" que na verdade é um monolito distribuído com todos os serviços plugados no mesmo banco, se o banco cai, cai tudo junto.

Gerar uma falta para evitar uma falha

Às vezes você provoca um fault de propósito para prevenir um failure. Circuit breaker e load shedding fazem exatamente isso, "não vou conectar nessa API agora e já retorno um erro" ou "vou descartar esse processamento porque não consigo lidar com ele agora". É melhor um setor indisponível do que amarrar todos os outros e derrubar o sistema inteiro sem entregar valor nenhum.

O e-commerce ilustra bem: em vez de fazer tudo síncrono (onde qualquer tropeço vira uma compra perdida, o pior pesadelo do varejo), eles te dão o "compra feita, pode ficar tranquilo" na hora e jogam o resto para background. Fazem o fan-out do processamento: e-mail, aprovação de cartão, logística. Se a logística tropeça, remarcam a entrega para dois ou três dias e te avisam. Fazem de tudo para a venda se efetivar, transformando o que seria uma falha síncrona em uma falta controlada e assíncrona.

Injeção de falhas e engenharia do caos

Como saber se o seu sistema é de fato tolerante a falhas? Você quebra ele de propósito. Fault injection é induzir falhas deliberadas para avaliar a tolerância; a chaos engineering (engenharia do caos) é a disciplina que faz isso de forma sistemática. Você bagunça as coisas para descobrir, no controlado, o que aconteceria no caos real: se um cloud provider cai, se o gateway de pagamento para, meu sistema cai junto ou continua funcionando minimamente? Isso reforça o valor de ter mais de um fornecedor, injeção de dependência para chavear qual serviço está em uso, e redundâncias físicas como o RAID.

Esse livro é profundo: ele traz muita coisa de forma transversal. Um tópico bate no outro e faz o conhecimento se distribuir, um fan-out no grafo de conceitos.

Os trade-offs até aqui

  • Funcional vs. não funcional: o funcional entrega valor visível ao negócio e é fácil de justificar. O não funcional sustenta esse valor a longo prazo, mas é invisível e precisa ser vendido.

  • Fan-out on write vs. on read: no write, a leitura é barata e instantânea porque a timeline já está pronta. No read, a escrita é barata e o problema das celebridades desaparece, mas a leitura fica impossível em alta escala.

  • View materializada vs. cálculo em tempo real: a view deixa a leitura pronta e resiste a picos. O cálculo em tempo real mantém a escrita simples e o dado sempre fresco, mas a leitura fica cara sob carga.

  • View materializada vs. cache: a view pré-computa na escrita e fica imune ao pico frio. O cache é mais simples de operar, mas está sujeito a cold start e invalidação.

  • Média vs. percentis: a média é um número fácil de comunicar, mas esconde outliers. O percentil conta a história real da cauda, mas exige mais tooling.

  • Parar no p99 vs. perseguir p99.99: parar no p99 tem bom custo-benefício e cobre quase todos os usuários. Perseguir o p99.99 melhora a cauda, mas o custo é altíssimo para um ganho marginal.

  • Degradar com fault vs. arriscar failure total: degradar entrega valor parcial de forma previsível, mas exige circuit breaker, load shedding e assincronismo. Arriscar mantém o sistema mais simples de construir, mas quando quebra, quebra inteiro.

O que vem na Parte 2

Paramos no meio do Capítulo 2 de propósito, tinha muita coisa boa e a conversa renderia madrugada adentro. Ficou para o próximo encontro o restante de confiabilidade (erros de software, o fator humano e cultura), além de escalabilidade e manutenibilidade.

Quer participar do próximo?

Este foi só o Capítulo 2, Parte 1. Os encontros acontecem a cada quinze dias (e, quando o capítulo é grande, vira dois encontros).

O clube é aberto, descontraído e feito da comunidade para a comunidade: sem aula, sem cobrança, só gente que vive esses problemas trocando ideia (e cicatrizes de produção). Não precisa ter lido tudo nem ser especialista.

Participe do Clube do Livro: Designing Data-Intensive Applications da comunidade Craft Code Club

Traga sua leitura, suas discordâncias e seus exemplos. A melhor parte nunca está só no livro, está na conversa em cima dele.

Referências

O livro

  • Designing Data-Intensive Applications, Martin Kleppmann e Chris Riccomini (2ª edição). Capítulo 2: Defining Nonfunctional Requirements. Site oficial.

Post anterior da série

Gravação do encontro

Links compartilhados na discussão

Para aprofundar (conceitos citados)