<.+> contra <b>bold</b> and <i>italic</i> não encontra <b>. Encontra a string inteira, do primeiro < ao último >. Nada está quebrado. É exatamente isso que um quantificador greedy deve fazer, e também é o bug mais comum de “o regex está casando demais”. Depois que você entende o que o motor faz enquanto lê o seu padrão, esse bug e a maioria dos parentes dele deixam de ser mistério.
Como um Motor de Regex Lê o Seu Padrão Um motor de regex não compara o seu padrão com a string como você compara duas strings por igualdade. Os motores que você usa no dia a dia (re do Python, RegExp do JavaScript, PCRE, .NET, Java) são regex-directed: eles percorrem o padrão da esquerda para a direita, token por token, e para cada token tentam consumir caracteres da string na posição atual. Quando um token casa, o motor avança tanto o ponteiro do padrão quanto o da string. Quando um token falha, o motor faz backtracking: volta ao último ponto em que tinha uma escolha, como um quantificador que poderia ter casado com menos caracteres ou um ramo de alternância ainda não tentado, e tenta a outra opção.
...