It gives you practice in mapping real world problems to code, by breaking the problems down into discrete chunks that can be independently implemented. It also teaches you how to design your code in a way that it can adapt easily to those real world problems changing over time. It teaches you where to abstract things out, where to be driven by configuration etc. It has to be driven by a real goal, not a made up one.
The other big thing, focus on readability over cleverness every time. Your code should feel easy to read even by YOU 3 months later. 6 months later. I’ve lost count at the number of times I cursed at my past self for writing some code where I thought I was being clever, but didn’t understand later. Mostly did that my younger years and then learned not to do that.