Naming

Um nome bem escolhido dispensa o comentário que explicaria o mesmo. Em C#, duas formas de capitalização dividem o trabalho: PascalCase em tipos, métodos e propriedades, e camelCase em parâmetros e variáveis locais. A capitalização mostra em que escopo o identificador vive. O texto do nome mostra o que ele significa, e para isso ele usa a palavra que o time já usa para falar do negócio.

Conceitos fundamentais

ConceitoO que é
PascalCase (capitalização inicial em cada palavra)Convenção OrderService, CalculateTotal; usada para tipos, métodos e propriedades
camelCase (capitalização a partir da segunda palavra)Convenção orderTotal, customerId; usada para parâmetros e variáveis locais
interface prefix (prefixo I em interfaces)Convenção .NET: IOrderRepository, ILogger; identifica contrato visualmente
Async suffix (sufixo Async em métodos assíncronos)Sinaliza retorno Task/Task<T>: LoadAsync, SaveAsync
domain term (termo de domínio)Palavra tirada do vocabulário do negócio: pendingInvoice conta o que o valor é; invoiceList só repete a estrutura de dados
abbreviation (abreviação)Evite contrações ambíguas (mgr, svc); siglas conhecidas mantêm forma do .NET (Id, Url)
boolean prefix (prefixo de booleano)is, has, can, should: isActive, hasInvoice, canCancel

Todo identificador é escrito em inglês

Variáveis, métodos, classes, interfaces e propriedades ficam em inglês. Português entra só em duas situações: no texto que o usuário lê na tela e no comentário // why:. Misturar os dois idiomas no mesmo arquivo obriga o leitor a trocar de vocabulário no meio da linha, e o código passa a ter dois nomes para a mesma coisa (Pedido e Order).

❌ Ruim: mistura de idiomas
public class PedidoService
{
    public async Task<Pedido> BuscarPedidoAsync(Guid id, CancellationToken ct)
    {
        var pedido = await _repository.FindByIdAsync(id, ct);
        return pedido;
    }
}
✅ Bom: inglês consistente
public class OrderService
{
    public async Task<Order> FindOrderAsync(Guid id, CancellationToken ct)
    {
        var order = await _repository.FindByIdAsync(id, ct);
        return order;
    }
}

A capitalização revela o escopo

Olhando só a primeira letra, o leitor sabe se está diante de um membro público, de um campo privado ou de uma variável local. Membro público usa PascalCase. Campo privado usa _camelCase, com underscore na frente. Parâmetro e variável local usam camelCase, sem underscore.

EscopoConvençãoExemplo
Público (método, propriedade, tipo)PascalCaseFindOrderAsync, OrderId
Privado (campo)_camelCase_repository, _notifier
Parâmetro / localcamelCaseorderId, cancellationToken
ConstantePascalCaseMaxRetries, DefaultTimeout
InterfaceIPascalCaseIOrderRepository
❌ Ruim: convenção inconsistente
public class orderService
{
    private IOrderRepository orderRepository;

    public async Task<Order> getOrder(Guid OrderId, CancellationToken CT)
    {
        var Order = await orderRepository.FindByIdAsync(OrderId, CT);
        return Order;
    }
}
✅ Bom: escopo declarado pela convenção
public class OrderService(IOrderRepository repository)
{
    private readonly IOrderRepository _repository = repository;

    public async Task<Order> FindOrderAsync(Guid orderId, CancellationToken ct)
    {
        var order = await _repository.FindByIdAsync(orderId, ct);
        return order;
    }
}

O sufixo Async avisa que a chamada precisa de await

Todo método que devolve Task ou ValueTask termina em Async. O sufixo existe para quem chama: ele vê SaveOrderAsync(...) na lista de sugestões do editor e já sabe que precisa de await. Sem o sufixo, descobrir se a chamada é assíncrona exige abrir a assinatura do método, e é assim que uma Task acaba esquecida sem await.

❌ Ruim: sem sufixo, natureza da operação obscura
public async Task<Order> FindOrder(Guid id, CancellationToken ct) { ... }
public async Task SaveOrder(Order order, CancellationToken ct) { ... }
public async Task<bool> ValidatePayment(PaymentRequest request) { ... }
✅ Bom: sufixo declara a natureza assíncrona
public async Task<Order> FindOrderAsync(Guid id, CancellationToken ct) { ... }
public async Task SaveOrderAsync(Order order, CancellationToken ct) { ... }
public async Task<bool> ValidatePaymentAsync(PaymentRequest request) { ... }

Interfaces começam com I

