Ultimamente eu tenho estudado muito de programação funcional, principalmente em Clojure. E nada melhor para estudar uma linguagem do que se aventurar num emprego em que se usa ela, certo? 

Foi assim que eu fiz o processo seletivo no Nubank. E passei, e comecei a trabalhar em maio.

Muitas coisas foram diferentes para mim nessa pequena jornada. A primeira empresa em que o atendimento ao cliente está muito próximo do dia a dia, a primeira empresa com muitos funcionários, a primeira empresa que opera com muito investimento, a primeira empresa que usa intensivamente micro serviços, e a primeira empresa em que eu, de fato, fui demitido (e não que eu pedi demissão). 

Apesar de tudo, eu já tinha meus planos de sair. Mas isso não vem ao caso, nem vem ao caso as circunstâncias que levaram ao acontecido. O importante é o que fica, e as coisas que eu aprendi. 

Pela primeira vez na vida eu trabalhei com microserviços, e pela primeira vez eu consegui ver a vantagem real de uma linguagem funcional nesse processo. Há muito tempo atrás, no mundo de Ruby, uma implementação chamada Maglev prometeu entregar persistência de objetos Ruby completos, inclusive exceptions. O Maglev mesmo nunca foi muito utilizado, mas essa ideia de persistir exceptions e sessions e depois reproduzir o bug simplesmente pegando a session e replicando os passos que causaram o erro ficaram na minha mente. 

Quando eu trabalhei com microserviços, era exatamente isso que eu fazia – cada ponto de entrada em um serviço era um conjunto de dados, e como a maior parte das coisas era imutável, se algo dava erro a mensagem era rejeitada, e depois podíamos simplesmente chamar o mesmo entrypoint com a mesma mensagem, e garantir que a mesma exception ocorreria. Se fosse algum erro de comunicação com banco de dados ou com outro entrypoint, nada de errado ocorreria – e aqui entra a segunda coisa diferente.

Para suportar todas essas características, todos os entrypoints tinham que ser indepotentes – ou seja, se uma mesma mensagem fosse enviada para ele duas, três, ou quatro vezes, o entrypoint se compraria como se tivesse recebido apenas uma. O importante aqui é não depender de um id de mensagem ou qualquer coisa assim – é importante que a mensagem inteira seja responsável por ser indepotente.

Na prática, isso é mais difícil do que parece – por exemplo, uma mensagem que salva um registro no banco de dados deveria buscar pelo registro no banco, e inserir caso ele não exista ou alterar caso exista. Se o entrypoint serve apenas para criar um registro, basta primeiro tentar inserir, e em caso de erro, ignorar o erro. Mas, se ele precisar repassar esse registro para outro serviço ou entrypoint, isso ainda terá que ser feito. Claro que este é um caso bem simples – o problema começa quando o entrypoint depende de um serviço externo, que não é indepotente, ou o serviço faz algo

Pra isso tudo funcionar, uma infraestrutura diferente é necessária. Primeiro, que agora há alguém que chama o próximo serviço – algo como o rabitmq ou kafka – e segundo que agora não há mais compartilhamento de banco de dados entre serviços, entre partes do sistema, e até regras super simples de casualidade não são mais verdadeiras – tudo é assíncrono, então a mensagem de mudança de endereço de um usuário, por exemplo, pode vir antes da mensagem que pede o cadastro desse mesmo usuário. Os bancos são bem mais granulares, os logs tem que ser repassados para um serviço externo sempre (log no disco não dá mais – é necessário usar algum agregador externo ou então qualquer bug é simplesmente absurdo de se buscar). Na verdade, usar um serviço externo para os logs é algo que deveria ser mais usado em qualquer situação – é muito mais fácil de se usar em praticamente qualquer caso, mas isso fica pra um próximo post.

Fora isso, outro ponto interessante é monitorar tudo isso. Como todo o fluxo de serviço foi dividido em pequenos serviços, não é mais simples saber se algo está no ar ou não. Além disso (e hoje isso parece óbvio para mim mas na época não era), as máquinas que rodam microserviços estão quase sempre operando quase no limite – tanto de processamento quanto de memória. Afinal, primeiro que a arquitetura espera que máquinas parem de funcionar, então uma falha não é catastrófica, e segundo que alocar uma máquina poderosa pra ficar ociosa a maior parte do tempo é perda de dinheiro – mais fácil um mecanismo que, ao identificar muitas mensagens que não estão sendo processadas, criar novas máquinas virtuais e subir mais serviços nela (e obviamente o contrário – identificando máquinas ociosas, basta desligá-las e o resto funciona normalmente).

Infelizmente, nem tudo são flores – é óbvio que essa estrutura toda exige maior automatização. O trabalho de dev-ops aumenta consideravelmente, e as falhas sempre tem que ser testadas (é comum, por exemplo, desligar um disjuntor inteiro de um Datacenter e garantir que o serviço ainda está no ar). Além disso, há muito mais conhecimento a ser repassado – infraestrutura agora também é código.

No fim, sempre é difícil decidir o tamanho de um serviço e até onde vai a responsabilidade dele. Além disso, o teste de integração disso tudo ainda é um grande mistério (havia um teste ponta a ponta mas isso criava umas interdependências virtuais bem estranhas, além de ser efetivamente bem difícil de fazer os testes).

No fim, foram aprendizados muito bons, que provavelmente vão me ajudar muito no futuro. Se microserviços são a resposta para a maioria dos problemas, ainda há muita discussão. Mas, em situações onde se espera que as coisas venham a falhar, ou em casos aonde se quer usar um ecossistema variado (várias linguagens, várias plataformas, até vários sistemas operacionais ou mesmo arquiteturas), digamos que há uma certa ordem no caos.


2 Comments

TK · 2018-12-19 at 19:51

Hoje Nubank parece um lugar interessante de se trabalhar, tanto em questão de desafios técnicos, quanto de aprendizado de business, pessoas, etc. Mas só quem trabalha, deve saber as coisas “ruins”, aliás, nenhum lugar é perfeito.
Qual foi o motivo do “Apesar de tudo, eu já tinha meus planos de sair.”?

    Maurício Szabo · 2018-12-20 at 11:05

    Sim, em desafios técnicos e business é interessante. Em questão de pessoas, eu sentia a empresa meio “fria” (mas também já me disseram que foi pelo momento da empresa que eu entrei).

    Fora isso, o Nubank esperava um profissional que tem um perfil diferente do que eu sou; e a AcessoCard, empresa que eu estava conversando na época, queria um profissional pra arquitetar sistemas e ajudar pessoas a fazer uma mudança técnica. Das duas empresas, eu me identifiquei mais com o perfil da segunda, então já estávamos negociando uma possível saída.

Comments are closed.