First of all, you should have reasonable expectations as to what portability can do for you. You will never be in a position to take the source code to a new mcu, compile it and expect it to work.
However, if you code to portability, your task of porting a code to a different hardware can be greatly simplified. For example, let's say that your code needs to send a string via i2c. You can simply code the hardware spi in your user code, or you can call a routine called i2c_write() to send a byte. You can then link in different i2c libraries (hardware or software, avr or stm32, etc.) And you know with confidence that your code will work.
The key to write portable code is really to modulize your code so you limit your hardware touchpoints.