HTTP para programadores Node.js

Você já se perguntou o que acontece quando você digita uma URL no navegador, do início ao fim da requisição?

Neste artigo, eu vou falar não apenas sobre como funciona o HTTP/1.1, mas também como o Node.js trabalha com este importantíssimo protocolo da web. Assim, mesmo que você não programe em Node, este artigo serve de referência para o protocolo HTTP em si. Caso programe em Node, vai te dar uma boa base do módulo http do Node, um dos mais essenciais.

Veremos neste artigo:

  1. O protocolo HTTP
  2. O módulo http do Node.js
  3. Construindo um servidor HTTP em Node
  4. Fazendo requisições HTTP em Node

Vamos lá!

O protocolo HTTP

Se você já fez uma entrevista para uma vaga de programador web provavelmente já lhe perguntaram “o que acontece quando você digita algo na caixa de busca do Google e aperta Enter?”

É uma pergunta muito popular pois os entrevistadores técnicos querem entender se você consegue explicar alguns dos conceitos básicos e se você tem alguma ideia de como a Internet realmente funciona.

Neste tópico eu vou mostrar pra você o que acontece quando você digita um endereço na barra do navegador e aperta Enter. É um tópico muito interessante e útil, pois é uma tecnologia muito presente, que raramente muda e que permite a existência de um complexo e vasto ecossistema que é a Internet.

Então começando a explicação, imagine que você digitou uma URL no navegador e apertou Enter. É aqui que nossa aventura começa…

DNS Lookup

O browser começa o DNS Lookup para obter o endereço IP do servidor. O nome do domínio é um apelido para nós humanos lembrarmos mais facilmente, mas a internet é organizada de maneira que os computadores trabalham apenas com os endereços IP para saber onde estão os computadores em um formato numérico como 222.324.3.1 (IPv4).

O primeiro lugar onde o IP é procurado pelo browser é no cache de DNS da sua máquina. Se não estiver lá, ele vai procurar no arquivos hosts (que varia de local conforme seu SO). Se não encontrar nada lá, a última alternativa é perguntar ao servidor de DNS.

O endereço do servidor de DNS fica salvo nas preferências do sistema, na parte de redes. Dois populares servidores de DNS são:

  • 8.8.8.8: o servidor de DNS do Google;
  • 1.1.1.1: o servidor de DNS da CloudFlare;

O mais comum é que as pessoas usem o servidor de DNS fornecido pelo seu provedor de Internet.

E quando esse servidor de DNS não possui a resposta para a requisição realizada? Aí é que entram os servidores raiz de DNS, 13 pontos de conexão ao redor do mundo que, ao invés de terem todos os endereços do mundo cadastrados, possuem o endereço dos servidores top-level.

Um servidor top-level é aquele responsável por uma extensão TLD (top-level domain) como .com, .br, .org, etc. O servidor top-level para os domínios .br, por exemplo, possui cadastrado todos os endereços de domínios terminados em .br.

Note que neste servidores existem caches. Conforme vão sendo feitas perguntas que eles não conhecem a resposta (i.e. domínios que eles não saibam o IP) eles descobrem onde fica perguntando uns aos outros e depois armazenam em seus caches locais, para que na próxima requisição seja mais rápido dar a resposta.

Mas ok, o processo de DNS lookup descobriu o IP da máquina onde está a URL que você está querendo acessar. Isso porque todos os domínios de Internet devem ser adquiridos junto a vendedores certificados, que possuem Name Servers online 24x7x365 (o ano inteiro, todos os dias, o tempo todo). Quando você compra um domínio, você recebe o endereço dos seus Name Servers, que são sempre mais de um para garantir alta disponibilidade, como por exemplo:

  • ns1.dreamhost.com
  • ns2.dreamhost.com
  • ns3.dreamhost.com

Inicia-se a procura pela máquina que queremos acessar no primeiro NS e somente são utilizados os outros no caso do primeiro não estar online. Mas enfim, nossa jornada de DNS Lookup termina aqui, acabamos de encontrar a máquina onde o site está hospedado.

