Por que os desenvolvedores são tão lentos: problemas comuns e suas soluções

Olá Habr! Apresento a você a tradução do artigo Por que as equipes de desenvolvimento são lentas: atolamentos e soluções comuns de software por Eric Elliot.



Se você gosta de ouvir mais do que ler, no formato de áudio, a tradução está disponível no Yandex.Music e no Apple Podcasts.Vamos

ver o que causa interrupções no processo de desenvolvimento de software e o que você, como gerente, pode fazer sobre isso. Pode haver muitas razões, portanto nossa lista, é claro, estará longe de ser exaustiva. Em vez disso, vamos nos concentrar em alguns dos problemas mais comuns :

  • Expectativas irrealistas
  • Demasiados bilhetes abertos
  • Escopo descontrolado de tarefas
  • Revisão do código de acumulação
  • Má preparação
  • Desenvolvedores Burnout
  • Insetos
  • Rotatividade de pessoal

O desenvolvimento lento não é a raiz do problema. Este é um sintoma dos outros problemas listados. Em 100% dos casos, se a equipe de desenvolvimento trabalha muito devagar, a culpa é do líder. Mas a boa notícia é que você pode corrigi-lo. Vejamos cada um dos itens com mais detalhes para entender o que podemos fazer com cada um deles.

Expectativas irrealistas


A maioria dos problemas com a produtividade dos desenvolvedores geralmente não afeta o próprio desenvolvimento. É um problema de nossa percepção do processo de desenvolvimento como gerentes e partes interessadas.

A parte mais difícil do trabalho de um gerente é entender que escrever código leva o tempo que for necessário e tentar acelerar esse processo só o tornará mais lento e aumentará o número de bugs. Paciência é o nosso tudo.

Na maioria das vezes, o problema com a velocidade do trabalho não é que a equipe não seja produtiva o suficiente, mas que enfrenta grandes expectativas. E isso é inteiramente sua responsabilidade. Se a pressão vem de uma liderança mais alta, você não formou a visão correta da situação. Se a pressão vier de você, continue a ler.

Muitas vezes esquecemos que o software que criamos é algo fundamentalmente novo. Se você já possui um software que faz o mesmo, compre, use, importe o módulo etc. Não há necessidade de reescrevê-lo do zero. O novo software é único. Ele faz algo novo ou faz algo diferente. É então que a criamos. E como ainda não fizemos isso, como sabemos quanto tempo levará?

Os construtores constroem paredes pré-fabricadas no mesmo ritmo e, portanto, podem fornecer estimativas mais ou menos precisas com base em observações. Os desenvolvedores de software não têm dados confiáveis ​​para confiar. Esse problema é exacerbado pelas diferentes velocidades de diferentes desenvolvedores e pode variar por uma ordem de magnitude.

Como escreve Steve McConnell, autor de The Perfect Code: “A conclusão de que existe uma diferença significativa entre a produtividade de diferentes programadores foi confirmada por muitos estudos de desenvolvedores profissionais (Curtis 1981, Mills 1983, DeMarco e Lister 1985, Curtis et al. 1986, Curtis et al. 1986, Card 1987 Boehm e Papaccio 1988, Valett e McGarry 1989, Boehm et al. 2000). Não temos dados suficientes para prever quanto tempo levará para concluir nosso projeto. Vamos descobrir qual é a escala e a complexidade que já estão começando a funcionar e esse processo geralmente é repleto de muitas surpresas. "O desenvolvimento não é apenas planejamento, mas também pesquisa, por mais que tentemos prever tudo".
« 90 10 , . 10 90 »
— , Bell Labs

Existem várias razões para altas expectativas que um gerente pode controlar. Uma das razões fundamentais é medir as coisas erradas.
Você pode estar familiarizado com o famoso ditado de Peter Drucker: "O que é medido é controlado."

E é claro que este é um ótimo conselho. Claro que devemos medir! Mas sentimos falta da essência dessa citação, além disso, viramos seu significado de cabeça para baixo.

A idéia toda é: "O que é medido é controlado, mesmo que seja medido e tentando controlar é completamente inútil, mesmo que prejudique os objetivos da organização."

