Desenvolvimento com o ecossistema Rails: acelere nas retas e diminua nas curvas.

Desenvolvedores Ruby On Rails são conhecidos pela facilidade em construir aplicações, escrever código e realizar testes. No entanto, frequentemente deixamos de abordar um aspecto fundamental ao finalizar um projeto: a otimização. Especialmente para estagiários, alguns juniores e projetos que não se enquadram nesse contexto, a otimização pode ser deixada de lado por falta de prazo, preocupação ou sensação de que não é uma prática necessária para o código.

No entanto, ao escrever código, é crucial levar em conta o tempo de resposta da API, o tempo de busca no banco de dados e o tempo de carregamento de telas, já que essas questões fazem toda a diferença tanto para o usuário final quanto para o chefe (se você não acha isso, talvez seja hora de repensar seus conceitos kkkkk). A otimização é uma prática que vai além do desenvolvimento de código e se estende a outras áreas relacionadas às demandas da empresa e sistemas que têm contato direto com usuários. Além disso, é uma prática que condiciona o cérebro a sempre buscar a melhor forma de entrega da atividade.

Neste artigo, apresentarei algumas situações rotineiras que você pode encontrar durante o desenvolvimento de projetos pessoais ou demandas de trabalho. Abaixo, veremos algumas aplicações práticas de otimização no Rails.

1 - Utilize o Active Record de forma eficiente: o Active Record é uma das ferramentas mais poderosas do Rails para trabalhar com bancos de dados. Imagine que sua tabela usuário no banco possui mais de 1 milhão de registros e você precisa retornar o nome “João” em ordem alfabética pelo sobrenome, com uma simples linha de código resolveríamos esse problema:

User.where("name LIKE '%João%'").order(:last_name)

Essa consulta irá buscar todos os registros de usuários e filtrar pelo nome "João". Irá ordenar os resultados pela coluna "last_name". Porém isso pode ser bastante ineficiente para uma tabela com muitos registros, já que estaremos buscando todos os registros para depois filtrar e ordenar.

Aplicando o Active Record podemos escrever da seguinte maneira:

User.where("name LIKE '%João%'").order("last_name ASC").pluck(:id, :name, :last_name)

Nesse caso, utilizamos o método "pluck" para buscar apenas as colunas que precisamos (id, name, last_name) e evitamos buscar todos os registros da tabela. Também utilizamos a sintaxe "order(column_name ASC)" para ordenar os resultados em ordem alfabética crescente pelo sobrenome. Essas mudanças tornam a consulta muito mais eficiente e rápida, e arrisco dizer que até mais legível (abraço do Uncle Bob rs).

2 - Aplique cache: No Rails, é possível utilizar diversas técnicas de cache para armazenar informações em memória, como o cache de fragmentos, cache de página inteira e cache de ações. Agora temos uma aplicação Rails que possui uma página que lista os últimos 10 posts de um blog. Essa página é acessada com frequência e cada vez que um usuário acessa a página, uma consulta ao banco de dados é feita para buscar os últimos 10 posts.

def index @posts = Post.order(created_at: :desc).limit(10) end

Observe que, a cada vez que um usuário acessar a página, uma consulta ao banco de dados será feita para buscar os últimos 10 posts do blog. Algo bastante ineficiente para uma página com muitos acessos, já que estaremos realizando a mesma consulta diversas vezes.

Podemos fazer da seguinte forma, utilizando cache, para otimizar:

def index @posts = Rails.cache.fetch('latest_posts', expires_in: 5.minutes) do Post.order(created_at: :desc).limit(10) end end

Utilizamos o método "fetch" do cache do Rails para armazenar em cache os resultados da consulta ao banco de dados por 5 minutos. Dessa forma, quando um usuário acessar a página novamente dentro desses 5 minutos, o resultado será buscado no cache, evitando a consulta ao banco de dados.

