Recentemente escrevi no twitter o seguinte:
Programming is like the movie Back to the Future. If you change old code, you can break the future. Even worst, new code also can break the past.
E agora com com mais espaço, quero escrever mais sobre isso:
Quando começamos uma nova classe ela nasce sem erros:
class x{};
O código compila, está perfeito e não tem erros.
Se um bug existir na classe X quer dizer que em algum momento durante o desenvolvimento ele foi introduzido.
Também quer dizer que é um momento em que a classe estava compilando, afinal para ver o bug quer dizer que o programa estava rodando.
Então por que não cuidar cada alteração compilável de forma que cada passo seja feito de forma segura?
A resposta é que isso é díficil, a complexidade vai aumentando como tamanho da classe. As dependências de estado internas e externas também aumentam.
Também existem momentos de instabilidade natural. Por exemplo, um TODO que está aguardando uma parte que não está pronta. Quando a parte A depende da B e a B depende de A, não é possível criar A e B simultaneamente. Pode ser preciso fazer A antes e testar as partes que não dependam de B. Ou deixar o comportamento um pouco diferente até que B esteja pronta.
O que ocorre então, é que não é fácil garantir que todos os incrementos anteriores na classe estejam seguros. Talvez uma modificação no futuro, quebre o comportamento de uma função que já foi testada.
Não é possível apenas criar pequenos incrementos, no desenvolvimento da classe é preciso revisar todos os estados e o código já criado que estava funcionando.
Então, por exemplo, para fazer a alteração de estado número 10 teriam que ser revisados as outras 9 modificações.
Essa influência do código novo sobre o código feito no passado é uma fonte comum de bugs.
Para melhorar a situação, é importante que as pré e pós condições estejam documentadas em cada função e que sejam usados ASSERTs para garantir isso.
Mesmo as pré-condições óbvias agora podem não ser no futuro.
Outra forma de melhorar este quadro, são os unit tests. Eles ajudam a testar se o comportamento anterior continua correto a cada incremento.