public é visível de qualquer lugar.
internal é visível apenas dentro de uma montagem
Você tende a usar interno apenas para proteger APIs internas. Por exemplo, você pode expor várias sobrecargas de um método:
public int Add(int x, int y)
public int Add(int x,int y, int z)
Ambos chamam o método interno
internal int Add(int[] numbers)
Você pode então colocar muita sofisticação em um método, mas "protegê-lo" usando métodos de fachada que podem ajudar o programador a chamar o método corretamente. (O método de implementação com o parâmetro de matriz pode ter um limite arbitrário de valores, por exemplo.)
Também vale a pena notar que usando o Reflection, todo e qualquer método pode ser chamado, independentemente de sua visibilidade. Outro "hack" para controlar / obter acesso a APIs ocultas internamente.