Dois exemplos de coisas que não valem a pena medir:

  1. Gráficos de burndown preditivos mostrando um gráfico do número de tickets abertos que prevêem a data final de um projeto com base em medições recentes da velocidade do trabalho;
  2. O número de tickets fechados pelo desenvolvedor, mostrando quantas tarefas um desenvolvedor individual concluiu.

Medir essas duas coisas custa inúmeras empresas a enormes perdas devido à perda de produtividade, funcionários e outros custos.

Diagramas de progresso da tarefa


Muitas ferramentas de software tentam prever a data de conclusão do projeto, com base na escala atual de tarefas e na velocidade do trabalho. O problema é que nenhum deles leva em consideração o volume inexplorado de tarefas. Além disso, isso é impossível, porque o tempo necessário para fechar uma tarefa pode variar em uma ordem de magnitude para tarefas diferentes, o que pode distorcer significativamente os valores médios que foram calculados para tickets já fechados.

Se você definir um prazo com base na data do gráfico, considere que não cumpriu os prazos. A única coisa que pode salvá-lo é jogar tantas tarefas fora de escopo no futuro quanto possível.

Quando você baseia suas notas em informações incompletas, você e sua equipe definitivamente pagam por isso. Estimativas irrealistas criam expectativas irrealistas. Isso pode ser um verdadeiro desastre se você também compartilhar essas classificações com a equipe de marketing, os clientes e a imprensa.

Nem todos os gráficos são maus. Aqueles que não tentam prever o futuro podem ser úteis. Eles podem nos alertar sobre a propagação do projeto e explosões combinatórias quando você perceber que o número de tickets aumenta em vez de diminuir ou se mover para cima e para baixo. Diagramas de tarefas úteis demonstram tarefas já fechadas de maneira realista, em vez de tentar prever o futuro.

Um bom indicador é uma curva que tem altos e baixos, mas no geral se move para baixo, o que mostra uma diminuição no número de tarefas abertas até o final do projeto.

Cronograma do projeto em que o número de tickets é reduzido

Um projeto que sofre de um escopo de crescimento excessivo será representado por uma curva que se inclina para cima.

Cronograma de um projeto se afogando em novas tarefas

Lembre-se de que o objetivo de observar essa curva não é tentar alterá-la, mas reconhecer e resolver problemas subjacentes. Não queremos que os programadores parem de abrir novos tickets.

O objetivo é a transparência dos processos, não uma bela curva descendente.

Cuidado com o princípio de Goodhart : "Se uma dimensão se torna um objetivo, deixa de ser útil".

Nenhuma previsão é prejudicial. Quando você tem um prazo final (por exemplo, está tentando lançar um jogo antes da Black Friday), pode controlar sistematicamente o escopo com base na velocidade média do trabalho, para saber quando começar a apará-lo. Se a previsão indicar que você terminará antes de dezembro, confie nela. Chegou a hora de priorizar e reduzir.

A regra geral para previsões desse tipo é:
"Se a previsão disser que você pode fazer algo em uma determinada data, não acredite, se ela disser que não pode fazê-lo, acredite.

Bilhetes fechados por um programador


A idéia de recontar todas as tarefas executadas por um programador e comparar esse valor com o valor médio é muito tentadora. Mas exorto você a resistir a essa tentação. Existem várias maneiras melhores de coletar dados de produtividade do desenvolvedor.

Existem duas falhas fundamentais no cálculo de tarefas fechadas. Primeiro, as tarefas não são idênticas em complexidade e importância e, de fato, o valor do trabalho depende da lei do poder. Um pequeno punhado de tarefas é responsável por uma ordem de magnitude mais significativa que a "média". É como a diferença entre a fundação de um arranha-céu e a última unha entupida. Assim, simplesmente contando o número de tickets fechados, é impossível saber exatamente o valor de um funcionário.



Muitos anos atrás, trabalhei em um carrinho de compras para um líder global de varejo. Certa vez, parei de escrever código e fechei os tickets em Jira e adicionei outro ticket: “Estudo de usabilidade”.

Estou trabalhando em um redesenho de cesto há mais de um ano e a data de lançamento está se aproximando rapidamente. Até aquele momento, nenhum teste de usabilidade do novo processo de pedido era conduzido pelo usuário, portanto demorou uma semana. Demos acesso antecipado aos milhares de apoiadores mais leais e os entrevistamos para obter feedback.