DNS Lookup
DNS Lookup

Conexão e Requisição

Uma vez que temos o endereço IP da máquina correta, o browser pode se conectar na mesma para podermos enviar as requisições.

A requisição é um documento de texto estruturado de uma maneira específica conforme determinada pelo protocolo de comunicação. Ela é composta de três partes:

  • a linha de requisição
  • o cabeçalho de requisição
  • o corpo de requisição

A linha de requisição, que é obviamente uma única linha, possui:

  • o método HTTP
  • o local do recurso a ser acessado
  • a versão do protocolo

Por exemplo:

O cabeçalho da requisição é composto e pares chave-valor que definem algumas propriedades da requisição. Existem dois deles que são obrigatórios: Host e Connection, enquanto que todos os demais são opcionais.

Exemplo:

O Host indica o nome do domínio que você quer acessar, enquanto que Connection indica se queremos que a conexão seja fechada ou se mantenha aberta após a requisição.

Alguns outros cabeçalhos muito comuns são:

  • Origin
  • Accept
  • Accept-Encoding
  • Cookie
  • Cache-Control
  • Dnt

Mas existem muitos outros. O cabeçalho da requisição termina com uma linha em branco.

Já o corpo da requisição é opcional, não sendo utilizados em requisições GET, por exemplo, mas muito utilizado em requisições POST e alguns outros verbos, além de poder conter dados em vários formatos como JSON.

Como estamos em um cenário de acesso a uma URL, ou seja, um GET, ignoraremos o corpo por enquanto.

A resposta

Uma vez que a requisição é enviada, o servidor processa ela e envia de volta uma resposta. A resposta começa com o código de status e a mensagem de status. Se a requisição foi bem sucedida e retornar um 200, ela começará com um:

Mas as requisições podem ser respondidas com diferentes códigos e status, como estes:

Logo após, a resposta possui uma lista de cabeçalhos HTTP e o corpo da resposta, que no caso de acessar uma página de um site, será um documento HTML.

Analisando o HTML

Uma vez que o navegador obtenha o HTML do corpo da resposta e inicie a sua análise, ele irá repetir o mesmo processo completo (do DNS lookup até receber o arquivo) para cada um dos recursos presentes na página HTML como:

  • arquivos CSS
  • imagens
  • ícones
  • arquivos JS

E aí inicia o processo de renderização da página conforme as regras de todos estes arquivos juntos na estrutura HTML.

É importante entender que, apesar do exemplo ter sido em cima de HTML, que ele é o mesmo para qualquer recurso na web.

O módulo HTTP do Node.js

O módulo http do Node.js fornece funções úteis e classes para construir um servidor HTTP. Ele é um módulo-chave para os recursos de rede do Node.

Ele pode ser facilmente incluído em um módulo Node.js usando:

O objeto http declarado ali possui algumas propriedades e métodos, além e algumas classes.

http.METHODS

Esta propriedade lista todos os métodos HTTP suportados:

http.STATUS_CODES

Esta propriedade lista todos os códigos de status HTTP e sua descrição:

http.createServer()

Retorna uma nova instância da classe http.Server e seu uso é muito simples:

http.request()

Realiza uma requisição HTTP para um servidor, criando uma instância da classe http.ClientRequest.

http.get()

Similar ao http.request(), mas automaticamente define o método HTTP como GET e já finaliza a requisição com req.end() automaticamente.

O módulo HTTP também fornece cinco classes:

  • http.Agent
  • http.ClientRequest
  • http.Server
  • http.ServerResponde
  • http.IncomingMessage

Um objeto http.ClientRequest, por exemplo, é criado quando chamamos http.request() ou http.get(). Quando uma resposta é recebida o evento response é chamado com a resposta, passando uma instância de http.IncomingMessage como argumento.

Os dados retornados por uma resposta pode ser lido de duas maneiras:

  • você pode chamar o método response.read()
  • no event handler response você pode configurar um listener para o evento data, assim você pode receber os dados em formato de stream (bytes)

