Conte as decisões
O refinamento é mais fácil de entender de trás para frente: olhe código pronto e conte as decisões que precisaram ser tomadas antes de ele poder existir.
O item
Imagine a linha que iniciou esta funcionalidade:
"Quando alguém do admin adiciona um parceiro, busque o logo dele no site automaticamente."
Uma frase. Razoável. É a cara de um item comum de backlog, e todo mundo na sala concordaria com ela.
O código
O app/services/partners/fetch_site_icon_service.rb, 86 linhas. Aqui está o que há nele:
MAX_BYTES = 1.megabyte
TIMEOUT = 5
ACCEPTED = %w[image/png image/jpeg image/webp image/svg+xml
image/x-icon image/vnd.microsoft.icon image/gif].freeze
# Preference: apple-touch-icon (largest) > <link rel="icon"> (largest) > /favicon.ico
def fetch(url, limit = 3)
return nil if limit.zero?
rescue URI::InvalidURIError, SocketError, Timeout::Error,
OpenSSL::SSL::SSLError, Errno::ECONNREFUSED => e
Result.new(success: false, url: nil, errors: [ e.message ])
As decisões
Lendo esses quatro fragmentos, pelo menos onze perguntas separadas foram respondidas:
| # | Decisão | Valor no código |
|---|---|---|
| 1 | Que tamanho de arquivo aceitamos? | 1 megabyte |
| 2 | Quanto esperamos? | 5 segundos, abertura e leitura |
| 3 | Quais formatos de imagem? | Sete, listados explicitamente |
| 4 | Qual ícone preferimos? | apple-touch-icon, o maior primeiro |
| 5 | E se houver vários do mesmo tamanho? | Não foi decidido: o sort_by não é estável, então um empate pode sair em qualquer ordem |
| 6 | E se a página não declara nenhum? | Cai no /favicon.ico |
| 7 | Seguimos redirecionamentos? | Sim, no máximo dois (três requisições no total) |
| 8 | O que acontece se o site está fora? | Devolve um Result de falha, não levanta erro |
| 9 | Quais falhas capturamos? | Cinco classes de exceção, nomeadas |
| 10 | Uma falha impede adicionar o parceiro? | Não: o admin ainda consegue salvar |
| 11 | Nós nos identificamos? | Um User-Agent nomeando a aplicação |
Onze decisões, e o item não continha nenhuma delas.
flowchart TB
T["'busque o logo automaticamente'"] --> D["11 decisões"]
D --> G["decididas numa sessão de refinamento, juntos, em 30 minutos"]
D --> A["decididas sozinho no teclado, em 11 interrupções"]
D --> P["decididas pela produção, em 11 incidentes"]
G --> R["as mesmas onze respostas, a preços diferentes"]
Os três lugares em que uma decisão é tomada
Numa sessão de refinamento. Juntos, antes de o trabalho começar. Todo mundo ouve a resposta, o raciocínio fica registrado, e discordar é barato porque nada foi construído.
Sozinho no teclado. Quem desenvolve esbarra em cada pergunta no meio do fluxo e escolhe algo razoável. Isso não é um fracasso: a maioria dessas onze são decisões perfeitamente boas e quem desenvolveu era a pessoa certa para tomá-las. O custo é que ninguém mais conhece as respostas, então o raciocínio mora numa cabeça e no git blame.
Em produção. O site de um parceiro serve um TIFF de 40MB e a requisição trava. Agora as decisões 1, 2 e 3 são tomadas, caro, por quem estiver acordado.
As mesmas onze respostas, a três preços bem diferentes. Refinamento é escolher a primeira coluna, e não uma cerimônia, para as decisões que são caras na terceira.
Quais das onze pertenciam à sessão
Não todas, e essa é a parte que separa refinamento útil de uma reunião.
A decisão 10: uma busca falha impede salvar o parceiro?: é uma decisão de produto. Ninguém além de quem é dono da funcionalidade pode respondê-la, e errar significa reconstruir o controller.
A decisão 8: devolver um Result ou levantar erro?: é uma convenção arquitetural que esta base já tem, então não precisa de discussão nenhuma.
A decisão 3: quais formatos de imagem?: é uma boa coisa de deixar para quem desenvolve. É barata de mudar, invisível ao produto, e consultar o que os navegadores aceitam é um trabalho de cinco minutos que uma reunião só piora.
Um primeiro corte da regra, refinado ao longo deste curso: refine as decisões caras de reverter ou que alguém fora do código precisa responder. Deixe o resto para quem faz o trabalho.
Uma sessão que decide todas as onze desperdiça uma hora. Uma sessão que não decide nenhuma deixa as caras para a produção.
Pontos-chave
- Um item de uma frase produziu 86 linhas contendo pelo menos onze decisões separadas.
- As mesmas decisões são tomadas no refinamento, no teclado, ou em produção: as respostas podem coincidir, os preços não.
- Decidido no teclado, o raciocínio mora numa cabeça e no
git blame. - Refine o que é caro de reverter ou o que alguém fora do código precisa responder.
- Decidir todas as onze numa reunião desperdiça uma hora; não decidir nenhuma deixa as caras para a produção.
Continue com a aula 2
O que refinamento é e não é
Separa o refinamento de outras quatro reuniões e define suas três saídas, participantes, tempo e entradas.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.