Analisei os resultados e encontrei uma tendência alarmante em pesquisas e registros: a frequência de usuários que saíam do site no estágio da cesta era muito alta e foi expressa em um número de dois dígitos. Um verdadeiro desastre estava chegando! Então, comecei a trabalhar e planejei gravar novos testes de usabilidade com uma presença pessoal. Sentei os novatos atrás de nossa nova cesta, dei algumas tarefas e os deixei sozinhos com o site. Eu não disse nada, apenas observei eles usarem a nova interface.

Percebi que, no processo de fazer um pedido, as pessoas têm dificuldades com erros nos formulários. Tendo esses dados, corrigi levemente nosso projeto de código aberto no github (sem notar nada no Gira). Depois de algum tempo, realizamos outro teste. É muito menos provável que os usuários deixem a página da cesta: a diferença de renda para a empresa é de US $ 1 milhão por mês.

Enquanto isso, meus colegas fecharam 10 a 15 bilhetes. Você poderia argumentar que eu poderia obter mais tickets de teste de usabilidade para refletir a realidade. Mas então eu teria que fazer mil ingressos adicionais, o que criaria apenas ruído e exigiria muito tempo.

Outra razão pela qual a contagem de tickets fechados é ineficaz é que os membros da equipe mais eficazes também são as pessoas para quem todos estão buscando ajuda. Eles sabem mais sobre a base de código, ou são ótimos desenvolvedores ou têm excelentes habilidades de comunicação. Eles ajudam você a obter o registro de pedidos pull, revisar o código de outros programadores, treinar e orientar seus colegas de equipe. Eles são os mais produtivos da equipe, porque ajudam o restante da equipe a dobrar a velocidade do trabalho. Talvez os tickets que eles fechem sejam a criação de estruturas ou bibliotecas que aumentam a produtividade de toda a equipe. Eles fazem a maior parte do trabalho, enquanto o resto é reconhecido.

Se você não for cuidadoso, veja a contribuição dos seus desenvolvedores mais produtivos. A melhor maneira de descobrir o que os programadores estão fazendo para um projeto é perguntar a eles. Pergunte à equipe quem eles acham que é o colega mais útil.

Geralmente, as informações refletidas nesse feedback são muito diferentes dos dados que podem ser obtidos simplesmente contando o número de tickets.

Colete dados de desempenho, mas não aborde cada desenvolvedor com uma medida. O desenvolvimento é um esporte de equipe e cada participante do processo desempenha um papel nele. Não existe um método mágico único que seja adequado para avaliar tudo, sem exceção.

Muitas tarefas abertas


Parece simples - abra um ticket no rastreador e siga em frente. Mas cada tarefa no rastreador requer um ciclo de processamento inteiro.

Eles precisam ser classificados, priorizados e atribuídos a um artista antes que os desenvolvedores possam começar a implementá-los. Este trabalho é repetido sempre que os desenvolvedores fecham uma tarefa e escolhem a próxima. Se você tem um gerente de projeto ou um scrum master, ele faz isso toda vez que prioriza novamente a lista de tarefas (o que geralmente acontece no início do sprint ou apenas uma vez a cada duas semanas).

Em seguida, o desenvolvedor precisa se aprofundar no contexto da tarefa, entender a essência do problema, decompor tarefas complexas em subtarefas e somente então você pode finalmente começar a executar.

Criar e ler tickets é muito trabalhoso, mas é um trabalho falso. Esta é uma meta tarefa. Ela é necessária para iniciar tarefas reais. Sozinhos, eles têm valor zero. E isso leva tempo toda vez que os programadores escolhem a próxima tarefa. Quanto menos tickets ao mesmo tempo estiverem no rastreador, melhor. Quanto menos tarefas de baixa prioridade estiverem na lista de pendências, maior a chance de o desenvolvedor escolher uma tarefa de alta prioridade.

Se houver um bug mencionado apenas por um usuário, qual a importância para nós? Ele tocou uma pessoa, mas temos erros que mais pessoas notaram? Existem novos recursos que serão mais úteis do que corrigir esse bug?
Provavelmente sim.

Remova o ruído da lista de pendências. Exclua o que você não planeja fazer no futuro próximo.
Se isso for realmente importante, adicione mais tarde quando houver tempo para isso.

Tamanho da tarefa não controlada


