en pt

Tag: csharp

Boas Práticas De Programação Para Os Apressados

Photo by Ales Nesetril on Unsplash

NOTA: Este post foi originalmente escrito para o blog da the SubMain. Você pode ler o artigo original no site deles, em inglês. Quando estiver por lá, baixe e experimente o CodeIt.Right.

Um dos tópicos em desenvolvimento de software que me interessa muito são boas práticas de codificação. Eu estou sempre pesquisando e buscando maneiras de aperfeiçoar meu trabalho e entregar valor de forma rápida e consistente.

Pode ser meio espinhoso definir o que “é realmente uma boa prática”. Há pessoas que inclusive sugerem aposentar o termo! Mas um ponto em que praticamente todos concordam é: descobrir e implementar estratégias - não importa o nome que você coloca nelas - para melhorar o resultado do seu trabalho é algo que qualquer programadora ou programador que faz jus a esse nome deveria fazer continuamente.

Claro, não existe almoço grátis. A adoção de uma boa prática leva tempo, o que você provavelmente não tem muito sobrando para começo de conversa. Isso sem mencionar a gerência, nem sempre muito animados a tentarem coisas novas.

Então, o que fazer se a sua equipe de desenvolvimento está sofrendo com a baixa qualidade de uma base de código, mas não tem tempo para implementar as boas práticas que remediariam a situação?

A resposta que eu ofereço é o que eu vou chamar de “pacote emergencial de boas práticas”: uma pequena lista de boas práticas de programação que você pode adotar em relativamente pouco tempo para levar sua equipe e sua aplicação do completo caos para um estado mais gerenciável.

Sim, eu sei que há tantos conselhos sobre boas práticas por aí que é até difícil não se sentir sobrecarregado. Por causa disso, eu restringi a minha lista de boas práticas a itens que atendam aos seguintes critérios:

  • As boas práticas precisam ser fundamentais, no sentido de que elas são blocos básicos a partir dos quais você pode implementar práticas mais sofisticadas depois.
  • Você pode adotá-las em relativamente pouco tempo. (Eu diria que uma semana é praticável.)
  • O seu custo é zero ou perto disso.

As práticas a seguir atendem os critérios listados. E sem mais enrolação, aqui está: meu pacote emergencial de boas práticas de codificação, com itens listados na ordem que eles deveriam ser adotados, começando pelo mais crítico.

Sistema de Controle de Versão

Eu trabalhei uma vez em uma empresa de desenvolvimento de software na qual nenhum sistema de controle de versão era usado. Os arquivos de código fonte ficavam em uma pasta compartilhada que qualquer desenvolvedor podia acessar. Qual era o processo usado para poder editar um arquivo? Você provavelmente adivinhou: nós criávamos uma cópia do arquivo e adicionávamos “_OLD” ao final do nome.

Isso aconteceu há oito ou nove anos, o que significa que as coisas devem ter melhorado, certo? Bom, provavelmente melhoraram, um pouco, mas não totalmente. Ainda tem empresas por aí que não usam controle de versão.

Como proceder?

De agora em diante, eu vou assumir que você concorda que versionamento é uma boa prática fundamental. Caso esse não seja o caso, há muitos recursos pela web afora explicando o que um versionador é e porque você deveria usar um.

Com isso resolvido, é hora de sermos mais específicos. Qual ferramenta usar? Como proceder com a sua adoção?

Git é uma escolha sólida. E apesar de ter uma curva de aprendizado mais acentuada paraquem já está mais acostumado com sistemas de controle de versão centralizados, como Subversion ou TFVC, Git é o padrão de facto da indústria. Então, sem sombra de dúvidas, você deve aprender git. Não fazê-lo pode prejudicar a sua carreira no futuro.

Mas é possível que o Git não seja a melhor escolha para o seu time agora. Lembre-se, você não tem muito tempo. Nós precisamos que a sua equipe adote as boas práticas o mais rápido possível.

Como nós podemos fazer isso? Suponha que você tenha experiência com Subversion, pois esse era o versionador usado na empresa que você trabalhou anteriormente. Sua experiência com Git, porém, é nenhuma. Se esse é o caso, eu diria que Subversion é a melhor escolha para você. Ter que aprender um novo sistema e ensiná-lo para seus colegas ao mesmo tempo que o coloca em vigor na empresa seria demais: você iria apenas se sobrecarregar.

Revisão de Código

