You just had to live with the constraints.
It biased your selection of data structures and algorithms.
Max 64KB array size meant pointers to allocated structs and linked lists were much more popular back then versus 1 large array of structs.
The Win16 HANDLE memory allocation also meant you had to worry about how you handle structs which had pointers to others structs (a FAR ptr may not be a stable value, unless you locked the HANDLE for the duration of the allocation)
Then you had to worry about stuff that no college programming book talked about (ignore the lack of error checking):
char FAR *p;
char FAR *mem = farmalloc(65536);
for (p = &mem[65535]; p >= &mem[0]; p--) {
dostuff(p);
}
Welcome to an infinite loop...
To be fair to Windows, good C courses should still teach this, but I'm not sure if they do :-)
It's UB to set a pointer to before the first element of an array, or after the last element plus one. So, if it knows the call to farmalloc/malloc returns the start of an object, a modern C compiler on a modern architecture may, in principle, optimise the above to an infinite loop.
I've seen something similar on architectures (long ago) where a zero-bit-pattern pointer was a valid memory address you might actually access. Of course p-1 is not less than p when p is zero.