https://a.storyblok.com/f/270183/89011/2688e3e5c2/performance-driven-development_php.png

Então, o que é o desenvolvimento orientado para o desempenho em PHP?

Publicado em February 14, 2023

Tempo de leitura: 6 minutos

Ao longo dos meus anos de experiência em desenvolvimento web, já vi códigos de tirar o fôlego. Embora não me considere exatamente um gênio da programação, tenho um bom domínio de alguns dos princípios fundamentais que precisamos conhecer para escrever Applications limpas e eficientes.

Um dos maiores desafios que enfrentamos neste setor é fazer com que os desenvolvedores adotem o Desenvolvimento Orientado a Testes (TDD). A boa notícia é que, pelo que posso perceber, as coisas parecem estar mudando — especialmente em startups e agências.

Então, nosso trabalho aqui está concluído, certo? (obviamente que não!)

Em uma palestra que dei sobre o Xdebug, mencionei a necessidade de um “Desenvolvimento Orientado ao Desempenho”. É hora de retomar esse assunto para cumprir minha promessa de aprofundar-me nele.

Se escrevemos código com o objetivo de torná-lo mais robusto por meio de testes — por que não adotamos a mesma abordagem quando se trata de desempenho? Quem já ouviu falar em PDD? O comportamento que tenho observado no desenvolvimento profissional de software sempre remete ao clichê de que os desenvolvedores consideram

  • Segurança

  • Desempenho

...por último. Você tem prazos a cumprir. Precisa lançar o produto. Funciona. Vamos nos preocupar com esses dois pontos mais tarde. É hora de começar a analisar um exemplo.

Um dos aspectos do desenvolvimento em PHP que mais tenho observado é o fato de os desenvolvedores criticarem duramente os ORMs no Symfony e Laravel “até que façam o que eu quero”, sem pensar na consulta SQL subjacente e inserindo loops forEach em todas as oportunidades possíveis. As relações, quando não são consideradas na arquitetura, podem facilmente gerar problemas n+1 ao usar consultas. Você poderia pensar que isso não seria tão comum, mas, como já afirmei com base na minha experiência: isso está em toda parte.

Por exemplo, considere o seguinte código do Laravel:

