Career Agent PRO A preparação perfeita para cada vaga
GUIA ATIVO
IA · PERSONALIZADO

# System Design — Desafio Técnico

### Pessoa Desenvolvedora Android Sênior | GOK Inovação Digital

## O Cenário

Você acaba de assumir como Android Sênior em um projeto da GOK para um cliente do segmento financeiro: uma fintech de médio porte que processa pagamentos via Pix, boleto e cartão.

O app atual tem ~800 mil usuários ativos mensais, foi construído há 4 anos com arquitetura monolítica, navegação baseada em Activities, callbacks aninhados e sem separação de camadas. O time anterior saiu. Você precisa propor a arquitetura do app reescrito — com uma restrição crítica:

> Atenção: O app não pode parar. Releases continuam acontecendo a cada duas semanas enquanto a refatoração ocorre.

O backend já foi modernizado: existe um BFF (Backend for Frontend) dedicado ao mobile, um API Gateway com rate limiting e circuit breaker, e microsserviços independentes para pagamentos, autenticação e notificações.

O entrevistador abre com a seguinte pergunta:

> “Me descreve como você arquitetaria esse app do zero — considerando que ele precisa continuar evoluindo em paralelo com a refatoração, que opera em ambiente financeiro crítico e que o time é pequeno.”

## Requisitos do Sistema

### Funcionais

- Autenticação com OAuth2, biometria e suporte a múltiplos fatores (MFA)
- Fluxo completo de pagamento Pix — chave, QR Code estático e dinâmico, copia-e-cola
- Histórico de transações com paginação e filtros
- Notificações push em tempo real para confirmação de transações
- Modo offline parcial: visualização de saldo e histórico recente sem conexão
- Deep links para abertura direta em fluxos específicos (ex: pagamento via link)
- Feature flags para rollout gradual de novas funcionalidades

### Não Funcionais

Dimensão | Requisito
--- | ---
Disponibilidade | Tolerância zero a crash em fluxos de pagamento
Performance | Telas de pagamento devem renderizar em menos de 300ms após navegação
Segurança | Tokens OAuth2 não podem ser armazenados em SharedPreferences plano; proteção contra repackaging e hooking
Escalabilidade | Arquitetura que permita múltiplos devs trabalhando em features paralelas sem conflito
Testabilidade | Cobertura mínima de 70% nas camadas de domínio e dados
Observabilidade | Crashes e erros de transação rastreáveis com contexto suficiente para reprodução

## Os Desafios Ocultos

Estes são os pontos que o entrevistador não menciona explicitamente, mas espera que um candidato sênior levante por conta própria. Não espere ser perguntado — antecipe.

### 1. Migração incremental sem feature freeze

A reescrita não pode ser do tipo big bang. É necessário propor uma estratégia de convivência entre código legado e código novo.

A abordagem recomendada é o padrão Strangler Fig aplicado ao mobile: módulos novos convivem com telas antigas até que a migração esteja completa, feature por feature.

> Dica estratégica: Se você não levantar esse ponto, o entrevistador vai perguntar diretamente: “Como você garante que o app funcione durante a transição?” Sair na frente demonstra senioridade.

### 2. Consistência de estado em fluxos de pagamento

O QR Code dinâmico do Pix tem janela de expiração. O estado da transação no cliente precisa ser gerenciado em pelo menos três cenários críticos:

- O usuário vai para o background durante o fluxo de pagamento
- A rede cai após o envio, mas antes da confirmação chegar
- O backend retorna timeout, mas a transação pode ter sido processada

> Erro comum: Tratar o timeout como falha definitiva. Em ambiente financeiro, um timeout ambíguo exige estado pendente, não erro.

### 3. Segurança do token e superfície de ataque

`EncryptedSharedPreferences` é a camada mínima aceitável — não o teto. Em dispositivos rooteados, essa proteção é insuficiente.

A abordagem correta envolve:

- Keystore para derivação de chave
- EncryptedSharedPreferences para armazenamento
- Certificate Pinning para comunicação com o BFF
- Detecção de root e hooking como camadas adicionais de defesa

> Dica estratégica: Segurança mobile é feita em camadas. Não existe proteção absoluta no dispositivo — o objetivo é elevar o custo do ataque.

### 4. Custo real da modularização em time pequeno

Modularização não é bala de prata. Ela traz overhead concreto: tempo de build, complexidade de navegação entre módulos e convenções que precisam ser mantidas.

Em um time pequeno com pressão de entrega, defender modularização completa desde o primeiro dia sem reconhecer esse custo é um sinal de imaturidade técnica.

> Dica estratégica: Proponha modularização incremental — comece com 2 a 3 módulos core e expanda conforme o time e o produto amadurecem.

### 5. Modo offline e consistência de dados

“Modo offline parcial” parece simples, mas esconde uma decisão arquitetural relevante: quando o cache local e o servidor divergem após a reconexão, qual é a fonte da verdade?

A abordagem correta para dados financeiros:

- Leitura: offline permitido, com indicador visual explícito de dados desatualizados (staleness)
- Escrita: online obrigatório — saldo e transações nunca devem ser enviados com base em cache

### 6. Observabilidade além do Crashlytics

O Crashlytics captura crashes. Mas o que captura os erros silenciosos?

- Uma transação que falhou sem lançar exception
- Um timeout tratado incorretamente como sucesso
- Uma resposta mal-formada do BFF que passou pela validação

> Dica estratégica: Pense em logging estruturado de eventos de negócio, não apenas em crash analytics. Em ambiente financeiro, o que não foi logado pode não ser auditável.

## As Perguntas do Entrevistador

As perguntas seguem uma progressão intencional: começam amplas, afunilam em trade-offs e terminam em cenários de pressão.

