so is it good coding practice to write my code with functions outside the main loop like this,
Yes, if the function encapsulates a specific function.
loop () {
func_to_do_everything();
}
No point in the above, but
loop () {
key = readKey();
switch (key) {
case 'a':
etc etc.
}
}
Good if the code required to read a key is complex or more than say 10 lines of code because the function does a specific task and is named appropriately. However if the readKey() func was just this
byte readKey() {
return PINB;
}
Then this is silly use of a function, the code may as well have been in the main loop.
An exception to the above is when you know that one day the code may run with different hardware so even though now it only does a read of PORTB in future the reading of a key may be a mor ecomplex process. In this case it make ssens to isolate this key reading code inside a finction.
I hope that hasn't made it more confusing

______
Rob