Eu gosto de pedir aos desenvolvedores da minha equipe que dividam o trabalho em tarefas que eles podem concluir em um dia. Isso é mais complicado do que parece, porque requer a capacidade de dividir tarefas complexas em tarefas pequenas, que também podem ser testadas separadamente do restante do aplicativo.

Exemplo: você está executando um novo processo de checkout em um site. Você não precisa misturar componentes da interface do usuário, gerenciamento de estado e comunicação com o servidor em um commit gigante envolvendo 13 arquivos, todos profundamente relacionados à base de códigos atual, porque o resultado será uma enorme solicitação de recebimento difícil de revisar e mesclar.

Em vez disso, comece com um módulo de estado da cesta do lado do cliente testado independentemente e faça uma solicitação de recebimento para isso. Em seguida, crie a API do servidor e faça um PR separado para ele também. Em seguida, escreva um componente da interface do usuário que importe o estado do módulo e se comunique com a API do servidor. Cada uma dessas tarefas pode ser dividida em tarefas separadas, embora tudo isso seja essencialmente um grande recurso. Como um bônus, as tarefas podem ser dispersas entre vários programadores e acelerar o desenvolvimento, aproveitando o tamanho da equipe.

O comutador de recursos tornará esse processo mais fácil e seguro, permitindo que você desative a funcionalidade desenvolvida até que esteja pronta para inclusão na produção.

Nota:Não tente fazer isso sem uma boa cobertura dos testes de fumaça. Você deve ter certeza de que não quebrou nada implantando recursos semi-acabados. Certifique-se de verificar como funciona, ligado e desligado.

Acumulação de tarefas por revisão de código


Quando os desenvolvedores se esforçam mais do que podem engolir, o resultado é uma enorme solicitação de recebimento aguardando revisão e verificação.

Esta é a fase de integração em “Integração Contínua” (IC). O problema é que, quanto mais o PR permanecer aberto, mais tempo será gasto nele. Os desenvolvedores irão abri-lo para ver se eles podem ajudar a controlá-lo. Eles deixarão comentários, solicitarão alterações e a solicitação retornará ao autor para fazer alterações e aprová-las. Enquanto isso, toda essa solicitação de pull acontecendo se afastará do mestre.
Quando vários desenvolvedores costumam fazer confirmações muito grandes, o número de solicitações pull começa a crescer como uma bola de neve e a integração se torna cada vez mais complicada.

Exemplo: Bob faz alterações em um arquivo que Jane também governa, mas ainda não hipotecou. A solicitação de recebimento de Bob é realizada primeiro e a PR Jane se torna uma confirmação mais distante do mestre. Agora ela não pode manter sua ramificação até resolver todos os conflitos com o código de Bob. Multiplique essa situação pelo número de programadores que trabalham com o mesmo código no seu projeto. Esses “engarrafamentos” causam um excesso de ações.

Calculamos o número de tais ações no cenário padrão:

  • Bob e Jane começam a trabalhar no mesmo ramo (0 ações)
  • Bob faz alterações e se compromete com seu ramo. Jane faz o mesmo (2 ações)
  • O código de Bob vai dominar. Jane baixa as alterações de Bob e descobre um conflito. Ela corrige e confirma o resultado em seu tópico. (3 ações)
  • Jane abre uma solicitação de recebimento. Bob observa que suas alterações no código dele quebrarão algo que ela não levou em consideração. Jane faz alterações com base nos comentários de Bob e confirma o código novamente (4 ações)
  • PR Jane finalmente se fundem. Apenas 4 ações.

Agora, considere uma situação em que as confirmações sejam menores e as solicitações de recebimento piscarão mais rapidamente:

  • Bob faz uma pequena alteração e seu código cai no mestre (1 ação)
  • Jane baixa a nova versão do assistente e escreve seu código levando em consideração as alterações de Bob. (2 ações)
  • Desde que cometer Jane também é pequeno, ele é rapidamente mantido no mestre. Total apenas duas ações.

Quando criamos PRs pequenos, reduzimos significativamente a necessidade de refazer o código, causado por conflitos e complexidade do código.

Má preparação


O setor de TI é terrível em termos de treinamento e suporte. As universidades ensinam detalhadamente algoritmos que já estão embutidos em bibliotecas padrão e apenas alguns programadores os criam do zero.