Eu não vou mentir: eu sou um grande fã de revisão de código. E eu não estou sozinho nisso.. Eu já testemunhei em primeira mão como um bom processo de revisão de código pode reduzir o número de problemas em uma aplicação, tornar o código mais consistente e, mais importante ainda, espalhar conhecimento por todo o time de desenvolvimento.

E aqui vai uma ótima vantagem dessa prática: revisão de código é algo relativamente fácil de ser implementado. Comece da maneira mais simples possível, e então faça adaptações na sua abordagem conforme as necessidades aparecerem.

Minha Definição de Revisão de Código

Falar de revisão de código pode ser complicado. As pessoas às vezes tem ideias totalmente diferentes sobre o que a expressão significa. Então eu acho que uma clarificação se faz necessária.

Eu não sou a favor de um processo de revisão de código altamente burocrático e estressante, no qual o seu código é esmiuçado, em público, durante horas. Eu não acredito que envergonhar as pessoas em público é uma forma eficaz de aumentar a qualidade de um projeto. Ao contrário, o tipo de revisão de código que eu defendo é um processo simples, geralmente iniciado ao submeter um pull request ou usando sua IDE favorita.

Como Proceder

Agora que nós estamos sintonizados em relação ao significado de “revisão de código”, a próxima pergunta é: “como implementar isso na prática?” Da maneira mais simples possível que funcione.

Por exemplo, se a sua empresa desenvolve em .NET e usa TFS/TFVC, você pode começar instalando uma política de check-in que exige uma revisão de código para cada check-in. Se a sua equipe usa GitHub, vocês podem usar pull requests. Apenas comece a realizar revisões de código. Então, com o tempo, vá fazendo os ajustes e adaptações necessários.

Estas são algumas das questões que podem surgir ao refinar o seu processo:

  • Qual é o objetivo da revisão de código? Estamos procurando por bugs? Tentando melhorar a legibilidade?Checando se o código adere ao padrão de codificação da empresa?
  • Como separamos “sugestões” de “impedimentos”? É OK recusar a alteração de alguém por causa de uma indentação ruim ou um nome de variável ligeiramente equivocado?
  • O que fazer se revisor e revisado não conseguem chegar a um consenso? Trazer um mediador para dar a palavra final? E quem seria essa pessoa?

A resposta para todas as perguntas acima podem ser encontradas na automação. Uma boa parte do desconforto das revisões de códigos pode ser eliminada quando você emprega um analisador de código para lidar com as partes automatizáveis do processo.

Por exemplo, SubMain possui um produto chamado CodeIt.Right que oferece feedback em tempo real de dentro do Visual Studio, lhe alertando de possíveis problemas de codificação, inclusive corrigindo problemas quando possível.

Ao abraçar a automação, você deixa as pessoas da equipe livres para se preocuparem com questões de mais alto nível durante as revisões, como claridade do código ou decisões arquiteturais.

Builds Automatizados

Talvez você esteja pensando que eu me equivoquei nessa seção. Afinal de contas, sequer faz sentido falar de builds automatizados sem mencionar testes automatizados?

Eu vou argumentar que sim, faz sentido, por uma razão muito simples: builds automatizados eliminam o famoso problema de “na minha máquina funciona”. 

Ter um local central onde os builds são feitos joga luz em vários tipos de problemas, de mal gerenciamento de dependências a falta de disciplina em testes.

Como Proceder

Meu conselho aqui é o mesmo que na seção anterior: faça a coisa mais simples possível que funcione.

Se sua equipe já usa TFS, aprenda como criar uma definição de build. Se os seus projetos estão no GitHub, dê uma olhada no Travis CI.

Com o tempo, você vai melhorando a sua estratégia. Lembra dos analisadores de código que eu mencionei anteriormente? É possível integrá-los no seu processo de build. Testes unitários e outros tipos de testes automatizados também são uma valiosa adição.

E por falar nisso…

Ausências Notáveis

Você talvez tenha se surpreendido por minha lista de boas práticas não incluir testes unitários, mesmo eu sendo um defensor da importância de testes automatizados para a qualidade de uma aplicação. Qual é a razão disso?

Infelizmente, adicionar testes unitários a uma aplicação legada é muito difícil, ao ponto de existir até um livro famoso que foca apenas nisso. Não é uma tarefa fácil de se fazer em pouco tempo.

