Seis semanas num app de assinatura rejeitado duas vezes pela Apple
Um aplicativo com base de usuários ativa e a próxima versão travada na revisão da Apple. Em seis semanas, cinco frentes em paralelo: o binário ficou 88% mais leve, a cobertura de testes saiu de 35% para 81%, e a leitura da fatura de nuvem encontrou capacidade ociosa faturada por 27 dias que ninguém tinha visto.
O cliente não é nomeado. Citar cliente exige autorização escrita — sem ela, a narrativa é anonimizada, o que preserva o valor demonstrativo do método. Todos os números abaixo são medidos, e o que é estimativa está dito com essa palavra.
Contexto
O produto funcionava; o que não funcionava era conseguir publicar. O trabalho não começou por um roadmap, e sim por uma rejeição da Apple já em curso — o primeiro commit substantivo do período foi a remediação dela. Mobile e backend viviam num repositório único, dividindo histórico, integração contínua e fila de revisão — com ciclos de publicação que não combinam, já que o aplicativo depende da revisão da loja e o servidor sobe quando quiser. Separar em dois repositórios foi o primeiro passo estrutural, feito antes de qualquer otimização, e só a partir daí cinco frentes correram em paralelo.
Desafio
A pergunta que chegou foi “por que a Apple rejeitou de novo?”, e ela parecia pedir uma correção pontual. A primeira rejeição era mesmo pontual: compra dentro do app, URL de produção e privacidade. A segunda veio pela Guideline 4.3(b) — o app foi percebido como duplicata de uma categoria saturada. Não existe correção de código para isso: a ficha inteira liderava pelo vocabulário que define a categoria. Enquanto a rejeição fosse lida como bug, cada resubmissão seria uma tentativa de adivinhar qual palavra desagradou.
Jornada
Separação do monorepo
Os dois lados do produto compartilhavam um repositório só. Na prática, um PR de servidor disputava a mesma fila de revisão que um de aplicativo, o histórico não distinguia mais o que era de cada lado, e revisar exigia separar duas bases a cada leitura. A divisão em dois repositórios foi feita nos primeiros dias, e é o que torna possível tudo o que vem depois: cada lado passou a ter seu próprio portão de qualidade, porque um pipeline único não consegue reprovar uma ponta sem travar a outra
Loja
A ficha foi reescrita liderando pelos diferenciais que a categoria saturada não tem — meditação, respiração, jornadas guiadas, especialistas ao vivo — com os 18 campos das duas lojas consolidados em três idiomas e contagem de caracteres conferida campo a campo. A lista de vocabulário proibido virou lint: a montagem dos screenshots aborta se um termo aparecer. Uma decisão de posicionamento que passou a ser impossível de violar por distração
Peso
Os assets embarcados saíram de cerca de 250 MB para 30,5 MB, em quatro degraus — reencode das narrações, áudio para CDN, imagens de conteúdo para CDN. A cada degrau, o orçamento no CI baixou junto. É a diferença entre limpar e impedir a regressão: hoje um PR que engorde o binário além do teto quebra o build
Segurança
O token de sessão estava em armazenamento local em texto puro. Passou para o enclave seguro do sistema, com migração que apaga o valor antigo. Depois veio o épico de sessão renovável em quatro entregas encadeadas, e no backend um refresh opaco com rotação a cada uso e detecção de reuso que derruba a família de tokens do aparelho
Custo de nuvem
A leitura da fatura encontrou o serviço de API preso em quatro instâncias rodando o tempo todo por 27 dias, com o app praticamente ocioso: CPU a 0,05%, pico de 0,06 requisição por segundo. A causa não era carga — o alarme de redução de escala ficava sem dados nas horas sem tráfego, e a escala nunca descia
Engenharia
A cobertura subiu de 35% para 81% no mobile e de um piso de 3% para 61% no backend, com o piso funcionando como catraca. Cada PR passou a exigir 80% de cobertura nas linhas novas. E as decisões arquiteturais deixaram de viver só em documento: existem testes que quebram o build se alguém mudar o número escrito na decisão
Ganhos
Medidos — cada um obtido rodando algo ou lendo um valor real, com a fonte ao lado.
de ~250 MB para 30,5 MB, com o teto travado no CI
era 35% no início do período
era 3%, e sobe a cada entrega
quatro instâncias o tempo todo, com CPU a 0,05%
Ressalvas
- A economia de nuvem projetada — cerca de metade da conta — é cálculo sobre preço de lista, não fatura realizada. Enquanto o mês seguinte não for reaberto para conferência, a frase honesta é “estimamos economizar”, nunca “economizamos”
- A redução de 88% é do binário, que é o que o usuário baixa e o que ocupa o aparelho dele. O histórico do projeto continua carregando os arquivos originais, então o repositório não encolheu
O que este case não prova
Um case que só lista vitórias obriga o leitor a descobrir os limites sozinho, e normalmente ele descobre na hora errada. Estes são os limites:
- A resubmissão na loja foi aprovada — não há comprovação, então não se afirma
- A sessão renovável do aplicativo está integrada, mas ainda não chegou a produção
- A janela de exposição de um token vazado segue em 24 horas: a peça final foi revertida
- A limitação de tentativas em login e cadastro está declarada, mas não dispara hoje
- Os três itens de maior impacto em performance de tela foram diagnosticados e não executados
Próximos passos
- Fechar a janela do token em produção — toda a infraestrutura de renovação já existe dos dois lados
- Descrever a infraestrutura base em código, porque hoje a economia não tem trava e uma edição manual a reverte sem deixar rastro
- Ligar alarme de orçamento e detecção de anomalia de custo, que é o que evita a reincidência do incidente de 27 dias
- Reabrir a conta do mês seguinte e trocar a projeção pelo número cobrado
As ofertas que este trabalho sustenta
Segurança mobile e perícia
Avalio aplicativos contra o padrão OWASP MASVS e entrego plano de remediação priorizado, com a cobertura da avaliação declarada, porque nenhum laudo honesto promete ausência de vulnerabilidade. Na frente pericial, atuo como perito em informática com laudo e parecer técnico.
Modernização mobile e arquitetura-alvo
Apoio plataformas brownfield e críticas (Super Apps, Server-Driven UI, modularização) com GenAI como alavanca, não como atalho que deixa dívida escondida. A saída é uma rota de migração que pode ser interrompida a qualquer passo.
Orquestração sustentável de agentes
Instalo o fluxo Research → Plan → Implement no repositório, com papéis humanos claros, specs versionadas e governança, para que agentes acelerem sem substituir decisão arquitetural. Em 90 dias, com o efeito medido em DORA e adoção.
Diagnóstico de prontidão para agentes no SDLC
Avalio como o time desenvolve hoje e onde GenAI e agentes criam risco de débito técnico, desalinhamento arquitetural ou falsa produtividade. A maturidade é medida contra evidência observável, não contra autoavaliação.
Um trabalho como este
Uma conversa de 30 minutos para entender o seu contexto. Ao fim dela dá para dizer qual oferta se aplica — e se nenhuma se aplicar, isso também é resultado.

