In my work, I end up writing code that compiles on a lot more platforms than that. These are my thoughts, I suspect I work at a much lower level than the author, so the disagreements I have are likely just because of that:

1) Don't use C++. Not all platforms have a c++ compiler.

2) Get your build system right. Don't let it get in your way.

3) Avoid #ifdefs. These don't scale particularly well as you increase the number of platforms. In the example in the article with the file size function, there would be a completely separate file with this function in it for each different version. The build system will compile the correct one.

4) Use plain boring ANSI C.

5) Developers don't have to compile on every platform (doesn't scale when you support bazillions of platforms and toolchains). If something is broken on your platform you fix it. If someone breaks something, you politely let them know what the problem was.

6) Avoid undefined behaviour. This is harder than you might think. Different compilers will be able to do different optimisations, and they will take advantage of different bits of undefined behaviour. Classic problems include overflowing a signed integer, right shifts on negative numbers, modulo on a negative number, type punning for endian checks, etc.

7) I agree with the point about standard 'C' types. However, there are a few extra bits. First, know what the types actually mean (char >= 8-bit, short/int >= 16-bit, long => 32-bit). Don't make any assumptions that aren't in the C spec. You might then want to put typedefs on top of these types to give them a bit more meaning.

8) Don't assume there is a FPU. Be prepared to do fractional maths in fixed point. There will always be float/double types, but they will be so dog slow on some platforms, and the memory cost of bringing in the library for them will be bad.

9) I disagree with UTF-8 for all APIs. Define a string abstraction, so on platforms (or rather, "on windows") you can have UTF-16 strings passed in and operated on without a conversion, and everywhere else you can use UTF-8. Avoid paying the conversion penalty on every operation.

10) Think about memory management. When memory is allocated, the malloc-like thing that is doing it should know what the memory is actually for. On some platforms, you will want to put different allocations into different spots in memory (this is also a nice place for another abstraction, since on some machines there won't be any different types of memory).

11) Don't avoid undefined behaviour by writing a single wrapper function (e.g. "safe signed integer add") and using it everywhere. The behaviour is typically undefined for a good reason (since different platforms will natively want to do it different ways). In the integer add example, you might want to assert that it never overflows, wrap around on overflow, truncate on overflow, return an error if it overflows, etc. Each of these things will have their spot, so don't bunch them all together.

12) Code that might want to be implemented in assembly should live in a small .c files. Then when you go to write it, you can just do it bit by bit (and again, use the build system to tell it how to build it, don't hack it with the preprocessor).