Reescrevendo Software Crítico em Elixir -- Um estudo de caso

Reescrevendo Software Crítico em Elixir -- Um estudo de caso

Apresentação realizada na ElixirBrasil2019

4569aec00cb223b3fbf484f9e7ba1256?s=128

Renan Ranelli

May 26, 2019
Tweet

Transcript

  1. @renanranelli Reescrevendo software crítico em Elixir -- Um estudo de

    caso 2019-05-08 Renan Ranelli
  2. @renanranelli

  3. Senior Software Engineer @ Telnyx (Chicago, IL) São Paulo @

    Brasil Elixir desde 2015 Organizador do ElugSP Antes de Elixir, varios Ruby, Python, C#, etc
  4. None
  5. None
  6. (Há vagas !)

  7. None
  8. Reescrevendo software crítico em Elixir

  9. disclaimer

  10. Contexto e Motivações

  11. Back-to-back User-Agent

  12. Back-to-back User-Agent “The Dialplan Service”

  13. None
  14. - (almost) Stateless - Super sensível à latência - Baixo

    throughput (bem baixo) Propriedades do Dialplan service
  15. - A antiga implementação do serviço era escrita em python.

    (De agora em diante chamada de “dialplan python”) - Todos os devs python acabaram saindo do time de telefonia. - Python é notoriamente mais difícil de escalar vertical & horizontalmente do que Elixir “The Python situation”
  16. A decisão: Vamos re-escrever esse serviço em Elixir!

  17. Mas pera lá… Elixir?

  18. Linguagem vs Plataforma

  19. Definições: 1. Erlang é uma linguagem 2. Erlang/OTP é plataforma

    3. Elixir é uma linguagem que roda na plataforma Erlang/OTP
  20. Erlang/OTP

  21. - "Erlang é uma linguagem funcional"! - Erlang "Resolve" concorrência!

    - É o "segredo" por trás do Whatsapp - Metade da telefonia do mundo é feita em Erlang (citation needed) - "Nine-nines availability" Coisas que ouvimos falar:
  22. The Erlang/OTP Runtime

  23. - "Soft real time" - Preemptive scheduling (responsivo, justo &

    baixa variância da latência) - Verticalmente Escalável - Distribuição e Concorrência são cidadãos de 1a classe - Garantias de execução fornecidas pelo runtime (a base para tolerância a falha) - "Alta performance" Características da VM do Erlang
  24. Tá, mas e Elixir com isso?

  25. - Elixir compila para o bytecode Erlang - Criada por

    um BR! José Valim <3 - A semântica é absolutamente a mesma entre Elixir e Erlang. (mais parecido com TypeScript/JS do que Clojure/Java, Elm/JS, etc) - Para a plataforma, código Elixir e código Erlang são *indistinguíveis* Elixir é só uma pequena gota
  26. "When designing the Erlang language and the Erlang VM, Joe,Mike

    and Robert did not aim to implement a functional programming language, they wanted a runtime where they could build distributed, fault-tolerant applications. It just happened that the foundation for writing such systems share many of the functional programming principles. And it reflects in both Erlang and Elixir. Therefore, the discussion becomes much more interesting when you ask about their end-goals and how functional programming helped them achieve them. The further we explore those goals,we realize how they tie in with immutability and the control of shared state, for example: ..." -- Valim, José Elixir Beyond Functional Programming
  27. TOLERÂNCIA A FALHAS

  28. Fault-tolerance requires hardware redundancy Hardware redundancy requires distribution Distribution on

    a single node is concurrency
  29. Mas… Se Erlang é tão topper, pra quê Elixir?

  30. - A sintaxe e ecossistema Erlang são *alienigenas* para os

    desenvolvedores dessa geração. - Existem inúmeras barreiras para adoção de Erlang: falta ferramental, docs difíceis de ler, zero polimorfismo de dados, etc. - Elixir traz o "21st century" para a Erlang-landia, através de metaprogramming, ferramental coeso e bem desenhado, documentação *excelente* e uma comunidade vibrante e acolhedora Raison d'être do Elixir:
  31. Erlang -> Hard Skills Elixir -> Soft Skills

  32. Exemplo: Preemptive Scheduling

  33. Suponha que temos 6 tarefas: T1 T2 T3 T4 T5

  34. Com *cooperative scheduling*: T1 T2 T3 T4 T5 T1 T2

    T3 T4 T5 T1 T2 T3 T4 T5 1) Or 2) Or 3)
  35. Sem *preemptive scheduling*: T1 T2 T3 T4 T5 1) Or

    2) E por ai vai... T4 T5 T1 T2 T3 T4 T5 T4 T5 T5
  36. Mais sobre isso: https://www.youtube.com/watch?v=a7s25To6oII

  37. Tá… e como re-escrevemos?

  38. - Buscar "buy-in" stakeholders de negócio - Reservar recursos para

    o projeto - Codar & deploy-ar continuamente (shadow deploys) - Verificar a paridade de features (Tudo isso enquanto o tráfego de *PRODUÇÃO* cresce dia após dia!) Nossa jornada
  39. - Tivemos de convencer vários stakeholders de negócio que seria

    necessário "desacelerar" o desenvolvimento de novas features para pagar "débito técnico" - É fundamental frasear os benefícios em *termos de negócio* Buscar "buy-in" stakeholders de negócio
  40. - Conseguimos aprovar um desenvolvedor dedicado full-time para esse projeto.

    Nenhuma outra responsabilidade. (#sqn) - Re-escrever é um trabalho imenso em que você busca um "alvo em movimento". Se você não conseguir focar, você vai fracassar Reservar recursos
  41. Codar & deploy-ar continuamente (shadow deploys)

  42. Codar & deploy-ar continuamente Proxy Python Dialplan Graylog XML Request

    XML Response
  43. Codar & deploy-ar continuamente Proxy Python Dialplan Graylog XML Request

    Elixir Dialplan Ignore response! XML Response XML Response
  44. Codar & deploy-ar continuamente Proxy Python Dialplan Graylog XML Request

    Elixir Dialplan Ignore response! XML Response XML Response “Operational database”
  45. Verificar paridade de Features SELECT p.xml_dialplan, e.xml_dialplan, p.request FROM python_dialplan_logs

    as p INNER JOIN elixir_dialplan_logs as e ON p.call_id == e.call_id WHERE e.xml_dialplan != p.xml_dialplan
  46. Verificar paridade de Features

  47. Logo no começo, percebemos: - A maioria das diferenças eram

    causadas por falhas em serviços "downstream". - Quando não era esse o caso, "gravar" a resposta das dependências era suficiente para reproduzir o cenário de erro. Verificar paridade de Features
  48. Logo no começo, percebemos: - A maioria das diferenças eram

    causadas por falhas em serviços "downstream". - Quando não era esse o caso, "gravar" a resposta das dependências era suficiente para reproduzir o cenário de erro. Verificar paridade de Features (Tipo o VCR do Ruby)
  49. Um efeito colateral top: Regressão

  50. E continuamos fazendo isso...

  51. E continuamos fazendo isso... … até que um dia acabaram

    as diffs!
  52. O familiar ciclo “TDD”

  53. Nosso ciclo “tipo-TDD” Capture um request com erro Corrige &

    cria cenário de regressão Reescreve
  54. Resultados

  55. Praticamente *zero* incidentes em produção após o cutover

  56. Um runtime muito superior. Code-base 100% Elixir.

  57. Melhorias *enormes* em observabilidade

  58. None
  59. Features shipando mais rápido

  60. Paralelização ridiculamente mais fácil & barata. (less latency -> happier

    users)
  61. Agora… A parte menos gloriosa

  62. Expectativas de negócio mal-gerenciadas

  63. Amostragem burra das discrepâncias

  64. O recurso "dedicado" acabou não ficando tão dedicado assim

  65. O serviço python *continuou sendo modificado* enquanto a re-escrita acontecia!

  66. 98% pronto por … muito tempo

  67. Tentando refatorar código bizarro no momento errado do ciclo "tipo-TDD"

  68. Wrapping up

  69. Valeu a pena?

  70. Vá para produção o mais cedo e o mais frequentemente

    possível!
  71. Monitoração e observabilidade valem a pena. Você nunca deve ter

    medo de *testar em produção*!
  72. Refatorar >>> Reescrever (sempre que possível)

  73. Re-escrever é difícil e perigoso. Porém, possível.

  74. Agora, uma reflexão final

  75. O que garante que a re-escrita em Elixir não vai

    degringolar da mesma forma que o python degringolou?
  76. Elixir economiza um pouquinho de tempo em *várias* situações

  77. Elixir economiza um pouquinho de tempo em *várias* situações …

    portanto, sobra *mais tempo* pra fazer o que é *importante*!
  78. “Fully utilize *all* your resources”

  79. “Fully utilize *all* your resources” (incluindo humanos)

  80. Renan Ranelli milhouse@taming-chaos.com renan@telnyx.com Obrigado! Check us out! www.telnyx.com @renanranelli

    We're hiring!!