Embora os princípios básicos do desenvolvimento, como os princípios de abstração, conectividade e engajamento, modularidade versus design monolítico, trabalhem com módulos, composição de funções, composição de objetos, design de estruturas e arquitetura de aplicativos, sejam negligenciados. Devido ao crescimento explosivo da indústria, aproximadamente metade dos desenvolvedores tem menos de cinco anos de experiência e 88% dos especialistas acreditam que o treinamento não os prejudicaria.

As equipes trabalham devagar porque têm um entendimento ruim do que estão fazendo e ninguém quer ensiná-las.

Nossa tarefa como gerentes é contratar especialistas experientes que possam orientar nossas equipes e alocar tempo para que eles façam isso.

O que pode ser feito:

  • Revisão de código: os desenvolvedores aprendem muito aprendendo o código um do outro
  • Criando pares de engenheiros seniores e juniores: não é necessário criar pares permanentes, uma união única para resolver um problema específico funciona bem.
  • Momento especial dedicado à orientação: contrate profissionais experientes que gostam de aprender e se comunicar bem e que tenham tempo para compartilhar experiências com desenvolvedores juniores. Isso ajudará os últimos a entender como eles desenvolvem suas habilidades.

Esgotamento


Fazer sua equipe se esgotar é um grande revés para o líder do que não cumprir os prazos.

O burnout é um problema sério que pode levar à perda de desenvolvedores, rotatividade de funcionários, custos enormes, aumento do fator de graves ( fator de barramento - uma medida da concentração de informações entre os membros individuais do projeto; o fator significa o número de participantes do projeto, após a perda da qual o projeto não pode ser concluído pelos demais participantes) .

Mas, mais importante, o esgotamento leva a problemas de saúde. As consequências do burnout podem levar à desestabilização do corpo e até à morte por um ataque cardíaco ou derrame. No Japão, esse fenômeno é tão difundido que eles até têm uma palavra especial: "karoshi".

O líder pode esgotar toda a equipe, anulando completamente sua produtividade. O problema de esgotar equipes inteiras é especialmente comum no setor de desenvolvimento de jogos de computador, onde a Black Friday é quase sempre um prazo difícil.

Infelizmente, “morra, mas faça” é um princípio comum de organização do trabalho dessas equipes, embora os gerentes raramente percebam os perigos dessa abordagem.

Em vez de forçar os desenvolvedores a trabalharem mais, os gerentes devem reconhecer que 100% da responsabilidade pelo cumprimento dos prazos cabe ao gerenciamento, não aos desenvolvedores.

O prazo final é mais fácil se você usar as seguintes técnicas:

  • Priorize melhor as tarefas e reduza os escopos
  • Agilizar processos
  • Reconhecer e refatorar confusão de código

Rotatividade de pessoal


Os dados coletados pelo Linkedin em 2018 mostraram que a rotatividade da equipe de TI é superior à de qualquer outro negócio. E isso é ruim, porque causa o risco do fator baixo, o risco de você perder os principais especialistas do seu projeto.

Muitas empresas não dão importância à retenção de especialistas. Vamos dar uma olhada em quanto custa a rotatividade de pessoal.

A colocação de vagas custa de 15 a 30 mil dólares. O tempo do engenheiro custa em média US $ 90 por hora. Multiplique isso por cerca de 50 entrevistas e muitas, muitas horas para responder às perguntas do novato e ajudá-lo a criar raízes na equipe. Assim, já gastamos 50 mil dólares, mas isso não é tudo.

Pode levar até um ano para um novo funcionário atingir o nível do desenvolvedor que ele substituiu. Com a primeira vez, ele cometerá muitos erros e gastará muito tempo corrigindo-os.

Assim, contratar e treinar um novo desenvolvedor, custos de oportunidade e perda de produtividade de uma equipe que precisa treinar um iniciante por algum tempo e, ao mesmo tempo, realizar parte de seu trabalho é quase 90% do salário de um desenvolvedor que partiu. Encontrar um substituto pode levar vários meses e, em seguida, levará mais tempo para o iniciante atingir sua eficácia total.

Isso tudo consome muito tempo e as equipes grandes sofrem constantemente com o trabalho em andamento, porque, de acordo com uma pesquisa de 2019 da Stack Overflow, 60% dos desenvolvedores mudaram de emprego nos últimos dois anos.

Quando o desenvolvedor finalmente começar a trabalhar com a máxima eficiência, você a perderá.

