Estudo · call com cliente

12 tópicos que a Samara já cobrou

Recap da call

Estudar isto. Não o currículo inteiro.

Na Samara você acertou o comercial e fraquejou nas 3 primeiras em inglês + pagamento + banco. O cliente agro vai cobrir o mesmo mapa. Cada tópico: o que falar, um exemplo real teu, o termo que faltou.

Regra das 3 dicas dela

  1. Ruby/Rails na prática — zero definição de livro.
  2. Exemplo de projeto real (iugu, Bling, Zaitt, Cão Cidadão).
  3. Por que essa estrutura para essa regra de negócio.

Frase-molde: “No X eu fiz Y porque Z. Se fosse de novo, faria W.”

1 · Ruby na prática

Na call: calculadora. Troca por regra de dinheiro.

Ruby brilha em objeto pequeno com regra, bloco e Enumerable — não em “é interpretada e tem duck typing”.

class Discount
  def initialize(percent)
    @percent = percent
  end

  def apply(amount_cents)
    amount_cents - (amount_cents * @percent / 100)
  end
end

# no pedido:
line_totals.map { |cents| discount.apply(cents) }.sum
Na iugu / boleto do Cão Cidadão a regra de valor não fica no controller. Uma classe pequena (PORO) recebe centavos e devolve o valor líquido. Escolho isso porque a regra de desconto muda mais que a tela — e dá para testar sem Rails.
In English: I wouldn’t start with a calculator. I’d put a business rule in a small Ruby object — for example a discount that takes cents and returns the net amount. Blocks and Enumerable keep the Rails layer thin. The controller only orchestrates.

2 · Cadastro de clientes no Rails

Na call: scaffold. Scaffold é gerador, não a estrutura.

  • db/migrate — tabela clients (name, document, email)
  • app/models/client.rb — validates :name, :email; uniqueness
  • config/routes.rb — resources :clients
  • ClientsController — index/show/create/update; client_params
  • Se a regra cresce: Clients::Create service, não 80 linhas no create
# routes
resources :clients

# controller
def create
  @client = Client.new(client_params)
  if @client.save
    redirect_to @client
  else
    render :new, status: :unprocessable_entity
  end
end

private
def client_params
  params.require(:client).permit(:name, :email, :document)
end
In English: migration, Client model with validations, resources :clients, controller with strong params. Scaffold can generate the files. I still own validations and what is permitted. If create grows — tax id, address, duplicate check — I pull a service object.

3 · API REST + Git

Na call: git init. Git de time é branch + PR.

  • rails new app --api se for só JSON
  • namespace :v1 { resources :clients } → GET/POST/PATCH/DELETE
  • Auth: token (Doorkeeper / API key) — outro sistema não entra aberto
  • Git: branch feat/clients-api, commits pequenos, PR, review, squash/merge, tag de release
API: JSON no /v1/clients, strong params, 401 sem token. Git: eu não mando direto na main. Branch, PR, o review pega contrato e teste do create. No Purple Stock / Gestão Bem o histórico vive no GitHub; no cliente eu sigo o fluxo deles.

4 · Case grande — iugu, não só a loja

Na Samara você foi de ERP + Bling (bom). No cliente, abre com iugu.

Na iugu eu estava no sistema principal de pagamentos. Squad de Operações e Banking: mensagens do SFN. Dinheiro real, API, SQL, fila, Git, qualidade. Tamanho: produto nacional de pagamentos, não um CRUD interno. Responsabilidade: feature ponta a ponta no domínio de operação, sem derrubar conciliação.

Bling entra como “integração com rate limit → Redis/Sidekiq → GoodJob+Postgres porque era app só meu”.

5 · O que testar + feature antiga

  • Sempre: model / PORO da regra (desconto, status, duplicidade).
  • Quase sempre: request spec do create/update (contrato HTTP).
  • Pouco: e2e da tela inteira.
  • Legado com muitos users: não faxina. Teste de regressão no caminho feliz, feature flag se der, entrega no desenho que já existe.
Eu testo a regra de dinheiro e o endpoint que o outro sistema chama. Tela eu cubro pouco. Numa feature antiga eu primeiro escrevo o spec do comportamento atual, mudo, vejo o spec quebrar ou não. Não refatoro o módulo inteiro no mesmo PR.

6 · Serviço de pagamento lento / fora

