Olá! Meu nome é Leonardo Gasparini, sou instrutor e desenvolvedor com mais de 10 anos de experiência trabalhando com .NET, especialmente na sustentação de projetos legados.
Audiodescrição: Leonardo é um homem pardo, de cabelos encaracolados e barba cheia. Ele veste uma camisa verde e está em um dos estúdios da Alura, com um fundo de luz azul e uma estante com acessórios de decoração.
Meu trabalho envolve corrigir erros e implementar novas funcionalidades em sistemas que já estão em produção há bastante tempo.
Uma das habilidades que consideramos fundamentais para realizar nosso trabalho é o entendimento de como aplicar boas práticas de código dentro dos projetos. Esse é justamente o propósito deste curso que estamos acompanhando. Vamos focar na aplicação de boas práticas de código, entender como esses códigos precisam ser criados, e quais são os princípios, técnicas e ferramentas que podemos utilizar para nos guiar dentro de projetos existentes. O objetivo é criar códigos que sejam mais fáceis de entender, mais fáceis de manter e que permitam que possamos ajustar partes de projetos que outras pessoas desenvolvedoras possam considerar difíceis de manter, corrigir ou mesmo implementar novas funcionalidades.
Vamos começar com a parte de boas práticas em C#.
Agora, vamos discutir uma das habilidades mais importantes no desenvolvimento de bons projetos de software: o Clean Code (código limpo). Mas o que é o Clean Code? O termo foi criado por Robert C. Martin para descrever o que significa um código elegante. E o que é um código elegante? É aquele que é fácil de ser entendido. Se conseguimos olhar para o código, para um método ou uma classe, e entender qual é o objetivo daquele código dentro do projeto, significa que foi escrito de forma elegante. Isso pode ser feito por nós ou por outra pessoa em um projeto no qual estamos inseridos. Esse é o jeito mais fácil de descrever o que significa um código elegante.
Por que devemos escrever código limpo? Qual é a base fundamental para isso? O principal motivo é que todo projeto de software enfrenta problemas ao longo do tempo. Podemos observar em um gráfico que, no início, o projeto de software tem uma complexidade de código bem baixa. O projeto é simples, fácil, e tudo funciona. Pode haver bugs, mas são fáceis de identificar e corrigir. Às vezes, poucas pessoas estão trabalhando no projeto, o que facilita a evolução do projeto para criar novas funcionalidades, resolver os bugs existentes e torná-lo mais robusto.
Com o passar do tempo, o desenvolvimento do projeto aumenta, assim como sua complexidade. Conforme esse desenvolvimento cresce, todo projeto acaba acumulando o que chamamos de dívida técnica. O que é dívida técnica? É quando desenvolvemos alguma função ou ponto do projeto que falta uma boa prática ou que funciona, mas não está adequado às regras definidas pelas empresas sobre como aquela funcionalidade deveria ser escrita. Isso pode ocorrer por conta de prazos ou falta de experiência, e essa dívida técnica vai se acumulando.
Se não for bem cuidada, a dívida técnica cresce ao longo do projeto, e isso é o principal fator que faz a qualidade do projeto cair. Em um gráfico, podemos ver um ponto marcado com um X, que representa o ponto crítico. O que é esse ponto crítico? É quando se torna tão difícil fazer a manutenção do código que fica impossível adicionar uma nova funcionalidade ou corrigir problemas antigos que surgiram desde o início do projeto. Nesse ponto, o projeto entra em uma fase na qual as pessoas desenvolvedoras começam a ter receio de fazer alterações.
Quando começamos a fazer alterações em um projeto, percebemos que qualquer modificação pode impactar outros módulos e funcionalidades. Isso pode gerar problemas em várias partes do sistema, tornando o projeto insustentável e, eventualmente, um legado difícil de modificar. Nesse ponto, temos duas opções: criar um processo para corrigir o projeto, já que ele é essencial para a empresa, ou decidir começar do zero devido aos problemas acumulados. Essa situação é comum em muitas empresas e nem sempre é vista de forma positiva, pois pode tanto resolver quanto perpetuar os mesmos problemas em novos projetos.
O principal motivo para escrever um código de maneira elegante e seguir boas práticas é reduzir a dívida técnica, mantendo a qualidade do código sem comprometer a adição de novas funcionalidades. O objetivo do Clean Code é melhorar a depuração dos projetos. Um código mais legível facilita a identificação e correção de problemas, tornando o debug mais eficiente. Embora projetos complexos apresentem desafios, o Clean Code é essencial para um debug assertivo.
Além disso, expectativas de trabalho se tornam mais visíveis e realistas quando o código é claro. Isso ajuda a prever o tempo necessário para corrigir problemas ou implementar novas funcionalidades. Um código claro também facilita a integração de novos membros na equipe, sem depender de uma única pessoa com anos de experiência na empresa.
A parte de testes, muitas vezes negligenciada, é fundamental para garantir que tudo funcione corretamente. Um projeto bem coberto por testes oferece a segurança de que a manutenção não causou impactos negativos inesperados. Os testes são um auxílio valioso, proporcionando tranquilidade ao final de uma entrega, ao invés de serem vistos como um problema adicional a ser desenvolvido.
No vídeo anterior, discutimos por que o código limpo é essencial. Agora, vamos nos concentrar em como podemos escrever código limpo na prática. Vamos explorar as convenções e os princípios que o Clean Code considera como bons exemplos em um contexto de projeto específico e discutir como criar códigos elegantes.
A primeira convenção abordada pelo Clean Code é o uso de nomes significativos. O objetivo é que, ao escrever uma classe, um método ou o nome de uma variável, deixemos claro e objetivo qual é a intenção daquele nome dentro do projeto. Vamos apresentar um exemplo ruim de como se escreve um código e um exemplo bom de como podemos aplicar e usar nomes significativos.
Quando começamos a desenvolver projetos, independentemente da linguagem, seja Python, C# ou Java, temos o hábito de nomear variáveis de forma abreviada. Por exemplo:
int d; // dias desde o último pedido
string n; // nome do produto
List<Product> p; // lista de produtos
decimal v; // valor total
Embora o projeto funcione com esses nomes, surgem problemas. Primeiro, se uma nova pessoa entrar no projeto, ela precisará se esforçar para entender o significado das variáveis, mesmo com comentários ao lado. Se precisar usar a variável em outros momentos e esquecer seu significado, terá que voltar à declaração para identificá-lo, o que pode causar perda de contexto. Além disso, fica mais difícil procurar variáveis no código. Usar "Ctrl+F" para encontrar uma variável como d
, n
ou p
é complicado, mesmo que a funcionalidade esteja correta.
A maneira correta de aplicar o Clean Code é usar nomes extensos que descrevam o objetivo da variável no projeto. Por exemplo:
int daysSinceLastOrder;
string productName;
List<Product> availableProducts;
decimal totalOrderValue;
Mesmo que o nome seja longo, ele deixa claro seu significado. Ao usar a variável novamente, saberemos imediatamente que se refere aos "dias desde o último pedido". Isso facilita o rastreamento do uso da variável no projeto. O mesmo vale para "nomeDoProduto", "produtosDisponíveis" e "valorTotalDoPedido". Assim, o objetivo da variável fica claro.
Um detalhe importante é que cada linguagem tem convenções sobre o uso de maiúsculas e minúsculas para variáveis, métodos ou classes. No caso do C#, usamos a primeira letra minúscula e, ao trocar de palavra, usamos maiúscula. Essa é a convenção do C# para separar variáveis. Outras linguagens podem ter convenções diferentes, como CamelCase ou SnakeCase.
Não se preocupe com o tamanho dos nomes. Se forem fáceis de entender e pesquisar, estão perfeitos. Evite comentários desnecessários, a menos que eles explicitem algo que não esteja claro apenas pela nomenclatura do código. Às vezes, duas variáveis podem ter nomes parecidos e objetivos semelhantes, e um comentário pode ser necessário para esclarecer informações ocultas no projeto. No entanto, se conseguir resolver isso apenas com nomes de variáveis, classes ou métodos, já é suficiente. Foque principalmente na intenção.
Qual é a intenção ao dar um nome para uma variável dentro de um projeto? Isso é fundamental para que fique claro para outra pessoa desenvolvedora que for trabalhar no projeto qual é o objetivo daquela variável. Apenas deixar um nome extenso não resolve todos os problemas. O mais importante é a intenção daquela declaração no momento em que estamos escrevendo o código.
Agora, vamos para a próxima convenção, que é o uso de funções pequenas. É interessante que o Kubob, no livro do Keycode, leva isso a alguns exageros, mas podemos simplificar. O objetivo dessa convenção é termos funções com o mínimo possível de código, mas com uma intenção clara e objetiva. Por favor, apresente um exemplo de código para que isso fique mais tangível.
Temos dois cenários: um terrível e um ainda pior, onde há um método chamado processarPedido
. Não seria possível apresentar aqui todo o código de um método de processamento de pedido, pois dentro de um e-commerce, o processamento de um pedido passa por várias etapas. No entanto, conseguimos identificar uma série de problemas se dentro de um método de processamento de pedido estivessem todas essas funções.
Se pegarmos um código que chamamos de God Class ou God Method, que são aqueles métodos ou classes que fazem tudo, dentro de um processarPedido
provavelmente teríamos a validação de cliente, a verificação de estoque, o cálculo de frete, a aplicação de desconto, o salvamento no banco de dados e o envio de e-mail. Embora tudo isso faça parte do processamento de um pedido, não é adequado que todas essas etapas estejam dentro de um único método chamado processarPedido
. Cada uma dessas funções precisa estar em métodos separados, já que cada uma tem um objetivo claro e pode ser utilizada em outros momentos. A validação de cliente, por exemplo, não ocorre apenas no momento de processamento de pedido, e a verificação de estoque é a mesma coisa.
Aqui está um exemplo de como isso pode ser problemático:
public void ProcessarPedido(Pedido pedido)
{
// Validação do cliente (5 linhas)
// Verificação de estoque (8 linhas)
// Cálculo de frete (6 linhas)
// Aplicação de desconto (4 linhas)
// Salvamento no banco (7 linhas)
// Envio de email (5 linhas)
}
Qual é o pior exemplo que pode acontecer? Às vezes, uma pessoa desenvolvedora bem-intencionada percebe esse problema de um método muito extenso com muitas responsabilidades e decide separar isso de outra forma. Ela começa a usar regiões, que são uma das práticas mais utilizadas pela comunidade C-Sharp, para dividir o código em partes, como a região de validação do cliente e a região de verificação do estoque. Isso é terrível, pois não resolve o problema de deixar o método pequeno. É como esconder a sujeira debaixo do tapete, colocando todas as separações por regiões. O código continua complicado de mexer, com muita responsabilidade no método, e os problemas persistem. Se houver um bug dentro desse método, será muito difícil fazer a manutenção e testar, pois ao testar o processamento de pedido, será necessário testar todos esses itens, como a validação do cliente e a verificação de estoque. Não há essa necessidade; processarPedido
deveria ser testado apenas naquela parte do código.
Veja como isso pode ser feito de forma inadequada:
public void ProcessarPedido(Pedido pedido)
{
#region Validação do cliente
// 5 linhas de código
#endregion
#region Verificação de estoque
// 8 linhas de código
#endregion
#region Cálculo de frete
// 6 linhas de código
#endregion
#region Aplicação de desconto
// 4 linhas de código
#endregion
#region Salvamento no banco
// 7 linhas de código
#endregion
#region Envio de email
// 5 linhas de código
#endregion
}
Qual seria a abordagem correta? Ter uma série de serviços que seriam injetados, e esses serviços seriam métodos separados, onde cada um executaria uma parte do processamento do pedido. Assim, teríamos um validador com um método de validar, que faz a verificação de todos os itens do pedido. Haveria um serviço de estoque que verificaria a disponibilidade, sendo outro método que passaria por outro teste e estaria isolado. O encapsulamento estaria perfeito. Teríamos uma variável que definiria o cálculo de frete, e o mesmo para o desconto. Haveria um momento de salvar no banco de dados, seguindo o padrão repository, e depois um serviço final que enviaria o e-mail. Aqui, cada um é um método.
O mais interessante desse exemplo é que o processarPedido
acaba sendo apenas um controlador de todas essas etapas. Não há lógica específica sobre como o e-commerce realizará todas essas etapas; cada uma delas está dividida entre os serviços, validadores e repositórios, que cuidarão de cada método separado para cada objetivo, como fazer as validações ou continuar a regra de negócio, o processamento de um pedido, para que a função seja elaborada corretamente.
Aqui está um exemplo de como isso pode ser feito corretamente:
public void ProcessarPedido(Pedido pedido)
{
_pedidoValidator.Validar(pedido);
_estoqueService.VerificarDisponibilidade(pedido.Items);
var frete = _freteCalculator.Calcular(pedido);
var desconto = _descontoService.Aplicar(pedido);
_pedidoRepository.Salvar(pedido);
_emailService.EnviarConfirmacao(pedido);
}
Sempre que encontrarmos um método muito grande, podemos identificar que ele provavelmente poderia ser dividido em uma série de serviços. Essa é uma excelente prática. Se começarmos a ver uma série de funções dentro de um método maior, significa que estamos escrevendo um código elegante de maneira correta.
Acabamos de ver alguns dos princípios do Clean Code. Isso já nos ajuda a identificar alguns problemas que podem existir dentro dos projetos ou, se estivermos criando novos projetos, a começar a identificar quando estamos começando a fazer um código um pouco desorganizado. No próximo vídeo, continuaremos vendo outras convenções e examinando como podemos escrever códigos elegantes, além de explorar outras convenções que podemos utilizar.
O curso C#: Boas práticas de código e projeto possui 253 minutos de vídeos, em um total de 49 atividades. Gostou? Conheça nossos outros cursos de .NET em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Matricule-se no plano PLUS e garanta:
Mobile, Programação, Front-end, DevOps, UX & Design, Marketing Digital, Data Science, Inovação & Gestão, Inteligência Artificial
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você participa de eventos exclusivos, pode tirar dúvidas em estudos colaborativos e ainda conta com mentorias em grupo com especialistas de diversas áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Para estudantes ultra comprometidos atingirem seu objetivo mais rápido.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.
Conecte-se ao mercado com mentoria personalizada, vagas exclusivas e networking estratégico que impulsionam sua carreira tech para o próximo nível.