What are your desired features?
One thing, of course, it to load the firmware. It might check the validity of the firmware.
Basic checks whether the data appears to be firmware data, checking CRC, or even cryptographic signatures.
Otherwise, updating said firmware. One question is where the updates come from.
Are you updating over UART, I²C, SPI? Perhaps something like USB, Bluetooth or downloading it from the internet for WiFi or cellular capable devices.
Are you using a single bank, overwriting the existing firmware, or a dual bank solution, first writing it to a different flash region prior to overwriting the firmware.
A dual bank solution would be more robust to update interruptions/failures, at the cost of requiring double the flash.
Also, in case of dual bank, who is responsible of getting the firmware into the update region of flash. Does the bootloader do this task, or is it the task of the application, and then handing it over to the bootloader. Or both, in the usual flow the application prepares the update, but the bootloader has a recovery mode.
Also, consider the features of the target microcontroller architecture. An ARM Cortex M3 (usually) has a relocatable vector table, meaning you can set the location of the firmware's vector table in the bootloader. However, a ARM Cortex M0 has not. This means you'll have to do interrupt redirection inside your bootloader. (Note: some manufacturers have custom redirection in their M0 implementation).
Many microcontrollers start executing from the beginning of flash, meaning, once a bootloader is used, the firmware has to be placed on a different location is flash. This also means, the firmware cannot run without the bootloader being present. On some architectures, such as Atmel Microchip AVR, therre is a fuse (their name for persistent settings register), where a location for the bootloader can be set, such that the location of the firmware does not change.