Termos que faltaram: timeout, retry, circuit breaker, fila, idempotência.

  1. Request do user não espera o gateway 30s → job Sidekiq.
  2. HTTP client com timeout curto (3–5s).
  3. Retry com backoff só em 5xx/timeout — nunca em 4xx.
  4. Circuit breaker: se o provedor cai, para de martelar.
  5. Idempotência (chave do pagamento) para não cobrir 2×.
  6. UI: “processando”; webhook confirma.
Na iugu o dinheiro não pode ficar preso no request web. Eu enfileiro, defino timeout, e o webhook fecha o status. Se o serviço externo cai, o job falha visível — a tela não fica girando.

7 · Tela lenta — ela perguntou banco

Paginação sozinha não responde “está no DB?”.

  1. Log ActiveRecord / Skylight / rack-mini-profiler: tempo SQL vs Ruby.
  2. N+1: includes(:orders) — cada cliente disparando pedido.
  3. EXPLAIN ANALYZE no SELECT pesado.
  4. Índice em client_id, created_at, o filtro da tela.
  5. Paginação no SQL (limit/offset ou keyset), não filtrar array em memória.
Eu abro o log de SQL. Se vejo 1 query por linha da tabela, é N+1. Se uma query demora, EXPLAIN e índice. Paginação é o passo seguinte, não o primeiro.

8 · 5× usuários — antes de subir servidor

  • O que é síncrono e pode ser fila (e-mail, PDF, sync Bling).
  • Queries e N+1 (item 7).
  • Cache (Redis) em leitura quente.
  • Connection pool do Postgres vs puma workers.
  • CPU vs IO vs DB — sobe web só se for CPU. Se for DB, replica de leitura ou índice.

9 · Controller gordo

Você já falou services. Fecha com exemplo.

No Cão Cidadão o boleto Iugu não nascia no controller. Controller autentica, chama um service, o service fala com a API e grava. Por quê: a regra de “gerar cobrança” aparece no job, no retry e no admin — se ficar no controller, copia.

Nomes: service object, form object, query object, job. Não precisa citar os quatro. Um basta.

10 · Muitas transações · DB no limite

O termo que faltou: lock (pessimista / otimista), connection pool, backpressure.

  • Ver se o gargalo é lock de linha, pool esgotado ou query lenta.
  • Pessimista: lock / FOR UPDATE em saldo — serializa na linha.
  • Otimista: coluna lock_version — dois updates, um falha, retry.
  • Fila na frente do DB (Sidekiq) = backpressure: o HTTP não abre 2 mil transações juntas.
  • Read replica se for leitura. Não replica escrita.
Eu olho pool e locks no Postgres. Se for pico de escrita no mesmo registro, lock de linha ou fila. Se for leitura, índice ou replica. Subir o RDS é o último passo.

11 · Microsserviços no legado

Eu não migraria tudo. Microsserviço troca complexidade de código por complexidade de rede, deploy e dado. Primeiro: modular monolith — pastas por domínio, services. Extraio um serviço só se uma integração (pagamento, SFN) já dói sozinha e tem time para operar. App nova em paralelo, big-bang não.

12 · Review + primeiros dias

Review com dívida futura

No PR eu digo o risco em uma frase e proponho o patch curto (índice, extração de 10 linhas). Se a pessoa segura e o bug não é de produção, eu não bloqueio — registro follow-up. Se for dado/pagamento, eu bloqueio e chamo mais uma pessoa.

Primeiros dias (legado + feature urgente + perf + 2 APIs, time pequeno)

  1. Dia 1: sobe o app, lê README, quem é dono das 2 APIs, onde está o p95.
  2. Fatia a feature no menor caminho que gera valor.
  3. Se a feature passa pelo gargalo de perf, trata esse risco. O resto do legado espera.

Inglês — 40 segundos cada

Ensaiar em voz alta. Devagar. Pedir para repetir a pergunta é ok uma vez.

Tell me about yourself

I’m Matheus. Eleven years in web, eight with Rails in production — payments at iugu, an autonomous store at Zaitt, and inventory software. I work as a contractor. I’m looking for a remote Rails role. My English is stronger in reading and writing; I don’t have recent calls with international clients.

How would you structure clients in Rails?

A migration, a Client model with validations, resources :clients, and a controller with strong params. Scaffold can generate files. I still own the validations. If create gets business rules, I extract a service.

REST API and Git?

JSON under /v1, token auth, versioned routes. Git: feature branch, small commits, pull request, review. I don’t push straight to main.