I find that code ports easily, but peripheral setup takes some tweaking. It should be reasonably simple if you're sticking with STM32s, but will still take some time and effort.
This would match my experience too.
The main logic of your application should be portable enough between platforms and families - but the bits that interface with peripherals (configuring timers/responding to their interrupts, talking to UARTs etc) will need to be adapted on a per device/family basis.
If your code is well written, the peripheral interfaces should be abstracted away from your application logic code so that they can be replaced easily. If not, this would be a good exercise to do just that. The build environment should ideally make some variables available to the compiler so that you can use ifdefs etc to include or exclude different pieces of code depending on the type of device the code is being compiled for.