<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Davi Max</title>
    <description>The latest articles on DEV Community by Davi Max (@davimaxmillan).</description>
    <link>https://dev.to/davimaxmillan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4075115%2F06ef02a7-d05a-4657-8223-6212f2cb9e18.jpg</url>
      <title>DEV Community: Davi Max</title>
      <link>https://dev.to/davimaxmillan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davimaxmillan"/>
    <language>en</language>
    <item>
      <title>20 anos entre consertar computador e dar aula sobre eles</title>
      <dc:creator>Davi Max</dc:creator>
      <pubDate>Wed, 16 Sep 2026 00:09:07 +0000</pubDate>
      <link>https://dev.to/davimaxmillan/20-anos-entre-consertar-computador-e-dar-aula-sobre-eles-35k4</link>
      <guid>https://dev.to/davimaxmillan/20-anos-entre-consertar-computador-e-dar-aula-sobre-eles-35k4</guid>
      <description>&lt;p&gt;Comecei a trabalhar com tecnologia em 2003, consertando computadores e dando suporte a sistemas de loja em uma fábrica de calçados. Vinte e dois anos depois, sou professor de Ensino Superior e ainda escrevo código todo fim de semana. Entre um ponto e outro, a trajetória não foi nada linear — e acho que é exatamente por isso que vale contar.&lt;/p&gt;

&lt;h2&gt;
  
  
  O começo: hardware, não software
&lt;/h2&gt;

&lt;p&gt;Meus primeiros anos em TI foram quase inteiramente técnicos, no sentido mais literal: manutenção de impressoras e micros, instalação de softwares empresariais, suporte por telefone e no balcão. Passei por uma fábrica de calçados, uma revenda de informática e, depois, virei gerente de suporte técnico em uma consultoria.&lt;/p&gt;

&lt;p&gt;Nenhum desses trabalhos envolvia "programação" no sentido que ensino hoje em sala. Mas envolvia algo que continua sendo a base de tudo: entender por que um sistema quebrou, com pouca informação e alguém esperando a solução na hora.&lt;/p&gt;

&lt;h2&gt;
  
  
  A virada: voltar a estudar sem parar de trabalhar
&lt;/h2&gt;

&lt;p&gt;Em paralelo a esses anos de suporte, nunca parei de estudar. Informática Empresarial e Comercial, depois Licenciatura em Informática, depois — já como professor — uma sequência de especializações que não parou mais: Mídias na Educação, Engenharia de Software com Métodos Ágeis, Gestão de Projetos, e por fim um Mestrado, cuja pesquisa resultou em um software protótipo de verdade para auditoria de dados clínicos.&lt;/p&gt;

&lt;p&gt;Se tem um padrão nessa lista, é este: nunca escolhi entre "ser professor" e "continuar técnico". Fui empilhando os dois ao mesmo tempo, mesmo quando isso significava estudar depois do expediente por anos seguidos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hoje: professor que ainda quebra a cabeça com código
&lt;/h2&gt;

&lt;p&gt;O que acho mais interessante nessa trajetória, olhando para trás, é que ela não terminou quando virei professor. Ainda desenvolvo sistemas — alguns para projetos de extensão da universidade, outros só para resolver problemas meus mesmo, como gerenciar minhas próprias turmas e notas.&lt;/p&gt;

&lt;p&gt;Ensinar sem continuar praticando me parece um risco silencioso: o conteúdo pode ficar desatualizado sem que ninguém perceba, nem o professor. Continuar construindo software de verdade é, para mim, a forma mais honesta de saber se o que estou ensinando ainda faz sentido no mundo real.&lt;/p&gt;

&lt;p&gt;Vinte anos depois do primeiro chamado de suporte técnico, a pergunta que mais uso em sala ainda é a mesma de quando eu tinha 22 anos consertando computador: "o que exatamente está acontecendo aqui, e por quê?"&lt;/p&gt;