### Abertura

> “Por onde você começaria? Me descreve a arquitetura em alto nível antes de entrar nos detalhes.”

O que está sendo avaliado: Capacidade de estruturar o raciocínio antes de codar e clareza de comunicação técnica.

### Aprofundamento arquitetural

> “Você escolheu Clean Architecture com MVI. Por que MVI e não MVVM nesse contexto específico? Quais são os trade-offs reais dessa decisão para o time?”

O que se espera: Ir além de “MVI é unidirecional”. Mencionar previsibilidade de estado em fluxos de pagamento, rastreabilidade de UI states para debug — e reconhecer o custo adicional de boilerplate em features simples.

### Modularização

> “Como você estruturaria os módulos? Como gerenciaria dependências compartilhadas — autenticação, tema, componentes de UI?”

O que se espera: Distinção entre feature modules, core modules e app module. Estratégia de navegação via contratos de interface ou navigation graph compartilhado. Reconhecimento do custo de configuração Gradle.

### Cenário de falha

> “O usuário confirmou um Pix. O app enviou a requisição para o BFF. A rede caiu antes da resposta chegar. O usuário reabre o app 30 segundos depois. O que acontece?”

O que se espera: Não existe resposta única correta — o entrevistador quer ver o raciocínio. Espera-se menção a:

- [ ] Estado pendente persistido localmente
- [ ] Polling ou push notification para confirmação assíncrona
- [ ] UI de estado ambíguo: “Verificando pagamento…”
- [ ] Estratégia de idempotência acordada com o backend

### Segurança

> “Onde você armazenaria o access token e o refresh token? E se o device estiver rooteado?”

O que se espera: Keystore para derivação de chave, EncryptedSharedPreferences para armazenamento, detecção de root como camada adicional — e a clareza de que segurança mobile é um conjunto de camadas, não uma solução absoluta.

### Migração incremental

> “O app atual está em produção com 800 mil usuários. Como você refatora sem parar os releases?”

O que se espera: Strangler Fig pattern, feature branches por módulo, testes como rede de segurança para a migração e feature flags para ativação gradual das novas implementações.

### Testabilidade

> “Como você garante testabilidade em uma Clean Architecture com MVI? Me dá um exemplo concreto de como testaria o fluxo de confirmação de pagamento.”

O que se espera:

- Teste unitário do Use Case com repositório mockado (MockK)
- Teste do ViewModel verificando a sequência de `UiStates` emitidos
- Teste de integração do repositório contra servidor fake ou interceptor de rede

### Pressão e ambiguidade

> “O PM quer lançar o novo fluxo de Pix em 3 semanas. A arquitetura nova ainda não está pronta. O que você faz?”

O que se espera: Maturidade, não perfeição. O candidato não cede à pressão sem análise. Propõe alternativas concretas — feature flag no legado, escopo reduzido, débito técnico explícito — e comunica riscos com clareza para o PM.

> Dica estratégica: Nessa pergunta, demonstrar que você não vai simplesmente dizer sim vale mais do que qualquer resposta técnica elaborada.

## Trade-offs que Você Deve Levantar por Iniciativa Própria

> Atenção: Um candidato sênior não espera ser perguntado sobre trade-offs. Ele os articula espontaneamente como parte do raciocínio arquitetural.

Decisão | Trade-off a articular
--- | ---
Clean Architecture | Mais testabilidade e flexibilidade, mas mais arquivos, mais boilerplate e curva de onboarding maior. Em times pequenos, pode gerar overhead real.
MVI vs MVVM | MVI oferece rastreabilidade de estado superior para fluxos complexos, mas tem mais boilerplate em telas simples. Para um app misto, pode ser válido usar MVI seletivamente nos fluxos financeiros e MVVM nas demais telas.
Modularização completa vs incremental | Modularização total reduz acoplamento e paraleliza o desenvolvimento, mas tem custo alto de configuração Gradle. Em time pequeno com pressão de entrega, começar com 2 a 3 módulos core é mais realista.
Offline-first vs online-first | Offline-first melhora resiliência, mas para dados financeiros pode comunicar informações desatualizadas como verdade — um risco de UX e confiança. A abordagem correta: offline para leitura com indicador de staleness, online obrigatório para escrita.
Feature modules vs módulos por camada | Feature modules isolam contextos de negócio. Módulos por camada são mais simples, mas criam acoplamento entre features. Para a GOK com time pequeno, feature modules com core compartilhado é o equilíbrio mais defensável.
EncryptedSharedPreferences vs Keystore direto | EncryptedSharedPreferences é uma abstração conveniente, mas insuficiente em dispositivos comprometidos. A chave deve ser derivada pelo Keystore, com proteções adicionais como camadas complementares.
StateFlow vs LiveData | StateFlow não está vinculado ao ciclo de vida do Android nativamente — exige `repeatOnLifecycle` para coleta segura. LiveData é mais simples, mas menos expressivo para estados complexos e não funciona bem com Compose. A escolha por StateFlow é a correta, mas o custo de gerenciamento de ciclo de vida precisa ser compreendido.
Strangler Fig vs Big Bang rewrite | Big Bang garante consistência arquitetural, mas congela features por meses — inaceitável para 800 mil usuários em produção. Strangler Fig tem complexidade temporária e risco de inconsistência, mas é o único caminho viável. O candidato deve definir também o critério de “módulo pronto para migrar”.

> Sinal de maturidade técnica: O candidato que espontaneamente diz “essa abordagem tem custo X, e em um time pequeno isso importa” demonstra que pensa em engenharia de produto — não apenas em engenharia de software. Esse é exatamente o perfil que separa um sênior executor de um sênior de referência.