Também é possível que muitos de vocês esperavam que eu falasse sobre código limpo ou os princípios SOLID. Eu encorajo vocês a lerem e pesquisarem sobre esses tópicos, mas eu não acredito que eles encaixam no propósito do post de hoje. Como o próprio nome deixa claro, eles são princípios. Pense neles como diretrizes filosóficas. Úteis? Claro. Mas não tão fáceis de decompor em conselhos pequenos, simples e acionáveis.

Implemente Essas Práticas Para Ontem!

É possível que vários de vocês tenha achado essas práticas extremamente básicas e não dignas de um post. “Quem é que não usa controle de versão em 2018???”

Bom, não é tão difícil assim encontrar evidência (anedótica, mas ainda assim) que as coisas ainda não são tão perfeitas.

Acreditar que mesmo práticas tão fundamentais como versionamento de código ou testes automatizados são aplicadas universalmente é mais ingenuidade do que talvez queremos admitir.

Para o restante de vocês, eu espero que essa lista seja útil.

Você já deve ter ouvido o ditado. “Quando estiver em um buraco, para de cavar.” E é exatamente esse o tipo de ajuda que eu quis oferecer com esse post: correções rápidas e fáceis, para que você e as demais pessoas em seu tipo possam recuperar o suficiente de sanidade para poderem focar e recuperar o controle de sua aplicação, garantido sua saúde a longo prazo.

leia mais...

4 Erros Comuns Com Data e Hora no C# — E como evitá-los

NOTA: Eu escrevi este post originalmente para o blog da SubMain. Você pode conferir o original no site deles, em inglês. Enquanto estiver por lá, dê uma conferida no CodeIt.Right, uma ferramenta que pode lhe ajudar com problemas relacionados a tempo e muitos outros.

Você se lembra daqueles posts no estilo “inverdades que programadores acreditam sobre X” que ficaram bastante populares em blogs de software há alguns anos? O primeiro foi sobre nomes, mas logo apareceram vários outros, cobrindo tópicos como endereços, geografia e compras online.

O meu favorito era o post sobre tempo. Até esse ponto, eu não havia pensado profundamente sobre tempo e e suas intricacies, e eu fiquei intrigado em saber que um domínio tão fundamental pudesse ser um terreno tão fértil para bugs e confusões.

Agora, mesmo eu tendo gostado do post, eu vejo um problema com ele: o post lista vários suposições erradas, e basicamente para por aí. Quem lê o artigo provavelmente termina se perguntando:

  • Por que essas suposições são falsas?
  • Qual é a probabilidade de eu me dar mal por causa dessas inverdades?
  • Qual é a maneira adequada de lidar com esses problemas?

O artigo é interessante, mas eu acho que faria sentido oferecer informações um pouco mais acionáveis.

E é exatamente esse é o objetivo do post de hoje. Eu vou mostrar 4 erros comuns que as pessoas cometem ao lidar com tempo em C#/.NET. E não para por aí. Eu também vou mostrar o que você deve fazer para evitar esses erros e tornar seu código mais seguro e mais fácil de ser compreendido.

1. Calculando Durações de Maneira Ingênua

Considere o código abaixo:

Ele funciona corretamente? Depende de onde e quando ele será executado.

Quando você usa DateTime.Now, o valor que você obtém representa a data e hora locais em relação à máquina atual (ou seja, a propriedade Kind está configurada para Local).

Se o lugar que você mora observa Horário de Verão, então você sabe que existe um dia do ano no qual você deve adiantar os relógios em uma certa medida (geralmente 1 hora, embora existam lugares que ajustam por outras quantidades). E é claro, existe também um dia no qual o oposto acontece.

Agora imagine o que seguinte: hoje é 12 de março de 2017, e você more na cidade de Nova York. Você começa a usar o programa acima. O método StartMatch() é executado exatamente às 13h. Uma hora e quinze minutos mais tarde, o método EndMatch é executado. O cálculo é realizado e o texto abaixo é exibido:

Duration of the match: 00:02:15

Eu imagino que você compreendeu o que aconteceu aqui: quando os relógios estavam prestes a marcar 14h, o Horário de Verão entrou em efeito, movendo-os diretamente para 15h. Então o método EndMatch recuperou o horário atual, somando uma hora adicional ao cálculo. Se o experimento tivesse acontecido no fim do Horário de Verão, o resultado seria apenas 15 minutos!

Sim, o código mostrado é apenas um exemplo, uma brincadeira. Mas e se fosse algo mais sério? Uma aplicação de folha de pagamento, digamos. Você gostaria de pagar o valor errado a um funcionário?

