Desenvolvimento backend
APIs REST em Node.js e Python, modelagem de banco relacional, autenticação, autorização por papel e desenho de rotas para consumo humano e máquina.
Mais de 4 anos construindo e sustentando software que não pode parar. Atuei em decisão em tempo real, integrações e engenharia de dados para Claro, IRB Brasil RE, HDI, Tribanco e Grupo Kroton — e hoje desenvolvo, sozinho e de ponta a ponta, um sistema de gestão full-stack em produção com automação por IA. Backend, dados e a operação no meio: é onde eu trabalho melhor.
Comecei resolvendo incidente em produção de cliente enterprise — o tipo de trabalho que ensina a ler log, achar causa raiz e entender que por trás de cada erro existe uma operação parada. De lá para cá passei a construir também: APIs de cálculo integradas a motor de decisão, pipelines de dados, e hoje um sistema completo em produção. O que me move é o encontro entre a técnica e o negócio.
APIs REST em Node.js e Python, modelagem de banco relacional, autenticação, autorização por papel e desenho de rotas para consumo humano e máquina.
Fluxos de decisão sob evento (SAS RTDM/ESP), otimização de SQL, migração de bases para nuvem e integrações REST/SOAP entre sistemas corporativos.
Análise de log, reprodução de falha, investigação de causa raiz e correção sem derrubar o serviço — em ambientes de alto volume com operação ativa.
Desenvolvimento full-stack de sistemas em produção, do modelo de dados ao deploy: um sistema de gestão com agentes de IA para espaço de eventos, uma plataforma de ofertas de afiliados e um portal imobiliário, rodando em infraestrutura própria.
Sustentação e evolução de fluxos de decisão em tempo real em ambientes produtivos de alto volume para clientes enterprise, com desenvolvimento de integrações, otimização de performance e investigação de incidentes.
Primeira atuação técnica: gestão da plataforma de e-commerce, unindo front-end, experiência de compra e apoio direto à operação da loja.
Clique em um card para ver o contexto, o diagrama da solução, o que eu fiz em cada frente e trechos de código comentados. A Plataforma de Afiliados tem o código aberto no GitHub.
Análise e visualização de dados: modelagem, indicadores e dashboards para apoiar decisão de negócio.
Formação em desenvolvimento backend com Python: orientação a objetos, acesso a banco de dados e aplicações web com Django.
Desenho de soluções, integração entre sistemas e decisão arquitetural com trade-off explícito.
Estruturação, administração e visão estratégica de ambientes de banco de dados.
Base que sustenta a construção de interface e a preocupação com a experiência de quem usa o sistema.
SAS Institute
Alura
Em andamento
Português nativo e inglês intermediário (leitura de documentação técnica).
Analítico e investigativo, com comunicação direta entre time técnico e área de negócio, organização, autonomia e compromisso com prazo.
Desenvolvedor de Software e Analista de Dados com mais de 4 anos entre sustentação de sistemas críticos, engenharia de dados e desenvolvimento full-stack. Experiência real de produção: incidente em cliente enterprise, API integrada a motor de decisão e sistema autoral mantido do banco ao deploy.
Sistema completo de CRM, orçamento, contrato e financeiro para um espaço de casamentos — com infraestrutura própria em VPS, 48 automações em n8n, agente de IA no WhatsApp, secretária virtual no Telegram e 52 suítes de teste automatizadas.
Um espaço de eventos operava em planilhas soltas, WhatsApp e PDF: lead chegava e se perdia, orçamento era refeito à mão, parcela era controlada de cabeça e contrato ia e voltava por e-mail. O desafio era transformar isso em um sistema único — do primeiro contato do casal até a última parcela paga — rodando em hospedagem compartilhada barata, sem equipe e sem poder ficar fora do ar.
Modelei 21 tabelas em MySQL cobrindo cliente, orçamento, itens, parcelas, cardápio, custos, fornecedores, leads, contrato e assinatura. Criei um sistema de migrations numeradas com script de verificação de schema, para evoluir a base em produção sem quebrar o que já estava rodando.
Construí 185 rotas organizadas em módulos isolados por domínio (visitas, CRM comercial, WhatsApp, contratos, social, compromissos), cada um recebendo os helpers de banco e autenticação por injeção de dependência. Isso permite adicionar um domínio novo sem tocar no núcleo — reduzindo o risco de regressão a cada entrega.
Implementei autenticação JWT com política fail-closed (sem segredo configurado, o login fica desabilitado em vez de abrir), hash bcrypt com comparação de tempo constante contra enumeração de e-mail, bloqueio progressivo por tentativa de login, rate limiting por IP e rota, autorização por papel e cabeçalhos de segurança HTTP.
Desenvolvi o front em React 19 com TypeScript, Vite e Tailwind: dashboard financeiro, funil de CRM, calendário de disponibilidade, construtor de cardápio, calculadora de custo, editor de contrato ao vivo e geração de proposta em PDF direto no navegador.
Escrevi em Python, com Playwright, uma automação que captura leads de portal de casamentos, faz upsert no sistema pela API (sem duplicar) e dispara a régua de primeiro contato no WhatsApp. Tem modo dry-run e flags para rodar cada etapa isolada.
Construí em n8n (nós nativos de AI Agent + Google Gemini) um agente que conversa com os casais no WhatsApp seguindo o roteiro comercial da casa. As instruções de tom e regras são editáveis pelo dono no próprio painel; o agente pausa sozinho quando um humano assume a conversa, detecta quando outro robô está respondendo e nunca informa preço sem a fonte oficial. Foi para produção em piloto controlado, um cliente por vez.
Um segundo agente responde ao dono pelo Telegram e pelo WhatsApp pessoal: consulta eventos, financeiro, contratos, agenda e o funil comercial por rotas protegidas por segredo, marca visitas, aprova pedidos de visita (avisando o cliente e criando o evento no Google Agenda) e agenda posts. Tem nova tentativa automática em falha de rede, datas calculadas no servidor para a IA não errar e mensagens de erro que dizem a causa.
Mantenho 69 fluxos (48 ativos): captação de leads do Casamentos.com com porteiro que não responde se o WhatsApp estiver fora, lembretes de visita D-1 e no dia, sincronização com o Google Agenda, resumo das conversas por IA, aprendizado contínuo a partir das conversas e etiquetas automáticas no WhatsApp Business (etapa do funil, visita, ano e temporada do evento), sincronizadas a cada 5 minutos respeitando o limite de requisições do WhatsApp.
Montei um servidor Linux com Docker Compose (12 containers: n8n, três sessões de WhatsApp via WAHA, proxy Caddy com HTTPS, automação Python), backup diário copiado para fora do servidor e um vigia que avisa no Telegram quando algo cai. Toda mudança vai para o GitHub na hora.
O site roda em hospedagem compartilhada (pool de conexões no teto do provedor, retry em queda de conexão, health check). O deploy sai da VPS por um comando só que roda 52 suítes de teste executando o código real, recusa publicar se faltar biblioteca no servidor ou se a tela apontar para o endereço errado, e confere o login e o boot depois de subir. Erros chegam ao usuário em português simples.
Se a variável de ambiente com o segredo não estiver configurada, o login não fica aberto — ele fica indisponível. Falhar fechado é mais seguro que falhar aberto.
const JWT_SECRET = process.env.JWT_SECRET || '';
// Sem segredo próprio, o login admin fica DESABILITADO
// (o site público continua no ar normalmente).
const JWT_SECRET_OK = !!JWT_SECRET && JWT_SECRET !== 'change-me-in-production';
function authMiddleware(req, res, next) {
if (!JWT_SECRET_OK) {
return res.status(503).json({ error: 'Servidor sem JWT_SECRET configurado' });
}
const m = (req.headers.authorization || '').match(/^Bearer\s+(.+)$/i);
if (!m) return res.status(401).json({ error: 'Sem token' });
try {
req.user = jwt.verify(m[1], JWT_SECRET, {
issuer: 'ranch-sistema',
audience: 'ranch-admin',
});
next();
} catch {
return res.status(401).json({ error: 'Token invalido ou expirado' });
}
}
Errar a senha uma vez é humano. Errar dez é ataque. O tempo de bloqueio cresce junto com a suspeita, sem nunca travar o usuário legítimo de vez.
function recordLoginAttempt(emailOrIp, success) {
const key = String(emailOrIp).toLowerCase();
let r = _loginAttempts.get(key) || { fails: 0, lockedUntil: 0 };
if (success) { r.fails = 0; r.lockedUntil = 0; return; }
r.fails++;
// 5 erros -> 5 minutos. 10 erros -> 30 minutos.
if (r.fails >= 10) r.lockedUntil = Date.now() + 30 * 60000;
else if (r.fails >= 5) r.lockedUntil = Date.now() + 5 * 60000;
}
// Hash descartavel: mantem o tempo de resposta constante mesmo quando o
// e-mail nao existe, para nao entregar quais contas sao validas.
const _DUMMY_HASH = bcrypt.hashSync('x', 10);
O provedor limita conexões por hora e suspende processo ocioso. O pool é pequeno de propósito, e a query tenta de novo quando a conexão cai — o usuário não vê o erro.
_pool = mysql.createPool({
...cfg,
connectionLimit: 5, // teto baixo: hospedagem compartilhada
idleTimeout: 30000, // devolve a conexao apos 30s ocioso
enableKeepAlive: true,
connectTimeout: 10000,
});
// O host suspende o processo ocioso e derruba o pool.
// Vale 1 retry: o pool entrega uma conexao nova.
async function _poolQuery(sql, params) {
try {
return await getPool().query(sql, params);
} catch (e) {
if (!_isConnLost(e)) throw e;
log('DB', 'conexao perdida, refazendo query');
return await getPool().query(sql, params);
}
}
Cada domínio novo é um arquivo isolado que recebe banco e autenticação prontos. Adicionar um módulo custa duas linhas no núcleo e não arrisca o que já funciona.
// visitas-routes.js — modulo isolado, nao duplica banco nem auth
function visitasRouter({ mysqlQuery, dbInsert, authMiddleware, asyncH }) {
const router = express.Router();
router.get('/api/visitas', authMiddleware, asyncH(async (req, res) => {
const rows = await mysqlQuery(
'SELECT * FROM `Visita` WHERE `data` BETWEEN ? AND ? ORDER BY `data`',
[req.query.from, req.query.to]
);
res.json(rows);
}));
return router;
}
// no main.js, o modulo entra com 1 linha:
app.use(visitasRouter({ mysqlQuery, dbInsert, authMiddleware, asyncH }));
A automação lê os leads do portal, envia para a API do sistema com upsert (não duplica) e só então dispara a régua de contato. O modo dry-run permite testar sem falar com ninguém.
def enviar_para_sistema(lead: Lead, cfg: Config) -> bool:
"""Upsert do lead no Ranch Life. Nao duplica: a API casa pelo telefone."""
resp = requests.post(
f"{cfg.ranch_api_url}/api/leads/ingest",
json={
"name": lead.nome,
"phone": lead.telefone,
"eventDate": lead.data_evento,
"guests": lead.convidados,
"source": "casamentos.com.br",
},
headers={"x-automation-secret": cfg.automation_secret},
timeout=20,
)
if resp.status_code >= 400:
log.error("falha ao enviar lead %s: %s", lead.nome, resp.text[:200])
return False
log.info("lead %s registrado no sistema", lead.nome)
return True
Operação inteira dentro de um sistema só: lead entra pela automação, é atendido e etiquetado no WhatsApp, vira visita, orçamento, contrato e parcela acompanhada — e o dono pergunta qualquer coisa à secretária no Telegram. Em produção, com servidor próprio, backup, monitoramento e testes, mantido e evoluído por uma pessoa.
Plataforma que encontra ofertas na Amazon, Mercado Livre e Shopee, monta a promoção com preço e cupom verificados, agenda campanhas e publica em grupos de WhatsApp — com vitrine pública em awaydev.com.br/ofertas.
Divulgar ofertas de afiliado em grupos de WhatsApp todo dia exige achar produto bom, conferir preço e cupom, escrever a chamada, montar o link de afiliado e postar na hora certa — sem nunca publicar um preço errado, porque oferta errada queima a confiança do grupo. O desafio era automatizar a rotina inteira mantendo o controle humano sobre o que sai e quando.
Organizei em rotas → serviços → repositórios → banco: toda regra de negócio mora nos serviços e só os repositórios sabem que o banco é SQLite. O app é dono dos dados e da fila de envio; o n8n só orquestra chamando a mesma API do painel — se o n8n cair, nada se perde, e o motor interno continua publicando.
Adaptadores para Amazon (API de criadores), Mercado Livre e Shopee atrás de uma interface comum. Loja sem credencial aparece como "não implementada" em vez de devolver dado falso. Uma extensão de navegador (Chrome, Manifest V3) captura os produtos da página que o usuário está vendo e manda para a plataforma.
A IA (Claude ou compatível) só escreve o texto da chamada. Preço, desconto e cupom vêm do serviço de precificação, e uma trava remove qualquer valor, percentual ou cupom que o modelo tente criar fora da ficha de fatos do produto.
Campanhas com janela de horário, intervalo e limite definidos na criação, barra de progresso, disparo imediato em lote e marcação do que já foi postado. O envio sai por WhatsApp (WAHA) com fila própria, também para Telegram.
A cada 10 minutos a plataforma sincroniza as ofertas com uma vitrine pública (Node/EJS na Hostinger), que conta os cliques e só revela o link de afiliado no redirecionamento. Roda em Docker na mesma VPS do Ranch Life, com publicação por um comando que roda os testes antes.
A IA escreve o texto, mas não pode criar número. Todo valor em reais que não estiver na ficha de fatos do produto é removido antes de a oferta ir para o grupo.
const MOEDA_REGEX = /R\$\s*\d[\d.]*(?:,\d{2})?/gi;
export function enforceFactualText(texto, fatos) {
const violacoes = [];
let saida = String(texto || '');
// so valem os precos calculados pelo pricingService
const permitidos = new Set(
(fatos.precos_permitidos || []).map((v) => Number(v).toFixed(2))
);
for (const match of saida.match(MOEDA_REGEX) || []) {
const valor = normalizeMoney(match);
if (Number.isFinite(valor) && !permitidos.has(valor.toFixed(2))) {
violacoes.push('preco inventado: ' + match);
saida = saida.replace(match, '');
}
}
// (o mesmo vale para percentual de desconto e codigo de cupom)
return { texto: saida, violacoes, limpo: violacoes.length === 0 };
}
A rotina de divulgação virou um painel: achar a oferta, conferir preço e cupom, escrever a chamada e postar no horário certo acontecem sozinhos, com a decisão do que sai continuando nas mãos de quem divulga — e nenhum preço sai sem ter vindo da loja.
Site completo de imobiliária: busca de imóveis com filtros, mapa interativo, galeria otimizada e um painel onde o corretor cadastra imóvel, gerencia fotos e responde mensagens — sem depender de programador.
A imobiliária dependia de terceiros para qualquer mudança: publicar um imóvel novo, trocar uma foto ou corrigir um preço virava chamado. Além disso, imóvel só vende se for encontrado — então o site precisava ser rápido, aparecer no Google e funcionar bem no celular, que é onde o cliente procura.
Construí o backend em Express com SQLite, modelando usuários, imóveis, fotos, mensagens e configurações do site. O banco em arquivo mantém o custo de hospedagem baixo e a operação simples — adequado ao volume real do negócio, sem servidor de banco dedicado.
Foto de imóvel vem do celular com vários megabytes e derruba o carregamento da página. Implementei um pipeline com Sharp que valida o tipo do arquivo, redimensiona e recomprime em JPEG progressivo antes de gravar — o corretor sobe a foto original e o site entrega a versão leve.
Helmet para os cabeçalhos HTTP, CORS restrito por lista, rate limit separado para login e para envio de mensagem, autenticação JWT com bcrypt e validação de tipo e extensão em todo upload. A aplicação também se recusa a subir se o segredo do JWT estiver ausente ou curto demais.
Gerei o sitemap.xml dinamicamente a partir dos imóveis disponíveis no banco, com data de atualização por página, além do robots.txt bloqueando as áreas administrativas. No front, cada página de imóvel monta seus próprios metadados.
Desenvolvi o site em React com Vite: busca com filtros, página de detalhe com galeria, mapa dos imóveis com Leaflet, lista de favoritos, formulário de contato e botão flutuante de WhatsApp.
Área administrativa para cadastrar e editar imóvel, ordenar e remover fotos, ler as mensagens recebidas e ajustar as configurações do site — o objetivo era tirar a imobiliária da dependência técnica.
Valida o tipo antes de tocar no arquivo, redimensiona respeitando o limite e grava em JPEG progressivo — que aparece por inteiro desfocado e vai ganhando nitidez, em vez de carregar de cima para baixo.
const ALLOWED_MIME = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
if (!ALLOWED_MIME.includes(foto.mimetype)) {
return res.status(400).json({
error: `Tipo de arquivo nao permitido: ${foto.name}`,
});
}
await sharp(foto.data, { failOn: 'none' })
.rotate() // respeita a orientacao do celular
.resize({
width: IMG_MAX_WIDTH,
height: IMG_MAX_HEIGHT,
fit: 'inside', // nao corta o imovel
withoutEnlargement: true, // nao estica foto pequena
})
.jpeg({ quality: IMG_QUALITY, mozjpeg: true, progressive: true })
.toFile(caminhoCompleto);
Cada imóvel disponível vira uma URL no sitemap com a própria data de atualização. Publicar um imóvel novo já o coloca na fila de indexação, sem passo manual.
app.get('/sitemap.xml', async (req, res) => {
const imoveis = await dbAll(
"SELECT id, atualizado_em FROM imoveis WHERE status = 'disponivel' " +
"ORDER BY atualizado_em DESC LIMIT 5000"
);
const urls = [
{ loc: `${baseUrl}/`, priority: '1.0', changefreq: 'daily' },
{ loc: `${baseUrl}/buscar`, priority: '0.9', changefreq: 'daily' },
];
imoveis.forEach((i) => urls.push({
loc: `${baseUrl}/imovel/${i.id}`,
priority: '0.7',
changefreq: 'weekly',
lastmod: formatarData(i.atualizado_em),
}));
res.set('Content-Type', 'application/xml; charset=utf-8').send(montarXml(urls));
});
Um segredo fraco não deve virar um problema descoberto depois. A aplicação verifica na inicialização e se recusa a subir — erro barulhento na hora certa.
if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 16) {
console.error(
'✗ JWT_SECRET ausente ou muito curto. ' +
'Defina uma chave de pelo menos 16 caracteres em .env'
);
process.exit(1);
}
// Limites separados: login e envio de mensagem sofrem abusos diferentes.
const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 20 });
const writeLimiter = rateLimit({ windowMs: 5 * 60 * 1000, max: 30 });
Portal no ar em domínio próprio, com a imobiliária publicando e editando os imóveis sozinha. As fotos entram pesadas e saem otimizadas, e cada imóvel novo entra no sitemap automaticamente.
APIs REST em Python, em arquitetura de microsserviços, integradas ao motor de decisão SAS RTDM para automatizar o cálculo de prêmio de contratos de resseguro.
A precificação de contratos de catástrofe era feita em planilhas de cálculo mantidas à mão. Cada cotação dependia de alguém abrir o arquivo certo, preencher na ordem certa e não errar uma célula — sem rastreabilidade e sem governança das regras. O objetivo era transformar esse conhecimento em serviço: automatizado, versionado e auditável.
Desenvolvi APIs REST em Python dedicadas aos modelos de precificação, cada modelo isolado em seu próprio serviço, com contrato de entrada e saída definido em documento funcional antes da implementação.
Os serviços foram desenhados stateless: como uma cotação pode conter várias estruturas com várias faixas cada, o contrato da API recebe listas aninhadas e devolve o resultado no índice correspondente. Cada estrutura é calculada no próprio escopo, sem interferência das demais — o que torna o serviço previsível e permite ao front decidir quantas estruturas enviar.
Integrei os serviços de cálculo ao SAS RTDM, de forma que a regra de negócio permanecesse governada no motor de decisão e o cálculo pesado ficasse no serviço especializado — cada camada fazendo o que faz melhor.
Fui autor de revisão da especificação funcional do precificador, documentando dados de entrada, cálculos e dados de saída de cada modelo. É o documento que serve de contrato entre negócio e engenharia — e de base para medir mudança de escopo.
Ajustei rotinas de cálculo e o processamento dos dados, e sustentei a integração em produção: investigação de falha, validação de resultado contra a referência de negócio e correção.
Cálculo de prêmio automatizado e integrado ao fluxo de decisão, substituindo a planilha manual por serviço rastreável — com as regras sob governança e a especificação versionada.
Pipeline de eventos de navegação e compra (SAS ESP) alimentando fluxos de decisão (SAS RTDM) que identificam o abandono no instante em que acontece e disparam a campanha de recuperação.
Cliente que abandona o carrinho esfria rápido. A recuperação rodava em lote e chegava tarde demais — quando a campanha disparava, a intenção de compra já tinha passado. Era preciso detectar o abandono no momento exato e reagir na hora, em uma operação com volume de operadora nacional.
Trabalhei com SAS ESP no processamento contínuo dos eventos de navegação e compra, identificando o padrão de abandono conforme os eventos chegavam — e não em uma carga posterior.
Construí e sustentei os fluxos no SAS RTDM que aplicam as regras de negócio sobre o evento detectado e decidem se, quando e com qual oferta o cliente deve ser abordado.
Refatorei lógica de regras e otimizei consultas SQL das rotinas de campanha, entregando mais de 20% de redução no tempo de processamento — em ambiente onde latência é o produto.
Atendi incidentes com impacto direto em campanha ativa: leitura de log, reprodução da falha, investigação de causa raiz e correção sem interromper o serviço.
Campanha de recuperação acionada no instante do abandono, com regras estáveis em produção de altíssimo volume e ganho mensurável de performance nas rotinas.
Migração de bases e do ambiente SAS Grid de infraestrutura on-premise para AWS, com foco em integridade dos dados, rastreabilidade e continuidade da operação.
Mover um ambiente analítico inteiro — dados, bibliotecas, conexões de banco e centenas de rotinas — para a nuvem sem perder integridade e sem parar a operação. Migração desse tipo falha no detalhe: uma biblioteca mal apontada ou uma conexão esquecida quebra rotina que só vai rodar no fechamento do mês.
Atuei na movimentação das bases para a nuvem AWS, acompanhando consistência e rastreabilidade ao longo de todo o processo — validando que o dado do outro lado é o mesmo dado.
Participei da migração das rotinas analíticas, das bibliotecas e das conexões de banco para o novo ambiente, incluindo o reapontamento das origens de dados e a validação de cada rotina migrada.
A migração foi conduzida em sprints de duas semanas, com planejamento, execução, plano de teste e revisão a cada ciclo — modelo que permite migrar em lotes e validar continuamente, em vez de apostar tudo em uma virada única.
Apoiei a estabilização depois da virada: investigação dos incidentes relacionados à transição e ajustes até o ambiente novo rodar sozinho.
Ambiente analítico rodando na nuvem com as bases íntegras e as rotinas validadas, com operação assistida até a estabilização completa.
Segmentação de base e automação da régua de comunicação com SAS Customer Intelligence 360, Marketing Automation, Information Maps e dashboards de acompanhamento.
A régua de comunicação dependia de processo manual: segmentar público, exportar planilha, anexar arquivo e enviar para o canal. Isso engessava a segmentação, atrasava a comunicação com o cliente e deixava a área sem visibilidade de performance — não havia acompanhamento de KPI das jornadas.
Construí segmentações de público com SAS Enterprise Guide e SAS Viya, traduzindo a regra comercial em critério executável sobre a base.
Modelei a camada semântica de leitura dos dados, para que a área de negócio consultasse a informação com o nome e o significado corretos, sem depender de quem escreve SQL.
Atuei na automação das jornadas com SAS CI360 e Marketing Automation, substituindo o envio manual por processo orquestrado de carga, segmentação e exportação para os canais de e-mail e SMS.
Apoiei a administração da plataforma via SAS Management Console — usuários, grupos, bibliotecas e permissões do ambiente analítico.
Régua de comunicação automatizada e segmentação liberada da planilha, com acompanhamento de indicadores das jornadas em dashboard.
Meu primeiro projeto na área: construção e sustentação de campanhas e jornadas de cliente orquestradas por dados no SAS Customer Intelligence 360 — onde aprendi a operar sistemas em produção atendendo o negócio no dia a dia.
Manter campanhas e jornadas de cliente rodando de forma estável e contínua, atendendo às mudanças pedidas pelas áreas de marketing sem quebrar o que já estava no ar.
Atuei na construção de campanhas e jornadas no SAS CI360, com a orquestração baseada nos dados do cliente.
Mantive os fluxos estáveis em operação e atendi diretamente às solicitações de ajuste das áreas de marketing, com visão sistêmica do impacto de cada mudança.
Campanhas e jornadas apoiadas por dados, com sustentação estável e resposta rápida às demandas de marketing.
O site que você está lendo. Aplicação Node/Express com renderização no servidor, conteúdo data-driven em um único arquivo e zero dependência de front-end.
Currículo em PDF não mostra como alguém escreve software. Eu queria que a própria apresentação fosse a demonstração: um site que carrega rápido, é indexável pelo Google, funciona sem JavaScript pesado e pode ser atualizado editando um único arquivo de dados — sem mexer em HTML.
Express com EJS montando a página inteira no servidor. O buscador recebe o HTML pronto — nada de conteúdo que só aparece depois que o JavaScript roda.
Todo o texto, projeto, competência e formação vive em data/content.js. O template percorre os dados e se monta sozinho: adicionar um projeto novo é adicionar um objeto na lista, e ele já vira card, modal e diagrama.
CSS próprio com variáveis de tema, tema claro/escuro persistido, animação de entrada com IntersectionObserver e modais acessíveis por teclado — tudo em JavaScript puro, sem framework.
Os diagramas de arquitetura desta página são SVG escrito à mão dentro do template: acompanham o tema claro/escuro, escalam sem perder nitidez e não custam nenhuma requisição extra.
O servidor entrega o objeto de conteúdo para o template. Para publicar um projeto novo, edita-se apenas o arquivo de dados.
const content = require('./data/content');
app.set('view engine', 'ejs');
app.use(express.static(path.join(__dirname, 'public'), { maxAge: '1h' }));
// A pagina inteira e montada no servidor a partir do conteudo.
app.get('/', (req, res) => res.render('index', { c: content }));
// Health check: a hospedagem usa para saber se o app esta de pe.
app.get('/health', (req, res) => res.json({ ok: true, uptime: process.uptime() }));
Um currículo que é, ele mesmo, um projeto entregue: versionado no GitHub, publicado e atualizado editando um arquivo.