</description>
      <category>career</category>
      <category>learning</category>
      <category>software</category>
    </item>
    <item>
      <title>Por que a primeira função de IA que construí não foi um chatbot</title>
      <dc:creator>Davi Max</dc:creator>
      <pubDate>Wed, 16 Sep 2026 00:07:32 +0000</pubDate>
      <link>https://dev.to/davimaxmillan/por-que-a-primeira-funcao-de-ia-que-construi-nao-foi-um-chatbot-55op</link>
      <guid>https://dev.to/davimaxmillan/por-que-a-primeira-funcao-de-ia-que-construi-nao-foi-um-chatbot-55op</guid>
      <description>&lt;p&gt;Quando decidi colocar IA generativa dentro do leanpulse, tinha uma lista de possibilidades óbvias — chatbot de dúvidas, resumo automático de frequência, previsão de desempenho. Escolhi nenhuma delas. Integrei o Google Gemini para ajudar professores a criar provas e atividades.&lt;/p&gt;

&lt;p&gt;A escolha não foi por ser a ideia mais impressionante tecnicamente. Foi por ser a que resolve a dor que eu mesmo sinto toda semana.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema não é falta de conteúdo, é tempo
&lt;/h2&gt;

&lt;p&gt;Montar uma prova boa não é o mesmo que montar uma prova rápida. Escrever questões que realmente avaliam o que foi ensinado, variar o formato, calibrar a dificuldade — isso consome um tempo que, na correria de um semestre com várias turmas, quase nunca sobra do jeito que deveria.&lt;/p&gt;

&lt;p&gt;Foi esse o problema que quis atacar: não gerar conteúdo pedagógico do zero sem critério, mas dar ao professor um ponto de partida sólido para revisar, em vez de partir da folha em branco.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como a funcionalidade funciona, na prática
&lt;/h2&gt;

&lt;p&gt;O professor descreve o que precisa — o tema, o formato desejado, o nível da turma — e o Gemini gera uma sugestão de prova ou atividade a partir disso. A palavra que mais importa aí é &lt;strong&gt;sugestão&lt;/strong&gt;: o conteúdo gerado não vai direto para o aluno. Ele entra em uma tela de revisão, onde o professor ajusta o que quiser antes de efetivamente aplicar.&lt;/p&gt;

&lt;p&gt;Essa decisão de design não foi acidental. Escrevi, há pouco tempo, sobre como uso IA para preparar minhas próprias aulas — e o princípio que defendo ali é o mesmo que apliquei aqui como funcionalidade: a IA acelera a produção, mas a responsabilidade pelo que chega ao aluno continua sendo do professor. Não faria sentido construir uma ferramenta que pedisse de mim, como usuário, menos cuidado do que eu mesmo pratico.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que aprendi integrando IA generativa de verdade
&lt;/h2&gt;

&lt;p&gt;Tecnicamente, a parte mais delicada não foi "chamar a API do Gemini" — isso é relativamente direto. Foi desenhar a interface para que o resultado gerado nunca parecesse definitivo. Cada sugestão vem claramente marcada como rascunho, com espaço de edição em destaque, não escondido atrás de mais um clique.&lt;/p&gt;

&lt;p&gt;Se existe uma lição que levo desse recurso para qualquer projeto futuro com IA generativa, é essa: a parte difícil raramente é fazer a IA gerar algo. É desenhar o produto para que ninguém confunda "gerado por IA" com "pronto para usar".&lt;/p&gt;

</description>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>productivity</category>
      <category>saas</category>
    </item>
    <item>
      <title>ProfessorOS</title>
      <dc:creator>Davi Max</dc:creator>
      <pubDate>Tue, 15 Sep 2026 23:59:30 +0000</pubDate>
      <link>https://dev.to/davimaxmillan/professoros-4apo</link>
      <guid>https://dev.to/davimaxmillan/professoros-4apo</guid>
      <description>&lt;p&gt;Quando comecei a construir o ProfessorOS — um sistema pessoal para gerenciar minhas turmas, notas e frequência — cheguei ao ponto que todo projeto com login enfrenta: como fazer a autenticação.&lt;/p&gt;

&lt;p&gt;A resposta mais rápida seria instalar uma biblioteca pronta e seguir em frente. Decidi não fazer isso. Não por implicância com bibliotecas, mas porque autenticação é exatamente o tipo de coisa que, se você nunca entendeu por dentro, vira uma caixa-preta que você só reza para estar configurada certo. Preferi construir a minha, entender cada decisão, e só então confiar nela.&lt;/p&gt;

&lt;p&gt;Aqui está o que ficou de pé no final.&lt;/p&gt;

&lt;h2&gt;
  
  
  Senha não se guarda, se transforma
&lt;/h2&gt;