O que fazer?

Quando precisar calcular a duração de atividades humanas, use UTC para os tempos de início e fim. Dessa forma, você será capaz de referenciar de maneira não ambígua um ponto específico no tempo. Ao invés de usar a propriedade Now, use UtcNow para recuperar a data e hora já em formato UTC para realizar os cálculos:

Mas e se os valores DateTime que você tem já são do tipo Local? Nesse caso, você deve usar o método ToUniversalTime() para convertê-los para UTC:

Uma Rápida Advertência Sobre ToUniversalTime()

O uso do método ToUniversalTime() - e seu irmão, ToLocalTime() - pode ser um pouco chato. O problema é que esses métodos fazem suposições sobre o que você quer baseados no valor da propriedade Kind do objeto datetime que você tem, o que pode trazer resultados inesperados.

Ao chamar ToUniversalTime(), uma das seguintes coisas vai acontecer:

  • Se Kind estiver configurado como UTC, o mesmo valor é retornado.
  • Por outro lado, se estiver configurado como Local, então o valor correspondente em UTC é retornado.
  • Finalmente, se Kind estiver como Unspecified, então é assumido que o objeto sempre teve a intenção de ser local,, e você recebe o valor correspondente à conversão para UTC.

O problema aqui é que valores de data/hora locais não não “transportáveis”. Como assim? Eles são locais enquanto eles permanecerem no contexto da máquina atual. Se você salva um datetime local para um banco de dados e depois o recupera de lá, a informação de que ele é local se perde: agora ele é Unspecified.

Assim, o seguinte cenário pode acontecer:

  • Você recupera a data e hora atuais usando DateTime.UtcNow.
  • Você salva esse valor no banco de dados.
  • Outra parte do código recupera esse valor. Sem estar ciente de que o valor já está em UTC, chama o método ToUniversalTime() na instância.
  • Como o valor recuperado do banco possui o tipo Unspecified, o método vai tratá-lo como local e realizar uma conversão desnecessária, gerando um valor errado.

Como evitar que isso aconteça? Uma prática recomendada é usar UTC para armazenar o tempo em que um evento aconteceu. Minha sugestão é seguir esse conselho e também esse fato bem explícito. Coloque o sufixo “UTC” em cada coluna de tabela no banco de dados e também em nomes de propriedades que se referem a um valor em UTC. Ao invés de “Inclusao”, use “InclusaoUTC” e assim por diante. Não é tão bonito, mas com certeza é mais claro.

2. Não Usar UTC Quando Deveria (e vice-versa)

Nós podemos definir isso como uma regra universal: use UTC para registrar quando eventos aconteceram. Ao logar, auditar, e registrar todo tipo de timestamps na sua aplicação, UTC é a resposta.

Então, é só usar UTC em todo lugar! Certo? Não, não tão rápido.

Digamos que você precisa ser capaz de reconstruir o tempo local - na perspectiva do usuário - de quando algo aconteceu, e a única informação que você tem é um timestamp em UTC. Mal dia.

Em casos assim, faria mais sentido (a) registrar o momento em UTC e gravar também o fuso horário do usuário ou (b) usar o tipo DateTimeOffset, que armazena a data/hora local junto com o deslocamento, ou offset, para UTC, permitindo que você reconstrua o valor em UTC quando precisar.

Outro caso de uso comum para o qual UTC não é a solução correta é o agendamento de eventos locais no futuro. Você não quer que seu alarme acorde você uma hora mais cedo ou uma hora mais tarde nos dias de transição do Horário de Verão, certo? Pois é exatamente isso que aconteceria se você configurasse o seu alarme pelo horário UTC.

3. Não Validar Entrada dos Usuários

Imagine que você criou uma aplicação desktop simples que permite que usuários configurem lembretes. A pessoa informa a data e hora que quer receber o lembrete, clica em um botão, e pronto.

Tudo parece estar funcionando direito até que alguém do Brasil envia um e-mail para você, reclamando que o lembrete que ela configurou para 15 de outubro às 0h15 não funcionou. O que será que aconteceu?

O Horário de Verão Contra Ataca

O vilão aqui é o bom e velho Horário de Verão novamente. Em 2017, o Horário de Verão no Brasil começou à meia-noite do dia 15 de outubro. Então, a combinação de data e hora que a usuária informou simplesmente não existe em seu fuso-horário!