Já a classe http.Server é comumente instanciada e retornada quando criamos um novo servidor usando http.createServer(). Uma vez que você tenha um objeto server, você pode acessar seus métodos:

  • close() que encerra as atividades do servidor, não aceitando mais novas requisições;
  • listen() que inicia o servidor HTTP e espera novas conexões;

A classe http.ServerResponse é muito utilizada como argumento nos callbacks que tratam a resposta de requisições, a famosa variável ‘res’ presente em muitos callbacks, como abaixo:

É importante sempre salientar que após utilizarmos o objeto res devemos chamar o método end() para fechar a resposta e enviar a mesma para o cliente que fez a requisição.

Objetos res/response possuem alguns métodos para interagir com os cabeçalhos HTTP:

  • getHeadernames() traz a lista de nomes dos header presentes na resposta;
  • getHeaders() traz uma cópia dos headers presentes;
  • setHeader(‘nome’, valor) altera o valor de um header;
  • getHeader(‘nome’) retorna o valor de um header;
  • removeHeader(‘nome’) remove um header;
  • hasHeader(‘nome’) retorna true se a response possui este header;
  • headersSent() retorna true se os headers já foram enviados ao cliente;

Depois de fazer as alterações que desejar nos cabeçalhos do response, você pode enviá-los ao cliente usando response.writeHead(), que aceita o statusCode como o primeiro parâmetro, a mensagem opcional e os cabeçalhos.

Ok, mas nem só de cabeçalhos vive a resposta, certo? Para enviar dados ao cliente no corpo da requisição, você usa write(). Ele vai enviar dados bufferizados para a stream de resposta.

E por fim, a classe http.IncomingMessage é criada nas requisições. Mostrarei ela na prática mais à frente.

Está curtindo o post? Para uma formação ainda mais completa como programador web recomendo meu livro sobre programação web com Node.js clicando no banner abaixo!

Construindo um servidor HTTP em Node

Um servidor web HTTP para fazer um Olá Mundo é algo muito simples e popularmente conhecido na Internet. Você pode copiar o código abaixo e colar em um arquivo de texto com extensão ‘.js’:

Analisando linha por linha, temos:

  • inicialização do objeto HTTP;
  • definição da porta que o servidor irá esperar requisições;
  • criação do servidor e definição do callback que irá tratar cada requisição (objeto http.IncomingMessage);
  • dentro do callback modificamos a resposta (objeto http.ServerResponse) para retornar o status 200 (ok) e definir o cabeçalho como sendo texto e o HTML como sendo apenas ‘Olá Mundo!’ (fechando a resposta também);
  • e por fim, iniciamos a execução do servidor na porta especificada, definindo o callback que vai executar quando o servidor começar a funcionar;

Depois, para executar (considerando que você já tenha instalado o Node.js na sua máquina), basta abrir o terminal de linha de comando, navegar até a pasta onde você salvou o arquivo com ‘cd’ e depois rodar:

Se tudo funcionar, uma mensagem irá aparecer no console indicando que o servidor está funcionando. Se você acessar no navegador o endereço localhost:3000 você receberá como retorno um Olá Mundo muito simples, mas funcional (ignore o problema de encoding, por favor).

HTTP Server
HTTP Server

Fazendo requisições HTTP com Node

Nesta seção veremos como fazer requisições HTTP com Node.js. Na verdade usarei HTTPS nestes exemplos, que é o mesmo protocolo mas operando sobre canal criptografado (TLS/SSL), pra evitar captura de dados por intrusos lendo o canal de comunicação.

GET Request

Fazer uma requisição GET em Node.js é bem simples, como mostra o código abaixo. Fazemos GET requests quando queremos ler uma página da Internet ou obter dados de uma API:

Nele, eu carreguei o módulo https ao invés do http que vínhamos trabalhando até então, defini que o site que vamos acessar é o google.com.br, na página ‘about’, usando o método GET (pois queremos obter as informações desta página) e na porta 443.

A porta 443 é a padrão para HTTPS, enquanto que a 80 é a padrão para HTTP.

