Pular para o conteúdo
Gabriel Sferra

Gabriel Sferra

Desenvolvedor Full Stack

Construo produtos web e mobile de ponta a ponta, do banco de dados à interface.

Marília, SP · Brasil

apps/api/src/db/schema.tsOrbital
// O banco recusa vincular um gasto a uma categoria de receita. A coerência
// deixa de depender de lembrarmos de validar em cada rota.
foreignKey({
  columns: [t.categoryId, t.kind],
  foreignColumns: [categories.id, categories.kind],
  name: 'transactions_category_kind_fk',
}),
netlify/functions/state.mjs(repositório privado)Grit
const SAVE_STATE = `insert into states (user_id, doc) values ($1, $2::jsonb)
  on conflict (user_id) do update set doc = excluded.doc, saved_at = now()`;
/* … */
const SAVE_STATE_IF_NOT_OLDER = `${SAVE_STATE} where states.updated_at <= excluded.updated_at returning user_id`;
backend/app/Services/RegistrationService.phpCadastro Full Stack
if (preg_match('/^(\d)\1{10}$/', $cpf)) {
    return false;
}

$sum = 0;
for ($i = 0; $i < 9; $i++) {
    $sum += intval($cpf[$i]) * (10 - $i);
}
$remainder = $sum % 11;
$digit1 = ($remainder < 2) ? 0 : 11 - $remainder;

Projetos

O que eu construí, com o código por trás de cada decisão.

Em desenvolvimento

Orbital

Controle financeiro pessoal para iPhone, Android e web, servido por uma única API.

O problema
Controlar gastos numa planilha não acompanha a pessoa no celular. O Orbital leva o controle para iPhone, Android e web sem duplicar regra de negócio: as três telas falam com a mesma API.
O que eu fiz
Projeto pessoal: modelagem do banco, API, app multiplataforma e testes.

Decisões técnicas

  • Monorepo com o contrato da API em schemas Zod compartilhados: se o formato de uma resposta muda, o TypeScript do app quebra na hora.
  • Dinheiro sempre em centavos (bigint) e lançamentos como data de calendário, sem erro de ponto flutuante nem de fuso horário.
  • Chave estrangeira composta: o próprio banco recusa um gasto ligado a uma categoria de receita.
  • Lançamentos recorrentes gerados na leitura, sem cron, com um índice único que impede duplicidade.

82 testes cobrindo valores em pt-BR, limites de mês e fuso e recorrência. Em desenvolvimento, com cadastro por convite.

  • TypeScript
  • Expo
  • React Native
  • Hono
  • Drizzle
  • Better Auth
  • Zod
  • Cloudflare Workers
  • PostgreSQL