É claro que o problema oposto também é possível. Quando o Horário de Verão chega ao fim e os relógios são atrasados, isso gera horas ambíguas.

Qual É A Solução?

Como lidar com esse tipo de problema no C#? A classe TimeZoneInfo pode lhe salvar. Ela serve para representar um fuso horário e também oferece métodos para verificar se um determinado objeto DateTime é válido:

Mas o que fazer então? O que deveria substituir os comentários “do something” nos trechos acima?

Você poderia mostrar uma mensagem dizendo que a data informada é inválida. Ou você poderia escolher outra data para a pessoa automaticamente.

Vamos abordar o caso das horas inválidas primeiro. Suas opções são: mover para frente ou para trás. É uma decisão meio arbitrária, então qual você deve escolher? Por exemplo, o app do Google Calendar no Android move para frente. E até que faz sentido se você parar pra pensar. Isso é exatamente o que seus relógios fizeram devido ao horário de verão. Por que sua aplicação não pode fazer o mesmo?

E no caso das horas ambíguas? Você também tem duas opções: escolher entre a primeira e segunda ocorrências. Novamente, é meio arbitrário, mas eu aconselho você a escolher a primeira ocorrência, pelo simples fato de tornar as coisas mais simples.

4. Confundir um Offset com um Fuso Horário

Considere o timestamp a seguir: 1995-07-14T13:05:00.0000000-03:00. Quando alguém pergunta o que o “-03:00” no final é chamado, muita gente responde “o fuso horário”.

A questão é essa. Essas pessoas provavelmente assumem corretamente que o número representa o offset, ou deslocamento, em relação a UTC. Também é provável que elas sabem que podem reconstruir a hora correspondente em UTC por meio desse offset. (Muitos desenvolvedores não entendem que, em uma string assim, o offset já está aplicado: para obter o tempo em UTC, você deve inverter o sinal do offset. E só depois, aplicá-lo ao valor da hora).

O erro está em achar que o offset é a única informação que um fuso horário representa. Mas não é. Um fuso horário é uma área geográfica, e contém muitas informações, tais como:

  • Um ou mais offsets. (Horário de verão existe, afinal de contas.)
  • As datas nas quais as transições do horário de verão acontecem. (As quais podem mudar e mudam, sempre que os políticos resolvem).
  • A quantidade de tempo pelo qual os relógios são atrasados ou adiantados na transição. (Não é uma hora em todo lugar.)
  • O registro histórico das mudanças nas regras acima.

Em resumo: não tente adivinhar um fuso horário pelo offset. Você vai errar a maioria das vezes.

Quer aprender sobre tempo? Já não era sem tempo!

Esta lista não é de forma alguma exausitiva. Eu apenas quis oferecer a vocês uma introdução ao fascinante e meio bizarro mundo dos problemas com hora e data em programação. Há muitos recursos valiosos por aí, comoa tag time zone no Stack Overflow ou blogs como o de Jon Skeet e o de Matt Johnson que são autores da popular biblioteca NodaTime.

E finalmente, sempre use as ferramentas que estão à sua disposição. Por exemplo, o produto da SubMain chamado CodeIt.Right tem uma regra que você a especificar um IFormatProvider em situações nas quais é opcional, o que pode acabar salvando você de bugs difíceis ao fazer tratamento de datas.

leia mais...

Programação Cargo Cult É A Arte de Programar Por Coincidência

NOTA: Eu escrevi este post originalmente para o blog da NDepend. Você pode clicar aqui para ler o artigo original no site deles, em inglês. Enquanto estiver por lá, baixe e experimente o NDepend.

Eu ouvi falar em programação cargo cult a primeira vez há alguns anos. Eu me lembro de ter pensado na época: “Que nome estranho para um conceito relacionado com programação”.

Se você compartilha do estranhamento do meu “eu” do passado, o post de hoje é para você.

Primeiramente, você verá o que programação cargo cult é e por que você deve se importar. Então, vamos dar uma olhada em alguns exemplos práticos, usando a linguagem C#. Finalmente, nós encerraremos com conselhos sobre o que você pode fazer para não cair nessa armadilha.

Programação Cargo Cult: Fazendo as coisas porque sim.

Segundo a versão em inglês da Wikipedia

Cargo cult programming is a style of computer programming characterized by the ritual inclusion of code or program structures that serve no real purpose.

Em tradução livre

