It is actually more complicated/worse than that and is due to a combination of undocumented behaviors ("issues") in the LiquidCrystal library.
The main issue here is that that the IDE bundled LiquidCrystal library createChar() function leaves the LCD in CGRAM mode after it writes the character data to the LCD.
The problem with that is that any future writes to the LCD will not go to DDRAM (which is where data needs to go to show up on the LCD screen) but rather to CGRAM since the LCD is still in CGRAM mode.
That means that no characters will be written to the display but will instead corrupt CGRAM.
The only way to put the LCD back into DDRAM ram mode after putting it into CGRAM mode is to either call
setCursor(), home(), or clear() as they send hd44780 LCD commands that cause the display to revert back to DDRAM mode.
The existing sketch code worked around this issue by calling createChar() before calling begin().
begin() does many commands and leaves the LCD in DDRAM mode ready to display characters.
Normally that wouldn't work as begin() is what initializes the h/w and puts the LCD into 4 bit mode.
However, in the LiquidCrystal library, they initialize the h/w and put the display into 4 bit mode in the constructor using a default configuration of 16x1.
Doing h/w initialization in a constructor is REALLY BAD practice and often can't be done due to many services not being available that early in the startup code.
For example you can't talk over i2c in a constructor so what they were doing in the LiquidCrystal constructor can not be done that way when using other interfaces like i2c when talking to a hd44780 LCD.
The real issue is in createChar(). Baring changing that, it comes down how to work around it.
You can either do what they did, move createChar() before begin() , (which in my opinion is not good)
or do something to put the LCD back into DDRAM mode,
setCursor(0,0), home(), or clear().
setCursor() is actually much faster than home() or clear() and that is why I mention doing that one.
In the large picture, this issue of CGRAM mode vs DDRAM mode is an issue anytime createChar() is used in that library not just during setup().
My hd44780 library does not have this issue.
So if you were move the begin() above the createChar() (which is required by the hd44780 library) it will work even without doing the clear() before sending characters to the LCD using print() or write()