Todas estas definições foram armazenadas em uma constante options que será passada para a função https.request que criará um novo objeto de requisição (o ‘req’). O callback desta função será disparado quando o GET terminar. Nesta situação, ele vai imprimir no console os dados retornados.

Além disso, foi determinado um listener (‘on’) para erros, que serão jogados também no console.

Finalizamos e enviamos a requisição através da chamada da função end(). O resultado será um monte de HTML no seu console, nada muito útil, mas funcional.

POST Request

Fazer um POST é igualmente simples, com a diferença de que aqui usaremos o corpo da requisição HTTP, que no GET não é utilizado. Fazemos POST requests quando queremos enviar dados para um servidor (ao invés de receber, como no caso do GET) e o trecho de código abaixo exemplifica isso:

Neste exemplo eu usei os serviços da RequestBin, um site onde você cria uma URL sua para fazer testes de requisições. O resultado da requisição você pode ver abaixo:

Resultado do POST no RequestBin
Resultado do POST no RequestBin

A grande diferença do código do POST vs GET é o objeto data, onde criamos um JSON (uma notação bem popular para envio de dados pela Internet) e enviamos ele usando a função req.write, lá no final do código.

Note que ao criarmos o https.request também definimos um callback para exibir os dados retornados pelo POST. Sim, não é somente o GET que retorna dados, o POST também retorna o conteúdo da resposta, embora seu uso primário seja para enviar dados.

PUT, DELETE, PATCH, etc

Existem muitos outros verbos HTTP, cada um com a sua utilidade. Embora possamos emular qualquer comportamento usando apenas GET e POST, é interessante usar e implementar os verbos adequados para garantir o correto funcionamento dos servidores web.

Outros verbos úteis são:

  • PUT: para atualizar um documento;
  • DELETE: para excluir um documento;
  • PATCH: para atualizar parte de um documento;

A forma de uso é a mesma que foi exemplificada no POST, mudando apenas no objeto options o method utilizado.

E com isso encerramos este nosso extenso artigo/tutorial sobre o protocolo HTTP e sua aplicação no Node através do módulo homônimo. Querendo aprender como utilizar HTTP na prática através de construção de WebAPIs, acesse este post com Mongo, este com MySQL e este com SQL Server.

Ficou com alguma dúvida? Deixe nos comentários terei muito prazer em ajudar!

Curtiu este tutorial? Conheça o meu curso online de Node.js e MongoDB para aprender a fazer aplicações web e APIs incríveis com esta fantástica plataforma. Basta clicar no banner abaixo!

Curso Node.js e MongoDB

15 dicas e truques da linguagem JavaScript

No artigo de hoje vou apresentar a você 15 dicas muito boas da linguagem JavaScript, geralmente atalhos de codificação, mas além disso também recursos da linguagem que poucas pessoas conhecem e algumas ‘manhas’ que podem ser muito úteis para programadores JS em geral mas principalmente aos meus alunos do curso de Node.js e MongoDB.

Atenção: nem todas dicas, especialmente os atalhos de código, vão agradar a todo mundo. A intenção é lhe mostrar possibilidades interessantes, mas saber quando é o melhor momento para usar cada uma delas fica à critério do programador (levando em conta a legibilidade do código, por exemplo).

A lista de dicas é composta por:

  1. Testes de Null ou Undefined
  2. Atribuição de Arrays
  3. Operador Ternário
  4. Declarando variáveis
  5. Auto-atribuição de Variáveis
  6. Uso de RegExp
  7. Atalho do charAt()
  8. Exponenciais base-10
  9. ES6 Template Literals
  10. ES6: Arrow Functions
  11. ES6: Argument Destructuring
  12. ES6: Key-Value Names
  13. ES6: Map
  14. ES6: Filter
  15. ES6: Reduce

#1 – Testes de Null ou Undefined

