Node.js: serious business - Pedro Franceschi - RuPy Brazil 2013

In this RuPy Brazil 2013 talk, Pedro Franceschi shares the story of building a high‑volume payment platform (Pagar.me) with Node.js. He starts by describing the market gap in Brazil for a developer‑friendly payment API and the technical requirements of the service: high I/O, low CPU, strict security, uptime, and scalability. He explains why Node.js was chosen over traditional thread‑based servers, detailing the event‑loop, V8, and libuv stack that enable non‑blocking I/O. He contrasts Node’s strengths with its drawbacks—callback hell, JavaScript quirks, single‑process limits, and the need for robust error handling. The talk then moves to practical engineering decisions: using modules, async libraries, Mocha testing, and a Google style guide; a hybrid data‑store architecture (MySQL for relational transaction data, MongoDB for non‑relational client data, Elasticsearch for fast indexing and search); and a deployment pipeline built around Git, Strider CI, and PM2 for zero‑downtime reloads and clustering. Monitoring and alerting are covered with Pingdom, PagerDuty, Librato dashboards, and public status pages. He concludes with lessons learned: Node.js excels at I/O‑bound workloads but is not a silver bullet, and disciplined coding, testing, and observability are essential for production success.
Chapters
Bom, bom dia e já boa tarde. Bom, estou aqui para falar um pouquinho sobre Node.js Serious Business, ou seja, usando em condições reais de pressão de cliente, de ambiente de produção e tendo que escalar, tendo que funcionar muito bem. Porque, bom, a gente vê muita gente falando de Node, só que assim, eu conheço pouca gente que tem experiência em produção e assim, quando a gente resolveu usar Node nesse nosso use case, a gente acabou não tendo contato com pessoas que passaram pelos problemas que a gente passou. Então acho que é legal passar um pouquinho disso porque foi uma experiência bem legal. Bom, só para, antes de começar, quem aqui usa Node em produção? Tá umas 4, 5 pessoas, beleza. Então, vamos lá, os assuntos que eu vou abordar aqui só para a gente poder ter mais ou menos uma ideia do que eu vou falar. Primeiro problema que a gente tinha e como é que a gente enfrentou esse problema por que que a gente começou a pensar em Node para resolver ele. Segundo, por que e quando usar o Node, porque isso é uma coisa que a gente vai falar, que eu vou falar mais para frente, que Node é muito legal, mas ele não resolve todos os problemas. Isso é uma coisa que não fica muito clara. E os problemas de Node. Node é muito legal, só que ele também tem problemas. Como fazer Node do jeito certo, porque assim, muita gente acaba assimilando JavaScript de browser com JavaScript de servidor, e são coisas que, embora seja JavaScript, é uma coisa bem diferente, um é back-end, outro é front-end, obviamente. E depois vou falar um pouquinho sobre a nossa infraestrutura que a gente montou por trás da nossa aplicação em Node e como a gente fez isso, a gente repensou uma infraestrutura de um jeito um pouco diferente do tradicional e que acabou funcionando muito bem também pra gente ligar na nossa aplicação Node. Também vou falar um pouco de deployment, integração contínua e monitoramento, que é uma coisa que eu conheço pouca gente falando sobre Node, porque é um assunto que realmente as pessoas falam muito sobre como começar no Node, por que usar o Node, mas como rodar ele, monitorar, que é uma coisa que em Rails é muito bem coberta, em Python também, você acaba que não tem isso muito bem documentado em Node. Eu queria também falar um pouquinho sobre o que a gente faz. E depois fechar com umas conclusões aqui sobre o que a gente vai falar. Bom, vamos falar sobre o problema que a gente tinha. Bom, no início desse ano, eu e um amigo meu, a gente viu que tinha um problema no mercado, que era o seguinte: não existia um meio de pagamento que fosse amigável para desenvolvedores e empreendedores. Então, quem queria receber pagamentos online não tinha uma forma fácil de fazer isso. Nos Estados Unidos tem o Stripe, Na Europa tem o Braintree e no Brasil não tinha nada. E a gente viu que isso era realmente uma coisa, tinha um gap de mercado e a gente resolveu atacar esse problema porque o problema do Brasil é que as pessoas que fazem serviços de pagamento não são pessoas de produto, são pessoas da área financeira que botam lá, bota um programador aí que ele vai fazer e acaba que o produto é muito ruim. E aí a gente viu que tinha esse problema, a gente começou a atacar, começou a ver o que a gente ia ter que fazer. E aí surgiu o Pagar.me, que é uma startup que a gente já está operando, a gente já recebeu investimento, a gente já está mais ou menos grande. E enfim, bom, mas isso aí é só para a gente ter um background de onde a gente usou Node, que foi num serviço de pagamento. E bom, e aí a gente começou a pensar: o que a gente vai usar para a gente montar esse cenário daqui? O cenário é o seguinte: a gente tem um monte de bibliotecas embaixo, que os clientes usam para, no serviço deles, na aplicação deles, fazer a comunicação com o serviço de pagamentos, que é o Pagar.me, e a gente queria centralizar tudo numa API RESTful, assim, a gente queria e não ter, por exemplo, nosso dashboard sem AngularJS, de forma que a gente não tivesse que manter em produção mais de uma aplicação backend. Então a gente resolveu centralizar tudo numa API e construir tudo embaixo dela. E essa API a gente começou a se perguntar: quais são as premissas que a gente tem nela? O que a gente vai ter que fazer nela? E em que a gente vai fazer ela? Que foi o problema que a gente teve. Mas, bom, antes da gente chegar aí, vamos ver aí o que a gente achou de premissas que a gente tinha que cumprir nessa API. Primeiro é ser simples, porque nenhuma é simples no mercado de pagamentos. Então, você ser simples era uma premissa que a gente tem. Não só em API, mas em design, mas a gente queria ter isso em tudo quanto é parte do projeto, inclusive na forma com que você integrasse com a gente. Então, não ter XML, só ter JSON, ser tudo RESTful e tudo só funcionar. Você não ter que adivinhar um método, você poder simplesmente intuir por ser RESTful. Segunda coisa, a gente ter um ambiente de testes isolados e decente, porque os serviços de pagamento hoje no Brasil, eles não têm ambiente de teste que você consiga testar igual ao ambiente de produção. Então a gente, quando a gente teve experiências com outros sistemas de pagamento anteriormente, não tem o sistema de sandbox deles, não é igual ao ambiente de produção. Então o código que você faz em sandbox não funciona em produção. Então isso não faz o menor sentido, assim, a gente queria resolver esse problema também. Então essa era uma premissa que a nossa API tinha que suportar. O terceiro é que assim, você está transacionando dados de cartão de crédito, você está transacionando dinheiro, E são coisas que segurança e privacidade são coisas que preocupam todo mundo e sem contar o dinheiro, obviamente. Então segurança sem comprometer a simplicidade que a gente tem como premissa número 1. E uptime, porque ninguém gosta de não receber dinheiro, então se você tem o seu sistema de pagamento não funcionando, as pessoas, os clientes vão ficar muito putos e com razão, então isso não pode acontecer. Escalabilidade, porque a gente quer, obviamente, crescer. E aí a gente chegou na decisão de usar Node.js no nosso cenário. E aí, assim, isso foi depois de muita conversa, depois de muita análise do nosso problema real e de entender exatamente qual que é o use case do Node. E, bom, e antes, assim, eu só vou deixar uma coisa clara aqui porque, assim, eu gosto muito de Ruby. Então, Para escolher usar Node não foi uma decisão de: "Ah, a gente está usando Node porque a gente gosta de JavaScript." A gente escolheu usar o Node, foi uma decisão muito do projeto, porque ele resolve muito bem o nosso problema. Então não foi: "Ah, a gente vai usar porque todo mundo está usando." O nosso ponto não foi esse. O nosso ponto foi: a ferramenta certa para resolver o problema é Node, então não tem porquê, mesmo a gente gostando de Ruby, de não usar Node. Foi mais ou menos por aí. E bom, pra gente entender o nosso cenário por trás, o cenário que a gente tem hoje, o que acontece na nossa aplicação? A gente tem um request de transação que conecta na nossa PayRestful, que vai num sistema antifraude, porque como vocês sabem, no Brasil transação de cartão de crédito e fraude é uma coisa que é praticamente sinônimo. E se a transação for legítima, ela vai no adquirente, na Cielo, na RedeCar, na operadora de cartão, e aí o sucesso ou erro volta pra PayRestful, que volta pro request de transação. Só que isso aqui é um ambiente que a gente tem, é o que a gente faz basicamente. Só que esses dois requests aqui, o sistema antifraude, ele roda um processamento muito, ele é terceirizado, é um request em um ambiente externo de um parceiro nosso que ele demora muito tempo. E sem contar que a transação de cartão de crédito, aqui você tem toda a cadeia de pagamentos por trás do adquirente. Então você tem que no banco para ver se o cliente tem saldo suficiente. Então tem um monte de coisas que são feitas por trás aqui pela Cielo, pela Redcar, pela Adquirente que demoram muito tempo. Então esses requests aqui demoram muito tempo. E bom, e quando você tem aqui no nosso cenário, você tem requests externos demorados, que mais de 1 segundo numa requisição é um tempo bastante considerável. Você tem I/O intenso em banco, porque quando você recebe uma transação você tem que gravar no banco, então a gente tem muito write no banco e acaba tendo muito read por causa de monitoramento, porque os nossos clientes usam o nosso sistema de monitoramento que a gente expõe totalmente para o cliente, então a gente tem I/O no banco muito intensa. E o Node se pareceu uma boa solução porque ele é totalmente assíncrono, ele é single-threaded, ele é event-based, e isso eu vou falar um pouquinho mais daqui pra frente, mas só pra gente começar a ter essa ideia. E uma outra coisa que foi muito importante na nossa decisão de usar Node é que a nossa aplicação não faz processamento. A gente basicamente pega uma requisição daqui, joga para outro lugar, interpreta o que volta, faz alguma operação muito simples aqui no meio, e a gente não tem blocking de CPU. A gente não trava fazendo algum processamento, tipo, redimensionando imagem, a gente não faz nada disso. A gente pega uma requisição que vem de um lugar e joga para outro. E aí quando você tem um problema que claramente você precisa de alguma coisa assíncrona, o que todo mundo fala é basicamente o seguinte: "Ah, mas você pode usar thread." E uma coisa que é muito diferente de thread é I/O assíncrona. E é bom deixar isso bem claro porque thread e I/O assíncrona são coisas bem diferentes e o Node resolve muito bem o problema do I/O assíncrono e foi por isso que a gente resolveu usar ele. Bom, imagina o seguinte cenário: você tem 1000 requests por segundo com uma conexão a uma API externa e I/O no banco. Então você tem 1000 threads por segundo sendo criadas. Isso aí é o que acontece, por exemplo, no Apache. O Apache chega uma requisição, ele lança uma thread ou ele pode lançar um processo, que é pior ainda, que consome muito mais. Então se cada request leva em média 10 segundos, que é o nosso cenário, em 9 segundos você tem 9000 threads. Você tem ramping de thread, chegando no final a primeira thread não terminou ainda, então Você acaba tendo 9.000 threads. E isso não escala muito, sim. Se alguém for rodar com isso, escala? Eu acho que não. Mas bom. E aí, o que as pessoas fazem? Fazem fila, só que fila é uma coisa que é chata de monitorar, você deixa sua aplicação complicada e acaba que no final você tem um monte de servidor que nada faz nada. Tudo passa por uma fila e a fila fica sobrecarregada, então você tem que escalar o servidor de fila. Então você acaba tendo um cenário que fila não resolve muito bem o problema. Se você não tem uma coisa ultra complexa. Então, como é que isso fica em Node? Você tem 1000 requests no banco, na sua API, com I/O num banco, você tem uma thread, porque Node é uma thread só. Se cada request leva 10 segundos, você continua tendo uma thread e isso escala, porque o Node usa o event loop, que eu vou falar agora. Bom, vamos comparar os 3 tipos de I/O que você tem. Você tem a I/O síncrona, que basicamente é o seguinte: você tem a sua aplicação e tem o sistema operacional. Você quer fazer I/O, você quer fazer alguma operação bloqueante, você quer ler um arquivo, você quer conectar num banco de dados, você quer conectar numa API externa, você quer fazer qualquer coisa que você tem que fazer uma operação de I/O, então você vai chamar o sistema operacional, só que enquanto essa chamada está sendo feita e o sistema operacional não responde, a sua aplicação está travada. E aí o sistema operacional uma hora responde, Você tem um retorno e se você quer fazer isso várias vezes, você simplesmente replica essa operação uma depois da outra. E aí você tem também a I/O assíncrona, entre aspas, com thread, porque não é assíncrona. Que você basicamente passa o bloqueio do I/O que está fazendo para uma thread. Então a thread fica bloqueada enquanto você está fazendo a operação do sistema operacional e aí depois que essa operação volta, a thread fica livre e aí a sua aplicação sempre está livre. Só que esse é aquele cenário que a gente estava falando. Isso não escala porque quando você tem muita thread no mesmo processo e você usa uma thread por requisição, você começa no final do dia, você não está escalando, você está criando thread. E bom, e o que o Node faz nesse cenário aqui? Como é que o Node atua? Não só o Node, mas também o Event Machine do Ruby, mas assim, vamos assim explicar também porque Por que é melhor você acabar usando o Node se você tem esse cenário do que usar alguma outra solução que não é em uma plataforma totalmente assíncrona igual é no Node? O Node faz o seguinte: o Node é essa stack daqui. Você tem um sistema operacional, não, vamos começar de cima, melhor. Você tem JavaScript rodando, você tem o seu event loop lá, que você chama as funções, que você tem os callbacks, você tem o v8 que é a máquina de JavaScript do Google, E o V8 é a mesma máquina de JavaScript que o Chrome usa e a vantagem do V8 é que ele compila JavaScript para Assembly direto. Então ele é muito rápido, ele faz isso on the fly. Então você tem uma operação muito rápida que você tem em JavaScript, ela roda muito rápido e é porque isso é automaticamente convertido lá do JavaScript direto para Assembly. Então ele é bem rápido. Você tem a libuv, que a libuv era parte do Node, Só que o que aconteceu? Quando o Node foi ficando mais complexo e tendo que abordar mais sistemas operacionais, a equipe do Node, o core team do Node, resolveu tirar a libuv do Node e colocar ela em uma stack separada. E a libuv, o que ela faz é controlar toda a operação assíncrona no sistema operacional. Então a libuv é o que permite que o Node seja usado no Windows, no Mac, em algum Unix diferente, porque o Windows, por exemplo, usa o WinSocket, no Mac você usa Socket, igual no UNIX, então você tem diversos cenários em que a I/O no sistema operacional é feita de forma diferente. E o motivo de computação ser feita com I/O síncrona é porque você tem código que tem que rodar e você tem formas diferentes de fazer I/O no sistema operacional. A forma síncrona é bem parecida, a forma assíncrona é bem diferente. Libuv unifica isso. Então quando você quer fazer I/O do JavaScript, Você passa pelo V8 que compila isso para Assembly, que chama a libuv, que faz operação assíncrona no OS, e aí aqui nesse meio tempo você tem operações assíncronas em nível de OS. Então você não tem nada travando nada, o sistema operacional está cuidando do assincronismo para você. E aí você tem a volta disso tudo até você chegar no seu callback, e isso por ser assíncrono, enquanto a operação V está sendo processada você pode lançar a laranja porque o sistema operacional vai cuidar de manter o assincronismo dessas tarefas e de executar elas e retornar elas para ele ver que vai retornar para toda a stack em cima. E bom, o que a gente quer, o que eu quero deixar bem claro aqui é o seguinte: Node escala em I/O bloqueante. Node escala quando você tem um problema que você vai travar, mas você não está travando para processar alguma coisa, você está esperando alguém responder alguma coisa, ou banco, ou alguma API externa, ou qualquer coisa que seja externa a você, que você mande uma requisição e a CPU fica em waiting I/O. Esse cenário que o Node é bom. E thread é bom em uma coisa: processamento paralelo. Então assim, é muito bom diversificar isso porque assim, thread, quando você lança thread para fazer uma operação assíncrona de I/O, a thread não está processando nada, a thread está só esperando. E nesse cenário é que a thread é muito ruim. Quando você quer processar duas coisas ao mesmo tempo, processamento paralelo em si, thread é muito bom, mas não é o nosso caso. Então assim, se a I/O bloqueante não for um problema, Node não tem tanta vantagem. Então assim, por que a gente resolveu usar Node? Porque a gente é uma— a nossa operação é totalmente com I/O bloqueante. A gente faz I/O bloqueante em banco, a gente faz I/O bloqueante no adquirente de cartão de crédito, no sistema de fraude. Então tudo isso contribuiu para a gente ter muita I/O bloqueante na aplicação e obviamente só escalar se a gente fizesse alguma coisa usando algo tipo Node. O que é bom deixar claro também, obviamente, é que Node não é bala de prata. Node não resolve todos os problemas. Quando as pessoas falam de Node, a pessoa acha que agora o cara usa uma máquina na AWS, a ultra-large lá, que tem 8 cores de CPU, que tem 32 GB de RAM, o cara roda o PHP, aí o cara muda para Node, que ele pode rodar numa micro. Não é assim que funciona. Tem seus limites e tem seus edge cases. Então, assim, não é porque todo mundo está usando que necessariamente é a melhor coisa para o meu problema, para minha aplicação. Então é bom deixar isso bem claro porque Node não resolve todos os problemas, resolve alguns que são os que a gente tinha. Bom, falar agora um pouquinho dos problemas do Node, porque ele é ótimo, mas obviamente ele tem problemas. Bom, o primeiro problema que todo mundo imagina de Node é código assíncrono, que você tem todos os problemas que código assíncrono tem, porque JavaScript é assíncrono, Node é puramente assíncrono. Então você tem race conditions, coisas que terminam antes de outra, e que você tem que lidar com qual termina antes e qual que chama o primeiro callback. Então, você tem situações que são bem chatas de lidar com isso. Por exemplo, callback hell, que é isso daqui, que você vai fazendo esse código lateral para a direita assim, infinito, e que obviamente não é legível, não é mantível, então é muito ruim de manter dentro da ação, etc. Testes assíncronos também, porque escrever teste assíncrono é uma coisa muito chata e muita gente acaba não escrevendo o teste e aí tendo uma aplicação em Node com bug porque é assíncrono, é chato de fazer teste. Então, vou falar de teste um pouquinho mais para frente, mas isso aqui é um ótimo exemplo do que não fazer. Bom, outro problema é que JavaScript é uma linguagem bizarra. Vocês podem falar o que vocês quiserem falar, mas JavaScript é bizarro. A prova disso é que tem um site chamado whatthefuckjs.com que é só bizarrices de JavaScript. E assim, isso aqui são alguns exemplos que, na minha opinião, são meio bizarros. E outra coisa que JavaScript não tem é uma forma clara de seguir orientação a objetos. Não é que não seja orientado a objetos, é que as pessoas não seguem quando programam em JavaScript. Então isso é uma coisa bem fácil de você não seguir padrões em JavaScript. Então, por exemplo, em Ruby você vai criar classes, você não consegue fazer uma aplicação funcional em Rails, por exemplo, se você não usa uma estrutura minimamente orientada a objetos. Em Node você consegue, e isso é perigoso. Bom, outro problema é que uma exception não tratada mata o processo, então se você tem, por exemplo, esse código aqui que pega essa string, divide em dois nomes de acordo com as palavras e dá o output nela, ela funciona muito bem, só que se você muda o código, você tem o processo morreu aqui. Então, assim, isso é uma coisa bem perigosa em produção, então manter o processo rodando em Node é bem importante. Obviamente para sua aplicação não cair, mas você tem que monitorar isso. Fora isso, você tem que— Node é single thread, então assim, você tem que escalar horizontalmente e verticalmente ao mesmo tempo. Tem uma ferramenta boa para escalar verticalmente que eu vou falar no final, chamada PM2, mas você tem que, se você quer ter o uso de multi-thread em Node, obviamente de multi-core da CPU no seu servidor, você tem que lançar dois processos do Node, porque obviamente vai ter uma thread só, a CPU vai estar ocupada com uma thread, então ela só vai fazer processamento paralelo em um processo. Outro problema é leaks de memória, mas que são basicamente, basicamente, não sempre, devido a código mal feito. Então, uma variável que é globalizada, que você acaba perdendo o escopo dela e o garbage collector não tem como coletar, closures mal feitos que você acaba, que você perde quem que é a referência da variável. Então, esses cenários assim, você pode ter memory leak em Node bem fácil e pouca gente se preocupa, mas isso é uma coisa que em produção pode virar uma bola de neve. E outro que é um problema que pouca gente percebe, mas que é um grande problema, principalmente quando você está contratando gente para mexer em Node, é que tem muito programador de front-end mexendo em back-end, e isso é bem perigoso. Afinal, é tudo JavaScript, né? Só que o ponto não é esse, o ponto é que Node é uma tecnologia de back-end. Você botar alguém de front-end que não conhece Unix, não conhece deployment, não conhece operação de uma aplicação. Isso é bem perigoso e assim, a gente já viu isso dar errado. Então assim, é um bom alerta porque saber JavaScript, saber jQuery, saber mexer em Angular não é a mesma coisa que saber Node. Node roda no servidor, um roda no cliente. E outro ponto de Node também que é um problema é que assim, embora ele exista há 4 anos, eu conheci Node em 2010, Mas embora ele exista há 4 anos, assim, desde o início ele sempre foi bem estável, mas ele é beta ainda, tá? Na versão 0.10.22, mas ele ainda é beta. Então, assim, obviamente não tem bugs atualmente porque tem muita gente usando, mas não é totalmente perfeito, assim. Tem situações muito anormais que você pode encontrar um bug porque é beta. E isso é bem claro para todo mundo que usa. Bom, e agora falar um pouco do que a gente aprendeu fazendo Node do jeito certo. Primeira coisa é você usar módulos, que é a solução do Angular para você fazer uma coisa próxima de orientação a objetos. Você consegue separar o código em controllers, em models, em tudo isso usando os módulos do Angular— do Angular, desculpa, do Node. Então, assim, é uma interface bem fácil de usar que tem gente que não usa ainda e assim, já é uma coisa bem disseminada, mas que Eu acho que é bom falar porque tem muita gente que acaba não usando ou não conhecendo ou fazendo orientação a objetos com prototype igual no browser. E o Node funciona muito melhor com módulos porque essa operação daqui em módulos, na verdade, ela usa o core do Node, uma biblioteca chamada util, que na verdade ela faz essa operação junto com o V8. Então é um objeto que ele é organizado junto com o interpretador JavaScript. Então ele funciona bem melhor. Outra coisa é evitar o callback hell que eu falei, que obviamente isso aqui não é legal, é você usar o async, que é uma biblioteca que você consegue rodar em paralelo as 3 operações que você quer fazer no banco, ou se você mudar aqui para sequencial, ele faz as operações sequencialmente, em série, e no final ele chama um callback com o resultado de cada operação, então isso obviamente é bem melhor do que isso aqui. E outra coisa, teste porque JavaScript quebra. Assim, eu já mostrei aquele exemplo do exception, mas assim, JavaScript realmente quebra e não ter teste, principalmente em uma aplicação RESTful, você não ter teste BDD testando todos os métodos end-to-end, você vai ter problemas com certeza absoluta. Então é bom, muito bom ter teste. E assim, a gente usa o Mocha e o Vue.js Eles fazem com que você manter os testes, escrever eles sempre seja uma coisa mais fácil. E isso é muito importante, porque escrever teste é uma coisa já um pouco assim chatinha de fazer, mas todo mundo sabe que tem que fazer, principalmente em Node. E teste assíncrono é mais chato ainda, porque assim, eles têm problemas de race condition em teste que são muito chatos de resolver. E o que o Mocha faz é ele expor um callback para você poder chamar depois com uma ação antes do teste tem que rodar. Então você consegue rodar uma coisa assíncrona e depois chamar o seu teste. Então fica mais— é menos chato de fazer os testes assim e dá para manter isso, escalar o número de testes de acordo com o código do projeto. Fora isso, manter um code style. A dica que eu dou assim é que a gente usa o Google JavaScript Style Guide, porque a Google passou muito tempo desenvolvendo esse guia, seguindo padrões de uso de JavaScript, como a Google usa, qual que é a melhor forma para o interpretador, porque assim, JavaScript quase que sempre tem duas formas de fazer a mesma coisa e você manter a uniformidade no seu projeto de como você faz as coisas é uma coisa importante, até porque se você precisar de um refactor de alguma coisa assim, é bom você manter isso. E outra coisa é você fazer um tratamento de erro consistente. Você retornar o erro no primeiro parâmetro do callback. Tem milhares de formas de fazer tratamento de erro em Node, mas é bom que você faça consistente no projeto inteiro, porque se você tem um erro lá no último callback que você tem, é bom que ele retorne até o primeiro íntegro, para você poder saber exatamente onde foi o problema que aconteceu. E bom, coisas que a gente usa, pacotes que a gente usa na nossa aplicação, a gente usa Express, que é um framework HTTP para basicamente gerenciar rotas, sessão, esse tipo de coisa. O SQLite para fazer ORM de MySQL, o Mongoose para fazer ORM no MongoDB, o Commander para fazer wrapper de linha de comando, para parsear argumento que você passa, fazer um output bom, e a gente também é bem adepto ao Vim para editar texto, obviamente não é do Node, mas é bom falar porque a gente gosta bastante. E bom, uma coisa que que eu fico vendo muito, que eu acho que é legal falar, é que assim, a gente, eu pelo menos percebo que inovação em padrão de código não é inovação. As ferramentas são feitas para serem usadas de uma forma, os sistemas operacionais são pensados para serem usados de um jeito, um framework é pensado de um jeito, uma biblioteca é pensada de um jeito. Então assim, não inova nisso, inova no seu modelo de negócio, inova na sua startup, na sua ideia, mas você inovar numa coisa que tem um padrão bem definido para funcionar, como você espera que ele funcione, não tem porquê você ficar inovando nisso. Então assim, siga os padrões que todo mundo segue porque é a coisa mais inteligente, é o que todo mundo faz e é a melhor forma de usar tudo para que tudo funcione como você espera. Desculpa. Bom, agora já falei um pouquinho de Node, agora vou falar um pouco do que tem atrás da nossa aplicação, da nossa infraestrutura. Assim, a gente tinha alguns problemas que a gente tinha que resolver nesse cenário, principalmente a questão do ambiente de teste, que a gente queria ter um ambiente de teste igual ao de produção, e ao mesmo tempo manter esses dados isolados. Porque assim, vai virar uma loucura se você misturar dados de transação de verdade com dados de teste. Então a gente queria isolar isso muito bem. E a conclusão que a gente chegou é que você pode usar o melhor de cada banco de dados. Você não tem por que usar só Mongo porque Mongo resolve parte do meu problema. A gente acabou montando uma infraestrutura que a gente usa muita coisa por trás para segurar uma aplicação que tem muitas coisas complexas acontecendo ao mesmo tempo. Ambiente de teste, ambiente de produção, indexação na frente, que eu vou falar daqui a pouco. E a nossa infraestrutura ficou basicamente assim: a gente tem o endpoint da nossa API, que vai num roteador, e a gente divide o ambiente de teste, que é sandbox, que os clientes usam, do ambiente de produção. E aí, para cá, a gente tem os bancos de dados que a gente usa. A gente tem na frente Elasticsearch, que eu vou falar um pouquinho daqui a pouco, na frente de um MySQL, que assim, Dado de transação é muito relacional. Então não tem a gente usar Mongo, a gente adora Mongo, só que Mongo não faz sentido aqui. Não tem o menor sentido usar Mongo nesse cenário porque dado de transação é muito relacional, porque você tem que ter um cliente, você tem um cartão de crédito, você tem uma transação, você pode ter uma assinatura que has many transactions, então você tem um cenário aí de tanto dado relacional que não faz sentido usar uma tecnologia de banco que não seja também relacional. E a gente também coloca um Mongo para unificar o que é acessado pelo ambiente de teste e ambiente de produção. No Mongo a gente mantém tudo que não é relacional, que é dado de cliente e a gente separa dados de transação, de teste, de produção, dados de cliente e a gente ainda coloca um layer de indexação no meio. Isso foi uma coisa que todo mundo que a gente conversou sobre perguntou para a gente: "Pô, mas vocês estão usando banco para cacete, por que vocês estão fazendo isso? Bota um, sei lá, um MySQL, um Postgres em cima ou um Mongo e resolve isso." E bom, assim, primeiro motivo foi separar os dados de teste e produção, como eu falei. O segundo foi manter dados relacionais em banco relacional e banco e dados não relacionais em banco não relacional, porque esses bancos são feitos para isso. Então, quando você faz uma query no Mongo, ela não é feita para ser relacional, porque o Mongo não vai fazer, ele não vai unir as queries, ele não vai, você vai ter que fazer tudo isso no client-side. Então, não é a tecnologia certa para você nesse caso. Então, a gente soube entender, tipo, qual que é o melhor para cada tipo de dado que a gente tem. A gente não teve medo de dividir isso porque para a gente era uma coisa que fazia bastante sentido. E a gente também conheceu Elasticsearch tem mais ou menos uns 2 meses e a gente colocou ele em cima, antes do nosso banco de dados MySQL, porque o Elasticsearch, vou falar um pouquinho dele agora, depois, é que ele é uma engine muito, muito, muito boa para fazer indexação e busca. Então você consegue expor para os clientes um sistema de busca muito bom. Que é uma coisa interessante também para o cliente poder trabalhar os dados de transação que ele tem. E a conclusão que a gente chegou assim nesse cenário é que não tem por que você se prender a uma tecnologia quando cada uma delas resolve uma parte do seu problema. Tudo bem que é chato manter banco diferente, mas se você tem um cenário que obviamente cada tecnologia resolve parte do problema, não tem por que você socar tudo num MongoDB se ele claramente não é uma solução para o seu problema. E agora sim, um parênteses aqui que eu vou falar um pouco de Elasticsearch porque Quem aqui usa Elasticsearch? Tá, tem um pouquinho de gente, mas eu acho que vale a pena falar porque é uma tecnologia que a gente se surpreendeu muito com ela. A gente não esperava que ela fosse ser tão escalável e tão boa para o nosso cenário. E a gente usou ele de um jeito um pouquinho diferente do normal, mas que também é uma forma de usar. O Elasticsearch, o slogan dele só tem palavras legais: open source, distribuído, tempo real, busca e análise. Então você olha isso aqui e já fala: "Pô, isso aqui é legal, mas vamos lá entender." O Elasticsearch é em Java, mas você não vai ter que mexer com Java. Você nem sabe que ele é em Java, mas ele é em Java e ele é em cima do Apache Lucene, que é uma engenharia de indexação do projeto Apache, que ela é muito robusta, ela é feita há muito tempo, então é um código muito estável, muito sólido e que tem um algoritmo de indexação muito bom. Ele faz MapReduce, Se você tem clusterização de banco de indexação, então ele faz muita coisa que é muito útil e muito bem há muito tempo. E Elasticsearch, ele basicamente faz o seguinte: ele te dá uma API RESTful que você usa um POST para inserir dados no model dele e GET para buscar. Então você basicamente indexa algum tipo de dado nele e ele te faz uma forma fácil de buscar esse dado, indexar esse dado. E assim, o Elasticsearch, o legal dele é que ele só funciona, ele just works, você não tem que configurar e ele já tem suporte a cluster direto da instalação, então você não tem que ficar lutando que nem é com uma SQL para você escalar e tal, então ele é bem legal. E o nosso uso especificamente é indexar e buscar as respostas incluindo metadata e objetos dentro de outros objetos, de módulos do MySQL. E eu vou falar um pouquinho aqui de como é que a gente faz isso, porque eu acho que pode ser útil para outras pessoas também. Bom, você tem, por exemplo, esse objeto aqui é o nosso objeto de transação de retorno. Está dando para ler ou não? Bom, vou dar uma... assim, você tem a transação aqui, os dados da transação, os dados do cliente, o endereço do cliente, o telefone. Só que no nosso banco isso está organizado assim: você tem a tabela de transação, a tabela de cliente, a tabela de endereço e a tabela de telefone. Para você fazer essa operação e montar esse objeto de resposta, você tem que fazer 3 joins no banco, no MySQL. Só que isso é uma operação bem custosa. Então, o que a gente faz? A gente faz essa operação uma vez, constrói esse JSON e a gente indexa esse JSON no Elasticsearch. Então a gente coloca ali o ID da transação 35, a gente indexa no ID 35 do Elasticsearch e cada vez que você muda o model, Você atualiza o Elasticsearch também para ele reindexar. Então você não tem que fazer mais join no MySQL, que é uma operação lenta, uma operação ruim de escalar, e é uma operação que Elasticsearch faz a indexação muito mais rápida que o MySQL. E ele indexa o JSON já montado pelo ID num índice do modo específico que você determina. E aí quando você der um GET lá no /35, ele vai pegar ali um headerzinho dele para você saber qual que é o ID, qual que já existia antes, qual é a versão dessa modificação do arquivo e dentro aqui do source ele vai ter o objeto já construído. E embora isso pareça que ele faz uma coisa parecida com Mongo, o que ele faz que é poderoso é isso daqui, que é você fazer um search no modo transactions e aí eu tô pegando aqui, por exemplo, transações feitas com cartão de crédito nesse CEP com valores maiores que R$10, porque a gente não usa vírgula ali. E essa operação aqui, a busca busca vai ser feita em cima do JSON e não do MySQL. Então isso aqui é muito poderoso para você expor para o cliente o Elasticsearch, e aí o cliente tem muito, tem como filtrar muito tipo de dado e montar queries muito avançadas que no MySQL você iria demorar uma eternidade. Então isso aqui é realmente muito poderoso, sim, uma das tecnologias mais poderosas que eu vi nos últimos tempos, e eu acho que vale a pena dar uma olhada no Elasticsearch porque promete bastante. Só deixando umas coisas claras, ele não é um banco de dados, ele é um indexador, então você ainda vai ter que ter uma SQL, você ainda vai ter que ter um Mongo por trás, só que o ponto é, todo mundo fala: "Por que eu não posso usar o Elasticsearch como um banco?" Porque é o seguinte, um banco ele é sólido, você não tem como perder dados se alguma indexação ocorre errado, o banco ele é muito sólido em persistir dados, o Elasticsearch ele é sólido em indexar dados. Essa diferença é muito boa de ter porque Elasticsearch não é um banco. Ele evita I/O no banco de dados, como eu já falei, e permite que você busque em cima do próprio objeto JSON que é retornado para o cliente. Então isso é bom porque se você tem uma estrutura no banco que não segue o padrão que você retorna, você teria que fazer uma conversão da estrutura do shapefile que você retorna, como o cliente vai buscar um parâmetro, para o parâmetro que é o nome da mesma coisa no banco. Se o seu id para o cliente é _id e no banco é id, você tem que converter isso. Com Elasticsearch não, você vai expor exatamente o que ele vê quando ele dá um GET na transação. Então você não expõe a estrutura do banco para você fazer query e você nem tem que ficar convertendo. Isso é uma coisa que para a gente foi muito boa. E outra coisa legal também é que Elasticsearch você consegue fazer um dashboard em tempo real porque ele suporta atualização em tempo real por socket.io, ele tem várias tecnologias assim que são bem legais. E uma dica que eu dou assim, que a gente teve, é que você criar uns scripts para indexar o seu banco, porque indexação em tempo real ela funciona, só que ela pode quebrar. E quando ela quebrar você vai querer ter um jeito de indexar o banco rápido, porque provavelmente você vai ter algum tipo de problema, então é bom você ter um jeito de indexar o banco manualmente. E só para fechar aqui o Elasticsearch, embora a gente use em Node, obviamente ele funciona com Ruby, com Python, com qualquer outra linguagem. A gente usa ele porque ele resolve bem parte do problema da nossa infraestrutura. Embora eu estivesse falando de Node, eu acho que é legal falar disso porque é uma coisa que acho que pode dar uma ajuda aí para quem tiver um problema parecido com a gente e tal. E basicamente é isso. E bom, falar um pouco de deployment em Node. Deployment em Node é uma coisa que muita gente acaba na hora de fazer acaba ficando meio um pouco assim para trás, porque o que todo mundo conhece é isso aqui: você dá um node server e sobe. Só que aí o processo não roda em background, e aí você saiu do SSH, você deu um Ctrl+C lá para ver o log e acabou, o processo morreu. Nível 2: você coloca isso num Git, então você não tem que atualizar na mão, e você roda o processo em background, você tem um npm controlando dependência aí. Nível 3, você roda com o nohoop, que aí você, o processo roda mesmo depois do logout do SSH, e você ainda tem o Git e o npm. Tem o nível 4, que você tem um serviço aí para fazer esse deployment, para indexar log também e para reiniciar o processo caso ele morra, que já é mais ou menos uma boa solução. E assim, aí você começa a ficar bom. Que é quando você bota um servidor de integração contínua e você coloca o serviço, que era o que a gente tinha basicamente antes, que o servidor de integração contínua lidava com Git, com dependência do npm, e a gente só chamava esse script aqui para reiniciar o processo lá. Só que aí a gente chegou na solução ideal que a gente usa hoje, que é o Strider, que é um servidor de integração contínua feito em Node para Node, e o PM2, que eu vou falar agora, que ele reinicia O Node está rodando um processo, então quando você usa o PM2 você consegue reiniciar o processo, recarregar o código sem perder nenhuma requisição, sem lançar dois processos, ele cuida disso para você. E bom, o PM2, como eu falei, ele roda e gerencia processos do Node, então ele mantém o processo rodando para sempre, se o processo morrer ele lança outro, e o que é legal dele é que ele faz reload no código on the fly. Então, enquanto a sua aplicação está rodando, você tem cliente fazendo requisição, transação lá, você consegue recarregar o código em tempo real. O que ele faz é que ele mexe nas internals do HTTP do Node e ele roteia o pacote para uma outra porta enquanto a aplicação está subindo, e aí ele mata a inicial, sobe outra, e ele faz isso mexendo, fazendo injeção de JavaScript na aplicação. Embora pareça uma ideia um pouco assim assustadora, ele funciona muito bem, assim, ele já foi a gente testado e é uma solução que funciona muito. A gente usa para todo deployment que a gente faz. A gente lança deployment hoje, Black Friday, a gente já tá vendo transação aqui, mas a gente lançou deployment já hoje mais cedo, então funciona muito bem assim para você não perder nenhum request, que é uma coisa que acontece com algumas pessoas assim. E outra coisa que é legal é que você consegue fazer multithread e clusterização sem mexer no código, porque como ele mexe nos entornos HTTP do Node, ele consegue rotear pacote, ele consegue distribuir carga Ele consegue montar um load balancer na frente sem ter que mexer em código. Isso é muito bom porque você não tem que se preocupar com isso. Você deixa uma ferramenta que é feita para isso se preocupar e você usa a melhor ferramenta para o seu caso, que é o que a gente sempre procura fazer. Você também tem monitoramento e gerenciamento dos logs por ele, que é uma coisa legal, que é bem chato fazer isso quando você é uma startup, você tem que fazer tudo desde operação até consertar o CSS no dashboard, então Legal quando você tem alguém que gerencia log para você. Ele também tem uma API RESTful para você controlar. E o Strider, que é o nosso servidor de integração contínua em Node. O legal dele é que ele é 100% em Node, então ele foi pensado para ser um servidor de integração contínua em Node. Ele é meio beta, assim, ele está na versão 1.4, ele funciona muito bem, ele tem features já muito boas, mas se você vê o roadmap dele você fala: Jenkins é um lixo, Travis é um lixo, isso aqui vai ser a melhor coisa do mundo daqui a algum tempo, então é legal ficar de olho porque ele tem features bem legais, é um projeto que está começando tem 1 ano, sei lá. Ele tem integração com GitHub, obviamente, e você consegue monitorar os testes em tempo real por sua GitHub ou no browser, então você roda o teste, você lê lá o que está acontecendo no servidor e você até consegue fazer algumas intervenções, por exemplo, se o processo morreu em algum tempo você consegue lançar um Node debug para você ver em qual linha que foi que deu problema pelo próprio browser, o que é legal porque como a gente tem problema de segurança, porque a gente tem que ser PCI, que é uma certificação de segurança para você transacionar dados de cartão de crédito, isso é legal porque para você ir no servidor de produção e ver onde deu problema é muito chato. Então você tem que entrar por uma jump box no SSH, então é um processo bem burocrático até você poder exatamente ver em qual linha que deu problema. Isso aqui ajuda a gente a debugar código quando a gente tem algum problema em Node e tal. E ele também envia e-mail com erro em teste, obviamente, e ele tem suporte a teste de regressão e scripts de deployment. Então, se você quebra um commit agora e você continua deployando numa branch, não uma branch em produção, obviamente, porque senão ele não iria comitar, mas ele vai testar para trás e vai ver em qual código, em qual qual foi o commit que quebrou e vai te avisar. Então isso é legal porque você consegue saber onde você tem que olhar o código e onde você tem que corrigir sem ter que ficar fazendo teste de regressão na mão. E bom, basicamente é isso que a gente usa para deployment e monitoramento, porque assim, a conclusão que a gente chegou é que, assim, é bem óbvia, mas não é tão óbvia assim, que uptime é uma coisa muito importante e ser transparente quando ele não existe também. O objetivo de monitoramento é você saber dos problemas antes do cliente e ser transparente quando ele acontecer, porque é isso que o cliente espera de você. É bom você abrir o jogo nesse sentido e a gente sempre foi bem incisivo nisso em não ficar escondendo alguma coisa que aconteceu de errado. A gente sempre ser bem direto porque facilita bastante a relação e tal. Para saber os problemas a gente usa o Pingdom e o PagerDuty, que são dois serviços que um basicamente monitora um endpoint que você indica para ele e o outro é o que te acorda à noite quando a API sai do ar. O PagerDuty ele te liga realmente, você configura o Pingdom para notificar o PagerDuty e aí ele te liga no seu celular e aí você acorda porque tem como você configurar no iPhone, no Android também tem, para você mudar o, mesmo o celular estando em silencioso, você tocar e aí é ele que te acorda quando o problema vem e é bem legal. Outra coisa também é a página de status para você informar o cliente se alguma coisa de errado aconteceu e um Twitter também porque é uma coisa que a gente usa justamente para ter a parte do cliente saber o que aconteceu. E uma coisa que é legal, que eu não sei se muita gente conhece, é um negócio chamado Librato que a gente usa, que ele te ajuda a monitorar e evitar problemas futuros. O Librato basicamente é o seguinte: você instala monitoramento no seu código e ele conecta na API dele, ele te dá um dashboard que você pode cruzar dados muito diversos e entender onde que é o real problema. Por exemplo, nessa screenshot aqui, a gente tem aqui a linha laranja, é os pacotes de rede chegando na API de produção, a linha verde é a CPU de produção, a linha azul é quando os requests do Node recebeu, na API de produção e a linha roxa é o banco de produção, o I/O em disco dele. Então, eu estou cruzando dados do Node com aplicação de produção, Node mesmo, e o banco de produção. E aí você consegue entender que, por exemplo, que quando chega um request, obviamente os dados de rede aumentam e logo depois o I/O em banco também, porque você tem o cache Elasticsearch aqui no meio. Então, você consegue ter basicamente uma ideia de como é que, o que está acontecendo de errado. E por que está acontecendo de errado, que é uma coisa que pouca gente se preocupa com monitoramento, mas que assim, quando o dia que você tem um problema é aí que você liga para ele, aí que você se importa. Então, você ter inclusive isso para trás para descobrir quando um problema começou a acontecer, quando você deixou de escalar, quando você perdeu tempo porque sua máquina estava com um I/O em disco lá no teto, uma coisa bem importante também. E monitorar a saúde da aplicação, quantos requests Como é que está a sua taxa de resposta de erro, resposta de sucesso, esse tipo de coisa também é bem importante você fazer porque quando dá errado é bom você saber antes do cliente, obviamente. E, bom, falando aqui, chegando nas minhas conclusões, Node é muito legal, resolve muitos problemas e teoricamente é perfeito, só que como eu falei antes, não tem bala de prata, então não resolve todos os problemas. Realmente não fiquem com essa ideia que Node é uma coisa que resolve tudo, porque não resolve. Node resolve muito bem o problema de I/O bloqueante, que é um problema que a gente tem, só que ele não é perfeito para tudo. Então é bom deixar isso bem claro, porque assim, é uma tecnologia nova, só que cada tecnologia tem seu use case e Node tem o seu. Outra coisa também é que JavaScript pode ser perigoso, então tomar cuidado porque assim, o cuidado diminui e você tem problemas. Então o ideal é você ter sempre cuidado com código, manter padrões e teste. Teste é o mais importante de tudo em JavaScript. Então se você quer evitar problemas, provavelmente tenha teste, porque você não vai ter eles. E bom, o que a gente queria dar o overall aqui é o seguinte: que dá pra criar algo grande com Node, mesmo com todos os problemas que ele tem. Assim, o Node tem um use case bem definido, E assim, já tem empresas bem grandes fazendo aplicações grandes com Node. O GitHub está usando, o LinkedIn está usando. E assim, não sei no Brasil quem está usando, mas assim, a gente usou porque resolve um problema que a gente tem. E assim, dá para resolver problemas grandes com Node e é uma ferramenta bem poderosa que eu acho que quem não conhece deveria conhecer, porque certamente tem suas vantagens e resolve muitos problemas que a gente pode ter no dia a dia. É isso, gente. Obrigado.
More from Pedro

Fundador da Brex fala ao CNN Money sobre venda de R$ 5,15 bi à Capital One | FECHAMENTO DE MERCADO

The Agentic Commerce Revolution is here - but it's not what you think! (Brex CEO Pedro Franceschi)

Brex founder Pedro Franceschi on the one question CEOs should constantly ask themselves

We developed 'best in class' enterprise travel expense solution, says Brex CEO on Navan partnership