3 - Agora vamos complicar um pouco mais e vamos falar de consulta SQL, mais especificamente, em um banco não relacional. Vamos supor que temos uma aplicação Rails que possui uma página de busca de produtos. Essa página permite que o usuário pesquise por produtos utilizando vários critérios, como nome, categoria, preço, entre outros. Veja o código abaixo:

def search @products = Product.where("name LIKE '%#{params[:search]}%' OR category LIKE '%#{params[:search]}%'") @products = @products.where(category: params[:category]) if params[:category].present? @products = @products.where(price: params[:price]) if params[:price].present?

... outras condições de busca ...

end

Percebemos que mesmo utilizando um conceito que deveria ser otimizado não é a melhor opção. Estamos utilizando o Active Record para realizar a busca dos produtos. No entanto, quando lidamos com uma grande quantidade de dados e múltiplos critérios de busca, a performance da consulta pode ser bastante prejudicada. Podemos otimizar da seguinte forma:

def search query = { bool: { must: [ { match: { name: params[:search] } }, { match: { category: params[:category] } }, { match: { price: params[:price] } } # ... outras condições de busca ... ] } }

@products = Product.search(query).records end

Agora utilizei o Elasticsearch para otimizar. Utilizei a biblioteca "elasticsearch-ruby" para realizar a construção da query de busca, que é muito mais poderosa e flexível do que a utilizada pelo Active Record. Além disso, o Elasticsearch é otimizado para realizar buscas em grande escala e com múltiplos critérios, o que torna a consulta muito mais rápida e eficiente.

Conclusão:

Com base nas aplicações práticas de otimização no Ruby On Rails apresentadas neste artigo, podemos concluir que a otimização é uma prática fundamental para garantir a eficiência e escalabilidade de nossas aplicações. Ao desenvolvermos aplicações Rails, devemos sempre nos preocupar com o tempo de resposta da API, o tempo de busca no banco de dados e o tempo de carregamento de telas, buscando sempre formas de otimizar o código e melhorar a performance da aplicação.

Além disso, a otimização é uma prática que deve se estender além do desenvolvimento de código e estar presente em outras áreas relacionadas às demandas da empresa e sistemas que têm contato direto com usuários. A otimização é uma habilidade importante para desenvolvedores de todas as habilidades, desde estagiários até seniores, e é uma prática que condiciona o cérebro a sempre buscar a melhor forma de entrega da atividade.

Ao implementar as técnicas de otimização apresentadas neste artigo, podemos melhorar significativamente a performance e escalabilidade de nossas aplicações, garantindo uma experiência mais rápida e eficiente para os usuários. A otimização não deve ser deixada de lado em prol de outras atividades, e deve ser considerada uma prática fundamental no desenvolvimento de aplicações Rails.

Boas dicas!

Eu usei ruby on rails no meu primeiro emprego, tinhamos um CMS pra sites de prefeitura em rails, enquanto os outros sites que faziamos era usando PHP (com um framework próprio).

Incrível como era fácil fazer páginas novas, implementar ideias do cliente e reproduzir as alterações de um projeto em outros com facilidade no rails em comparação com o PHP.

Na época eu não entendia métodos como o pluck pra "arrancar" apenas as colunas de interesse. O fetch é uma sacada muito boa que eu sinto falta em outras linguagens.

Quanto a sua dica para a busca, não entendi como o

must: [
{ match: { name: params[:search] } },
{ match: { category: params[:category] } },
{ match: { price: params[:price] } }
]

vai otimizar o query. Ele ainda realiza um LIKE no banco de dados?

vai ficar assim:

def search
    @products = Product.where("name LIKE '%#{params[:search]}%' OR category LIKE '%#{params[:search]}%'")
    @products = @products.where(category: params[:category]) if params[:category].present?
    @products = @products.where(price: params[:price]) if params[:price].present?
    # ... outras condições de busca ...
end
Gostei muito do post, muito interessante.