Nomes, estrutura e código para o qual dá para voltar
Nomes
Um nome diz o que a coisa faz na linguagem do domínio, não como está implementada. Um nome que precisa de um comentário para ser entendido é um nome ruim.
Seja consistente: o mesmo termo para o mesmo conceito em todo o sistema, incluindo no banco de dados e na interface. Dois nomes para uma coisa são uma fonte constante de bugs.
Estrutura
Organize por domínio de negócio e não por tipo de arquivo. Uma pasta de 'pedidos' que contém tudo relacionado a pedidos é mais fácil de navegar do que pastas separadas por camada.
Mantenha fronteiras: um módulo chega a outro por uma interface definida, não pelas partes internas. Fronteiras mantidas dentro de um sistema são o que permite dividi-lo depois, se necessário.
Simplicidade
Prefira código entediante. Uma abstração criada antes de haver três casos reais é quase sempre errada, e mais difícil de remover do que de adicionar.
Indo mais fundo
Quando código é difícil de testar, é quase sempre sinal de uma dependência emaranhada com a lógica. Em vez de adicionar mocks, injete a dependência — o teste se simplifica e a estrutura melhora juntos, e esse é um dos sinais mais confiáveis de um design que precisa de correção.