O indicador número um de módulos mal acoplados, na minha opinião, são dependências bilaterais. Por exemplo, o Módulo1 chama alguma função no Módulo2 e o Módulo2 chama alguma função no Módulo1.
A maioria das interfaces deve ser unidirecional. Se o módulo chamado precisar passar algumas informações para o módulo de chamada que não é retornado como parte da chamada, ele deve usar algum tipo de passagem de mensagem ou mecanismo de acionamento de evento, como uma fila de mensagens. Idealmente, o identificador para a interface de passagem de mensagens deve ser passado durante algum processo de inicialização ou registro. Isso abstrai completamente a interface de tal maneira que o módulo não se importa com quem é o evento ... portanto, é dissociado.
Outra indicação é quando um módulo está constantemente chamando outro módulo para algum conjunto de dados específico. Isso deve fazer você questionar quem realmente deve ser o proprietário do conjunto de dados. Por que este módulo em questão sempre precisa ver dados que pertencem a algum outro módulo?
Uma terceira ferramenta, por assim dizer, é perguntar a si mesmo: "Posso retirar este módulo e substituí-lo sem exigir alterações em outros módulos.
Esta não é de forma alguma uma lista exaustiva, mas são as três principais coisas que me pergunto ao projetar software.