Uma das coisas que logo aprendemos em JavaScript é que nem tudo é o que parece ser e que existem diversas maneiras de uma variável lhe causar problemas em uma linguagem dinâmica como essa. Um teste muito comum de ser feito é para verificar se uma variável está null ou undefined, ou ainda ‘vazia’, como abaixo:

Um jeito bem mais simples de fazer o mesmo teste seria:

Se não acredita, pode testar!

#2 – Atribuição de Arrays

Então você tem de criar um objeto Array e depois popular ele com seus elementos, certo? Provavelmente seu código irá se parecer com isso:

Que tal fazer a mesma coisa em apenas uma linha?

Bacana, hein!

#3 – Operador Ternário

O famoso ‘if/else em uma linha’, o operador ternário já é um velho conhecido de diversos programadores de linguagens C-like como Java e C#. Pois é, ele existe em JS também e pode facilmente transformar blocos de código como esse:

Nisso aqui:

Entendeu agora porque muita gente chama o ternário de ‘if/else em uma linha’?

Mas será que funciona com chamadas de funções também? Se eu tenho duas functions diferentes e quero chamar uma no caso do if ser verdadeiro e outra no caso do if ser falso, corriqueiramente você faria algo assim:

Mas…segure-se na cadeira, talvez você não goste do que vai ver…você pode fazer a mesma chamada de função usando o ternário:

Wow! Aqui no RS temos uma expressão para quando ficamos espantados com algo que é: “me caiu os butiá do bolso“.

Vale uma menção honrosa também aos ifs que testam se uma variável é verdadeira, onde alguns programadores ainda fazem assim:

Quando podem fazer assim:

Ou a versão negada:

Que pode facilmente ser resumida com:

#4 – Declarando Variáveis

Sim, mesmo a declaração de variáveis possui os seus melindres. Embora isso não seja exatamente um segredo, ainda se vê muito programador fazendo declarações como essa:

Quando podia estar fazendo assim:

#5 – Auto-Atribuição de Variáveis

Também outra dica bem comum, ao menos em meus cursos, para, ao invés de auto atribuir valores em variáveis numéricas desta forma:

Os programadores usem versões mais enxutas, como essas:

E se você não conseguiu entender o terceiro exemplo (*=) considere que x=10 e y=5 para as seguintes expressões, verificando o resultado manualmente no seu caderno:

#6 – Uso de RegExp

Expressões Regulares já são, por si só, uma baita ferramenta para criar códigos mais elegantes e poderosos quando o assunto é análise e validações textual, além de extração de dados no caso de alguns tipos de webcrawlers.

No entanto, em JS muitas vezes os programadores usam o objeto RegExp desta maneira:

Quando poderiam estar usando desta maneira:

Eu sei, não é o código mais legível do mundo, mas em situações em que o tamanho do arquivo JS importa, essa é uma excelente dica de minificação de código.

#7 – Atalho do charAt()

Então você quer pegar um caracter apenas de uma String, em uma posição específica, certo? Aposto que a primeira coisa que lhe vem em mente é usar a função charAt, como abaixo:

Mas veja só, você consegue o mesmo resultado lembrando-se daquela analogia da String ser um Array de chars:

#8 – Exponenciais Base-10

Esta é apenas uma notação mais enxuta para números exponenciais na Base-10 ou os famosos ‘números cheios de zeros’. Para quem é mais chegado em matemática não vai se surpreender muito ao ver um desses, mas um número 10.000 pode ser facilmente substituído em JS por 1e4, ou seja, 1 seguido de 4 zeros, como abaixo:

As dicas a seguir são todas da versão 6 do padrão JavaScript, chamada ECMAScript. Essa é a versão de JavaScript que mais utilizo no meu curso de Node.js e MongoDB, e além de mostrá-las na prática durante o curso, registro aqui para quem ainda não é aluno.

#9 – ES6: Template Literals

Essa funcionalidade semântica é exclusiva da versão ECMAScript 6 ou superior e simplifica bastante a leitura de concatenação de strings em conjunto de variáveis. Por exemplo, a concatenação abaixo:

Essa até é simples e provavelmente você já fez concatenações piores. A partir do ES6, podemos fazer esta concatenação usando template literals:

