Duas dúzias de apps Android mantidas por um só dev
Por Nesim Razon, desenvolvedor Android independente · 2026-11-12
Mantenho sozinho cerca de duas dúzias de apps e jogos Android pequenos: uma mistura de jogos de quebra-cabeça e utilitários do dia a dia. Isso só funciona porque quase tudo o que se repete é copiado de um padrão conhecido e testado, ou é tirado do app por completo. Este texto descreve os padrões que sobreviveram e os hábitos em torno deles.
A regra por trás de tudo: escrever uma vez e copiar a forma
Com um único app, você pode se dar ao luxo de ser engenhoso. Com duas dúzias, cada caso especial que você inventa é um caso especial que vai esquecer. Por isso, cada recurso que aparece em mais de um app tem uma implementação de referência, e os apps novos a copiam em vez de reinventá-la. A referência fica em um dos jogos mais recentes, e o próximo app parte desse arquivo, com o teste junto.
Copiar não é elegante, e uma biblioteca compartilhada ficaria mais organizada no papel. Na prática, os apps diferem em nomes de pacote, configuração de anúncios e telas, são publicados no seu próprio ritmo, e um módulo compartilhado obrigaria a publicar todos de forma coordenada a cada mudança. Copiar um arquivo pequeno e bem testado mantém cada app independente. Quando o padrão melhora, os apps antigos recebem a mudança na próxima versão natural, sem pressa.
Promoção cruzada como dado, não como código
Cada jogo tem uma janela de Outros jogos que lista o resto da família. No início essa lista era compilada dentro de cada app, o que significava que lançar um jogo novo exigia publicar uma atualização de todos os antigos. Isso não escala.
Agora a lista é um pequeno arquivo JSON servido por um site estático. Uma única classe em cada app o baixa no máximo a cada 48 horas e o guarda em cache no aparelho. Se não houver rede, ou se o arquivo em cache estiver ausente ou corrompido, o app recorre a uma cópia incluída em seus recursos, então a janela nunca fica vazia. Um jogo novo é uma linha nova no JSON mais um ícone, e nenhum app antigo precisa de versão nova.
Como o arquivo vem pela rede, o app o trata como não confiável. Ele só abre links da Google Play, verifica o nome do pacote e a chave com padrões rígidos, limita o número de entradas e o tamanho do arquivo, e recusa qualquer servidor diferente do esperado. A janela lê o idioma do aparelho e, se não o encontrar, usa inglês. O arquivo é gerado por um script curto a partir de uma lista de jogos, então nunca edito o JSON à mão.
Aviso de atualização e salvamento na nuvem, copiados de uma referência
Há dois recursos fáceis de esquecer quando se está focado na jogabilidade e caros de acrescentar depois: avisar o usuário de que existe uma versão nova e preservar o progresso quando ele troca de celular. Os dois já estão na minha lista de verificação para a primeira versão de qualquer app novo.
Para atualizações, uso a API de atualização dentro do app da Google Play no modo flexível. Um objeto pequeno verifica quando o app volta ao primeiro plano. Ele só pergunta se há mesmo uma atualização disponível, só quando a versão nova já está publicada há pelo menos um dia, porque a Google Play libera as versões de forma gradual, e no máximo uma vez a cada três dias para a mesma versão. Quando o download termina, oferece reiniciar. Se o app não foi instalado pela Google Play, ou o aparelho está sem conexão, a verificação simplesmente não faz nada. A lógica de decisão é uma função pura, por isso tem testes unitários, e os textos do diálogo existem em quatro idiomas.
Para o progresso, uso os jogos salvos do Play Games nos jogos que têm um estado de progresso relevante, de modo que um celular novo continue de onde o anterior parou. Que todo jogo novo tenha isso desde a primeira versão é uma decisão deliberada, tomada uma única vez. Alguns jogos antigos são exceções propositais, porque o estado deles é trivial.
Uma chave de envio e o Play App Signing
Os apps novos usam o Play App Signing. O Google guarda a chave de assinatura real e eu tenho apenas uma chave de envio, compartilhada entre os apps mais recentes. Se uma chave de envio for perdida ou comprometida, ela pode ser redefinida pela Play, o que assusta bem menos do que perder uma chave de assinatura real. Os apps anteriores a esse sistema mantêm a assinatura original e são tratados à parte; separo claramente os dois grupos nas minhas anotações para não os confundir.
Os scripts de publicação pedem a senha da chave no momento de assinar, e nada secreto é escrito em arquivo, repositório ou código.
Um site que se constrói sozinho
Cada app precisa de uma página de apresentação, uma de privacidade e uma de termos de uso, e para os jogos escrevo também guias e textos de blog. Fazer isso à mão para duas dúzias de apps em quatro idiomas é um jeito certo de ficar para trás, então o site é gerado.
Um único comando reconstrói tudo. Ele executa os geradores em ordem fixa: a página inicial, as páginas de detalhe de cada app, os guias dos jogos e o blog, e por fim uma etapa que divide uma única fonte em quatro idiomas em páginas separadas por idioma. O inglês fica na raiz e os demais em pastas de idioma. Essa etapa também escreve os links hreflang, os endereços canônicos, um mapa do site com datas de última modificação e dados estruturados. Os dados da página inicial ficam em um único arquivo JSON: um app novo é uma linha a mais, e um app ainda não publicado pode ser marcado como em breve, de modo que seu cartão aparece sozinho no dia do lançamento.
Uma regra que mantenho: arquivos gerados nunca são editados à mão. Se uma página está errada, corrijo o gerador ou os dados e reconstruo. As pastas de idioma são apagadas e reescritas a cada compilação, então uma edição manual sumiria de qualquer maneira, e é essa a ideia. Isso mantém a saída coerente, e a coerência é o que um desenvolvedor solitário não pode se dar ao luxo de perder.
Scripts para o trabalho chato
Tudo o que faço mais de duas vezes vira script. Um script de sincronização atualiza todos os repositórios, um por app, e informa quais têm trabalho não confirmado ou não enviado, de modo que vejo numa só tela se é seguro trocar de computador. Cada app tem um script de compilação e um de publicação que gera o pacote assinado, e a saída da compilação vai para um arquivo de registro com nome único, no qual procuro as palavras que importam em vez de ficar olhando o texto rolar.
Trabalho com dois computadores em dois lugares diferentes, então o script de sincronização já me salvou mais de uma vez de commits esquecidos. É uma coisa pequena e se paga toda semana.
Anotações e lista de tarefas, mantidas com honestidade
A memória não dá conta de duas dúzias de projetos. Cada app tem na sua raiz um arquivo de anotações com o estado atual: o que foi publicado, qual versão está no ar, o que está pendente e por que uma decisão passada foi tomada. Uma única lista de tarefas geral reúne tudo o que está em aberto em todos os apps, e um breve registro contínuo anota o que aconteceu em cada sessão de trabalho.
O que importa é a disciplina de mantê-los verdadeiros. Quando termino uma tarefa, atualizo a lista e as anotações do app na mesma sessão, e antes de planejar qualquer coisa confronto o que está escrito com a realidade; por exemplo, pergunto à Google Play qual versão está no ar em vez de confiar no que anotei na semana passada. Uma lista que mostra como aberto o que já foi feito, ou o contrário, é pior do que não ter lista, porque faz você desconfiar dela inteira.
O que essa abordagem custa
Não é de graça. Arquivos copiados se afastam com o tempo, então um app antigo pode ter uma versão um pouco mais velha de um padrão até a próxima publicação. Um site gerado significa que o próprio gerador é um software que preciso manter e testar. E uma lista remota é mais uma coisa que pode falhar, por isso cada recurso remoto tem uma cópia de reserva incluída e validação rígida.
Em troca, ganho previsibilidade. Um app novo já começa com as atualizações, o salvamento na nuvem, um menu, uma página de privacidade, a promoção cruzada e uma entrada no site resolvidos, e posso dedicar meu tempo ao que faz o app valer a instalação. Se você mantém mais do que alguns poucos apps, meu conselho é escolher as duas ou três coisas que você refaz sempre, dar a cada uma uma implementação de referência e tirar do binário tudo o que puder ser dado.
Perguntas frequentes
Quantos apps essa abordagem cobre?
Cerca de duas dúzias de apps e jogos, entre jogos de quebra-cabeça e pequenos utilitários do dia a dia. Os padrões importam mais nos mais recentes.
Por que não usar uma biblioteca compartilhada em vez de copiar arquivos?
Apps independentes são publicados em ritmos diferentes. Um módulo compartilhado exigiria lançamentos coordenados, enquanto um arquivo pequeno, copiado e testado mantém cada app independente.
Como a lista remota de Outros jogos se mantém segura?
O app a baixa apenas de um servidor esperado, guarda em cache por 48 horas, valida cada campo, limita o tamanho e recorre a uma cópia incluída se algo der errado.
Quando um app deve pedir ao usuário que atualize?
O fluxo flexível de atualização dentro do app funciona bem quando pergunta só depois de a versão estar publicada há um dia, no máximo uma vez a cada poucos dias e em um momento calmo, como quando o app volta ao primeiro plano.
Você edita o site gerado à mão?
Nunca. As correções vão para o gerador ou para o arquivo de dados, e o site inteiro é reconstruído com um único comando.
Mais do blog
- Como dar o mesmo quebra-cabeça diário a todos sem servidor
- Como centenas de fases de quebra-cabeça são geradas e verificadas
- Detectar um anúncio intersticial travado no Android com eventos
Como garantimos que um Mahjong Paciência tenha solução
História do Mahjong Paciência e a diferença para o Mahjong- Jogo da velha: história de um jogo resolvido e suas variantes