I think autoconf (and similar tools) lead to bad programming, for a number of reasons. They don't force bad programming — they just make it simple to do bad things.
Moreover, the value they offer doesn't generally justify their problems. Autoconf's most-used single feature is prehaps the --prefix option to install the software in a user-determined location. Implementing that functionality is hardly rocket science.
There are three main reasons for my dislike.
Autoconf encourages use of #ifdef, which leads to a vast increase in testing load. Suppose a program contains ten "_HAVE_FEATURE_X" #ifdefs. Then there are up to 1024 combinations to test. In real life, maybe 20 or 50 or 100 of those combinations occur in production use of the program. Noone knows precisely which ones — not knowing that is the key feature of autoconf.
The number of #ifdefs tends to increase, too. They can be hard to remove, because one often doesn't know whether removing one will break some platform. And if they're used, people tend to assume that using #ifdef is part of the code style, and add more.
Autoconf adds another level of indirection to the build process. Not a problem unless something goes wrong, but something always goes wrong, and it makes debugging very unpleasant. I've seen programs whose generated build files were far larger than the program's source code, and I've tried to follow problems through ten thousand lines of generated sh and m4 code.
I know, I shouldn't need to debug the generated files. But like other abstractions, autoconf is leaky and the underlying system sometimes needs attention.
Autoconf makes the programmer think about features, not platforms. That adds distance between the programmer and the final program which is to be tested and used.
As mentioned above, this adds to the testing load. But it also adds to the cognitive load while programming: The programmer must keep the possible combinations of perhaps 50 _HAS_FEATURE_X possibilities in mind, instead of five target platforms.
So, what do I prefer as an alternative?
It's important to keep in mind that my goal is user satisfaction. I want to satisfy as many users as I can, as well as I can, and do it with limited development resources. Exactly how do to that is not important to me.
Using an inferior portable implementation is sometimes a very good alternative. Maybe it's inferior, but at the same time there's just one case to implement and test. The time saved can be used to implement and test something else.
Probing at runtime is also possible, and sometimes can be better. For example, if you want to make sound in an X11 program, it's much better to probe for /dev/audio, NAS, aRts and esound at runtime than at compile-time.
Doing something else seems to be possible always. If users are asking for forty-seven new features and there is only time for two, it's easy to find two good features that don't need #ifdef.