Note que ${} delimitam que dentro vai uma variável que deve ser concatenada, sendo number a mesma, ok? Note também o uso de crase na string ao invés das tradicionais aspas.

#10 – ES6: Arrow Functions

Arrow Functions são formas abreviadas de declarar funções. Sim, mais formas de fazer a mesma coisa que funciona desde a primeira versão do JavaScript. Por exemplo, abaixo uma função de soma:

Também podemos declarar esta function desta forma:

Mas com Arrow Functions podemos levar essa declaração a outro patamar como abaixo:

E se for usar anonymous functions, fica mais fácil ainda, como nos callbacks do Node! Note que o return ficou implícito nessa declaração também, pois ela tem apenas uma linha, então é óbvio o que ela deve retornar.

#11 – ES6: Argument Destructuring

Essa dica é para aquelas functions que são cheias de parâmetros e você decidiu substituir todos por um objeto. Ou então para aquelas functions que realmente exigem um objeto de configuração por parâmetro.

Até aí nenhum problema, afinal quem nunca passou por isso? O problema é ter que ficar acessando o objeto que foi passado por parâmetro seguido de cada propriedade que queremos ler, certo? Tipo isso:

O recurso de argument destructuring (desestruturação de argumentos) serve justamente pra simplificar isso e ao mesmo tempo ajudar na legibilidade de código, substituindo a declaração anterior por essa:

E para completar, ainda podemos deixar valores default em propriedades do nosso objeto-parâmetro:

Dessa forma, o valor de s será “1”, mas o de t será o default para essa propriedade que será “def2”.

#12 – ES6: Key-Value Names

Um recurso muito viciante é a forma abreviada de se atribuir propriedades para objetos. Imagine que você tem um objeto person que tem uma propriedade name que vai ser atribuída através de uma variável name. Isso ficaria assim:

Enquanto que em ES6 você pode fazer assim:

Ou seja, se sua variável possui o mesmo nome da propriedade, não precisa chamar a mesma, apenas passe a variável. O mesmo vale para múltiplas propriedades:

#13 – ES6: Map

Esse operador é muito bacana, me lembra um dos principais motivos pelos quais eu usava JQuery em meus projetos web e desde o ES6 é um recurso nativo do JavaScript!

Para os exemplos de código, considere o array de objetos a seguir:

Imagine agora que queremos pegar apenas os nomes dos animais para, por exemplo, adicionar em outro array. Normalmente faríamos isso:

Mas com o Map, podemos fazer assim:

Note que o map espera uma function por parâmetro com três argumentos: o primeiro é o objeto atual (como em um foreach), o segundo é o índice da iteração atual e o terceiro é o array inteiro. Obviamente esta função será chamada uma vez para cada objeto no array animais.

#14 – ES6: Filter

E se queremos iterar através do mesmo array de objetos animais da dica anterior, mas desta vez retornando apenas aqueles cujo tamanho seja “pequeno”?

Como faríamos isso com JS regular?

No entanto, usando o operador filter, podemos fazê-lo de uma maneira bem menos verbosa e mais clara:

O filter espera uma function por parâmetro com o argumento que é o objeto da iteração atual (como em um foreach) e ela deve retornar um booleano indicando se este objeto fará parte do array de retorno ou não (true indica que ele passou no teste e fará parte).

#15 – ES6: Reduce

Outro recurso importantísimo dessa geração do ECMAScript, o Reduce permite que façamos agrupamentos e cálculos em cima de coleções de maneira muito fácil e poderosa. Por exemplo, se quisermos somar o peso de todos os animais do nosso array de objetos animais, como faríamos?

Mas com reduce podemos fazer assim:

O reduce espera uma function por parâmetro com os seguintes argumentos:

  • o primeiro é o valor atual da variável acumuladora (ao término de todas iterações, ele conterá o valor final);
  • o segundo argumento é o objeto da iteração atual;
  • o terceiro argumento é o índice da iteração atual;
  • o quarto argumento é o array com todos objetos que serão iterados;