apps/app/lib/api.tsOrbital
// Só o TIPO cruza a fronteira: o TypeScript enxerga cada rota e cada resposta,
// mas o Metro não empacota uma linha do servidor dentro do app.
import type { AppType } from '@financas/api'
/* … */
const rpc = hc<AppType>(API_URL, {
  /* … */
  1. Linha 3Só o tipo da API entra no app. Nenhuma linha do servidor vai junto para o iPhone, o Android ou a web.
  2. Linha 5O cliente conhece cada rota e cada resposta. Se a API mudar um formato, o app deixa de compilar na hora.
No ar

Grit

App de dieta e treino para iPhone, com checklist diário, que funciona offline.

O problema
Seguir uma dieta e uma ficha de treino exige registrar refeições, cargas e água ao longo do dia. O Grit faz isso no iPhone, funciona offline e sincroniza quando há conexão.
O que eu fiz
Projeto pessoal em produção: interface, banco, API, notificações e testes. Usado por mim e por amigos, cada um com sua conta.

Decisões técnicas

  • Offline-first: os dados vivem no aparelho e sincronizam com o Postgres; um conflito é resolvido numa única escrita condicional, sem corrida.
  • SQL escrito à mão e migrações versionadas, sem ORM.
  • Notificações push no iPhone pelo padrão Web Push, sem app nativo nem conta de desenvolvedor da Apple.
  • Dietas propostas por um agente de IA só chegam ao usuário depois de validadas pelas regras de calorias e macros do próprio app.

No ar e em uso diário, com 274 testes automatizados, incluindo a API contra um Postgres real em Docker.

  • JavaScript
  • Vite
  • Tailwind CSS
  • PWA
  • Netlify Functions
  • PostgreSQL (Neon)
  • Web Push
  • node:test
Tela de treino do Grit no iPhone: Treino A, peito e tríceps, com a lista de exercícios
src/lib/diet-plan.js(repositório privado)Grit
export function checkProposal(proposal, { targets, customFoods = [] }) {
  /* … */
  if (errors.length) return refused(errors);
  const totals = roundMacros(sumMacros(planMeals.flatMap(meal => meal.foods))), items = targetItems(totals, targets);
  /* … */
  return { errors, plan, items, ok: items.every(item => item.ok) };
}
  1. Linha 3Uma proposta com formato errado é recusada antes de qualquer conta.
  2. Linha 6Só é aprovada se as calorias e cada macro ficarem dentro da faixa da meta da pessoa.
Código aberto

Cadastro Full Stack

Sistema de cadastro de usuários com login Google OAuth 2.0.

O problema
Cadastrar e listar usuários com login social, validação de dados brasileiros e envio de e-mail, sem travar a interface.
O que eu fiz
Back-end em Laravel, front-end em Vue, testes e ambiente em Docker.

Decisões técnicas

  • Repository Pattern e camada de serviços, com injeção de dependência pelos Service Providers.
  • Refresh token automático, validação de CPF e paginação com filtros feita no servidor.
  • E-mails enviados de forma assíncrona por filas no Redis.
  • Ambiente inteiro (API, front, MySQL e Redis) sobe com um docker compose.

Testes unitários e de API com PHPUnit no back-end e Vitest no front-end.

  • Laravel 11
  • PHP
  • Vue 3
  • TypeScript
  • MySQL
  • Redis
  • Docker
backend/app/Services/RegistrationService.phpCadastro Full Stack
$user = $this->userRepository->update($userId, [
    'name' => $data['name'],
    'birth_date' => $data['birth_date'],
    'cpf' => $data['cpf'],
    'registration_completed' => true,
]);
/* … */
SendRegistrationEmail::dispatch($user, $email);
  1. Linha 1O serviço grava pelo repositório, não direto pelo Eloquent: a persistência pode mudar sem mexer na regra de negócio.
  2. Linha 8O e-mail vai para uma fila no Redis, então a resposta ao usuário não espera o envio.
Gabriel Sferra de terno azul-marinho e gravata

Sobre

Sou desenvolvedor full stack em Marília, SP. Comecei no front-end, criando lojas virtuais para clientes da Agência Auaha, e hoje trabalho na Tray como Analista de Testes, validando features das integrações com marketplaces.

A experiência com testes de software mudou meu jeito de construir: prefiro modelar os dados para que o banco recuse um estado inválido, gosto de tipos que atravessam do servidor ao cliente e concentro os testes onde um erro custaria caro.

Fora do trabalho, construo meus próprios produtos: um app de finanças que roda no iPhone, no Android e na web, e um PWA de treino e dieta que uso no dia a dia com amigos.

  • Inglês fluente

Tecnologias

O que uso no trabalho e nos meus projetos, por área.

Front-end

  • TypeScript
  • JavaScript
  • Vue 3
  • React
  • React Native (Expo)
  • Tailwind CSS
  • SCSS

Back-end

  • Node.js (Hono)
  • PHP (Laravel)
  • C# (ASP.NET Core MVC)

Dados e infra

  • PostgreSQL
  • MySQL
  • Redis
  • Docker
  • Cloudflare Workers
  • Netlify

Qualidade

  • PHPUnit
  • Vitest
  • node:test
  • ESLint
  • Testes manuais e exploratórios

E-commerce

  • TrayCorp
  • Wake
  • MercadoShops

Experiência

Trabalho

  1. – atual

    Analista de Testes

    Tray (abre em nova aba)

    Validação de features nas integrações com marketplaces do ecossistema Tray, com planejamento de testes e automação.

  2. – , 2 anos e 1 mês

    Desenvolvedor Front-End

    Agência Auaha

    Entrei como estagiário e segui como desenvolvedor front-end, criando lojas virtuais para clientes como Mãe Terra, Spilpack, Defitness, RS Air Filter, ABC da Construção e Suggar, nas plataformas TrayCorp, Wake e MercadoShops. Time ágil, com Scrum.

Formação

  1. –

    Pós-graduação em Desenvolvimento Full Stack

    PUCRS (Pontifícia Universidade Católica do Rio Grande do Sul)

  2. –

    Bacharelado em Ciência da Computação

    Univem (Centro Universitário Eurípides de Marília)