Blog

  • Tutorial de Captcha com ReactJS

    Tutorial de Captcha com ReactJS

    Se você criar alguma aplicação web pública, que receba um mínimo de atenção, é comum que você passe a ter problemas com bots. Principalmente se a sua aplicação fornece alguma vantagem ou ganho financeiro aos usuários ou ainda se a área logada dela é atrativa o bastante para alguém querer invadi-la. É certo que nestas situações algum hacker vai buscar automatizar a atividade de login ou de uso da aplicação, o que pode ser um risco de segurança grave ou ao menos um risco econômico.

    Recentemente passei por isso em um projeto de faucet para uma nova criptomoeda e para evitar que os usuários automatizassem a emissão de tokens, optei por colocar um captcha, uma solução que já havia usado no passado para evitar bruteforce em telas de login e que veio a me ajudar novamente para evitar abusos no clique do meu único botão da aplicação.

    Neste tutorial eu quero lhe ensinar como implementar este recurso também na sua aplicação.

    #1 – O que é captcha?

    Captcha é uma sigla para “Completely Automated Public Turing test to tell Computers and Humans Apart” ou “teste de Turing completamente automatizado para separar humanos e computadores” em uma tradução livre. Um Teste de Turing é um teste conceitual na Ciência da Computação para entender se um dado problema é computável, isto é, pode ser resolvido por um computador. Assim, um captcha nada mais é do que um teste de Turing automático para dizer se algo ou alguém é um humano ou um computador.

    Existem diversas maneiras de criar captchas e geralmente eles envolvem decifrar enigmas que são muito difíceis para máquinas, geralmente usando imagens e padrões complexos e aleatórios. Certamente você já teve de resolver algum puzzle envolvendo números e letras, ou selecionar as imagens certas em uma lista, mesmo que não soubesse na ocasião o que era um captcha.

    No mercado existem diversas soluções de captcha e neste tutorial usaremos uma gratuita fornecida pelo Google, chamada de reCaptcha.

    #2 – Setup do Projeto

    O Google reCaptcha é uma solução captcha criada e fornecida pelo Google que ajuda tanto os sites deles, quanto os nossos, a se tornarem mais seguros. Confesso que não sei detalhes sobre o algoritmo do teste, mas sei que ele vai muito além do que apenas perguntar sobre “quais imagens são corretas”, analisando aspectos da sessão do usuário no browser como o movimento do mouse e outros, para entender se ele é um bot ou humano. Além disso, certa vez ouvi falar que eles usam as respostas dos usuários para as imagens a fim de ajudar no treinamento das suas IAs. Vai saber.

    Para que possamos usar o Google reCaptcha em nosso projeto precisamos primeiro criar uma chave de API, o que você pode fazer neste link.

    Preencha um nome como etiqueta, escolha a V2 do tipo de reCaptcha (a mais popular) e em domínios você vai colocar os domínios dos sites onde vai usar captcha, não esquecendo de adicionar também localhost para que possamos usar em nossos testes. Aceite os termos de serviço e finalize a criação para, na tela seguinte, poder pegar as suas chaves de API sendo que a que vamos precisar é a “chave de site”.

    Para criar o projeto, usaremos um toolkit chamado Vite que vai facilitar a estruturação inicial do projeto. Basta rodar o comando abaixo na pasta onde deseja criar o projeto.

    Pronto, agora temos todo o ambiente necessário para implementar nosso captcha.

    #3 – Implementando o Captcha

    Agora crie um arquivo .env na raiz do projeto e coloque nele a seguinte variável de ambiente, com exatamente este mesmo nome e o valor da sua chave de site (sem os < e >).

    VITE_SITE_KEY=<sua chave aqui>

    Na sequência, vá até o seu src/App.jsx e adicione as importações abaixo no topo do arquivo.

    import { useState } from 'react';
    import ReCAPTCHA from "react-google-recaptcha";

    As adições são: useState para gerenciarmos mudança de estado da aplicação e a importação do componente ReCaptcha que usaremos na página.

    Agora declare um estado para a situação do captcha e uma função de click que quando disparada irá verificar se o captcha já foi resolvido. Se ainda não foi, a mensagem irá avisar o usuário. Se ele já foi, outra mensagem.

    const [captcha, setCaptcha] = useState("");
    
    
    function onClick() {
      if (captcha)
        alert('captcha resolvido');
      else
        alert('captcha pendente');
    }

    Em uma situação real, como em um login por exemplo, pense que quando o captcha foi resolvido você pode fazer a request para o backend processar o login ou o que quer que você queira fazer agora que sabe que o usuário é um ser humano que completou o captcha. Já no caso de cair no else, o entendimento é que o captcha não foi resolvido ou até foi, mas errado.

    Para finalizar, vamos ajustar a interface da aplicação de exemplo para incluir o componente de captcha e usar um botão para disparar nossa função de click.

    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100vh', width: 1200 }}>
      <div style={{marginBottom: 10}}>
        Test the captcha feature below
      </div>
      <div style={{marginBottom: 10}}>
        <button onClick={onClick}>Click Me</button>
      </div>
      <div>
        <ReCAPTCHA
          sitekey={import.meta.env.VITE_SITE_KEY}
          onChange={setCaptcha} />
      </div>
    </div>

    Repare que o componente ReCaptcha é bem simples de usar, bastando referenciar a sua site key que armazenamos no .env do projeto e a função onChange que será disparada toda vez que o captcha for resolvido.

  • Inteligência artificial física: a revolução silenciosa que sai das telas para tocar, ver e agir

    Inteligência artificial física: a revolução silenciosa que sai das telas para tocar, ver e agir

    A inteligência artificial que conhecemos permanece, em grande parte, confinada às telas. Comunica-se por meio de assistentes virtuais, gera imagens e vídeos, redige textos, traduz idiomas, esclarece dúvidas e educa. Tudo isso no ambiente digital.

    No entanto, uma revolução silenciosa avança rapidamente, rompendo essa barreira: a inteligência artificial física. Diferente da IA tradicional, que apenas processa dados, a IA física combina percepção sensorial avançada, raciocínio adaptativo e ações motoras reais.

    Ela interpreta e transforma espaços físicos, produzindo resultados que surpreendem até os mais otimistas entusiastas da tecnologia. Essa nova geração utiliza sensores sofisticados — como visão computacional, audição aprimorada e tato artificial — integrados a sistemas de decisão inteligente para atuar no mundo real.

    Carros autônomos, drones e robôs assistenciais já interagem de forma eficiente e proativa com o ambiente físico. A grande inovação está justamente nessa capacidade de interação concreta.

    O que parecia ficção científica há poucos anos é agora uma realidade em laboratórios de universidades renomadas e nas linhas de produção das maiores empresas do mundo. Robôs que percebem o ambiente e se movem de acordo com essa percepção, buscando atingir objetivos definidos como seus propósitos, já executam tarefas complexas em fábricas e centros médicos com precisão milimétrica.

    No Laboratório de Movimento de Stanford, por exemplo, robôs são treinados para auxiliar pacientes com limitações físicas a se vestirem, utilizando aprendizado profundo para se adaptar às necessidades individuais.

    Pessoas com deficiência ganham autonomia por meio de próteses robóticas inteligentes e assistentes pessoais. Idosos preservam a independência por períodos mais longos graças a essa tecnologia transformadora.

    A indústria manufatureira também passa por uma revolução profunda. Avanços no aprendizado por reforço permitem que robôs tomem decisões autônomas em tarefas físicas desafiadoras, desde ações aparentemente simples, como pendurar camisetas, até montagens técnicas e minuciosas.

    Um exemplo recente é a inserção automática de parafusos e a aplicação de cola em peças em movimento na linha de montagem. Robôs FANUC, equipados com visão 3D da Inbolt e inteligência artificial em tempo real, ajustam seus gestos com base no formato e na posição da peça, mesmo durante o deslocamento.

    A personalização em escala industrial tornou-se viável, permitindo adaptações rápidas a demandas específicas. No setor logístico, robôs autônomos reorganizam armazéns com uma eficiência inédita, otimizando cada etapa da cadeia de suprimentos.

    Já o transporte talvez concentre as mudanças mais visíveis: veículos autônomos processam milhares de variáveis em tempo real (do clima ao comportamento dos pedestres) e tomam decisões seguras. A promessa é clara e urgente: reduzir drasticamente acidentes e salvar vidas.

    Máquinas já assumem tarefas perigosas em ambientes hostis, como soldagens em temperaturas extremas ou a manipulação de materiais radioativos. Na medicina, a precisão em cirurgias aumentou significativamente, reduzindo complicações graves e ampliando as possibilidades de tratamento.

    Mas ainda há grandes desafios. A precisão sensorial exigida é imensa: uma máquina deve ser capaz de interpretar nuances sutis, como o toque humano delicado ou a complexidade de uma expressão facial. Além disso, questões éticas se tornam inevitáveis diante de decisões em tempo real.

    O dilema de um carro autônomo ao escolher entre proteger o condutor ou os pedestres é emblemático, e recorrente nas discussões do Fórum Econômico Mundial. A automação física também levanta preocupações sociais inadiáveis: a substituição de trabalhadores operacionais, a crescente dependência de sistemas automatizados e a exposição a riscos cibernéticos requerem respostas urgentes.

    De acordo com o próprio Fórum Econômico Mundial, cerca de 12% dos empregos industriais poderão ser impactados diretamente pela automação física até 2028. Isso exige políticas públicas robustas voltadas à requalificação profissional e à proteção social.

    Apesar desses desafios, é preciso reconhecer: o avanço tecnológico é não apenas inevitável, mas desejável. Quando bem aplicado, o progresso da IA física traz benefícios substanciais para indivíduos, empresas e sociedades inteiras.

    A integração entre modelos avançados de linguagem e robótica ampliará ainda mais a presença da IA física no cotidiano, tornando dispositivos comuns mais inteligentes e interativos. E isso é apenas uma etapa intermediária de um futuro ainda mais fascinante.

    Sistemas híbridos, capazes de operar simultaneamente nos mundos físico e digital, surgem como o próximo passo. Robôs atuarão com fluidez entre o espaço real e o virtual, conforme a necessidade. Isso poderá criar indústrias trilionárias com o desenvolvimento de robôs humanoides para fábricas, empresas e lares.

    Mais do que automatizar tarefas, a inteligência artificial física redefinirá nossa relação com o mundo material. Seu potencial não está apenas em melhorar a eficiência, mas em libertar os seres humanos de funções repetitivas e perigosas, permitindo o foco em atividades criativas, sociais e estratégicas.

    O futuro da IA física, ao que tudo indica, será marcado por uma profunda simbiose entre homem e máquina. Uma parceria em que o toque da tecnologia não apenas complementa, mas muitas vezes potencializa e supera a ação humana.

  • Melhores práticas de desenvolvimento do Vision Pro

    Melhores práticas de desenvolvimento do Vision Pro

    Melhores práticas de desenvolvimento do Vision Pro: aproveitando Swift e SwiftUI para computação espacial

    Com a crescente adoção do Vision Pro, os desenvolvedores navegam pela empolgante fronteira da computação espacial, utilizando Swift e SwiftUI para criar aplicativos imersivos. A transição de interfaces 2D tradicionais para ambientes espaciais 3D apresenta desafios e oportunidades únicos. Este artigo analisa as melhores práticas para o desenvolvimento do Vision Pro, com foco na otimização do desempenho, no design de interfaces 3D intuitivas e na utilização das atualizações mais recentes do ARKit.

    1. Otimizando o desempenho para computação espacial

    O desempenho é fundamental na computação espacial para garantir experiências fluidas e imersivas. Os aplicativos Vision Pro precisam lidar com renderizações 3D complexas e interações em tempo real com eficiência.

    Dicas de otimização de desempenho

    Renderização Eficiente: Use o Metal para renderização gráfica de alto desempenho. A integração do Swift com o Metal permite gerenciar recursos da GPU e executar técnicas avançadas de renderização.

    import Metal
    import MetalKit
    
    class MetalRenderer: NSObject, MTKViewDelegate {
        var device: MTLDevice!
        var commandQueue: MTLCommandQueue!
    
        override init() {
            super.init()
            device = MTLCreateSystemDefaultDevice()
            commandQueue = device.makeCommandQueue()
        }
    
        func draw(in view: MTKView) {
            guard let drawable = view.currentDrawable else { return }
            let commandBuffer = commandQueue.makeCommandBuffer()
            let renderPassDescriptor = view.currentRenderPassDescriptor
            guard let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor!) else { return }
    
            // Drawing commands here
    
            renderEncoder.endEncoding()
            commandBuffer?.present(drawable)
            commandBuffer?.commit()
        }
    }

    • Dica: certifique-se de otimizar o código do shader e minimizar as chamadas de desenho para manter altas taxas de quadros.
    • Ferramentas de Criação de Perfil: Use os Instrumentos do Xcode para criar o perfil do seu aplicativo e identificar gargalos de desempenho. Concentre-se em áreas como uso de memória, desempenho da GPU e tempos de renderização.

    2. Projetando interfaces 3D intuitivas

    Projetar para 3D requer uma abordagem diferente da IU 2D. A interface deve ser intuitiva e naturalmente interativa dentro de um ambiente espacial.

    Dicas de design para interfaces 3D

    Profundidade e Escala: Use os novos recursos 3D do SwiftUI para criar camadas e efeitos de profundidade. Garanta que os elementos da interface sejam dimensionados adequadamente para parecerem naturais no espaço 3D.

    import SwiftUI
    
    struct RotatingCube: View {
        @State private var rotation: Double = 0
    
        var body: some View {
            VStack {
                Text("Interact with the cube")
                    .font(.headline)
                    .padding()
                Cube()
                    .rotation3DEffect(.degrees(rotation), axis: (x: 1, y: 1, z: 0))
                    .frame(width: 200, height: 200)
                    .onTapGesture {
                        rotation += 45
                    }
            }
        }
    }
    
    struct Cube: View {
        var body: some View {
            VStack {
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: 100, height: 100)
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 100, height: 100)
            }
        }
    }
    • Dica: use dicas de áudio espaciais e feedback tátil para aumentar a sensação de imersão e tornar as interações mais intuitivas.
    • Menus contextuais: implemente menus sensíveis ao contexto que aparecem com base no foco e nas ações do usuário dentro do ambiente espacial.

    3. Aproveitando as atualizações do ARKit

    As atualizações mais recentes do ARKit trazem novos recursos que são cruciais para a criação de aplicativos Vision Pro robustos, como rastreamento de movimento aprimorado e melhor compreensão de cena.

    Exemplo de integração do ARKit

    Utilize o ARKit para incorporar a percepção espacial ao seu aplicativo Vision Pro. Aqui está um exemplo básico de configuração do ARKit com o SceneKit para posicionamento de objetos 3D:

    import SwiftUI
    import ARKit
    import SceneKit
    
    struct ARViewContainer: UIViewRepresentable {
        func makeUIView(context: Context) -> ARSCNView {
            let arView = ARSCNView()
            arView.autoenablesDefaultLighting = true
            arView.session.run(ARWorldTrackingConfiguration())
            return arView
        }
    
        func updateUIView(_ uiView: ARSCNView, context: Context) {}
    }
    
    struct ContentView: View {
        var body: some View {
            ARViewContainer()
                .edgesIgnoringSafeArea(.all)
        }
    }

    Dica: teste seu aplicativo regularmente em diferentes ambientes e condições de iluminação para garantir reconhecimento espacial e interação precisos.

    Conclusão

    Desenvolver para o Vision Pro oferece possibilidades empolgantes, mas também exige atenção especial ao desempenho, design e integração com o ARKit. Ao otimizar o desempenho com o Metal, projetar interfaces 3D intuitivas com o SwiftUI e aproveitar os recursos avançados do ARKit, os desenvolvedores podem criar experiências de computação espacial envolventes. Ao desenvolver seus aplicativos Vision Pro, lembre-se de se manter atualizado com as ferramentas e práticas recomendadas mais recentes para entregar os aplicativos mais imersivos e eficientes possíveis.

    Fique ligado para mais insights e atualizações sobre o desenvolvimento do Vision Pro!

    Boa codificação!

  • Mockando um back-end com o JSON Server

    Mockando um back-end com o JSON Server

    Você já passou por uma situação onde precisava do back-end pronto para implementar/testar a integração com o seu front-end? Pois é… isso pode ocorrer com mais frequência do que gostaríamos. É para este e outros cenários semelhantes que eu gostaria de apresentar o JSON Server.

    Com este pacotinho, conseguiremos “criar um back-end” em menos de 30 segundos que será capaz não somente de armazenar dados, assim como dar suporte para requisições do tipo GET, POST, PUT… etc, seguindo todo o padrão Rest.

    JSON Server

    JSON Server é um dos pacotes mais completos que eu já vi no que diz respeito a quantidade de configurações e funcionalidades. Para entender o seu funcionamento, vamos partir de um exemplo prático.

    Criaremos um novo projeto de front-end usando o parcel e mais algumas dependências. Basta digitar os comandos abaixo no seu terminal de preferência:

    npm init -y
    npm i -D parcel
    npm i json-server axios

    E depois no package.json, configure um script para rodar o projeto em um servidor de desenvolvimento:

    "scripts": {
    "dev": "parcel index.html"
    },

    Muito bem. Para o nosso teste, faremos uma tela com dois botões: um que deverá fazer uma requisição do tipo GET e trazer todos os dados de usuários; e outra que fará uma requisição do tipo POST para cadastrar um novo usuário. Minha intenção é te mostrar que podemos fazer vários tipos de requisições diferentes (como em uma API REST).

    Crie um arquivo index.html na raiz do projeto com essa estrutura:

    <!DOCTYPE html>
    <html lang="pt-br">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>JSON Server</title>
    </head>
    <body>
      <button class="search">Todos os usuários</button>
      <div><input type="text" class="user" /><button class="insert">Inserir usuário</button></div>
      <script type="module" src="index.js"></script>
    </body>
    </html>

    Em seguida, crie um arquivo index.js na raiz do projeto com essa estrutura:

    const axios = require("axios");
    async function getAllUsers() {
      try {
        const { data } = await axios.get("http://localhost:3000/users");
        console.log("users", data);
      } catch (error) {
        console.log("error", error);
      }
    }
    async function addNewUser() {
      const user = document.querySelector(".user").value;
      try {
        await axios.post("http://localhost:3000/users", {
          name: user
        })
      } catch (error) {
        console.log("error", error);
      }
    }
    window.onload = () => {
      document.querySelector(".search").addEventListener("click", getAllUsers);
      document.querySelector(".insert").addEventListener("click", addNewUser);
    }

    Note que as requisições estão sendo feitas para o localhost:3000. Este endereço não é por acaso. Por padrão, é neste local que o servidor local do JSON Server irá funcionar.

    O primeiro passo para configurar o JSON Server é criarmos um arquivo JSON que será o nosso banco de dados. Crie-o na raiz do projeto. Eu chamarei o meu de db.json e o iniciarei com alguns dados. A estrutura deste arquivo também fica ao seu critério.

    {
      "users": [
        {
          "id": 1,
          "name": "diego"
        },
        {
          "id": 2,
          "name": "luiza"
        },
        {
          "id": 3,
          "name": "marcela"
        },
        {
          "id": 4,
          "name": "maria"
        },
        {
          "id": 5,
          "name": "gilberto"
        }
      ]
    }

    Feito isso, tudo o que precisamos fazer agora é criar um script para subir este back-end. Para isso, volte no package.json e crie uma nova instrução:

    "scripts": {
      "dev": "parcel index.html",
      "dev:json": "json-server db.json"
    }

    E se eu te disser que só isso é o sufiente pra fazer tudo funcionar? Abra dois terminais e rode o comando npm run dev em um e npm run dev:json em outro! Pronto! 😃

    Criando uma base aleatória

    Em alguns cenários mais complexos, você vai querer uma quantidade maior de dados para testar. É o caso, por exemplo, de uma paginação (que olhem só, tem suporte nativo no JSON Server!). Nestas situações, se torna bastante desgastante ter que ficar criando os dados um por um. E se utilizarmos a biblioteca Faker para nos ajudar?

    O JSON Server disponibiliza uma forma de criar um banco em memória baseado no retorno de uma função. Nesta função, podemos usar o Faker (por exemplo), para gerar os dados do nosso teste. Para ver isso funcionando, crie um novo arquivo na raiz do projeto chamado seed.js.

    Neste arquivo, coloque o seguinte conteúdo:

    const { faker } = require("@faker-js/faker");
    module.exports = () => {
      const data = { users: [] };
      for(let i = 0; i < 1000; i++) {
        data.users.push({
          id: i,
          name: faker.name.firstName()
        })
      }
      return data;
    }

    Perceba que estamos criando 1000 usuários, returnando este array e exportando essa função. Com isso em mãos, basta criarmos um script no package.json que fará o JSON Server usar estes dados:

    "scripts": {
      "dev": "parcel index.html",
      "dev:json": "json-server db.json",
      "dev:json:seed": "json-server seed.js"
    }

    Agora, ao invés de usar o npm run dev:json, só trocar pelo npm run dev:json:seed!

    Considerações finais

    O JSON Server está longe de ser a única opção de ferramenta para a criação de mocks de back-end, por outro lado, é muito difícil encontrar ferramentas tão completas e fáceis de usar como essa. Aqui só mostrei pra vocês a pontinha do iceberg, vale entrar na documentação e explorar as possibilidades.

    Edição em vídeo

    Prefere assistir? Não tem problema, gravei também em vídeo!

     

  • Código, Fumaça e Assinaturas: Vault Transit na Linha de Fogo

    Código, Fumaça e Assinaturas: Vault Transit na Linha de Fogo

    Introdução: Selando confiança em tempos hostis

    A cidade nunca dorme. Nem seus pipelines.

    Em um mundo onde ataques de supply chain se tornam tão comuns quanto commits em um repositório monorepo, não basta mais verificar o conteúdo do código. É preciso comprovar a origem, assinar digitalmente a confiança e rastrear cada mudança até a última linha.

    Já discutimos como acessos efêmeros com OTP via Vault revolucionam a segurança SSH. Agora, trago uma evolução desse mesmo princípio: assinatura de artefatos na linha de produção, com Zero Trust como base e o HashiCorp Vault como caneta criptográfica.

    O Problema da Proveniência

    Assumir que todo artefato gerado no CI/CD é confiável é um erro clássico.

    • Quem garante que o build foi mesmo daquele commit?
    • Quem impede que alguém injete algo no runner entre etapas?
    • Como confiar no deploy se o artefato não prova quem o gerou?

    É aqui que entra o conceito de validação de proveniência com assinatura digital.

    Assinatura Digital com Vault Transit

    O Vault Transit Engine permite assinar qualquer conteúdo sem expor a chave privada. Ele atua como um “cofre com caneta criptográfica”:

    • Você envia o hash do conteúdo.
    • O Vault assina usando a chave assimétrica protegida.
    • Você recebe uma assinatura verificável com validade garantida.

    Essa abordagem encaixa perfeitamente em ambientes com pipelines GitLab CI/CD, onde o CI apenas invoca o Vault, sem nunca ter acesso direto à chave.

    Arquitetura na prática

    No exemplo real a seguir, usamos:

    • GitLab CI/CD
    • Chave ed25519 gerenciada no Vault
    • Assinatura do hash de todos os arquivos do diretório
    • Armazenamento da assinatura + metadados em um path do Vault (kv-v2)

    Pipeline Real com GitLab

    Etapa 1: Hash de todos os arquivos do projeto

    package_artifact:
      stage: package
      image: alpine:latest
      before_script:
        - apk add --no-cache tar
      script:
        - mkdir -p artifact/
        - find $CODE_DIR -type f ! -name ".filehashes" -exec sha256sum {} \; > artifact/.filehashes
      artifacts:
        paths:
          - artifact/.filehashes

    Gera o arquivo .filehashes com o hash de todos os arquivos presentes na pasta do código.

    Etapa 2: Assinar o .filehashes via Vault

    sign_artifact:
      stage: sign
      image: alpine:latest
      needs:
        - job: package_artifact
      before_script:
        - apk add --no-cache curl bash coreutils openssl jq xxd
      script: |
        ARTIFACT_PATH="artifact/.filehashes"
        HASH_HEX=$(sha256sum "$ARTIFACT_PATH" | awk '{print $1}')
        HASH_B64=$(echo -n "$HASH_HEX" | xxd -r -p | base64)
        SIGNATURE=$(curl -s --header "X-Vault-Token: $VAULT_SIGN_TOKEN" \
            --request POST \
            --data "{\"input\": \"$HASH_B64\"}" \
            "$VAULT_ADDR/v1/transit/sign/$VAULT_KEY" | jq -r .data.signature)
        echo "$SIGNATURE" > "$ARTIFACT_PATH.sig"
        echo "$HASH_HEX" > "$ARTIFACT_PATH.sha256"
        VAULT_PAYLOAD=$(jq -n \
          --arg sig "$(cat "$ARTIFACT_PATH.sig")" \
          --arg hash "$(cat "$ARTIFACT_PATH.sha256")" \
          --arg datetime "$(TZ='America/Sao_Paulo' date -d '-3 hours' '+%d/%m/%Y - %H:%M:%S')" \
          --arg size "$(stat -c%s "$ARTIFACT_PATH" | numfmt --to=iec)" \
          --arg executed_by "$(whoami)" \
          '{sign: $sig, hash_sha256: $hash, datetime: $datetime, artefact_size: $size, executed_by: $executed_by}')
     

      curl -s --header "X-Vault-Token: $VAULT_ARTEFATO_TOKEN" \
            --request POST \
            --data "$VAULT_PAYLOAD" \
            "$VAULT_ADDR/v1/artefatos/monitoramento_acesso"
      artifacts:
        paths:
          - artifact/.filehashes
          - artifact/.filehashes.sig
          - artifact/.filehashes.sha256

    Verificação de assinatura (manual ou automática)

    # Validar integridade
    sha256sum artifact/.filehashes | grep $(cat artifact/.filehashes.sha256)

    # Validar autenticidade com Vault
    HASH_B64=$(cat artifact/.filehashes.sha256 | xxd -r -p | base64)

    curl -s --header "X-Vault-Token: $VAULT_SIGN_TOKEN" \
        --request POST \
        --data "{\"input\": \"$HASH_B64\", \"signature\": \"$(cat artifact/.filehashes.sig)\"}" \
        "$VAULT_ADDR/v1/transit/verify/my-signing-key"

    Se válido:

    {"data":{"valid":true}}

    Benefícios reais

    Expansões possíveis

    • Uso com containers (cosign + Vault)
    • Integração com OIDC/JWT do GitLab (sem token fixo)
    • Auditoria automática com Lambda ou webhook
    • Verificação em runtime antes do deploy via webhook de validação

    Conclusão

    Se você já pensa em segurança como efemeridade, sua próxima etapa é a assinatura granular de tudo que o CI/CD gera. Vault deixa de ser só um cofre de senhas e vira o cartório digital do seu DevSecOps.

    Em tempos de código, fumaça e ameaças, só sobrevive quem assina o que entrega.
    A confiança agora tem um hash. E uma assinatura.

  • Entendendo os Backups Distribuídos do SQL Armazenados na Nuvem

    Entendendo os Backups Distribuídos do SQL Armazenados na Nuvem

    Á medida que crescem os bancos de dados, maior se torna o problema para fazer backups, especialmente o temido (mas necessário) backup completo (“full”).

    Todo mundo sabe que os backups completos são feitos em horários deslocados porque seu impacto na performance do ambiente é muito grande. Por isso, costumam ser criados de madrugada ou em finais de semana.

    Mas o que fazer quando o tempo de criação desse backup  acaba ultrapassando a janela definida? 

    Existem várias possibilidades para contornar o problema, mas elas costumam combinar alguns fatores, como uso de discos mais rápidos e a separação do backup em vários discos para que a operação seja paralelizada.

    A criação de backups apontando para múltiplos discos não é nenhuma novidade. Estes são os chamados backups distribuídos.

    A grande vantagem de se manter backups (sejam simples ou distribuídos) na nuvem é que estes serviços de armazenamento garantem um SLA de 99,99% de disponibilidade, além da redundância do armazenamento (ao menos 3 réplicas nos serviços mais simples).

    Neste artigo, apresento princípios básicos para utilização de backups distribuídos armazenados na nuvem (Azure Storage Account).

    Preparação da Storage Account

    Logo após a implementação da conta de armazenamento do AZURE, é necessário criar um “container” de uso exclusivo para fins de gravação dos backups, como mostra a figura a seguir (container “backupfull”).

    Figura 01 – criação do container “backupfull”

    Ao abrir o container “backfull”, acesse a aba SHARED ACCESS TOKENS. O token que vamos criar permitirá acesso de leitura e escrita no container e seus arquivos. Ou seja, será possível executar operações de backup e restauração na base de dados.

    Além das permissões de READ e WRITE (respectivamente, para restore e backup), é preciso definir o prazo de validade do token. Não é boa prática que se use prazos muito longos, geralmente não maior que 06 meses.

    Para finalizar, clique o botão azul para criar o token, como se vê na imagem abaixo, e copie o resultado. Ele será usado no próximo passo desse roteiro.

    Figura 2 – geração do SAS Token

    Preparação da Credencial do SQL Server

    Para que o SQL Server tenha acesso ao container onde estão os backups, é necessário criar uma credencial para o SQL usando o token que acabamos de gerar.

    Neste exemplo, criarei uma credencial usando um “SAS Token”, que é o modo mais simples de acesso. Para isso é preciso informar a URI do container “backupfull”, definir a identidade com o tipo “shared access signature” e informar o token, como mostra o quadro a seguir:

    CREATE CREDENTIAL 

     [https:// stodemowgc01.blob.core.windows.net/backupfull/<opcional>]

     WITH IDENTITY = 'SHARED ACCESS SIGNATURE', 

     SECRET = '<SAS_TOKEN>';

    Observe que após o nome do container existe um sufixo opcional. Isso é usado quando se precisar criar credenciais para perfis diferentes de usuários. Por exemplo: eu poderia criar uma credencial para quem executa os backups e restaurações da base (permissões de READ e WRITE) e um segundo perfil que precisa apenas a permissão para executar o RESTORE. Nesse caso, eu criaria uma credencial adicional usando um novo token com permissão de leitura, como mostra o quadro a seguir:

    CREATE CREDENTIAL 

     [https:// stodemowgc01.blob.core.windows.net/backupfull/credencial2]

     WITH IDENTITY = 'SHARED ACCESS SIGNATURE', 

     SECRET = '<novo_SAS_TOKEN>';

    Criando o Backup

    Uma vez que se tenha a credencial SQL, o backup pode ser criado informando a URI seguida dos nomes de arquivos que vão compor este backup.

    Assim temos a seguinte declaração SQL para gerar um backup da base WideWorldImporters segmentado em 10 arquivos diferentes:

    BACKUP DATABASE [WideWorldImporters]

    TO URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_01.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_02.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_03.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_04.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_05.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_06.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_07.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_08.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_09.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_10.bak'

    WITH FORMAT, INIT, COMPRESSION, STATS = 10;

    Verificando o Conteúdo do Backup

    Para verificar os apontamentos dos “datafiles” copiados neste backup distribuído, basta o comando RESTORE FILELISTONLY, como mostrado a seguir:

    RESTORE FILELISTONLY 

    FROM 

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_01.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_02.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_03.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_04.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_05.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_06.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_07.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_08.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_09.bak',

       URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_10.bak'

    GO

    Esta verificação lhe mostra quais os datafiles usados pela base de dados e em que discos eles serão gravados quando houver o RESTORE.

    Restaurando a Base de Dados

    Finalmente, para restaurar uma cópia da base de dados para um ambiente novo (digamos, ambiente de DEV), muitas vezes é necessário redirecionar os “datafiles” da base para os discos que existem nessa nova máquina. 

    A declaração SQL de restauração do backup distribuído seguido da movimentação de “datafiles” é apresentada a seguir:

    RESTORE DATABASE WideWorldImporters

      FROM 

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_01.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_02.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_03.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_04.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_05.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_06.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_07.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_08.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_09.bak',

      URL = 'https://stodemowgc01.blob.core.windows.net/backupfull/WWI_202250909_10.bak'

      WITH RECOVERY,

      MOVE 'WWI_Primary'   TO 'C:\Database\Data2\WideWorldImporters.mdf',

      MOVE 'WWI_UserData'  TO 'C:\Database\Data2\WideWorldImporters_UserData.ndf',

      MOVE 'WWI_Log'       TO 'C:\Database\LOG2\WideWorldImporters.ldf' ,

      MOVE 'WWI_InMemory_Data_1' TO 'C:\Database\Data2\WideWorldImporters_InMemory_Data_1'

    GO

    Reciclagem das Credenciais

    Como comentei acima, as credenciais SQL usam um token que tem um ciclo de vida curto, normalmente alguns meses.

    Isso significa que as credenciais precisam ser atualizadas periodicamente, apontando para um novo token.

    O processo de renovação desses segredos é bastante simples, envolvendo duas operações:

    1. Criação de um novo token, conforme mostrado na Figura 02
    2. Atualização da credencial usando o segredo, através dessa declaração:

    ALTER CREDENTIAL 

      [https:// stodemowgc01.blob.core.windows.net/backupfull/[opcional]]

     WITH IDENTITY = 'SHARED ACCESS SIGNATURE', 

     SECRET = '<SAS_TOKEN>';

    Comentários Finais

    Com esta visão geral, acredito que você já consiga criar seu primeiro backup distribuído na nuvem.

    Existem outros métodos de autenticação ainda mais seguros que o SAS Token, mas eu resolvi apresentar este recurso por ser mais simples que as demais opções. De qualquer maneira, é importante saber que o uso de SAS Token é largamente utilizado no mundo todo.

    Até a próxima.

    Leitura Sugerida

    1. BACKUP (Transact-SQL) – SQL Server | Microsoft Learn
    2. Restaurando de backups armazenados no Microsoft Azure – SQL Server | Microsoft Learn

     

  • Guia Prático: Previsão de entregas de software com simulação de Monte Carlo

    Guia Prático: Previsão de entregas de software com simulação de Monte Carlo

    1. O problema das estimativas fixas

    No universo do desenvolvimento de software, a pergunta “Quando vai ficar pronto?” é uma constante. A abordagem tradicional para respondê-la geralmente envolve uma matemática simples e, infelizmente, enganosa: dividir o total de tarefas restantes pela velocidade média da equipe.

    Essa estimativa de ponto único (ex: “2.5 semanas”) cria uma falsa sensação de precisão. Ela ignora a realidade fundamental do nosso trabalho: a incerteza. Imprevistos acontecem, a complexidade de uma tarefa pode ser maior que o esperado, um membro do time pode ficar doente… Confiar em uma estimativa fixa é preparar o terreno para prazos estourados, frustração e desconfiança.

    Este guia propõe uma mudança de paradigma: sair do campo das promessas e entrar no campo das probabilidades. Utilizaremos a Simulação de Monte Carlo para abraçar a incerteza e transformá-la em uma ferramenta estratégica para gerenciar expectativas e riscos.

    2. Preparando a simulação: Os dados essenciais

    Antes de rodar a simulação, a qualidade dos dados de entrada é fundamental. Um modelo, por melhor que seja, produzirá resultados ruins se alimentado com informações imprecisas.

    Como coletar a vazão (Throughput) do time?

    A vazão é o número de tarefas que a equipe entrega por semana. Para obter esse dado:

    • Ferramenta: Utilize o histórico de um sistema como o Jira, Azure DevOps ou similar.
    • Período: Análise um período de tempo relevante e estável. Uma boa recomendação é coletar dados das últimas 8 a 12 semanas (2 a 3 meses). Períodos mais curtos podem ser voláteis, e períodos muito longos podem não refletir a realidade atual do time.
    • Exemplo Prático: Exporte as tarefas concluídas por semana. Você terá uma lista de números, como: [5, 8, 4, 6, 7, 5, 8, 6]. A menor vazão foi 4 e a maior foi 8. Essa é a faixa que alimentará nossa simulação.

    Quais tarefas contar? Foco em valor

    Este é um ponto crucial. Para prever a entrega de novas features, não devemos olhar para todas as tarefas que o time executa. É preciso filtrar e focar no que agrega valor direto ao produto.

    • Incluir: Tarefas de implementação de features.
    • Excluir (Descontar): Correção de bugs, débitos técnicos, refatorações, incidentes etc.

    Ao fazer isso, medimos a vazão de entrega de valor, o que torna a previsão sobre novas features muito mais acurada.

    3. A Ferramenta: Script em Node.js

    Para colocar a simulação em prática, utilizamos o script em Node.js abaixo. Ele foi projetado para ser flexível, aceitando não apenas uma variação na vazão do time, mas também um grau de incerteza no escopo (ex: “restam de 15 a 20 tarefas”), tornando a simulação ainda mais próxima da realidade.

    // previsao_cli.js
    
    // --- PARÂMETROS DA SIMULAÇÃO ---
    const VALORES_PADRAO = {
      tarefasMin: 15,
      tarefasMax: 15, // Por padrão, o intervalo é um número fixo
      vazaoMin: 3,
      vazaoMax: 8,
      simulacoes: 10000,
    };
    
    const args = process.argv.slice(2);
    
    if (args[0] === '--help' || args[0] === '-h') {
      console.log(`
      Uso: node previsao_cli.js [tarefas_min] [tarefas_max] [vazao_min] [vazao_max]
    
      Argumentos:
        [tarefas_min]  - (Opcional) O número mínimo de tarefas restantes. Padrão: ${VALORES_PADRAO.tarefasMin}
        [tarefas_max]  - (Opcional) O número máximo de tarefas restantes. Se omitido, será igual ao mínimo. Padrão: ${VALORES_PADRAO.tarefasMax}
        [vazao_min]    - (Opcional) A menor vazão semanal. Padrão: ${VALORES_PADRAO.vazaoMin}
        [vazao_max]    - (Opcional) A maior vazão semanal. Padrão: ${VALORES_PADRAO.vazaoMax}
    
      Exemplos:
      - Cenário fixo (15 tarefas, vazão 3-8):
        node previsao_cli.js 15
      - Cenário com escopo variável (15 a 20 tarefas, vazão 3-8):
        node previsao_cli.js 15 20
      - Cenário completo (15 a 20 tarefas, vazão 4-9):
        node previsao_cli.js 15 20 4 9
      `);
      process.exit(0);
    }
    
    const TAREFAS_MIN = parseInt(args[0], 10) || VALORES_PADRAO.tarefasMin;
    const TAREFAS_MAX = parseInt(args[1], 10) || TAREFAS_MIN; // Se o max não for passado, é igual ao min
    const VAZAO_MINIMA_SEMANAL = parseInt(args[2], 10) || VALORES_PADRAO.vazaoMin;
    const VAZAO_MAXIMA_SEMANAL = parseInt(args[3], 10) || VALORES_PADRAO.vazaoMax;
    const NUMERO_DE_SIMULACOES = VALORES_PADRAO.simulacoes;
    
    if (VAZAO_MINIMA_SEMANAL > VAZAO_MAXIMA_SEMANAL || TAREFAS_MIN > TAREFAS_MAX) {
        console.error("\x1b[31mErro: O valor mínimo não pode ser maior que o valor máximo.\x1b[0m");
        process.exit(1);
    }
    
    // --- LÓGICA DA SIMULAÇÃO ---
    function sortearNumero(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    
    function executarUmaSimulacao() {
      let semanas = 0;
      // Sorteia o total de tarefas para ESTA simulação específica
      const totalTarefasNestaSimulacao = sortearNumero(TAREFAS_MIN, TAREFAS_MAX);
      let tarefasConcluidas = 0;
    
      while (tarefasConcluidas < totalTarefasNestaSimulacao) {
        semanas++;
        tarefasConcluidas += sortearNumero(VAZAO_MINIMA_SEMANAL, VAZAO_MAXIMA_SEMANAL);
      }
      return semanas;
    }
    
    function analisarResultados(resultados) {
      resultados.sort((a, b) => a - b);
      const calcularPercentil = (percentil) => {
        const index = Math.ceil(percentil / 100 * resultados.length) - 1;
        return resultados[index];
      };
    
      const p50 = calcularPercentil(50);
      const p70 = calcularPercentil(70);
      const p85 = calcularPercentil(85);
      const p95 = calcularPercentil(95);
    
      console.log("--- Previsão de Entrega (Simulação de Monte Carlo) ---");
      let escopoTexto = TAREFAS_MIN === TAREFAS_MAX ? `${TAREFAS_MIN}` : `entre ${TAREFAS_MIN} e ${TAREFAS_MAX}`;
      console.log(`\nBaseado em ${NUMERO_DE_SIMULACOES.toLocaleString('pt-BR')} simulações para completar ${escopoTexto} tarefas:`);
      console.log(`Vazão semanal de valor estimada entre ${VAZAO_MINIMA_SEMANAL} e ${VAZAO_MAXIMA_SEMANAL} tarefas.`);
      
      console.log("\nResultados Probabilísticos:");
      console.log(`\x1b[33m%s\x1b[0m`, `  -> 50% de chance de terminar em ${p50} semanas ou menos.`);
      console.log(`\x1b[32m%s\x1b[0m`, `  -> 70% de chance de terminar em ${p70} semanas ou menos.`);
      console.log(`\x1b[32m%s\x1b[0m`, `  -> 85% de chance de terminar em ${p85} semanas ou menos.`);
      console.log(`\x1b[32m%s\x1b[0m`, `  -> 95% de chance de terminar em ${p95} semanas ou menos.`);
    }
    
    // --- EXECUÇÃO PRINCIPAL ---
    const todosOsResultados = [];
    for (let i = 0; i < NUMERO_DE_SIMULACOES; i++) {
      todosOsResultados.push(executarUmaSimulacao());
    }
    analisarResultados(todosOsResultados);
    

    Como Usar:

    • Cenário com escopo fixo (15 tarefas, vazão 3-8): node previsao_cli.js 15
    • Cenário com escopo variável (15 a 20 tarefas, vazão 3-8): node previsao_cli.js 15 20
    • Cenário completo com escopo e vazão variáveis (15 a 20 tarefas, vazão 4-9): node previsao_cli.js 15 20 4 9
    • Para visualizar todas as instruções de uso, execute o comando de ajuda: node previsao_cli.js --help

    4. Decifrando a saída: O que os números realmente significam

    Ao rodar a simulação, você obterá uma saída como esta:

    Resultados Probabilísticos:
      -> 50% de chance de terminar em 3 semanas ou menos.
      -> 70% de chance de terminar em 3 semanas ou menos.
      -> 85% de chance de terminar em 4 semanas ou menos.
      -> 95% de chance de terminar em 4 semanas ou menos.

    A repetição de valores não é um erro; é a informação mais valiosa.

    A Natureza dos Dados

    A saída de cada simulação é um número inteiro de semanas. O projeto não pode terminar em 3.5 semanas. A lista de 10.000 resultados é ordenada, e os percentis são extraídos dela.

    Interpretando os percentis passo a passo

    1. 50% de chance de terminar em 3 semanas ou menos: O resultado na metade da lista(5000) ordenada é 3.
    2. 70% de chance de terminar em 3 semanas ou menos: Na posição 7.000, o resultado ainda é 3. Isso mostra que o cenário “3 semanas” é muito comum.
    3. 85% de chance de terminar em 4 semanas ou menos: Na posição 8.500, o resultado muda para 4. Esse é o “pulo do gato”, o ponto onde o risco aumenta.
    4. 95% de chance de terminar em 4 semanas ou menos: O resultado “4 semanas” também é comum, cobrindo a faixa de 85% a 95% de confiança.

    O que isso significa na prática? O mapa de riscos.

    1. Cenário Provável: Existe uma chance muito grande (até 70%) de o projeto terminar em 3 semanas.
    2. O “Degrau” de Risco: A simulação alerta que, se as coisas não saírem como o esperado, o prazo salta para 4 semanas.
    3. Planejamento de Alta Confiança: Para comunicar um prazo com 85% de confiança, você deve usar 4 semanas. A conversa com o PM ou stakeholder se torna mais estratégica: “O mais provável são 3 semanas, mas para termos 85% de certeza, o plano é para 4 semanas”.

    5. Quando ter cautela: Limitações do modelo

    Nenhuma ferramenta é uma bala de prata. A Simulação de Monte Carlo é poderosa, mas sua eficácia depende do contexto. É importante saber quando seus resultados podem ser menos confiáveis:

    • Falta de dados Históricos: Se o time é novo ou nunca mediu sua vazão, o modelo não tem como ser alimentado. Qualquer faixa de vazão será um “chute”, o que invalida o propósito da simulação.
    • Mudanças drásticas na Equipe: Se a composição do time mudou significativamente (ex: saíram 2 seniores e entraram 2 juniores), o histórico de vazão do time antigo não é mais válido para prever o futuro do time novo.
    • Mudança no tipo de Trabalho: Se a equipe está mudando de um tipo de desenvolvimento (ex: front-end) para outro completamente diferente (ex: ciência de dados), o desempenho passado não é um bom preditor do futuro.
    • Projetos muito pequenos: A técnica se beneficia da lei dos grandes números. Para um projeto com apenas 1 ou 2 tarefas, a variação é tão alta que a simulação oferece pouco valor em relação a uma conversa direta.

    6. Conclusão: Equilibrando qualidade e previsibilidade no Asaas

    No Asaas, a qualidade não é uma variável negociável, é o pilar que sustenta a confiança de nossos clientes. Dedicamos tempo e esforço para garantir que cada entrega atenda aos mais altos padrões de excelência técnica e de experiência do usuário.

    No entanto, em um ambiente de negócios dinâmico, a previsibilidade é igualmente crucial. Ter uma estimativa confiável de quando uma nova feature estará disponível não é apenas sobre cumprir prazos. É sobre alinhar estratégias, coordenar equipes de marketing e vendas, e, fundamentalmente, planejar nossos OKRs (Objectives and Key Results) com base em metas tangíveis.

    A Simulação de Monte Carlo surge como a ponte perfeita entre esses dois mundos. Ela nos permite manter nosso compromisso inabalável com a qualidade, ao mesmo tempo que nos fornece um mapa probabilístico de entrega. Em vez de datas frágeis, trabalhamos com cenários de confiança, permitindo que o Asaas planeje seu futuro de forma mais inteligente e estratégica, garantindo que continuaremos inovando e entregando valor de forma consistente.

     

    Biografia: Líder Técnico | CTO | Engenheiro de Software

    +15 anos de experiência em tecnologia, conectando engenharia e estratégia de negócio.

    Liderou como CTO a jornada de uma startup, do dia zero à venda.

    Experiência em múltiplas funções: de Engenheiro Full-stack a Tech Lead, CTO e COO.

    Apaixonado por criar ferramentas para resolver problemas reais e explorar novas tecnologias.

    Atualmente Tech Lead de Engenharia na tribo ERP do Asaas.

     

  • Como criar uma FAKE API REST para testes — JSONPlaceholder

    Como criar uma FAKE API REST para testes — JSONPlaceholder

    Você não precisa mais esperar o back-end estar pronto 😉

    Fora os aventureiros que tentam encontrar altas emoções no mundo Full Stack, boa parte dos profissionais de programação escolhem uma área específica para atuar: front-end, back-end, devops, etc. Felizmente as opções são abundantes.

    A grande questão é que na vida real um único produto/serviço precisa da poderosa união de todas essas áreas para que possa funcionar de forma satisfatória. E é ai que mora um dos grandes desafios no desenvolvimento de software.

    Para facilitar este trabalho de sinergia entre as áreas, hoje gostaria de mostrar como funciona o JSONPlaceholder, uma simples ferramenta que simula o back-end de uma aplicação, deste modo permitindo que o front-end consiga trabalhar nas questões visuais antes mesmo do back-end estar finalizado — claro, desde que exista um consenso em como as APIs irão funcionar (endpoints, estrutura dos json, tipos de respostas, etc). Além de ser uma boa ferramenta de estudos para treinar pequenas aplicações e protótipos.

    É tudo muito simples e sem complicação. Basta seguir os passos adiante para sair usando.

    Conhecendo o JSONPlaceholder

    A proposta do JSONPlaceholder — projeto de código aberto hospedado no GitHub e com mais de 44 mil estrelas — é bem simples: ser uma ferramenta de fácil acesso para testes e prototipação.

    É possível utilizá-la de duas formas: com dados prontos da plataforma ou com dados personalizados. Vamos ver como trabalhar com cada uma das formas.

    Consumindo respostas

    O JSONPlaholder já possui toda uma estrutura de dados que podem ser utilizados na sua aplicação. Estes recursos (endpoints) são:

    1. /posts : 100 posts
    2. /comments : 500 comments
    3. /albums : 100 albums
    4. /photos : 5000 photos
    5. /todos : 200 todos
    6. /users : 10 users

    Estes recursos possuem relações entre si. O que significa na prática que os posts possuem comentários, albúns possuem fotos, e assim por diante. Estas relações podem ser observadas nas rotas que podemos consumir, como por exemplo:

    • GET /posts
    • GET /posts/
    • GET /posts/1/comments
    • GET /comments?postId=1
    • GET /posts?userId=1
    • POST /posts
    • PUT /posts/1
    • PATCH /posts/1
    • DELETE /posts/1

    Como é possível notar, todos os tradicionais métodos HTTP estão disponíveis para uso, sendo que cada um dos métodos tem um objetivo (conforme padrão em APIs REST).

    Para usar o serviço em sua aplicação, basta fazer uma requisição para qualquer um dos endpoints disponíveis, como segue o exemplo:

    Consumindo uma API do JSONPlaceholder

    Configurando respostas

    Para usar dados próprios na plataforma, o processo é bem simples e até certo ponto criativo. O primeiro passo será criar um novo repositório no GitHub. Feito isso, crie um arquivo chamado db.json . É neste arquivo que criaremos nossa “estrutura” de endpoints.

    Para mostrar um exemplo, imagine que estamos desenvolvendo uma API para acessar os cursos da Code Prestige. Poderíamos ter algo como:

    {
      "cursos": [
        { "id": 1, "titulo": "Entendendo o ES6", "url": "https://www.codeprestige.com.br/cursos/es6" },
        { "id": 2, "titulo": "Produtividade Máxima com o VS Code", "url": "https://www.udemy.com/truques-vscode/" },
        { "id": 3, "titulo": "Entendendo o ES7/ES8", "url": "https://www.codeprestige.com.br/cursos/es7-8" },
        { "id": 4, "titulo": "React 16 Definitivo", "url": "https://www.udemy.com/react-16/" }
      ],
      "alunos": [
        { "id": 1, "nome": "Marcela da Silva", "curso": 1 },
        { "id": 2, "nome": "José Luiz Felipe", "curso": 2 },
        { "id": 3, "nome": "Paulo Rogério", "curso": 3 },
        { "id": 4, "nome": "Felipe da Cunha", "curso": 1 },
        { "id": 5, "nome": "Luiza Cardoso Pereira", "curso": 1 }
      ],
      "empresa": {
        "nome": "Code Prestige"
      }
    }

    E pronto! Para acessar o conteúdo da sua Fake API basta acessar:

    https://my-json-server.typicode.com/<seu-usuario>/<seu-repositorio>

    No caso deste artigo, podemos acessar em:

    My JSON Server – CodePrestige

    my-json-server.typicode.com

    Se tudo estiver certo, uma página como essa será criada para sua Fake API:

    Press enter or click to view image in full size

    Tela do repositório estruturado pelo JSONPlaceholder

    Esta página é como a “documentação” da API. Agora basta criar uma aplicação que consuma esta API, como no exemplo abaixo onde fazemos uma requisição para listar os cursos:

    Bem legal, né? Mas antes que você saia usando ela por aí, vale ressaltar quais são as limitações:

    • As mudanças são fake e NÃO SÃO PERSISTIDAS
    • As requisições possuem cache de 1 minutos
    • db.json tem limites (você não pensou em subir o back-end de produção aqui, né?)
    • Todos os servidores são públicos
    • Repositórios privados ainda não são suportados

    Se nenhuma destas limitações for um real problema para o que você quer fazer, não há porque perder tempo. É só sair usando 😉

    Conclusão

    Depender do back-end para o desenvolvimento do front-end pode ser um grande empecilho para o desenvolvimento de um projeto (e claro, o vice-versa também pode se tornar um grande problema). Felizmente mais soluções como o JSONPlaceholder estão aparecendo, pois assim até mesmo os mais inexperientes em back-end poderão simular as respostas e fazer o seu trabalho com mais tranquilidade.

    Versão em Vídeo

    Além das instruções que darei abaixo, você também pode acompanhar todos os passos que serão feitos na versão em vídeo!

    Não se esqueça de se inscrever no meu canal!

    Referências

  • Converse com seu software: o papel dos CIOs na era do desenvolvimento com IA

    Converse com seu software: o papel dos CIOs na era do desenvolvimento com IA

    O desenvolvimento de software está passando por uma das maiores transformações do setor de tecnologia. Com o avanço das plataformas Low-Code, a criação de aplicativos já se tornou mais rápida e robusta, além de contar com a convergência entre essa ferramenta e a Inteligência Artificial Generativa, que inaugura uma nova era, impactando profundamente a forma como as empresas constroem, utilizam e evoluem seus sistemas.

    Ambas as tecnologias vêm avançando por empresas em todo o mundo. O mercado de Low-Code deve crescer de US $ 37,39 bilhões em 2025 para US$ 264,40 bilhões em 2032, exibindo uma taxa de crescimento anual composta  (CAGR) de 32,2% durante o período de previsão, segundo a Fortune Business Insights. Já a McKinsey afirma que, em 2024, 72% das empresas do mundo já adotaram a IA no trabalho, um avanço significativo comparado aos 55% em 2023.

    Ao combinar a dupla tecnológica, as áreas de negócios e tecnologia começam a colaborar de forma mais ágil e eficiente, possibilitando a criação de aplicações a partir de descrições em linguagem natural, eliminando a dependência da codificação manual e permitindo que as empresas transformem ideias em soluções funcionais com velocidade.

    Ao mesmo tempo, os sistemas estão se tornando mais inteligentes e intuitivos. Em vez de exigir que os usuários se adaptem às interfaces, agora é o software que aprende a entender as necessidades dos usuários por meio de interações conversacionais e adaptativas. Essa transformação simultânea, na forma como desenvolvemos software e como interagimos com ele, representa um ponto de inflexão que potencializa a transformação digital em todas as áreas de negócio.

    Este cenário traz um novo desafio estratégico aos CIOs (Chief Information Officer) quando vemos uma demanda que não envolve apenas adotar novas tecnologias para inovar, mas de reprogramar completamente a estratégia de TI das organizações. O papel dos líderes passa a ser o de orquestrador da Transformação Digital, responsáveis por integrar sistemas abertos, flexíveis e sustentáveis, capazes de evoluir continuamente em um ambiente de constante inovação.

    A convergência entre Low-Code e IA Generativa exige uma revisão completa da arquitetura de soluções. O desenvolvimento tradicional, baseado na escrita manual de código, dá lugar a uma lógica orientada à modelagem do conhecimento, cujas aplicações são geradas automaticamente, de forma coerente e escalável. Isso não significa menos controle para os CIOs, mas sim um foco maior em curadoria e orquestração: definir as regras, os fluxos de trabalho e as bases de conhecimento garantirão a consistência e a sustentabilidade dos sistemas.

    Outro ponto crítico é a governança de dados. À medida que a IA Generativa passa a atuar em processos cada vez mais estratégicos, garantir a integridade, a privacidade e a rastreabilidade da informação se torna imprescindível. Os CIOs terão o desafio de estruturar políticas de dados robustas, criar ambientes seguros e, ao mesmo tempo, assegurar que a empresa mantenha autonomia tecnológica, evitando dependência excessiva de um único fornecedor ou plataforma.

    Mais do que uma tendência, as organizações que conseguirem estruturar uma estratégia eficiente de Low-Code e IA Generativa estarão mais preparadas para responder às constantes transformações do mercado e explorar novas oportunidades de crescimento.

    Para os CIOs é uma oportunidade de liderar a evolução das empresas, transformando a área de TI em um verdadeiro motor de inovação, capaz de entregar soluções mais ágeis, inteligentes e alinhadas aos objetivos estratégicos do negócio. O futuro do desenvolvimento de software já começou, e será moldado por quem souber orquestrar o conhecimento de forma escalável e sustentável.

  • As 5 melhores ferramentas de geração de imagens com IA em 2025 (guia prático e atualizado)

    As 5 melhores ferramentas de geração de imagens com IA em 2025 (guia prático e atualizado)

    Antes de escolher “a melhor”, alinhe critérios que realmente importam em 2025:
    qualidade (fotorrealismo/estilo), consistência de mãos e tipografia, controles (referência, in-painting/out-painting), licenciamento/comercial, integração (APIs, ecossistema) e custo.

    1) Midjourney v7 — precisão de prompt e consistência visual

    O que há de novo: o v7 foi lançado em 3 de abril de 2025 e virou padrão em 17 de junho de 2025. Traz ganhos visíveis em mãos, corpos e objetos, além de Draft Mode (interação rapidinha) e Omni Reference (referência multiimagem).

    Por que está no Top 5: permanece referência em qualidade artística e fotorrealismo “plug-and-play”, com comunidade madura e fluxo criativo muito rápido.

    Para quem: criativos, marketing e estúdios que querem qualidade imediata sem mexer em pipeline.

    Planos: quatro níveis (Basic, Standard, Pro, Mega) com recursos distintos. Veja a matriz oficial de planos.

    2) OpenAI — GPT-4o Image Generation (texto perfeito na imagem + edição contextual)

    O que há de novo: em 25 de março de 2025, a OpenAI apresentou image generation no GPT-4o — com renderização de texto muito mais fiel, obediência precisa ao prompt e edição dentro do chat usando o próprio contexto (inclusive transformando e combinando imagens enviadas).

    Por que está no Top 5: une chat + geração + edição no mesmo lugar; excelente quando a peça precisa conter palavras legíveis (rótulos, thumbnails, mockups).

    Acesso e preços: disponível no ChatGPT e via API (tabela oficial indica custos por qualidade/tamanho da imagem). DALL·E 3 segue listado como geração anterior.

    3) Adobe Firefly (Image 3) — integração Creative Cloud e créditos generativos

    O que há de novo: a Adobe lançou o Firefly Image 3 com salto em fotorrealismo e controle em 23 de abril de 2024, e em 24 de abril de 2025 divulgou a “próxima evolução” do Firefly com modelos e recursos aprimorados no app web e na Creative Cloud.

    Créditos generativos: em 17 de junho de 2025 a Adobe detalhou o uso de um tipo único de crédito (padrão x premium).

    Por que está no Top 5: fluxos CC (Photoshop, Illustrator, Express) + prioridade de uso comercial seguro e controles criativos finos.

    Planos: A página oficial reúne opções Firefly/CC com generative credits e integrações (inclusive acesso a modelos parceiros).

    4) Google Imagen 3 (ImageFX e Vertex AI) — tipografia forte e opções enterprise

    O que há de novo: a Google ampliou o acesso ao Imagen 3 no fim de 2024 — disponível no ImageFX (Google Labs) e para clientes Google Cloud via Vertex AI; houve atualização conjunta com Veo 2 (vídeo).

    Por que está no Top 5: boas letras/legibilidade, controles sólidos e ecossistema enterprise (governança, billing, watermark SynthID).

    Preços: consulte a página oficial de Vertex AI Generative AI pricing (modelo faturado por imagem/uso).

    5) Stability AI — Stable Diffusion 3.5 (open weights, controle máximo)

    O que há de novo: o SD 3.5 chegou em 22 de outubro de 2024 com família de tamanhos e código/inferência abertos; em 12 de agosto de 2025, a Stability lançou o SD 3.5 NIM com a NVIDIA, facilitando deploy corporativo.
    Licença: uso comercial ao amparo da Community License (há requisitos de registro e condições — leia antes de monetizar).

    Por que está no Top 5: customização completa (LoRA, ControlNets, pipelines locais) e custo sob controle — ideal para quem precisa de modelos ajustáveis e privacidade.

    Comparativo rápido (modelo mais recente / onde brilha)

    Ferramenta Modelo/Marco mais recente Pontos fortes
    Midjourney v7 (default desde 17/06/2025) Qualidade out-of-the-box, Draft Mode, Omni Reference
    OpenAI GPT-4o image generation (25/03/2025) Texto na imagem, edição no chat, integração ChatGPT/API
    Adobe Firefly Image 3 (23/04/2024) + evolução 24/04/2025 CC workflow, licenças claras, generative credits
    Google Imagen 3 Disponível em ImageFX/Vertex AI (dez/2024) Tipografia, governança enterprise, SynthID
    Stable Diffusion 3.5 SD 3.5 (22/10/2024) + NIM (12/08/2025) Open weights, fine-tuning, privacidade/deploy local

    Como escolher “a sua” em 2025

    • Quero qualidade imediata e direção de arte: Midjourney v7.
    • Preciso de texto perfeito na imagem e edição conversacional: GPT-4o image generation.
    • Vivo no ecossistema Adobe e preciso de segurança comercial/fluxo CC: Firefly (Image 3).
    • Sou enterprise e quero compliance, billing unificado e watermarking: Imagen 3 via Vertex AI.
    • Quero controle total, fine-tuning e/ou rodar localmente: Stable Diffusion 3.5.

    Dicas de professor (para tirar mais das suas gerações)

    1. Modele o prompt em blocos: conteúdo (assunto/ação), forma (lente/iluminação/estilo), restrições (proporção, tipografia).
    2. Use referências (quando suportado): adiciona consistência de identidade visual.
    3. Itere rápido, refine devagar: rascunhe em modo rápido (Draft/fast), depois refine com variações e upscales.
    4. Cheque licenças e metadata: políticas variam; salve a procedência das imagens e leia os termos antes de uso comercial.

    Menção honrosa (de olho para 2025/2026)

    FLUX.1 (Black Forest Labs) — modelos de flow matching com pesos abertos; atualizações recentes incluem FLUX.1 [dev] (12B) e variantes como Krea [dev]/Kontext. Bom equilíbrio entre qualidade e liberdade de pipeline.

    Em 2025, não existe “um único campeão” — existe o melhor para o seu contexto. Se você precisa de acabamento premium em minutos, vá de Midjourney v7. Para texto impecável e edição dentro do chat, OpenAI GPT-4o image generation é o atalho. Quem trabalha na Creative Cloud ganha velocidade e segurança com Adobe Firefly. Ambientes enterprise com requisitos de governança tendem a preferir Imagen 3 no Vertex AI. E, para controle absoluto e custo previsível, Stable Diffusion 3.5 continua imbatível.

    Atualizado em 19 de agosto de 2025, com base em anúncios/portais oficiais e documentação das plataformas citadas.