&lt;p&gt;A primeira decisão é a mais óbvia, mas vale reforçar: senha em texto puro no banco de dados não é opção, nem por um segundo. Uso &lt;strong&gt;bcrypt&lt;/strong&gt;, que não é só um algoritmo de hash — é um hash deliberadamente lento.&lt;/p&gt;

&lt;p&gt;Isso é intencional. Um hash rápido (como MD5 ou SHA-256 sozinho) permite que um invasor teste bilhões de combinações por segundo se o banco vazar. O bcrypt inclui um "custo" configurável que torna cada tentativa mais cara computacionalmente — lento o suficiente para não incomodar um login legítimo, e caro o suficiente para inviabilizar um ataque de força bruta em escala.&lt;/p&gt;

&lt;h2&gt;
  
  
  Um cookie que não pode ser falsificado
&lt;/h2&gt;

&lt;p&gt;Depois do login, o sistema precisa lembrar quem você é nas próximas requisições. A forma mais simples seria guardar um ID de sessão em um cookie e comparar com o banco a cada requisição.&lt;/p&gt;

&lt;p&gt;Fui um passo além: o cookie de sessão é &lt;strong&gt;assinado com HMAC-SHA256&lt;/strong&gt;. Na prática, isso significa que o conteúdo do cookie carrega uma "assinatura" matemática gerada com uma chave secreta que só o servidor conhece. Se alguém tentar alterar o conteúdo do cookie manualmente — trocar o ID de usuário, por exemplo — a assinatura não bate mais, e o servidor rejeita a sessão na hora.&lt;/p&gt;

&lt;p&gt;O cookie também é &lt;code&gt;httpOnly&lt;/code&gt;, o que impede que ele seja lido por JavaScript no navegador — uma proteção direta contra ataques de XSS tentando roubar a sessão.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duas camadas de verificação, não uma
&lt;/h2&gt;

&lt;p&gt;Essa foi a parte que mais me fez repensar o próprio desenho do sistema. Segui um padrão recomendado pela própria documentação do Next.js: verificar a sessão em &lt;strong&gt;dois lugares diferentes&lt;/strong&gt;, com propósitos diferentes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uma verificação &lt;strong&gt;otimista&lt;/strong&gt;, na borda (&lt;code&gt;proxy.ts&lt;/code&gt;), que só confere se existe um cookie com formato válido — rápida, usada para decidir se redireciona o usuário para a tela de login antes mesmo de carregar a página.&lt;/li&gt;
&lt;li&gt;Uma verificação &lt;strong&gt;definitiva&lt;/strong&gt;, na camada de acesso a dados (&lt;code&gt;auth.ts&lt;/code&gt;), que valida a assinatura do cookie de verdade e confere permissões — essa é a que decide se uma ação sensível (ver notas de um aluno, editar uma turma) realmente acontece.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lógica é simples: a primeira camada existe para dar uma resposta rápida ao usuário; a segunda existe porque a primeira, sozinha, nunca deveria ser a única linha de defesa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitando tentativas de login
&lt;/h2&gt;

&lt;p&gt;Por fim, um limite de tentativas: 5 tentativas de login a cada 15 minutos por IP, com um teto global de 30 tentativas no sistema todo. Sem isso, toda a engenharia de hash e assinatura acima ainda deixaria uma porta aberta para um ataque simples de tentativa e erro repetido.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que fica disso
&lt;/h2&gt;

&lt;p&gt;Nenhuma dessas quatro peças é complexa isoladamente. O que exige cuidado é a soma delas — e entender por que cada uma existe, não só copiar um trecho de código que "funciona".&lt;/p&gt;

&lt;p&gt;Como professor, isso mudou até a forma como falo sobre segurança em sala: não como uma lista de boas práticas para decorar, mas como um conjunto de perguntas — o que essa camada impede, especificamente, que aconteça? Se a resposta não é clara, a camada provavelmente não deveria estar ali só por estar.&lt;/p&gt;

