Olá! Meu nome é Patrícia Silva, e serei a instrutora deste curso sobre debugging, logs estruturados, feature flags e muito mais. Essas são ferramentas poderosas e necessárias para quem escreve e mantém código, além de serem essenciais para ter um ambiente mais previsível e entregar com qualidade e segurança.
Audiodescrição: Patrícia é uma mulher branca, com cabelos cacheados, e usa óculos de aro preto.
Atualmente, atuo como Tech Lead e Fullstack Engineer, com mais de 15 anos de experiência no desenvolvimento de software. Ao longo da minha carreira, já presenciei diversas situações, como bugs que ocorrem apenas em produção, logs que não fornecem informações úteis, ou sistemas que sequer possuem logs. Além disso, já vi deploys que deixam as pessoas em pânico por não saberem como agir, reverter ou entregar uma solução rapidamente.
Por isso, estou aqui para mostrar como entender o que está acontecendo por trás do código e ensinar técnicas para resolução de bugs. Neste curso, vamos trabalhar em um projeto real chamado CodeConnect, uma rede social para pessoas desenvolvedoras. O projeto possui uma área logada, onde é possível criar e registrar um usuário, além de fazer login. Vamos explorar tudo isso juntos, e garanto que será uma experiência emocionante.
Durante este curso, teremos uma experiência enriquecedora ao trabalhar com uma página que exibe todos os posts e o detalhe de cada post. Aprenderemos a utilizar diversas ferramentas de depuração para investigar o comportamento da aplicação. Além disso, criaremos logs estruturados e com contexto, o que proporcionará uma melhor observabilidade do que ocorre em nosso sistema.
Compreenderemos a diferença entre logs técnicos e logs de eventos de negócio. Também abordaremos um problema comum: as limitações de componentes que operam tanto do lado do cliente quanto do servidor, adaptando a estratégia de log para cada um deles.
Implementaremos feature flags (sinalizadores de funcionalidades), uma técnica robusta para ativar, testar e reverter funcionalidades, permitindo realizar deploys de novas funcionalidades com segurança. Caso algo dê errado, poderemos reverter essas mudanças com um clique, sem a necessidade de um novo deploy.
Ao final, perceberemos que debugging não se trata apenas de apagar incêndios, mas de ter controle total sobre nosso sistema e produto, possibilitando tomar decisões com segurança. Para realizar tudo isso, é necessário ter conhecimento em React. Portanto, preparemos nosso ambiente, abramos nosso editor de texto favorito e avancemos juntos, pois elevaremos nosso código e nossa confiança como pessoas desenvolvedoras a outro nível. Nos vemos em breve! [♪]
Nós herdamos este projeto de uma equipe que não poderá mais trabalhar nele. Isso é bastante comum, pois pessoas desenvolvedoras podem sair ou entrar no projeto, ou o projeto pode ser transferido para outros times. Idealmente, quando isso ocorre, é importante que haja um onboarding para entendermos a situação do projeto, quais são os principais desafios, o que já foi resolvido e quais são os pontos de atenção.
Conversei com a equipe anterior e foi mencionado que existem alguns bugs. Bugs são uma palavra-chave que usamos frequentemente para nos referirmos a defeitos no código que precisamos resolver. Ao longo do curso, nosso foco será justamente nos especializarmos em diferentes métodos de depuração, para percebermos como podemos ser mais eficientes nesse processo. Muitas vezes, o código está rodando localmente em nossa máquina, mas às vezes está em um servidor em produção. Dependendo do problema, precisamos ter em nossa caixa de ferramentas as ferramentas de depuração adequadas e saber quando utilizá-las.
Antes de tudo, precisamos entender a topologia do código. Não precisamos ser especialistas em todos os métodos existentes neste projeto; vamos ganhando experiência à medida que trabalhamos com o código. Esse conhecimento e os detalhes do projeto vêm sob demanda, à medida que adquirimos experiência.
Vamos começar. Estamos com o VSCode aberto e já clonamos o projeto. Vamos iniciar pelo package.json, que está na raiz do projeto. Na linha 6, temos os scripts normais de uma aplicação React Next.js. Aqui está um exemplo de como os scripts estão configurados:
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "eslint",
"seed": "node supabase/seed.js"
},
Na linha 7, há o script rundev para subir a aplicação, além dos scripts build, start e lint. Na linha 11, encontramos o CID, o Supabase CID. Esta aplicação possui uma área logada que exige autenticação para acessar os posts, e precisaremos do CID para popular inicialmente os dados dos posts.
No next.config.js, as imagens dos posts estão hospedadas no GitHub User Content, e já está configurado o remotePatterns, permitindo que qualquer caminho do GitHub tenha acesso para ser consumido nesta aplicação. Veja como isso é configurado:
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
// Github Raw Content
{
protocol: "https",
hostname: "raw.githubusercontent.com",
port: "",
pathname: "/**", // Permite qualquer path do GitHub
},
// Github Assets (caso use github.com/user/repo/blob/)
{
protocol: "https",
hostname: "github.com",
port: "",
pathname: "/**",
},
],
},
};
export default nextConfig;
Dentro do diretório "supabase", temos o supabase-schema.sql, que contém as tabelas de usuário, post e comentário que precisaremos criar manualmente. Vamos copiar e colar no Supabase para criar essas tabelas. A tabela User, na linha 4, é onde salvaremos um usuário que é um autor. Esta aplicação é uma prova de conceito, então os posts serão escritos por um único autor.
Aqui está o script SQL para criar as tabelas:
-- Execute este script no SQL Editor do Supabase
-- Criar tabela User
CREATE TABLE "User" (
"id" SERIAL PRIMARY KEY,
"name" TEXT NOT NULL,
"username" TEXT NOT NULL UNIQUE,
"avatar" TEXT NOT NULL
);
-- Criar tabela Post
CREATE TABLE "Post" (
"id" SERIAL PRIMARY KEY,
"cover" TEXT NOT NULL,
"title" TEXT NOT NULL,
"slug" TEXT NOT NULL UNIQUE,
"body" TEXT NOT NULL,
"markdown" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"authorId" INTEGER NOT NULL,
"likes" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- Criar tabela Comment
CREATE TABLE "Comment" (
"id" SERIAL PRIMARY KEY,
"text" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"authorId" INTEGER NOT NULL,
"postId" INTEGER NOT NULL,
"parentId" INTEGER,
CONSTRAINT "Comment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Comment_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Comment"("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- Criar índices para melhor performance
CREATE INDEX "idx_post_author" ON "Post"("authorId");
CREATE INDEX "idx_comment_author" ON "Comment"("authorId");
CREATE INDEX "idx_comment_post" ON "Comment"("postId");
CREATE INDEX "idx_comment_parent" ON "Comment"("parentId");
-- Função para atualizar updatedAt automaticamente
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW."updatedAt" = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
No cid.js, no topo, o CID rodará no escopo do Node. Na linha 4, carregamos as variáveis de ambiente que estarão no .env.local que vamos criar, permitindo que possamos interagir com o Supabase. O autor, como mencionado, na linha 22, é um autor fictício, Ana Beatriz. Ana Beatriz é a única autora, e as imagens estão hospedadas no GitHub User Content. Utilizamos minha conta, mas poderia estar em um AWS S3, Google Cloud Object ou outro bucket. Por questões de facilidade, optamos pelo GitHub Content.
Mais abaixo, temos os mocks dos posts. Os posts terão a imagem de capa, na linha 59, o título, o slug, que é o identificador e parte da URL do detalhe do post, o corpo em markdown, que é um trecho de código. Como esta é uma rede social de pessoas desenvolvedoras, é natural que queiramos mostrar exemplos de código. Na linha 66, temos o authorId, que é o Ana.id, a autora que já salvamos na base de dados.
Vamos para o diretório "src" e dar uma olhada em "app", onde ficam os API routes e o app router. Temos o P.js, que é a página inicial. Na linha 17, a página inicial lista todos os posts.
Temos uma proteção nas linhas 18 ou 27 que redireciona a pessoa para a página de login caso não esteja autenticada. Também há páginas de erro que são exibidas em caso de falhas, além de uma página de registro. O componente de registro está dentro do diretório "components", que contém todos os componentes da aplicação. O register component é a página onde registramos uma conta de usuário para acessar a rede social.
Também temos a post slug page, que permite acessar os detalhes de um post ao clicarmos nele. No topo, podemos ver que é uma página que roda do lado do cliente. Há também a página de login, além de outras páginas como "esqueci minha senha" e páginas de erro. Temos a API route e a API de comentários, que permite responder a comentários, chamados de replies. A API de slug é uma API route para buscar detalhes do post.
Além disso, há um diretório "actions" que contém as server actions, incluindo autenticação, login e logout. Na linha 1, por exemplo, em alphjs, temos a diretiva use server, que indica que o cliente pode chamar componentes que rodam estritamente do lado do servidor. Essa diretiva é oposta ao use client.
Os componentes em "components" são muitos, incluindo uma sidebar. Não precisamos saber todos de cabeça, pois podemos depurar ao longo do tempo. Temos hooks de autenticação para chamar os métodos de sign in e sign out, além de um hook de proteção de rota. Os componentes que rodam do lado do cliente podem chamar um hook que verifica se o usuário está logado. Se estiver, ele pode ver a página; caso contrário, é redirecionado para a página de login.
No diretório "lib", há um arquivo chamado database.js, que é a camada de dados para centralizar todas as consultas do Supabase. Essa camada de dados permite que nossos componentes interajam com o Supabase. Por exemplo, na linha 7, o método getAllPosts lista todos os posts. Na linha 14, as queries são feitas na tabela de posts para trazer todos os posts.
Também temos o método getPostBySlug, útil para obter detalhes do post, e getPostById, que busca um post pelo seu ID na base de dados. Na linha 111, podemos pegar o post cujo ID seja igual ao postId. Na linha 122, o método incrementPostLikes incrementa os likes de um post. Na linha 138, fazemos um update na coluna de likes, incrementando o valor atual em um.
Os comentários podem ser criados na linha 159, com um insert na tabela de comentários. O método createComment e getCommentReplies permitem buscar respostas a um comentário. Na linha 208, o método getOrCreateUser verifica se o usuário existe e retorna os dados ou cria um novo usuário. Na linha 243, há um método para buscar o usuário pelo nome de usuário, além de utilitários para contar a quantidade de posts e a instância do database.
Temos componentes de servidor, componentes do cliente e uma camada de dados que busca informações no Supabase. Com esse conhecimento básico, podemos começar a trabalhar na aplicação, configurar o Supabase e ver o que nos aguarda. Nos vemos na sequência! Até já!
Dando continuidade ao projeto, vamos configurar o Supabase. Ele precisa existir neste momento para que possamos subir a aplicação, rodá-la e verificar seu estado atual. Esta etapa é manual, não há nenhum script que a automatize.
Com o navegador aberto, acessamos o site supabase.com. Caso não estejamos logados, a tela de login será exibida. Podemos fazer login com a conta do GitHub ou criar uma nova conta, se necessário. O importante é ter uma conta e realizar o login. No canto superior direito, encontramos o dashboard, que exibirá nossas organizações. A organização é uma forma de organizar os projetos no Supabase, podendo conter mais de um projeto. Já temos uma organização criada, chamada GSS Patrícia Org. Podemos criar uma nova organização no canto direito e, dentro dela, criar nosso projeto Supabase. Como já temos a GSS Patrícia criada, utilizaremos essa. Ao clicar nela, são exibidos os projetos existentes. Temos dois projetos de teste e vamos criar um novo projeto chamado CodeConnect, definindo uma senha.
Podemos escolher a região do servidor desejada, mas já há uma recomendada, Américas. Escolheremos essa por ser uma prova de conceito. Após clicar em "Create New Project", o processo pode demorar um pouco, dependendo da carga dos servidores. Uma vez criado, o projeto CodeConnect aparece com zero tabelas, zero funções e zero réplicas, indicando que está rodando.
Em seguida, rolamos a página para baixo para obter os detalhes da API. Precisaremos do Project URL e da API Key para criar a autenticação entre nosso projeto e o projeto específico do Supabase. Voltamos ao VSCode e, na raiz do projeto, salvamos essas variáveis de ambiente no arquivo .env.local. Criamos duas variáveis de ambiente: a primeira é o NEXT_PUBLIC_SUPABASE_URL, onde colamos o Project URL copiado.
NEXT_PUBLIC_SUPABASE_URL=https://jtbvciaekicdtpgfeyez.supabase.co
A segunda variável é o NEXT_PUBLIC_SUPABASE_ANON_KEY, onde colamos a API Key pública, chamada de AnonPublic.
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imp0YnZjaWFla2ljZHRwZ2ZleWV6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTc2MTYwMzksImV4cCI6MjAxMzE5MjAzOX0.Y2Q1ODIwNzYtNDI2Zi00ZDYyLWI5YmYtZjk2NTQ4NDY1ZjFk
Dessa forma, já apontamos qual é o projeto do Supabase e fornecemos a API Key pública. No diretório "Supabase", abrimos o arquivo supabaseSchema.sql e copiamos todo o script SQL que será rodado manualmente no dashboard do banco de dados. Voltamos ao Supabase, onde, no lado esquerdo, há a opção SQL Editor. Clicamos nela para abrir o editor e colamos o script.
-- Execute este script no SQL Editor do Supabase
-- Criar tabela User
CREATE TABLE "User" (
"id" SERIAL PRIMARY KEY,
"name" TEXT NOT NULL,
"username" TEXT NOT NULL UNIQUE,
"avatar" TEXT NOT NULL
);
-- Criar tabela Post
CREATE TABLE "Post" (
"id" SERIAL PRIMARY KEY,
"cover" TEXT NOT NULL,
"title" TEXT NOT NULL,
"slug" TEXT NOT NULL UNIQUE,
"body" TEXT NOT NULL,
"markdown" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"authorId" INTEGER NOT NULL,
"likes" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- Criar tabela Comment
CREATE TABLE "Comment" (
"id" SERIAL PRIMARY KEY,
"text" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"authorId" INTEGER NOT NULL,
"postId" INTEGER NOT NULL,
"parentId" INTEGER,
CONSTRAINT "Comment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Comment_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Comment"("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- Índices para otimização de consultas
CREATE INDEX "idx_comment_author" ON "Comment"("authorId");
CREATE INDEX "idx_comment_post" ON "Comment"("postId");
CREATE INDEX "idx_comment_parent" ON "Comment"("parentId");
-- Função para atualizar updatedAt automaticamente
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW."updatedAt" = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
-- Triggers para atualizar updatedAt automaticamente
CREATE TRIGGER update_post_updated_at BEFORE UPDATE ON "Post"
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_comment_updated_at BEFORE UPDATE ON "Comment"
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
Colei o script e, logo abaixo, do lado direito, há o botão Run. Basta clicar em Run; ele demora um pouco, mas executa. A saída do comando indica que foi um sucesso. Ótimo! Vamos conferir. No lado esquerdo, existem outras opções, como Database. Vamos acessar Database para verificar se o esquema foi realmente gerado e aplicado. Estamos em uma visualização de esquema que mostra as tabelas criadas e relacionadas. Também podemos, do lado esquerdo, logo abaixo de Schema Visualizer, clicar em Tables. Assim, conseguimos ver as tabelas. Às vezes, o Supabase apresenta alguns erros, como o que apareceu aqui: falha ao criar o título, ignore.
Temos três tabelas: Comment, Post e User. São as tabelas mencionadas anteriormente. Vamos voltar para o VSCode porque precisamos executar o seed no package.json. Vou abrir o package.json, que está na raiz do projeto. Na linha 11, temos o script seed.
"seed": "node supabase/seed.js"
Então, vamos executar um yarn run seed para aplicar e popular as tabelas que acabamos de criar.
yarn run seed
Ocorreu um erro, pois as variáveis de ambiente do Supabase não foram encontradas. Certifique-se de ter o PUBLIC_SUPABASE_REL e o SUPABASE_SERVICE_ROLE. Não criamos o SUPABASE_SERVICE_ROLE. Vou copiar e colar essa variável no .env.local, pois é necessária. Note que ela está sem o prefixo NEXT_PUBLIC, pois não é para ser injetada no lado do cliente. Essa variável de ambiente só existirá no lado do servidor.
Vou voltar para o Supabase e pegar o ServiceRole. Basicamente, ele é um bypass que nos permite ser administradores e realizar inserções, atualizações, etc., nas entidades do Supabase. Para obtê-lo, precisamos acessar a engrenagem, Project Settings. No Project Settings, no menu esquerdo, há várias opções, incluindo API Keys. Clicamos em API Keys e aparecerá o ServiceRole. Também há o AnonPublic, caso tenha sido esquecido ou não colocado no momento da criação. Nas tabelas, encontramos as API Keys: a pública, que é o AnonPublic, e a secreta, que é o ServiceRole. Vou clicar em Reveal e copiar o ServiceRole. Vou voltar para o VSCode e colar essa variável de ambiente.
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imp0YnZjaWFla2ljZHRwZ2ZleWV6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTY5NzYxNjAzOSwiZXhwIjoyMDEzMTkyMDM5fQ.Y2Q1ODIwNzYtNDI2Zi00ZDYyLWI5YmYtZjk2NTQ4NDY1ZjFk
Ótimo. Vamos rodar novamente no terminal o yarn run seed, e funcionou, a conexão foi estabelecida. Era necessário apenas configurar essas três variáveis de ambiente. Sucesso, post criado.
Vou voltar para o Supabase novamente, pois quero verificar se as tabelas foram realmente populadas. No menu esquerdo, há a opção Database, e vou em Tables. Em Tables, conseguimos ver quantas linhas foram inseridas. Por exemplo, na tabela Comment há zero, em Post há 12, e em User há 1, que é o usuário Ana Beatriz. Se clicarmos nos três pontinhos em View in Table Editor, conseguimos ver os dados populados desse post. Ótimo, está populado, é isso que precisamos. Agora, conseguimos rodar a aplicação e acessar a primeira página.
O curso React: implementado técnicas modernas de Debugging e Release com Next.js possui 259 minutos de vídeos, em um total de 44 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
2 anos de Alura
Matricule-se no plano PLUS 24 e garanta:
Jornada de estudos progressiva que te guia desde os fundamentos até a atuação prática. Você acompanha sua evolução, entende os próximos passos e se aprofunda nos conteúdos com quem é referência no mercado.
Mobile, Programação, Front-end, DevOps, UX & Design, Marketing Digital, Data Science, Inovação & Gestão, Inteligência Artificial
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você participa de eventos exclusivos, pode tirar dúvidas em estudos colaborativos e ainda conta com mentorias em grupo com especialistas de diversas áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
2 anos de Alura
Todos os benefícios do PLUS 24 e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Para estudantes ultra comprometidos atingirem seu objetivo mais rápido.
2 anos de Alura
Todos os benefícios do PRO 24 e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.
Conecte-se ao mercado com mentoria individual personalizada, vagas exclusivas e networking estratégico que impulsionam sua carreira tech para o próximo nível.