Esta function será executada uma vez sobre cada objeto do array, retornando o valor agregado ao término de sua execução.

E você, tem alguma dica para adicionar à esta lista? Deixe nos comentários!

Se você quer aprender a ser um programador Node.js e MongoDB, não deixe de conhecer o meu curso clicando no banner abaixo!

Curso Node.js e MongoDB
Curso Node.js e MongoDB

Qual a diferença entre um Product Manager e um Product Owner?

A ideia deste artigo é justamente dar um pouco de luz a alguns temas bem polêmicos, especialmente dentro de grandes corporações que estão passando por transformações digitais e ágeis, algo cada vez mais comum hoje em dia por uma questão de sobrevivência.

No modelo tradicional, Product Managers fazem suas pesquisas de mercado e especificam suas Visões de Produto durante alguns meses até que o documento esteja pronto e seja encaminhado para o Escritório de Projetos (PMO) da empresa. O Escritório irá delegar a um Project Manager transformar a visão de produto em um projeto de software (geralmente usando MS Project ou similar) que uma vez que esteja construído será delegado a um time de desenvolvimento da área de TI, que regularmente terá de prestar reports ao Project Manager sobre o andamento do projeto que por sua vez reportará ao Product Manager. Ao término do desenvolvimento, o software será enviado para um Departamento de Qualidade (QA) para os testes e o resto da historia você já conhece, é o bom e velho waterfall. Ou nem tão bom assim…

Waterfall Product Development
Waterfall Product Development

No entanto, quando mudamos este paradigma e passamos a trabalhar com métodos ágeis, em especial o Scrum, o mais famoso deles, esse ciclo em cascata é rompido drasticamente, silos são destruídos e papéis são drasticamente modificados ou novos papéis são introduzidos. É o caso da extinção do Project Manager e o surgimento do Product Owner e do Scrum Master. Mas e o Product Manager? Ele deve também ser extinto nesse modelo de trabalho?

Sim e não.

Mas antes vamos entender o que é Gerenciamento de Produto (Product Management).

Gerenciamento de Produto ou Product Management

A disciplina de Gerenciamento de Produtos data da década de 1930. Gerenciar um produto é sobre fazer análise e pesquisa de mercado, descobrir product/market fit, inteligência competitiva, customer discovery, definição de requisitos de produto, desenvolvimento do dito-cujo (embora na prática isso seja terceirizado a outro setor ou empresa), go to market, estratégia de precificação (pricing), marketing inbound e outbound, treinamentos no uso do mesmo, revisões e até extinção do produto.

Ufa! É muita coisa!

A primeira coisa mais fácil de notar aqui é que não tem como uma única pessoa cuidar de tudo isso sozinha, certo? Sendo assim, se você acha que o Product Manager da sua empresa é um deus, você está errado. Geralmente Product Managers contam com uma equipe para lhes ajudar: profissionais de marketing, designers, desenvolvimento, pesquisadores e analistas.

Note também que o próprio papel de Product Manager só faz sentido em organizações com produtos maduros, que exijam uma governança mais organizada sobre todo o seu ciclo de vida. Ou seja, corporações. Dificilmente você vai ver um Product Manager, que é um orquestrador de todo o ciclo de vida de um produto, em pequenas empresas ou em startups.

E o Product Owner do Scrum, não seria ele um Product Manager com outro nome? Não faria ele as mesmas atividades que um Product Manager mas em uma empresa ágil?

Para responder a esta pergunta, vamos relembrar o conceito do Product Owner.

Product Owner ou Dono do Produto

O foco da criação do papel do PO, lá na década de 1990 pelos criadores do Scrum era tornar o time de desenvolvimento mais próximo do cliente, ou o contrário, tanto faz. O gap de comunicação do dono do problema até os desenvolvedores da solução era enorme, o que mais se via eram projetos entregues que não atendiam às necessidades reais dos usuários.

O PO nasceu como um representante máximo do cliente dentro do time de desenvolvimento, o porta-voz dos usuários do produto, o dono das prioridades e das regras de negócio para o time de desenvolvimento.