</description>
      <category>authentication</category>
      <category>backend</category>
      <category>programming</category>
      <category>security</category>
    </item>
    <item>
      <title>Passei dias quebrando o Git de propósito — e aprendi mais que em anos de uso</title>
      <dc:creator>Davi Max</dc:creator>
      <pubDate>Tue, 15 Sep 2026 23:54:23 +0000</pubDate>
      <link>https://dev.to/davimaxmillan/passei-dias-quebrando-o-git-de-proposito-e-aprendi-mais-que-em-anos-de-uso-5g4h</link>
      <guid>https://dev.to/davimaxmillan/passei-dias-quebrando-o-git-de-proposito-e-aprendi-mais-que-em-anos-de-uso-5g4h</guid>
      <description>&lt;p&gt;Sou professor de Análise e Desenvolvimento de Sistemas, estou há mais de 20 anos na área de TI, e recentemente preparei um módulo inteiro de Git e GitHub para minha turma. Parecia simples: eu já uso Git há anos, é só passar o conteúdo adiante.&lt;/p&gt;

&lt;p&gt;Não foi bem assim.&lt;/p&gt;

&lt;p&gt;Decidi rodar, no meu próprio terminal, cada comando que ia ensinar — e, de propósito, provocar os erros que sabia que a turma ia encontrar, só para saber exatamente como explicar cada um quando acontecesse em aula. O resultado foi uma quantidade generosa de mensagens de erro na minha tela, e alguns aprendizados que valem compartilhar.&lt;/p&gt;

&lt;p&gt;A tela que trava todo mundo na primeira vez&lt;/p&gt;

&lt;p&gt;Rodei git commit sem o -m, e o terminal simplesmente... parou. Sem prompt, sem resposta ao Enter, parecia travado.&lt;/p&gt;

&lt;p&gt;Era o Vim, o editor de texto padrão do Git, esperando a mensagem do commit. A saída: i para editar, digitar a mensagem, Esc, :wq e Enter.&lt;/p&gt;

&lt;p&gt;Não é um bug. É só um editor que ninguém avisou que ia abrir.&lt;/p&gt;

&lt;p&gt;As três fases de um merge que ninguém te conta&lt;/p&gt;

&lt;p&gt;Essa foi a que mais me ensinou. Tentei mesclar um branch com conflito de propósito, e fui evoluindo por três erros diferentes, cada um revelando uma parte do que o Git realmente faz internamente:&lt;/p&gt;

&lt;p&gt;Fase 1 — conflito aberto. O Git marca o arquivo com &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;, ======= e &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;, e recusa qualquer merge novo enquanto isso não for resolvido.&lt;/p&gt;

&lt;p&gt;Fase 2 — resolvido, mas sem commit. Depois de editar o arquivo e dar git add, tentei um novo merge e levei um fatal: You have not concluded your merge (MERGE_HEAD exists). O conflito já tinha sumido do arquivo, mas o Git ainda guardava uma referência interna (MERGE_HEAD) esperando o git commit final.&lt;/p&gt;

&lt;p&gt;Fase 3 — concluído. Só depois do commit é que o merge realmente termina, e o próximo pode começar.&lt;/p&gt;

&lt;p&gt;Até então, eu explicava merge como "um comando só". Agora sei que é um processo de três estados — e faz muito mais sentido assim, tanto pra mim quanto pra quem está aprendendo.&lt;/p&gt;

&lt;p&gt;Clonar um repositório dentro do outro&lt;/p&gt;

&lt;p&gt;Esse eu fiz sem querer, mas ficou de exemplo. Rodei git clone de dentro da pasta de outro repositório, e o Git avisou: "adding embedded git repository".&lt;/p&gt;

&lt;p&gt;Cada pasta .git é uma unidade independente — clonar um projeto dentro de outro cria uma espécie de repositório dentro do repositório, que quebra assim que alguém tenta clonar o de fora. Resolvido com git rm --cached e movendo a pasta para o lugar certo.&lt;/p&gt;

&lt;p&gt;Por que valeu a pena&lt;/p&gt;

&lt;p&gt;Hoje esses mesmos comandos — branch, merge, commit, resolução de conflito — são parte do dia a dia de projetos reais que venho construindo, incluindo sistemas com autenticação própria, testes automatizados e até integração com IA generativa.&lt;/p&gt;

&lt;p&gt;Mas o que fica desse processo não foi só prática técnica. Foi perceber que entender os erros — de propósito, sem pressa, antes de precisar explicar pra alguém — é provavelmente a forma mais rápida de aprender qualquer ferramenta de verdade.&lt;/p&gt;

&lt;p&gt;Se você está começando com Git agora: os erros que você vai encontrar não são sinal de que está fazendo errado. São, muito provavelmente, os mesmos que todo mundo encontra — inclusive quem está do outro lado, ensinando.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