public function getDashboardData()
{

	$dashboardData = [];
	
	foreach (Contact::all() as $contact) {
	
	   $settings = $contact->settings()->get();
	
	   foreach ($settings as $setting) {
	       if ($setting->setting_name === 'CanPhone') {
	           if ($setting->setting_value === '1') {
	               $dashboardData['canPhone']['contact'][] = $setting->contact()->with('settings')->get()->toArray();
	           }
	       }
	   }
	}
	 
	return view('dashboard', ['data' => $dashboardData]);

Antes de podermos realmente nos aprofundar nos mecanismos de como os desenvolvedores usam o ORM, nosso ponto de partida é o Contact::all(). Já vi coisas assim em todos os lugares — na cabeça dos desenvolvedores, a maneira mais fácil de compilar os dados que esse painel exibe é percorrer todas as entidades de uma tabela, ou seja, pegar tudo. Em termos de varreduras de tabela no MySQL, isso significa que você já está recuperando uma tabela inteira e, em seguida, executando mais consultas além disso para obter a relação Setting. Nesse caso (e este é um trecho de código real e anônimo, garanto a vocês), a relação `Setting` faz uma chamada para buscar o contato novamente, apesar de ele já existir dentro do contexto do loop.

Exemplos como esses talvez não sejam os que um desenvolvedor experiente escreveria — mas isso não importa. O fato é que você pode herdar ou prestar consultoria em um monólito completo que outra pessoa escreveu. Se você não tem experiência, é possível que tenha entrado diretamente no desenvolvimento de aplicativos sem primeiro entender detalhadamente como o SQL realmente funciona (em consultas mais complexas, para ser sincero, eu também não entendo). Portanto, surgirão problemas do tipo n+1 se você não souber programar um ORM para usar conceitos de SQL como LEFT JOIN e INNER JOIN de maneira eficaz.

Temos várias ferramentas para ajudá-lo a identificar problemas como esses — O Symfony tem sua excelente barra de depuração, o Laravel também tem uma, o Xdebug vem com um poderoso profiler e a Tideways agora é dona do Xhprof. O problema que vejo é que os desenvolvedores, de baixo para cima, tendem a pensar na performance por último quando se trata do trabalho cotidiano de escrever funcionalidades, em vez de usar essas ferramentas como parte do processo de escrever código e enviá-lo para Pull Requests. De certa forma, isso é bastante semelhante ao clichê de que os desenvolvedores pensam na segurança por último — quando, na verdade, uma solução mais ideal seria fazer com que toda a sua equipe levasse em consideração tanto a segurança quanto o desempenho ao escrever código PHP.

Conversei com Blackfire e Platform.sh Engenheiro de Relações com Desenvolvedores Thomas di Luccio sobre o que devemos levar em consideração ao escrever Applications que tratam o desempenho como uma preocupação de “primeira ordem”. O argumento principal, segundo ele, é que nós, como desenvolvedores, precisamos considerar o desempenho desde o nível mais básico ao escrever código, e precisamos nos dedicar mais a isso.

Isso ficou evidente quando trabalhei em uma aplicação, há muitos anos, que adotava uma abordagem só de ouvir já dá para imaginar. Em vez de levar em conta as instruções SQL e a carga de trabalho das diversas classes que utilizavam funções do SPL, como foreach() e array_map(), somado à falta de controle sobre a estrutura subjacente (que era proprietária, o que limitava o controle sobre o ORM), o Fastly foi simplesmente acoplado ao front-end. Sim, o problema foi resolvido. Mas a base de código subjacente ainda estava repleta de dívida técnica.

É hora de encontrar soluções. Tomando como exemplo nossa função getDashboardData() acima, precisamos corrigi-la. No entanto, antes de corrigi-la, precisamos analisar o que está acontecendo quando acessamos essa página do painel. Ao executar a barra de depuração do Laravel, obtemos o seguinte resultado:

Duzentas e oitenta e seis consultas SQL para carregar o painel — em 11,92 segundos. Como isso acontece?

Bem, é um clássico “n+1”.

No computador dos desenvolvedores, o painel leva 1 segundo para carregar, pois há alguns dados de teste no banco de dados. Assim que esse código entra em produção: acontece isso.

A questão é que, se você tiver essas ferramentas ativadas nos ambientes de desenvolvimento de suas equipes, esse tipo de ocorrência pode se tornar coisa do passado. O primeiro passo para controlar problemas como esse é definir seus limites. Ferramentas como Blackfire e Tideways permitem que você defina limites e até mesmo as integre aos seus pipelines, mas seja qual for a abordagem escolhida (ou seja, ferramentas gratuitas ou produtos corporativos), você ainda precisará ter expectativas sobre o que é aceitável para o seu aplicativo quando os desenvolvedores estiverem programando.

Digamos que queiramos que a meta aqui seja menos de 5 consultas e um tempo de carregamento inferior a um segundo.

Um desenvolvedor mais experiente verifica o código, identifica os problemas do tipo “n+1” e ensina o desenvolvedor júnior a obter os dados de uma só vez. Eis o resultado:

A boilerplate dashboard with an upsetting amount of queries

Duzentas e oitenta e seis consultas SQL para carregar o painel — em 11,92 segundos. Como isso acontece?

Bem, é um clássico “n+1”.

No computador dos desenvolvedores, o painel leva 1 segundo para carregar, pois há alguns dados de teste no banco de dados. Assim que esse código entra em produção: acontece isso.

A questão é que, se você tiver essas ferramentas ativadas nos ambientes de desenvolvimento de suas equipes, esse tipo de situação pode se tornar coisa do passado. O primeiro passo para controlar problemas como esse é definir seus limites. Ferramentas como o Blackfire e Tideways permitem que você defina limites e até mesmo as integre aos seus pipelines, mas seja qual for a abordagem escolhida (ou seja, ferramentas gratuitas ou produtos corporativos), você ainda precisará ter expectativas sobre o que é aceitável para sua aplicação quando os desenvolvedores a programarem.

Digamos que queiramos que a meta aqui seja menos de 5 consultas e um tempo de carregamento inferior a um segundo.

Um desenvolvedor mais experiente verifica o código, identifica os problemas do tipo “n+1” e ensina o desenvolvedor júnior a obter os dados de uma só vez. Eis o resultado:

public function getDashboardData()
{
	$dashboardData = Contact::with('settings')
	   ->whereRelation('settings', 'setting_name', '=', 'canPhone')
	   ->paginate(10)
	   ->toArray();

	return $dashboardData;
}

Ficou bem mais claro. Mas o importante é: como isso aparece na barra de depuração?

A much quicker Dashboard!

Ficou bem mais claro. Mas o importante é: como isso aparece na barra de depuração?

Quatro instruções SQL, 324 ms para carregar.

A lição deste artigo não é “aqui está um código obviamente ruim, eu o corrigi e o desempenho melhorou”. Você provavelmente perceberá que o código era ruim e precisava ser corrigido, com certeza. A lição é que, se você estiver em uma posição de liderança, as barras de depuração do Symfony e do Laravel devem estar ativadas e ser utilizadas como parte do seu processo de desenvolvimento, de modo que toda a sua equipe as utilize. Você pode definir um limite aproximado, introduzir um teste que force um método a trabalhar muito mais e avaliar o tempo de carregamento. Temos as ferramentas para fazer isso, então por que isso deveria ser uma consideração secundária?

O que é o Desenvolvimento Orientado ao Desempenho? É o processo.

Isso significa capacitar seus desenvolvedores com as ferramentas de que precisam enquanto trabalham em um recurso. Assim como a segurança, isso deve ser um elemento fundamental do ciclo de desenvolvimento e, no mundo do PHP, contamos com ferramentas de código aberto de altíssima qualidade para entregar software de qualidade.

Compartilhar:

https://a.storyblok.com/f/270183/400x385/12b3020c69/james-seconde.png
James SecondePromotor Sênior de Desenvolvimento em PHP

Sou ator formado, com uma dissertação sobre stand-up comedy, e comecei a me dedicar ao desenvolvimento em PHP por meio dos encontros da comunidade. Você pode me encontrar dando palestras e escrevendo sobre tecnologia, ou ouvindo e comprando discos curiosos da minha coleção de vinil.