Melhores Práticas de Produção
Seu app está publicado, seguro e os usuários estão fazendo onboarding. Agora vem o longo prazo: mantê-lo funcionando bem, lidando com atualizações sem quebrar coisas e escalando suas operações.
Esta última lição aborda as práticas operacionais que separam projetos de fim de semana de aplicações de qualidade de produção.
Monitorando o Que Importa
Você não pode consertar o que não pode ver. Configure monitoramento desde o primeiro dia.
Relatórios de crashes
Crashes destroem a confiança do usuário, especialmente em apps financeiros. Use um serviço de relatório de crashes:
// Usando Sentry (funciona com React Native)
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'https://your-dsn@sentry.io/project',
tracesSampleRate: 0.2, // 20% das transações para desempenho
environment: __DEV__ ? 'development' : 'production',
});
// Envolva seu componente raiz
const App = () => {
return (
<Sentry.ErrorBoundary fallback={<ErrorScreen />}>
<YourApp />
</Sentry.ErrorBoundary>
);
};
// Adicione contexto para depuração
Sentry.setUser({ id: userId });
Sentry.setTag('wallet_connected', hasWallet.toString());Monitoramento de transações
Para apps de cripto, você precisa de visibilidade nas interações com a blockchain:
// Registre tentativas de transação com contexto
const executeTransaction = async (tx: Transaction, type: string) => {
const startTime = Date.now();
try {
const signature = await connection.sendTransaction(tx, [wallet]);
await connection.confirmTransaction(signature);
// Registre o sucesso
analytics.track('transaction_success', {
type,
duration: Date.now() - startTime,
signature,
});
return signature;
} catch (error) {
// Registre a falha com detalhes
analytics.track('transaction_failure', {
type,
duration: Date.now() - startTime,
error: error.message,
errorCode: error.code,
});
Sentry.captureException(error, {
tags: { transaction_type: type },
extra: { tx: tx.serialize().toString('base64') }
});
throw error;
}
};Métricas-chave para acompanhar
Limites de alertas
Configure alertas para anomalias:
Taxa de crashes > 1% na última hora → Acione o plantonista
Taxa de falha de transações > 10% → Investigue imediatamente
Erros de API > 5% → Verifique backend/serviços de terceiros
Zero transações por 30 minutos (se você espera uso constante) → Possível queda
Gerenciamento de RPC
Endpoints RPC da Solana são sua ligação vital com a blockchain. Gerencie-os com cuidado.
Use múltiplos provedores RPC
Não dependa de um único provedor:
const RPC_ENDPOINTS = [
'https://your-primary.rpc.com',
'https://your-backup.rpc.com',
'https://api.mainnet-beta.solana.com', // Fallback público
];
let currentEndpointIndex = 0;
const getConnection = () => {
return new Connection(RPC_ENDPOINTS[currentEndpointIndex], 'confirmed');
};
const rotateEndpoint = () => {
currentEndpointIndex = (currentEndpointIndex + 1) % RPC_ENDPOINTS.length;
console.log(`Switched to RPC: ${RPC_ENDPOINTS[currentEndpointIndex]}`);
};
const executeWithRetry = async <T>(
operation: (connection: Connection) => Promise<T>,
maxRetries = 3
): Promise<T> => {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await operation(getConnection());
} catch (error) {
lastError = error;
// Rotacione em erros de conexão
if (error.message.includes('429') || error.message.includes('timeout')) {
rotateEndpoint();
}
}
}
throw lastError;
};Consciência sobre rate limiting
Provedores RPC têm limites de taxa. Trate-os elegantemente:
// Implemente exponential backoff
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const fetchWithBackoff = async (fetchFn: () => Promise<any>, maxRetries = 5) => {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fetchFn();
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries - 1) {
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
console.log(`Rate limited, waiting ${delay}ms`);
await sleep(delay);
} else {
throw error;
}
}
}
};Saúde do WebSocket
Para atualizações em tempo real, conexões WebSocket podem falhar silenciosamente:
const setupAccountSubscription = (publicKey: PublicKey) => {
let ws: number;
let heartbeatInterval: NodeJS.Timeout;
const connect = () => {
ws = connection.onAccountChange(
publicKey,
(accountInfo) => {
handleAccountUpdate(accountInfo);
}
);
// Heartbeat para detectar desconexões silenciosas
heartbeatInterval = setInterval(async () => {
try {
await connection.getSlot();
} catch {
console.log('WebSocket health check failed, reconnecting...');
cleanup();
connect();
}
}, 30000);
};
const cleanup = () => {
if (ws) connection.removeAccountChangeListener(ws);
if (heartbeatInterval) clearInterval(heartbeatInterval);
};
connect();
return cleanup;
};Estratégia de Atualização
Atualizações de apps mobile exigem mais planejamento do que deploy web.
Compatibilidade de versões
Seu app terá múltiplas versões em uso simultaneamente. Projete para isso:
// Versionamento de API
const API_VERSION = '2024.1';
const apiRequest = async (endpoint: string, options: RequestInit) => {
return fetch(`https://api.yourapp.com/v1/${endpoint}`, {
...options,
headers: {
...options.headers,
'X-App-Version': APP_VERSION,
'X-API-Version': API_VERSION,
}
});
};
// O backend pode rotear ou transformar com base na versãoMecanismo de atualização forçada
Às vezes você precisa que os usuários estejam na versão mais recente (correção de segurança, mudança que quebra a API):
const checkForceUpdate = async () => {
const response = await fetch('https://api.yourapp.com/app-config');
const config = await response.json();
const currentVersion = DeviceInfo.getVersion();
if (compareVersions(currentVersion, config.minimumVersion) < 0) {
// Atualização forçada necessária
Alert.alert(
'Atualização Necessária',
'Por favor, atualize para a versão mais recente para continuar.',
[
{
text: 'Atualizar',
onPress: () => Linking.openURL(config.storeUrl)
}
],
{ cancelable: false }
);
return false;
}
if (compareVersions(currentVersion, config.latestVersion) < 0) {
// Aviso suave de atualização
Alert.alert(
'Atualização Disponível',
'Uma nova versão está disponível com melhorias.',
[
{ text: 'Depois', style: 'cancel' },
{ text: 'Atualizar', onPress: () => Linking.openURL(config.storeUrl) }
]
);
}
return true;
};Feature flags
Controle a liberação de funcionalidades sem atualizações do app:
interface FeatureFlags {
newSwapInterface: boolean;
enableNftGallery: boolean;
showPromotionalBanner: boolean;
maxTransactionRetries: number;
}
const defaultFlags: FeatureFlags = {
newSwapInterface: false,
enableNftGallery: true,
showPromotionalBanner: false,
maxTransactionRetries: 3,
};
const fetchFeatureFlags = async (): Promise<FeatureFlags> => {
try {
const response = await fetch('https://api.yourapp.com/feature-flags');
const serverFlags = await response.json();
return { ...defaultFlags, ...serverFlags };
} catch {
return defaultFlags; // Fallback para padrões se a requisição falhar
}
};
// Uso
const FeatureFlagProvider: React.FC = ({ children }) => {
const [flags, setFlags] = useState<FeatureFlags>(defaultFlags);
useEffect(() => {
fetchFeatureFlags().then(setFlags);
}, []);
return (
<FeatureFlagContext.Provider value={flags}>
{children}
</FeatureFlagContext.Provider>
);
};Atualizações over-the-air
Para React Native, ferramentas como CodePush permitem atualizações de JavaScript sem revisão da App Store:
import CodePush from 'react-native-code-push';
// Verifique atualizações ao iniciar o app
const codePushOptions = {
checkFrequency: CodePush.CheckFrequency.ON_APP_START,
installMode: CodePush.InstallMode.ON_NEXT_RESTART,
};
const App = () => {
// ... seu app
};
export default CodePush(codePushOptions)(App);Limitações:
Não é possível atualizar código nativo (apenas o bundle JavaScript)
Apple e Google têm políticas sobre atualizações OTA
Não use para burlar a revisão com conteúdo que viole políticas
Suporte ao Usuário e Feedback
Apps de cripto têm desafios únicos de suporte. Os usuários perdem dinheiro se algo der errado.
Suporte no app
Facilite o relato de problemas com contexto:
const reportIssue = async (userDescription: string) => {
const diagnostics = {
appVersion: DeviceInfo.getVersion(),
buildNumber: DeviceInfo.getBuildNumber(),
os: Platform.OS,
osVersion: DeviceInfo.getSystemVersion(),
deviceModel: DeviceInfo.getModel(),
walletConnected: !!currentWallet,
walletType: currentWallet?.type,
recentTransactions: await getRecentTransactionLogs(),
timestamp: new Date().toISOString(),
};
await fetch('https://api.yourapp.com/support/issue', {
method: 'POST',
body: JSON.stringify({
description: userDescription,
diagnostics,
userId: currentUser?.id,
})
});
};Solução de problemas de transações
Quando os usuários relatam transações travadas ou falhas, você precisa da assinatura:
// Armazene o histórico de transações localmente
const logTransaction = async (tx: {
signature: string;
type: string;
status: 'pending' | 'confirmed' | 'failed';
timestamp: number;
error?: string;
}) => {
const history = await getTransactionHistory();
history.unshift(tx);
// Mantenha as últimas 100 transações
if (history.length > 100) history.pop();
await AsyncStorage.setItem('txHistory', JSON.stringify(history));
};
// Exponha na tela de suporte/depuração
const TransactionHistory = () => {
const [history, setHistory] = useState([]);
useEffect(() => {
getTransactionHistory().then(setHistory);
}, []);
return (
<FlatList
data={history}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => {
Clipboard.setString(item.signature);
Alert.alert('Copiado', 'Assinatura da transação copiada');
}}
>
<Text>{item.type} - {item.status}</Text>
<Text style={styles.signature}>{item.signature.slice(0, 20)}...</Text>
</TouchableOpacity>
)}
/>
);
};FAQ e autoatendimento
A maioria das solicitações de suporte é previsível. Construa autoatendimento:
Transação pendente por muito tempo? → Mostre como verificar no Solscan
Carteira não conecta? → Passos de solução de problemas para carteiras comuns
Saldo não atualiza? → Pull-to-refresh, explique tempos de confirmação
Perdeu acesso à carteira? → Explique opções de recuperação (ou a falta delas para carteiras externas)
Considerações de Escala
À medida que sua base de usuários cresce, diferentes partes do seu sistema sofrerão pressão.
Custos de RPC
Endpoints RPC públicos da Solana têm limites de taxa baixos. Provedores pagos cobram por requisição:
// Otimize o uso de RPC
// Ruim: Polling a cada segundo
setInterval(() => connection.getBalance(publicKey), 1000);
// Melhor: Use subscrições WebSocket
connection.onAccountChange(publicKey, (accountInfo) => {
updateBalance(accountInfo.lamports);
});
// Ainda melhor: Agrupe requisições quando possível
const [balance, tokenAccounts, recentTx] = await Promise.all([
connection.getBalance(publicKey),
connection.getParsedTokenAccountsByOwner(publicKey, { programId: TOKEN_PROGRAM_ID }),
connection.getSignaturesForAddress(publicKey, { limit: 5 })
]);Custos de backend
Se você faz proxy através de um backend:
Faça cache de respostas quando apropriado (metadados de token, imagens NFT)
Limite a taxa por usuário para prevenir abuso
Use edge functions (Cloudflare Workers, Vercel Edge) para distribuição global de baixa latência
Considerações de banco de dados
Histórico de transações e preferências de usuário crescem ao longo do tempo:
Arquive logs de transações antigos
Indexe campos frequentemente consultados
Considere bancos de dados de séries temporais para dados de analytics
Conformidade e Legal
Isso não é aconselhamento jurídico, mas conscientização ajuda você a fazer as perguntas certas.
Termos de Serviço
Todo app precisa de um ToS. Para apps de cripto, cubra:
Aviso de autocustódia (você não guarda as chaves deles)
Aviso de que não oferece aconselhamento financeiro
Restrições geográficas, se houver
Política de uso aceitável
Política de Privacidade
Exigida pela App Store e Play Store. Para apps de cripto:
Quais dados você coleta (endereços de carteira, histórico de transações, info do dispositivo)
Como você os usa
Serviços de terceiros com quem compartilha (analytics, provedores RPC)
Conformidade com GDPR se atender usuários da UE
Restrições geográficas
Algumas funcionalidades exigem licenciamento em jurisdições específicas:
Funcionalidades de trading/exchange: Licenças de transmissão de dinheiro em estados dos EUA
On/off ramps fiduciários: Requisitos de processador de pagamentos
Securities: Se seus tokens podem ser títulos, proceda com cautela
Muitos apps simplesmente bloqueiam certas jurisdições para evitar exposição regulatória.
Checklist de Manutenção
Tarefas contínuas para manter seu app saudável:
Semanalmente
Revise relatórios de crashes e priorize correções
Verifique as taxas de sucesso de transações
Monitore a saúde e os custos do RPC
Revise feedback de usuários/tickets de suporte
Mensalmente
Atualize dependências (verifique patches de segurança)
Revise analytics para padrões de uso
Teste fluxos críticos nas versões mais recentes do SO
Revise atualizações de políticas da App Store/Play Store
Trimestralmente
Audite dependências de terceiros por vulnerabilidades
Revise e rotacione chaves de API/credenciais
Teste recuperação de desastres (consegue restaurar a partir de backups?)
Atualize documentação (API, guias do usuário)
Anualmente
Renove certificados (notificações push, SSL pins)
Revise a arquitetura geral para necessidades de escala
Audite práticas de segurança
Planeje o roadmap da próxima versão principal
Resumo do Curso
Você completou o curso final do currículo de desenvolvimento mobile na Solana. Vamos recapitular o que cobrimos:
Lição 1: Cenário de distribuição
Três canais: Play Store, App Store, Solana dApp Store
Cada um tem políticas e trade-offs diferentes
Apps de cripto enfrentam desafios únicos nas lojas tradicionais
Lição 2: Publicação na Solana dApp Store
Registro baseado em NFT (Publisher → App → Release)
Ferramentas CLI para minting e submissão
Requisitos de assets e processo de revisão
Lição 3: Estratégias de revisão da App Store
Diretrizes 3.1.5 de criptomoedas da Apple
Política de conteúdo blockchain do Google Play
Contas de teste e modos de demonstração para revisão
Tratamento de rejeições e recursos
Lição 4: Segurança mobile
Realidade da engenharia reversa (tudo pode ser extraído)
Armazenamento seguro com Keychain/Keystore
Padrões de segurança de API (proxy, tokens de curta duração)
Proteção de chaves privadas
Lição 5: Operações de produção
Monitoramento e alertas
Gerenciamento de RPC e fallbacks
Estratégias de atualização e feature flags
Rotinas de suporte e manutenção
O Que Vem a Seguir
Agora você tem o conhecimento para construir, proteger, publicar e manter uma aplicação mobile Solana em todos os canais de distribuição.
O melhor próximo passo é construir algo. Escolha um problema que você se importa, aplique o que aprendeu e publique.
Algumas ideias para explorar:
Uma UI de carteira simples com carteiras embutidas Privy
Uma experiência mobile com token-gating
Um app de galeria NFT
Um browser de dApps otimizado para mobile
O ecossistema mobile da Solana ainda é jovem. Há espaço para construir ferramentas e apps que milhões vão usar. Agora você sabe como levá-los até lá.