Codekanox

Homeschooling e supervisão familiar em um só app: os pais acompanham tarefas, progresso de estudo e localização dos filhos, com privacidade e bateria sob controle.

Rol: Lead mobile2024–2025Equipe de 3
React NativeExpoTypeScriptFirebaseexpo-locationGeofencingCloud Functions
99.5%
crash-free
−45%
bateria em segundo plano
+38%
tarefas concluídas

01 · Contexto e problema

A Codekanox é um homeschooling com supervisão familiar em versão mobile: os pais montam o plano de estudo dos filhos, acompanham o progresso e o tempo de estudo, e veem sua localização (zonas seguras como casa ou o centro de estudo) com alertas — tudo em um app com dois papéis.

Recebi o projeto com três problemas que se chocavam: a localização em segundo plano usava GPS contínuo e drenava a bateria; o modelo de tarefas misturava o plano com o progresso (sem histórico confiável); e não havia separação real de papéis pai/filho, com risco de cruzar dados entre famílias.

O objetivo: localização em segundo plano confiável sem matar a bateria, papéis e isolamento por família server-side, e registro de estudo que não se perca sem sinal — com a privacidade de menores como base, não como anexo.

Restrições

Monitoramento de menores → LGPD + consentimento parentalLocalização em 2.º plano (Doze + battery managers)Permissão background (revisão Google Play / Apple)Firebase como único backendConectividade intermitenteAndroid gama média-baixaDois papéis (pai / filho) em um app

02 · Decisões técnicas

1

Papéis + isolamento por família

Problema

O estado vivia disperso e sem separação de papéis; as regras não isolavam por família, então um dispositivo podia alcançar dados de outra.

Opções

Single role com flags · dois apps separados · custom claims por papel + familyId

Decisão ✓

Papéis com custom claims (pai/filho) + documento de família e vínculo por código de convite; regras Firestore que exigem o mesmo familyId. React Native + Expo (dev client) com Feature-Sliced + DDD pragmático.

Trade-off

Onboarding mais complexo (convite, verificação do vínculo), em troca de isolamento por família garantido server-side, não por convenção.

2

Localização em segundo plano + bateria

Problema

O GPS contínuo drenava a bateria em gama baixa, as atualizações se perdiam sem rede e os geofences eram pouco confiáveis.

Opções

GPS contínuo · geofencing nativo · significant-location-change · híbrido adaptativo

Decisão ✓

Geofencing nativo (entrar/sair de zonas seguras) + amostragem adaptativa (alta frequência só em movimento ou dentro de zonas críticas) + fila offline que sincroniza ao reconectar. expo-location + expo-task-manager para a tarefa em segundo plano.

Trade-off

Menos granularidade quando a criança está parada, em troca de −45% de bateria e zero perda de dados quando a rede cai.

3

Estudo/tarefas (homeschooling) + offline

Problema

O modelo misturava o plano de estudo com o progresso; o avanço se perdia sem rede e as listas longas derrubavam os FPS.

Opções

Um só modelo com flags · plano e progresso separados · fila idempotente

Decisão ✓

Separar o plano (currículo) do progresso (registro por dia, imutável), fila idempotente para o avanço, cache local (MMKV) e notificações de tarefa/zona.

Trade-off

Mais modelos e migração do schema legado, em troca de histórico confiável e avanço que não se perde offline.

Arquitetura

UI · Features (RN + Expo)
App · Hooks + Use Cases
Domain · TS puro (VOs)
Infra · Firestore + Geofencing + CF

03 · Localização em segundo plano confiável sem drenar a bateria (e sem perder dados offline)

O desafio mais difícil foi ter localização confiável em segundo plano no Android gama baixa, onde o Doze e os battery managers agressivos matam processos e cortam o GPS — sem transformar o app num drenador de bateria nem perder posições quando a rede cai.

O GPS contínuo era inviável: descarregava a bateria e gerava escritas inúteis com a criança parada. Mas a precisão também não podia cair a ponto de falhar um alerta de «saiu da zona segura».