Segundo o Scrum Guide, o Product Owner é responsável por maximizar o valor do produto resultante do trabalho do Time de Desenvolvimento. Ou seja, guiar o time para a construção do produto que trará o maior ROI possível. O próprio Scrum Guide deixa claro que os detalhes de como este indivíduo vai conseguir fazer isso varia enormemente de organização para organização.

O Scrum Guide finaliza a curta descrição deste importantíssimo papel se referindo a ele como o ‘manager’ do Product Backlog e que tão somente ele tem a palavra final sobre o mesmo, embora o PO possa ser o representante de um comitê maior que ele.

O foco do PO é o Product Backlog, segundo o Scrum. Talvez o nome mais correto seria Product Backlog Owner, mas soaria muito mal esse nome. O Product Backlog é a ferramenta do PO para maximizar o ROI de um produto desenvolvido por um Time de Desenvolvimento.

Só com essas informações, per se, já é possível notar uma diferença gritante entre os dois papéis, certo?

Product Owner e Product Manager
Product Owner e Product Manager

Product Management é algo mais estratégico, enquanto que Product Ownership é algo mais tático. Pra finalizar, o Product Development seria o operacional (responsabilidade do Development Team). O que responde basicamente a pergunta que iniciou esse artigo: qual a diferença entre Product Manager e Product Owner.

Product Manager ou Product Owner?

Claro, agile nunca foi e nunca será algo preto e branco, algo prescritivo como as metodologias tradicionais.

No passado acreditou-se que os Product Owners substituiriam os Product Managers. E até o fazem em empresas pequenas ou em times de produto que estão recém iniciando (trabalhei muito em startups, nelas o PO conversa direto com o CEO e pronto). Conforme a empresa cresce e seus produtos tornam-se mais e mais complexos e maduros, a necessidade de ter pessoas diferentes cuidando do estratégico e do tático de um produto torna-se indispensável até para garantir a sanidade desses profissionais.

Imagine um produto complexo como uma linha de financiamento bancária. Uma visão limitada poderia acreditar que basta desenvolver este recurso em um aplicativo e está tudo resolvido. No entanto, como eu bem sei pois trabalho em uma instituição bancária, isso envolve desde core banking, APIs, disponibilização em canais (mobile e internet banking, no mínimo), regulamentação do banco central, atendimento aos clientes no SAC, solução comercial para vendas, infraestrutura para suportar treinamento para a galera de backoffice, inclusão no site, conteúdo no blog e muito mais.

Desenvolver o software para este produto é apenas uma parte (obviamente muito importante) desta cadeia completa que envolve o gerenciamento deste produto. É o mesmo que acreditar que Engenharia de Software resume-se apenas a programar sistemas.

Mas o Product Owner não pode cuidar sozinho de todos os aspectos de um produto? E abaixo dele ter uma série de outros profissionais para ajudá-lo a gerenciar o produto como um todo?

Pode. Daí ele acabou de se tornar um Product Manager com outro nome e talvez ele não consiga cuidar da sua principal atribuição que é gerenciar o Product Backlog, maximizando a entrega de valor do time de desenvolvimento.

Note que desde que isso esteja claro e a empresa decida que este é o melhor modelo para se trabalhar, está ok. Apenas atente ao fato de que gerenciar um produto por completo é uma tarefa que exige muito mais senioridade e conhecimento de mercado do que se focar no aspecto tático de como guiar um time de desenvolvimento. Sem obviamente desmerecer esta atividade, mas são skills diferentes que são necessárias.

Mas este assunto, de como escalar e dividir o trabalho dos profissionais de produto de uma empresa é assunto para outro artigo. Para quem quiser se aprofundar recomendo este e este artigo da Ellen Gottesdiener, uma Agile Product Coach, especialista neste assunto.

Gostou do assunto? Quer aprender mais sobre métodos ágeis para construção de produtos? Conheça meu livro Scrum e Métodos Ágeis clicando no banner abaixo!