jira_rbg_blue

Roses are red, JIRA is blue, Using JIRA is easy, Grasping how it works Is the hell of a steep learning curve.

Esta é uma continuação da aventura que comecei a descrever em Hello, Australia e Hello, Atlassian. Em resumo, mudei para a Austrália na metade de 2015 e vim trabalhar na Atlassian.

Você deve ter ouvido falar do JIRA, certo? Aquele sistema para controlar issues, isto é, bugs, tarefas e outros itens rastreáveis de projetos. Pois é, vim fazer parte justamente da equipe que desenvolve o produto mais popular da Atlassian. 😀

Arquitetura

Uma das primeiras perguntas que me fazem é sobre qual o stack de tecnologias do JIRA. Embora se diga que ele é basicamente uma aplicação Java web, a resposta não é tão simples quanto parece.

A Atlassian tem vários produtos em tecnologias completamente diferentes e já vi nos repositórios daqui desde Shell Script até Closure, passando por Scala, PHP, Python, Ruby, JavaScript e provavelmente outras linguagens que tenha esquecido, sem contar os diferentes frameworks, bibliotecas e ferramentas de cada ecossistema.

A base de código que contém o JIRA e os seus principais plugins é um repositório Git com o tamanho de alguns Gigabytes e leva algumas horas para clonar. O Git é rápido, mas ele sofre um pouco por aqui. Isso deve dar uma ideia do tamanho da "coisa".

É bom lembrar que o JIRA é um produto que vem sendo desenvolvido há aproximadamente 15 anos por centenas de pessoas, além de ser distribuído na nuvem como SaaS e para instalação dentro das empresas, sendo usado por milhões de pessoas diariamente no mundo todo.

Essa é uma escala onde muitas vezes usar uma biblioteca ou solução pronta de mercado não funciona. Por exemplo, algumas bibliotecas e frameworks de uso comum em sistemas web não suportam alto volume de tráfego ou não funcionam bem com um software que está em evolução acelerada. É por isso que você vê empresas como Google, Facebook e (posso incluir também) Atlassian criando novas bibliotecas ou forks para atender suas necessidades mais específicas. Por exemplo, a Atlassian tem sua própria solução para Single Sign On (SSO). Ainda, os desenvolvedores front-end mantém uma biblioteca de Web Components chamada Skate.js criada especificamente para atender às demandas de manutenção e performance de produtos complexos como o JIRA, embora possa também ser usada em qualquer projeto.

Sistema de Plugins

Desde muito cedo o JIRA é conhecido por ser extremamente extensível via configuração e plugins. Então é preciso diferenciar o núcleo daquilo que estende o produto. Recentemente, com o lançamento do JIRA 7, essa característica ficou ainda mais evidente. Agora ele foi dividido em JIRA Core, JIRA ServiceDesk e JIRA Software, cada para um público alvo e com funcionalidades diferentes.

O JIRA Core, como o nome diz, é a base dos outros produtos JIRA e inclui o gerenciamento de issues e os pontos de extensão, que são muitos. Novas funcionalidades são adicionadas usando um mecanismo de plugins desenvolvido pela empresa, que são basicamente pacotes contendo classes, web resources e um descritor XML.

Plugins são instalados, atualizados e desinstalados dinamicamente como módulos OSGi. Eles podem declarar e injetar componentes gerenciados pelo Spring, disponibilizar novos RESTful Web Services usando JAX-RS, contribuir nas views usando Velocity, Soy Templates (Closure) ou JSPs e não acaba aqui.

Há plugins "nativos", desenvolvidos e mantidos pela Atlassian, mas qualquer pessoa pode criar um plugin e instalar na sua instância particular do JIRA Server, isto é, hospedado pela empresa que compra o JIRA. Entretanto, isto não é possível no JIRA Cloud, já que a instalação de código executável seria um risco à segurança da infraestrutura em nuvem.

Mas isso não significa que o JIRA Cloud é inferior.