Solução: geofencing nativo para os eventos que importam (entrar/sair de casa ou do centro de estudo) + amostragem adaptativa (subir a frequência só em movimento ou dentro de zonas críticas) + uma fila offline com chave idempotente por marca de tempo que sincroniza com o Firestore ao reconectar. A tarefa roda com expo-task-manager para sobreviver em segundo plano.

Resultado: −45% de consumo em segundo plano, geofences confiáveis para os alertas e zero posições perdidas em quedas de rede — sem sacrificar o alerta que realmente importa.

04 · Privacidade e proteção de menores

Monitorar um menor é delicado: o design parte da minimização de dados e do consentimento. Só se coleta o necessário (localização e progresso), atado à família e nunca exposto fora dela.

Transparência: o filho sabe que está sendo acompanhado (não é espionagem encoberta); o papel e as permissões são explícitos no onboarding.

Isolamento server-side: as regras Firestore exigem o mesmo familyId para ler localização ou progresso — nenhum dispositivo alcança dados de outra família, por construção.

Retenção e exclusão: purga real dos dados ao desvincular ou excluir a conta (com dry-run prévio), cumprindo a LGPD; os dados sensíveis não são vendidos nem reutilizados para outra coisa.

Demo interativa

Em breve: execute o código e veja-o rodando ao vivo, sem instalar nada.

main.dart
Run

Editor ao vivo (DartPad) — em breve

Em breve

04 · Resultados · antes / depois

crash-free
97.0%
99.5%
autonomia com tracking ativo
~6 h
~11 h
latência de alerta de zona
~90s
<20s
posições perdidas sem rede
frequentes
0
tarefas concluídas (semana)
+38%

05 · Retrospectiva

Definiria a estratégia de bateria/geofencing desde o dia 1: a amostragem adaptativa chegou tarde, após queixas reais de bateria; tê-la antes teria evitado retrabalho e avaliações ruins.

Fecharia antes a revisão de permissões de localização em background: as políticas da Google Play e da Apple para apps que rastreiam menores são estritas; convém projetá-las desde o início.

Manteria o isolamento por família nas regras (não no cliente), a separação plano/progresso e a transparência com o menor como regra de produto.

Código em destaque · Localização em segundo plano (amostragem adaptativa + fila offline)

A tarefa nativa (expo-task-manager) que roda em segundo plano: descarta posições irrelevantes para poupar bateria, enfileira localmente com chave idempotente e sincroniza ao reconectar — sem perder o alerta que importa.

src/tasks/background-location.ts
import * as TaskManager from "expo-task-manager";
import type { LocationObject } from "expo-location";

import { isInsideCriticalZone } from "@/lib/geofences";
import { enqueue, flush } from "@/lib/location-queue";

export const BG_LOCATION = "codekanox.bg-location";

// Tarea nativa (corre aunque la app esté en segundo plano). Muestreo adaptativo
// + cola offline: no drena batería ni pierde datos cuando la red cae.
TaskManager.defineTask(BG_LOCATION, async ({ data, error }) => {
  if (error || !data) return;
  const { locations } = data as { locations: LocationObject[] };
  const last = locations.at(-1);
  if (!last) return;

  const { latitude, longitude, accuracy, speed } = last.coords;

  // Casi quieto y fuera de una zona crítica → no registramos: ahorra batería
  // y escrituras (la posición no cambió de forma relevante).
  const quieto = (speed ?? 0) < 0.6;
  if (quieto && !isInsideCriticalZone(latitude, longitude)) return;

  // Encolamos local (sobrevive sin red) con clave idempotente por timestamp,
  // y vaciamos la cola contra Firestore si hay conexión.
  await enqueue({
    id: String(last.timestamp),
    lat: latitude,
    lng: longitude,
    accuracy: accuracy ?? null,
    at: last.timestamp,
  });
  await flush(); // si falla por red, queda en cola para el próximo ciclo
});

Em entrevista se nota: trabalho em segundo plano real, amostragem adaptativa por bateria, fila offline com idempotência (sem duplicados nem perdas) e a decisão do que NÃO registrar — critério, não só código.

Caso anterior
Próximo caso