Programação cargo cult é um estilo de programação de computadores caracterizado pela inclusão ritualística de código ou estruturas de programação que não servem nenhum propósito real.

Em outras palavras, é quando um(a) desenvolvedor(a) escreve código sem entender realmente o que aquele código faz. Talvez uma abordagem por tentativa e erro tenha sido usada - copia o código de um lugar, cola em outro, e vai mexendo e testando até que funciona, mais ou menos. Quando chega nesse ponto a pessoa geralmente para de mexer no código, por medo de fazer parar de funcionar. No processo, talvez sobrem resquícios de código que não servem realmente para nada.

Ou talvez a pessoa tenha tentado usar uma técnica aprendida com um colega, mas falhou em compreender que os conceitos são diferentes e que a tal técnica é inútil na situação atual.

Por fim, também é possível que o problema seja simplesmente educação insuficiente: talvez o desenvolvedor tenha um entendimento pobre a respeito de como as ferramentas usadas funcionam.

Por que a programação cargo cult é um problema?

Como Eric Lippert diz, programadores cargo cult sofrem para fazer alterações significativas em um programa e acabam usando uma abordagem de tentativa e erro já que eles não entendem o funcionamento interno do código que estão prestes a alterar.

Isso não é tão diferente do que os programadores pragmáticos chamam de “programação por coincidência”:

Fred doesn’t know why the code is failing because he didn’t know why it worked in the first place. It seemed to work, given the limited “testing” that Fred did, but that was just a coincidence.

Em tradução livre:

Fred não sabe porque o código está falhando porque ele não sabe porque ele funcionou da primeira vez. Parecia estar funcionando, com o “teste” limitado que Fred fez, mas era apenas uma coincidência.

A frase acima resume tudo para mim: se você não sabe como ou por que seu código funciona, você também não vai entender o que aconteceu quando ele parar de funcionar.

Origem do termo

Embora práticas que são consideradas “culto à carga” (cargo cult) atualmente tenham sido registradas tão cedo quanto o final do século XIX, o termo em si data de 1945, quando foi usado pela primeira vez para descrever práticas que surgiram durante e depois da Segunda Guerra Mundial entre habitantes da Melanésia.

Os nativos começaram a imitar o comportamento dos soldados, vestindo-se como controladores de voo e balançando gravetos, na esperança de que isso faria com que aviões carregados de suprimentos descessem dos céus.

Desde então, o termo culto à carga tem sido usado em uma variedade de contextos para significar imitar forma sem conteúdo - copiar perfeitamente os elementos superficiais mas ao mesmo tempo falhando em entender de maneira mais profunda o significado e funcionamento do que se está tentando emular.

Falar é fácil; me mostre o código!

Chega da aula de História por hoje. Hora de ver código! Eu vou mostrar cinco exemplos de programação cargo cult usando a linguagem C#. Vamos lá.

Checar um tipo de valor não-nulável for Null

O primeiro item é algo que me incomoda já que eu vejo isso bastante em código de produção. É algo assim:

	public Product Find(int id)
	{
   	   if (id != null) // essa verificação é inútil
	   {
	       Console.WriteLine("Esta linha sempre será executada.");
	   }
	
	   return new Product();
	}

Aqui nós temos o caso de um(a) desenvolvedor(a) que provavelmente não entende a diferença entre tipos de valor e referência. Seria completamente perdoável, no caso de um profissional iniciante, se não fosse pelo fato de que o compilador te avisa disso.

Você pode achar isso um exagero da minha parte. Afinal de contas, o código vai rodar perfeitamente mesmo assim. Na verdade, a verificação não será nem ao mesma incluída no IL resultante, como você pode ver nesse print de uma ferramenta de descompilação:

Uma imagem mostrando um trecho de código que não contem a checagem de nulo.

Você pode ver no trecho de código acima que o compilador otimizou o código, removendo a checagem por nulo.

Tem problemas muito piores, claro. Sim, a aplicação não vai quebrar por causa disso. Então, qual é o ponto?

Bom, pra começo de conversa, eu me preocuparia com uma empresa de desenvolvimento cujo único critério de qualidade é “roda sem quebrar”. Mas o problema de verdade aqui é que esse tipo de código demonstra uma falta de entendimento sobre características fundamentais da linguagem e da plataforma que podem lhe causar problemas no futuro.

Uso Desnecessário de ToList() em consultas do LINQ to Object