Primeiro, tanto para o JIRA Cloud quanto para o JIRA Server, existe uma série de plugins validados pela Atlassian que podem ser encontrados no Market Place da Atlassian. Certamente os melhores plugins e os mais úteis estão lá. Desenvolvedores podem contribuir com novos plugins, mas somente os validados pela Atlassian podem ser instalados na nuvem.

Segundo, a Atlassian criou um novo mecanismo para estender o JIRA Cloud sem que seja necessário executar código inseguro na nuvem. Essa solução chama-se Atlassian Connect e é baseada em Webhooks. Basicamente você pode configurar o JIRA para avisar outros sistemas quando ações ocorrem no sistema. A diferença aqui é que o cliente é responsável por manter esses outros sistemas e eles não apresentam risco de interferir com outros clientes executando na nuvem. Outra forma de estender o JIRA Cloud é contribuindo com componentes na página, o que é feito através de um avançado mecanismo baseado em iframes que permite outro sistema contribuir com um trecho da página sem que seja possível, por exemplo, que um script malicioso tenha acesso a informações sigilosas do restante da página.

Um detalhe importante é que tanto o sistema de plugins tradicional quanto o Atlassian Connect não são específicos do JIRA, mas podem, ser usados também para estender outros produtos, tais como o Confluence, Bamboo, Bitbucket e HipChat.

Linguagens

No código principal usamos Java 8, então programação funcional, streams e lambdas estão em toda parte. Na verdade, já estavam antes com o uso de Guava e outras bibliotecas interessantes como Fugue.

Na verdade, há um certo consenso por aqui de que há problemas inerentes com as classes funcionais no Java 8, tal como Optional, porque elas não respeitam a lei da composição quando uma das funções de uma função composta, que não a última, retorna null. Basicamente a culpa é sempre do null. Então muitos preferem continuar a usar as classes das bibliotecas.

Alguns plugins tanto do JIRA quanto de outros produtos usam Scala. Estaríamos usando mais Scala não fosse o tempo excessivo de compilação que torna penoso o processo de release e integração contínua em geral. Na verdade, código Scala teve de ser completamente removido da base de código principal.

Outro problema com a linguagem Scala é que ela tem (pelo menos tinha até a versão 2.11) problemas de incompatibilidade binário entre diferentes versões. Isso significa que uma pessoa não pode usar uma dependência Scala compilada com uma versão anterior que está usando em outro plugin. No mundo OSGi do JIRA isso é um blocker.

O uso de JavaScript é intenso. Ultimamente tem sido feito um árduo trabalho de converter todos os scripts para módulos AMD. A modularidade e a possibilidade de analisar estaticamente as dependências dos scripts de uma determinada página traz ganhos enormes. Um dos maiores desafios de um produto complexo e, principalmente, extensível, é determinar quais os scripts e outros resources necessários para uma página, pois quanto menos scripts desnecessários, mais leve e mais rápida será essa página. OUtras vantagens incluem melhor reuso, melhor organização dos scripts, menos erros estranhos por uso de variáveis globais e conflitos de nomes, a lista continua…

Camadas

O servidor de aplicação padrão é Tomcat, geralmente na última versão estável. Ele é usado internamente para desenvolver e testar, nas instâncias internas do JIRA, nas instâncias do JIRA Cloud. Também vai distribuído nos pacotes standalone do JIRA, exceto na versão WAR obviamente.

Na interface com o usuário, os produtos da Atlassian geralmente usam a biblioteca JavaScript/CSS criada pela própria empresa, chamada AUI (Atlassian User Interface). Pense nela como uma versão aprimorada e especializada do Bootstrap.

O JIRA é compatível com todos os principais bancos de dados do mercado, tais como Oracle, SQL Server, PostgreSQL, MySQL e outros. Para testes rápidos e nas versões de avaliação do produto usamos o HSQLDB e a preferência para executar em produção é pelo Postgres.

O JIRA Core usa um tipo de mapeamento objeto-relacional (ORM) não convencional, um fork de uma versão antiga do Apache OFBiz, que hoje é mantido internamente. Mas isso também não é tão simples, pois as pesquisas são baseadas no Apache Lucene, então todo o conteúdo de uma instância é indexado fora do banco de dados.