Como evitar a fluidez? Aqui estão algumas dicas de várias fontes:

  • Pague honestamente
  • Aumente seu salário regularmente
  • Deixe as pessoas passarem férias longas
  • Oferecer trabalho remoto
  • Mantenha expectativas realistas
  • Forneça tarefas que serão de interesse do desenvolvedor
  • Não deixe a pilha de tecnologia ficar velha demais
  • Oferecer treinamento e oportunidades de carreira
  • Fornecer benefícios para a saúde
  • Não force os desenvolvedores a trabalhar mais de 40 horas por semana
  • Fornecer aos funcionários equipamentos modernos

Insetos


Se você acha que não tem tempo para implementar um processo de desenvolvimento de alta qualidade, não pode ficar sem ele.

De acordo com o Avaliando Tecnologias de Engenharia de Software (David N. Card, Frank E. Mc Garry, Gerald T. Page, 1978), processos bem otimizados podem reduzir erros sem aumentar os custos. O principal motivo é que, de acordo com outro livro, "Avaliações de software, benchmarks e práticas recomendadas" (Casper Jones 2000), a detecção e correção de defeitos é uma das tarefas mais demoradas e caras no desenvolvimento.

Os bugs são notórios por causar a necessidade de processamento de código e, quanto mais tarde você os encontrar, mais caro será corrigi-los. Quando um desenvolvedor é instruído a corrigir um bug já descoberto na produção, isso geralmente o afasta do que estava fazendo. No livro “Um Estudo Diário de Troca e Interrupção de Tarefas” (Mary Czerwinski, Eric J. Horvitz, Susan Wilhite), diz que uma tarefa da qual estamos distraídos pode levar o dobro do tempo e conter o dobro de erros, o que sugere que os bugs de alta prioridade são, de certa forma, contagiosos: corrigindo um, é provável que geremos novos.

Os erros na produção também exigem que prestemos mais atenção ao suporte e são usuários muito irritantes e cansativos, o que acabará custando dinheiro. Você precisará investir na correção da funcionalidade antiga em vez de criar uma nova.

Um bug encontrado no estágio de desenvolvimento pode ser corrigido em alguns minutos, enquanto um bug encontrado na produção passará por muitas fases adicionais: relatar um bug, verificar, priorizar, nomear um artista e finalmente desenvolver.

Mas isso não é tudo. Esse bug terá sua própria confirmação, solicitação de recebimento, código de revisão, integração e possivelmente até sua própria implantação. E, em qualquer estágio, alguns testes podem cair e todo o ciclo de CI / CD terá que ser iniciado novamente.

Como já mencionado, um erro na produção custará muito mais do que um erro encontrado durante o desenvolvimento.

As dicas a seguir ajudarão a melhorar a qualidade do processo.

  • Diminua a velocidade para acelerar. Lento significa ininterrupto, ininterrupto significa rápido.
  • Realize uma revisão do projeto. A combinação de verificação de código e requisitos permite capturar até 70% dos bugs.
  • Revisão do código postal. Código bem testado é 90% mais fácil de manter. Uma hora de revisão economiza 33 horas de suporte. Programadores que realizam revisões de código são 20% mais produtivos.
  • Use a abordagem TDD. Reduz o número de bugs em 30 a 40%.
  • CI/CD. , , . , . CI/CD .
  • Aumente a cobertura do teste . Seu processo de CI / CD deve executar testes e parar se pelo menos um deles travar. Isso ajudará a evitar a implantação de bugs na produção e a economizar muito dinheiro e tempo. Seu objetivo é ter pelo menos 70% de cobertura, mas tente ficar perto de 80%. Ao se aproximar de 100%, você notará que a cobertura de processos importantes do usuário com testes funcionais fornecerá mais do que um aumento adicional nos testes de unidade.

Conclusão


Existem muitas maneiras de influenciar o desempenho da equipe, incluindo:

  • Defina expectativas realistas
  • Acompanhe e controle o número de tarefas abertas
  • Controlar tamanhos de tarefas
  • Não permita que tarefas sejam acumuladas pela revisão de código
  • Treinar desenvolvedores
  • Proporcionar um bom equilíbrio entre trabalho e lazer
  • Implementar processos de desenvolvimento eficazes
  • Preste atenção à retenção de funcionários

All Articles