Assim como o problema anterior, o item atual é algo que eu rotineiramente vejo em código de produção. Considere o código abaixo:

	var result = users.ToList()
	.Where(x => x.PremiumUser)
	.ToList()
	.Select(x => new { Name = x.Name, Birth = x.DateOfBirth })
	.ToList();

O problema que temos aqui é que as chamadas ao método ToList() são totalmente desnecessárias (exceto talvez a última, caso você realmente precisasse que o resultado fosse um lista e não apenas um IEnumerable).

Em minha experiência, isso acontece quando quem escreveu o código não entende bem a natureza do LINQ; eles erroneamente acham que os métodos do LINQ pertencem ao tipo concreto List<T> ao invés de serem métodos de extensão que podem ser usados com qualquer implementação de IEnumerable<T>.

Ao chamar ToList() diversas vezes dessa forma, o desenvolvedor na verdade cria diversas listas novas, o que pode prejudicar o desempenho da aplicação.

O código acima pode ser reescrito da seguinte forma:

	var result = users.Where(x => x.PremiumUser).Select(x => new { Name = x.Name, Birth = x.DateOfBirth });

Conversões Desnecessárias

Considere a linha seguinte:

	DateTime creationDate = DateTime.Parse(row["creation_date"].ToString());

Aqui temos não apenas uma mas duas conversões desnecessárias. Primeiro, criamos uma nova string e então a “parseamos” para DateTime quando um simples cast seria suficiente:

	DateTime creationDate = (DateTime)row["creation_date"];

Esse exemplo assume que o tipo no banco de dados é um tipo específico para lidar com datas (como date ou datetime no SQL Server). É claro que se você estivesse usando um tipo inadequado (como varchar) então isso já seria um outro problema.

Try-Catch em todo lugar

Também conhecido como síndrome Pokémon (“Gotta catch’em all!”), o anti-pattern aqui é adicionar um bloco try-catch em cada linha em que exista a remota possibilidade de uma exceção ser disparada.

Pontos bônus se o código estiver tentando capturar System.Exception ao invés de uma exceção mais específica, acabando com a distinção entre erros esperados e não esperados.

Mais pontos se o bloco do catch não conter código nenhum!

A dica geral aqui é: jamais capture uma exceção a não ser que você tenha uma razão muito específica para fazê-lo. Do contrário, deixe que a exceção suba até que o gerenciador de exceções geral no nível mais alto lide com ela.

Se esse conselho parece vago (“Como vou saber se eu tenho uma boa razão para capturar a exceção?”), é porque de fato é. Explorar esse tema mais a fundo iria além do escopo desse post, , mas ler o excelente artigo do Eric Lippert chamado “Vexing Exceptions” vai aumentar e muito o seu entendimento sobre exceções.

Usar StringBuilder Demais

Você já deve ter visto o filme: depois de ler em algum lugar que concatenar strings usando + é ineficiente, nosso intrépido desenvolvedor resolve tomar pra si a tarefa hercúlea de mudar cada concatenação de string no projeto para o uso de StringBuilder.

A justificativa para isso, claro, é que System.String é imutável. Então, cada vez que você “muda” a string, você na verdade está criando uma instância nova na memória, o que pode prejudicar o desempenho da aplicação.

Mas adivinha só. O compilador é bem esperto. Digamos que você tenha a seguinte linha:

	string a = "Hello " + "World";

Isso vai ser no fim das contas traduzido para:

	string a = "Hello World";

A regra geral é: tudo bem usar concatenação simples se você sabe o número de strings a anexar em tempo de compilação. Do contrário, o uso de StringBuilder provavelmente faz mais sentido.

Lógico, alguns cenários não são tão claros assim. O único conselho que faz sentido dar aqui é: faça seu dever de casa. Quando estiver com dúvida, pesquise e faça benchmark sem dó.

Eu termino com mais uma dica sensata do Eric Lippert:

Unnecessary code changes are expensive and dangerous; don’t make performance-based changes unless you’ve identified a performance problem.

Existe Solução?

Eu diria que é justo supor que pessoas com menos experiência são mais propensas a cometer erros devido à programação cargo cult. Mas desenvolvedor nenhum está realmente a salvo, independentemente de seu nível de conhecimento ou experiência.

Nós somos apenas humanos no fim das contas. Cansaço, prazos, vieses cognitivos e (para ser realmente honesto) a preguiça eventual pode transformar até o melhor de nós em um programador cargo cult.