O antigo framework WebWork é usado na camada de controle, portanto a maior parte das requisições são tratadas em web actions. O WebWork é o framework que deu origem ao Struts2 e é bem pouco usado hoje. Lembre-se, o JIRA está aqui há mais tempo do que muitos frameworks web e esta era uma das boas opções na época.

A segurança é realizada utilizando filtros (Servlet filters) e não um framework como Spring Security. Isto se deve a razões históricas e também pelo fato de que Autenticação e Autorização no JIRA possuem cenários variados e complexos onde um framework genérico poderia ser mais problema do que solução.

O JIRA também conta milhares de testes em diferentes níveis: unitários (JUnit), funcionais (usando cliente HTTP), Web Driver, sem contar os testes específicos de cada plugin. Todo o processo de build, testes, release, deploy é automatizado no Bamboo, o servidor de integração contínua da Atlassian.

Para se ter uma ideia, um dos planos de testes funcionais do JIRA é configurado no Bamboo para executar com 20 máquinas virtuais simultaneamente e ainda leva mais de uma hora para completar. Obviamente, isso ainda não chega perto de 100% de cobertura (diga-se de passagem, algo impossível), mas é um bom balanço entre agilidade e segurança.

Basicamente, nenhuma versão sai sem passar por um complexo processo de promoção de releases, onde uma versão candidata é promovida por várias fases, passando por testes em cada uma delas, até finalmente atingir a produção. Poucas versões sobrevivem até o fim desta árdua jornada. 🙂

O que anda acontecendo por aqui

Nuvem

A Atlassian sempre teve sucesso no mundo server, isto é, em vender aplicações que são instaladas e mantidas nas empresas. Hoje ela pretende vencer também no mundo cloud.

Devido a toda a bagagem da empresa, alguns dos produtos ainda são mais bem sucedidos em sua versão server, mas um dos objetivos da Atlassian é que os produtos em nuvem sejam melhores e mais atrativos para os clientes, o que possibilitaria uma completa revolução em diversos aspectos tais como disponibilidade, velocidade de atualização, qualidade, preço.

Por enquanto, o JIRA tem sido adotado gradualmente na nuvem, mas queremos chegar a um ponto onde todos os clientes possam migrar para a nuvem sem interrupção.

Do ponto de vista das empresas, não vale a pena manter administradores de aplicações e a respectiva infraestrutura quando ela pode pagar um valor ínfimo por usuário para usar uma aplicação web como um SaaS. Mas, principalmente, a empresa fica livre da responsabilidade do serviço e problemas relacionados ao produto poderiam ser corrigidos em tempo infinitamente menor.

Mobile

Outra frente de ataque da Atlassian é alavancar o uso dos produtos em dispositivos móveis. Isso não é tão fácil quanto pode parecer a princípio.

O primeiro erro que deve ser evitado é simplesmente refazer todas as funcionalidades da versão web num app mobile. Além de desperdício de dinheiro, seria inútil para a maioria dos usuários. O uso de aplicativos não é igual ao de uma página web, portanto o trabalho acaba sendo o de propor formas mais específicas, mais focadas de uso do produto.

O que posso dizer por enquanto, ainda mais porque não estou envolvido diretamente com isso, é que num futuro próximo o gerenciamento dos projetos e tarefas no JIRA estará na palma de nossas mãos (e não mais limitados à nossa mesa trabalho – Desktop).

Microservices

Os objetos da Atlassian para Nuvem e Mobile envolvem muitas mudanças fundamentais, incluindo o uso cada vez mais de micro-serviços (microservices).

Ainda não está determinado quais partes do JIRA serão convertidas em micros-serviços, mas algumas funcionalidades tangentes à plataforma são obviamente melhor implementadas usando esta arquitetura.

Hoje já usamos diversos desses micro-serviços em ferramentas internas e existe inclusive uma infra-estrutura padronizada para isso. Dessa forma, novos plugins desenvolvidos para o JIRA, Confluence, Bamboo, HipChar e outros preferencialmente são feitos em formato de micro-serviços.