O I na frente do nome (IOrderRepository) é a convenção do .NET para separar contrato de implementação. A implementação recebe um nome que conta alguma coisa sobre ela: onde os dados moram, qual tecnologia usa. Sufixos como Impl, Default e Base ocupam espaço sem responder nenhuma dessas perguntas.

❌ Ruim: distinção entre interface e classe ausente ou com sufixo ruído
public class OrderRepository { ... }       // é interface ou classe?
public class OrderRepositoryImpl { ... }   // Impl não agrega nada
public class DefaultOrderRepository { ... } // Default não diz onde persiste
✅ Bom: interface clara, implementação pelo domínio
public interface IOrderRepository { ... }
public class SqlOrderRepository : IOrderRepository { ... }
public class InMemoryOrderRepository : IOrderRepository { ... }

Todo booleano começa com is, has, can ou should

O prefixo diz que tipo de pergunta o booleano responde. active sozinho deixa a dúvida no ar: é o estado atual do usuário, é a permissão de ativar, é uma ordem para ativar? isActive responde na primeira leitura. Por isso booleano sem prefixo (active, loading, valid) fica de fora.

PrefixoSignificadoExemplo
isEstado atualisActive, isValid
hasPresençahasDiscount, hasError
canCapacidade dinâmicacanDelete, canSubmit
shouldDiretiva comportamentalshouldRetry, shouldRedirect
❌ Ruim: booleanos sem prefixo semântico
bool active = user.Status == "ACTIVE";
bool discount = order.Discount > 0;
bool delete = user.Role == "ADMIN";
✅ Bom: prefixo declara a semântica
bool isActive = user.Status == "ACTIVE";
bool hasDiscount = order.Discount > 0;
bool canDelete = user.Role == "ADMIN";

Identificadores sem significado

data, info, obj, item, result e temp cabem em qualquer lugar, e é esse o problema: eles descrevem a caixa, e o leitor queria saber o conteúdo. Quem lê var result = await _repository.FindAsync(id, ct) precisa subir até a assinatura do repositório para descobrir o que veio. Trocar por order resolve a dúvida na própria linha.

❌ Ruim: nomes genéricos sem contexto de domínio
public async Task<object> GetDataAsync(Guid id, CancellationToken ct)
{
    var result = await _repository.FindAsync(id, ct);
    var data = MapToDto(result);

    return data;
}
✅ Bom: nomes expressivos pelo domínio
public async Task<OrderSummary> FindOrderSummaryAsync(Guid orderId, CancellationToken ct)
{
    var order = await _repository.FindByIdAsync(orderId, ct);
    var summary = MapToSummary(order);
    return summary;
}

Código como documentação

Um comentário que repete o que a linha abaixo já diz envelhece sozinho: o código muda, o comentário fica. Quando o nome é expressivo, o comentário some por falta de assunto. Guarde // why: para o que o código não consegue mostrar, como uma restrição do fornecedor ou uma regra que parece errada e não é.

❌ Ruim: comentários repetem o código
// busca o usuário pelo id
var u = await _repository.FindAsync(id, ct);

// verifica se o usuário está ativo
if (!u.Flag)

    return Result<Order>.Fail("User inactive.", "UNAUTHORIZED");
✅ Bom: código se explica; comentário só para restrições não óbvias
var user = await _repository.FindByIdAsync(userId, ct);
if (!user.IsActive) return Result<Order>.Fail("User inactive.", "UNAUTHORIZED");

Domínio primeiro, ação depois, qualificador por último

CalculateOrderTotalAsync se lê na ordem em que a frase acontece em português: calcula o total do pedido. TotalCalculateOrderAsync embaralha as mesmas palavras e obriga o leitor a remontar a frase. A ordem também ajuda o editor: métodos do mesmo domínio ficam agrupados na lista de sugestões quando começam pelo mesmo verbo e pelo mesmo substantivo.

❌ Ruim: qualificador antes do domínio
public async Task<decimal> TotalCalculateOrderAsync(...) { ... }
public async Task<bool> StatusValidatePaymentAsync(...) { ... }
public async Task<User> ByIdFindUserAsync(...) { ... }
✅ Bom: domínio primeiro, ação depois
public async Task<decimal> CalculateOrderTotalAsync(...) { ... }
public async Task<bool> ValidatePaymentStatusAsync(...) { ... }
public async Task<User> FindUserByIdAsync(...) { ... }

DoDocs v3.7.0 · Desenvolvido por @thiagocajadev · Baseado no trabalho de pmndrs/docs · Poimandres.