Much of what is called "portability" in the Unix world refers to porting to different Unix versions. This is a secondary consideration for GNU software, because its primary purpose is to run on top of one and only one kernel, the GNU kernel, compiled with one and only one C compiler, the GNU C compiler. The amount and kinds of variation among GNU systems on different cpu's will be like the variation among Berkeley 4.3 systems on different cpu's.
All users today run GNU software on non-GNU systems. So supporting a variety of non-GNU systems is desirable; simply not paramount. The easiest way to achieve portability to a reasonable range of systems is to use Autoconf. It's unlikely that your program needs to know more information about the host machine than Autoconf can provide, simply because most of the programs that need such knowledge have already been written.
It is difficult to be sure exactly what facilities the GNU kernel will provide, since it isn't finished yet. Therefore, assume you can use anything in 4.3; just avoid using the format of semi-internal data bases (e.g., directories) when there is a higher-level alternative (readdir).
You can freely assume any reasonably standard facilities in the C language, libraries or kernel, because we will find it necessary to support these facilities in the full GNU system, whether or not we have already done so. The fact that there may exist kernels or C compilers that lack these facilities is irrelevant as long as the GNU kernel and C compiler support them.
It remains necessary to worry about differences among cpu types, such as the difference in byte ordering and alignment restrictions. It's unlikely that 16-bit machines will ever be supported by GNU, so there is no point in spending any time to consider the possibility that an int will be less than 32 bits.
You can assume that all pointers have the same format, regardless of the type they point to, and that this is really an integer. There are some weird machines where this isn't true, but they aren't important; don't waste time catering to them. Besides, eventually we will put function prototypes into all GNU programs, and that will probably make your program work even on weird machines.
Since some important machines (including the 68000) are big-endian, it is important not to assume that the address of an int object is also the address of its least-significant byte. Thus, don't make the following mistake:
int c; ... while ((c = getchar()) != EOF) write(file_descriptor, &c, 1);
You can assume that it is reasonable to use a meg of memory. Don't strain to reduce memory usage unless it can get to that level. If your program creates complicated data structures, just make them in core and give a fatal error if malloc returns zero.
If a program works by lines and could be applied to arbitrary user-supplied input files, it should keep only a line in memory, because this is not very hard and users will want to be able to operate on input files that are bigger than will fit in core all at once.