Por exemplo, vamos supor que eu esteja fazendo um plugin para encontrar issues repetidos ou por similaridade no JIRA. Ao invés de sair adicionando ainda mais complexidade ao produto e ao banco de dados, seria melhor criar uma outra aplicação com o único propósito de indexar os dados usando os WebHooks do JIRA e com um serviço onde, dado um texto, ela retornasse uma lista de issues semelhantes. Se essa aplicação tiver qualquer problema (travamento, atualização, manutenção, etc.) o JIRA continua funcionando normalmente, apenas com uma pequena funcionalidade ausente por alguns momentos.

Hello, JIRA Cloud Performance

A Atlassian quer que seus produtos na nuvem sejam os melhores, tanto em comparação com a versão server como em relação aos concorrentes. A grande preocupação é com a satisfação dos clientes, pois melhor aqui significa mais gente usando e gostando do produto.

Dentre as principais reclamações dos usuários do JIRA que usam a versão cloud está o péssimo desempenho de algumas páginas. Algumas levam vários segundos para carregar e não é sem razão, basta considerar toda a complexidade técnica e a flexibilidade do produto que mencionei acima. Para lidar com essa situação, a Atlassian criou equipes especificamente para melhorar o desempenho de seus produtos. É aqui que eu entro na estória, pois faço parte da equipe que tenta melhorar o desempenho do JIRA. 🙂

Melhorar o desempenho geral de uma aplicação web complexa e mundialmente usada não é tarefa básica. Por exemplo, deve-se considerar todos os aspectos desde que o usuário executa uma ação, como clicar num link, até ele receber o último byte de resposta e depois conseguir usar o sistema. Há várias perspectivas que podem ser consideradas aqui:

  • Latência da rede entre o usuário e os servidores: é precisar manter servidores em diferentes partes do mundo.
  • Tratamento da requisição no servidor: o tempo que o servidor de aplicação leva responder ao usuário após ter recebido a requisição.
  • Download dos recursos da página como imagens, CSS e JavaScript: usamos CDNs, minificação e concatenação de arquivos.
  • Tempo em que o usuário consegue ver a página: quanto antes o usuário conseguir ler as informações, melhor, então informações menos relevantes são carregadas assincronamente. BigPipe é uma técnica bem interessante aqui e está sendo grandemente considerada.
  • Tempo em que o usuário consegue atuar: scripts que executam no carregamento da página não podem demorar, ações menos importantes podem ser postergadas.
  • Tempo de inicialização total da página: quando todos os elementos estão prontos e os scripts são executados.
  • Tamanho total da página: quantos bytes foram transferidos, somando HTML e assets. Isso envolve remover elementos desnecessários das páginas e fazer melhor uso de cache.

Há muitos problemas em todos esses elementos que começam a surgir quando temos grande escala de usuários e um produto complexo. No caso do JIRA, um dos desafios é que muitos plugins e configurações podem contribuir para o conteúdo dos scripts e estilos. Há regras complexas que são executadas para gerar um script de forma que ele atenda a todos as funcionalidades de numa página. Em cima disso, os scripts e estilos são grandes e contribuem muito para a lentidão apresentadas em algumas páginas, principalmente quando o usuário acessa o sistema pela primeira vez após alguma atualização e todos os recursos precisam ser baixados novamente.

Para não atirar no escuro, foram feitas análises e estudos com base em estatísticas coletadas sobre o desempenho do JIRA na nuvem. Com base nos dados coletados, determinou-se um padrão baseado no Apdex para medir a performance geral do produto.

O Apdex é basicamente um indicador de desempenho de propósito geral. Ele não é um valor absoluto, pois algumas variáveis são determinadas arbitrariamente. Além disso a expectativa de desempenho é variada entre determinadas funcionalidades. Então não se pode comparar na prática duas aplicações usando o Apdex, nem mesmo se pode confiar cegamente em um número sem entender o contexto do sistema.

Outro fruto do trabalho foi coletar o desempenho médio de cada página e distribuir estatisticamente de forma que o trabalho de otimização seja concentrado nos piores casos. Essa distribuição inclui ainda a contribuição de cada fase da requisição (como mencionado acima) na duração da requisição.