Infelizmente, não há uma maneira 100% garantida de impedir isso de acontecer. Mesmo assim, aqui vão algumas medidas que você pode tomar para, ao menos, diminuir as chances.

Vamos ver algumas delas.

Use Revisão de Código/Programação em Par

A primeira medida que você pode tomar para evitar o cargo cult é simplesmente ter uma segunda pessoa olhando seu código. Os benefícios de ter uma outra pessoa revisando cada linha de código antes que ela chegue em produção não podem ser subestimados. E embora revisão de código e programação em par não são exatamente equivalentes, ambas as práticas podem lhe trazer esse benefício.

Sempre Teste Suas Hipóteses

Escreva testes de unidade (e outros tipos de testes também). Monitore sua aplicação em produção. Se algo não está tendo um bom desempenho, faça benchmarks exaustivos. Não faça só suposições. Testar as suas hipóteses pode trazer insights valiosos e salvar a sua pele naqueles momentos em que a sua intuição não for certeira.

Leia Código de Outras Pessoas

Ler código escrito por outras pessoas é uma ótima maneira de aprender. É uma ferramenta perfeita para comparar suas ideias e suposições contra o que outros desenvolvedores estão fazendo, expondo você a novos conceitos que podem lhe forçar a ganhar um entendimento maior dos problemas que está tentando resolver.

Na era do GitHub, não tem muita desculpa para não fazer isso.

Aprenda Com Suas Ferramentas

Existe um número enorme de ferramentas que podem ajudar sua equipe com a qualidade do seu código. Aqui vai a dica principal: você não deve só usar essas ferramentas. Você deve também aprender com elas. Se você usa NDepend, leia sobre suas regras. Tente entender a justificativa por trás de cada uma delas. Quais são os princípios e boas práticas que guiaram seus autores durante a criação delas?

A mesma dica vale para outros tipos de ferramentas, e até para os warnings que o compilador lhe mostra.

Ciência da Computação, Não Superstição da Computação

Embora ninguém seja imune à programação cargo cult, nós devemos nos esforçar para superá-la. Há sabedoria na nossa área à nossa disposição, acumulada lentamente ao longo de mais de 7 década. Vamos usá-la. Vamos entender melhor nossas ferramentas e nossa profissão e escrever software de qualidade.

leia mais...

Funcionalidades do C# 8.0: Um Vislumbre do Futuro

C# 8.0 está chegando e vai trazer algumas funcionalidades muito interessantes. Vamos dar uma olhada no que o futuro reserva.

leia mais...

Testes Unitários Para Iniciantes - Parte 2

Antes tarde do que mais tarde! Hora de continuar nossa série sobre testes unitários para iniciantes. Hoje você vai escrever seu primeiro teste unitário.

leia mais...

Testes Unitários Para Iniciantes - Parte 2

Antes tarde do que mais tarde! Hora de continuar nossa série sobre testes unitários para iniciantes. Hoje você vai escrever seu primeiro teste unitário.

leia mais...

Funcionalidades do C# 7 que vale a pena conhecer - Parte 2

Neste artigo, vamos continuar a ver algumas das features mais interessantes do C# 7.

leia mais...

Funcionalidades do C# 7 que vale a pena conhecer - Parte 1

C# 7 está finalmente entre nós. Hora de conhecer algumas de suas features.

leia mais...

Já está na hora de começar a usar essas features do C# 6!

A sétima versão do C# está chegando, e vai provavelmente trazer várias funcionalidades novas e úteis para nossas caixas de ferramentas. Mas deixa eu perguntar uma coisa: você já está usando as funcionalidades da versão anterior?

leia mais...

Tipos de valor e referência em C#, Parte 2 - Por que DateTime não pode ser nulo?

“Por que uma variável DateTime não pode receber null?” Esta é uma pergunta que vive aparecendo no StackOverflow e site similares. Às vezes escrita de modo um pouco diferente, às vezes com um tipo diferente, mas no fundo é a mesma dúvida. O que é natural, se você considerar que provavelmente milhares de pessoas entram na área a cada ano.

leia mais...

Tipos de valor e referência em C#

Este é o meu primeiro post pra valer aqui no meu blog, e eu decidi escrever sobre tipos de valor e tipos de referência em C#. Isso é um assunto relativamente básico, no sentido de que é algo que você já deveria entender caso você programe em C# profissionalmente. Mas ao mesmo tempo, é algo que pode ser um pouco contra-intuitivo caso você não seja um desenvolvedor experiente.

leia mais...