Quando me juntei à equipe de desempenho, pensei que ia gastar a maior parte do tempo fazendo profiling no servidor e otimizando queries SQL. Embora isso seja importante e há pessoas fazendo isso, me surpreendeu o quanto outros fatores podem contribuir negativamente sobre o desempenho.

Descobriu-se que o tempo de carregamento dos recursos, como JavaScript e CSS, era um dos maiores impactos no carregamento de algumas das páginas mais pesadas. Isso ocorre tanto pela latência de rede quanto pelo fato de que o JIRA gera scripts e estilos dinamicamente, o que aumenta o problema quase exponencialmente, Soma-se a isso a quantidade crescente de funcionalidades e, portanto, novos scripts sendo adicionados à página e temos um sério e difícil problema.

Minha equipe tem trabalhado intensamente para que seja possível calcular com antecedência o conteúdo dos arquivos gerados dinamicamente, de forma que possam então ser gerados colocados num CDN próximo dos clientes antes do lançamento de cada versão. Dessa forma, desde o primeiro acesso do usuário teremos o melhor desempenho, isto é, baixa latência e conteúdo estático.

Mas isso não é fácil porque mesmo uma simples alteração num plugin pode afetar várias páginas do sistema. E temos de considerar ainda que na maioria dos casos os diferentes scripts incluídos numa página são concatenados em um ou mais grandes arquivos. Sem falar nos estilos que são gerados baseados em configurações dos usuários.

Outras considerações

Algo bem legal em trabalhar na Atlassian é que usamos todos os produtos da empresa internamente, logo eles precisam funcionar para que o nosso trabalho vá bem. Por exemplo:

  • JIRA Core para organizar os projetos.
  • JIRA Software para organizar o desenvolvimento.
  • JIRA ServiceDesk para as solicitações internas.
  • Confluence para documentar toda e qualquer informação: arquitetura, decisões, reuniões, planos, propostas, blog posts e as piadas também.
  • HipChat para comunicação contínua entre as pessoas e equipes em diferentes aspectos, por exemplo, existem salas dos desenvolvedores onde você pode simplesmente perguntar qualquer coisa, pedir ajuda, etc.
  • Bamboo para automatizar builds e testes usando planos dos mais variados tipos.
  • Bitbucket como repositório de código.
  • FishEye e Crucible para navegar e criar links para trechos de código e para fazer PRs (Pull Requests) onde a equipe toda discute os commits de cada um antes de incluir as alterações na branch principal através de um merge.

Algo que achei interessante é a flexibilidade do Bamboo para automação. No começo eu não havia gostado muito, preferindo Jenkins, por exemplo. Mas depois de entender melhor como ele funciona (e ainda falta muito para dominar o produto), percebi que o quanto ele tem potencial. Num caso, consegui criar um plano de testes que pega a última versão do JIRA e alguns plugins específicos, compila tudo e gera os pacotes necessários, inicia um servidor na nuvem, instala a versão gerada, executa um script bash para executar algumas chamadas REST no JIRA e depois verifica os resultados. Com exceção do bash script, tudo isso foi feito visualmente na configuração do plano do Bamboo, sem que fosse necessário alterar quaisquer projetos.

Além disso, no dia-a-dia, também sentimos na pele a qualidade e a performance – ou a falta delas – dos produtos, então tudo o que fazemos para melhorar e criar um ambiente de trabalho saudável em nosso dia-a-dia também irá refletir em todos os clientes.

Outro ponto é que tem sido um desafio para toda a empresa escalar os produtos, particularmente o JIRA. Encontrar bons desenvolvedores para contratar, lidar com a comunicação e gerenciamento de um número sempre crescente de pessoas, enfrentar os novos desafios do mundo cloud enquanto se mantém a liderança no mundo server são alguns dos desafios cotidianos aqui.

Certamente não encontramos a solução definitiva para todos esses desafios e creio que nenhuma empresa em crescimento jamais encontrou, mas posso dizer que tem sido um enorme aprendizado estar participando de tudo isso.