I'm kind of doing that right now--we've got that issue pretty badly, actually. It's surmountable, but annoying.
Here are some observed causes or environmental factors:
Using an older or low-level language. If you're doing code in asm/C/C++/Matlab/Fortran (arguably Perl, PHP), odds are you aren't in a position to easily pull in other people's solutions. That means that you are probably trying to reinvent the wheel, because you don't know of a pre-existing solution, because you don't want to pull in the massive baggage of other solutions, or because you enjoy making the wheel over again yourself. Newer, more abstract languages don't have this problem, mostly I think because they come from a time of easy collaboration via the internet. The whole gem/npm/hex ecosystems simply couldn't be possible without easy internet access and github. I'm going to call out C++ specifically here, because using that language with old code is terrible because odds are that, even if you are doing things correctly, the library author isn't (from a strict software engineering standpoint).
Not understanding your problem space. Oftentimes, being overly conservative about your design requirements (we must be webscale, fault tolerant, absolutely have to support XYZ vendor integration) means that you overdesign--and when you do that, you are more likely to be fighting old wars. If you really grok and have cleanly defined your problem space, you know where you can cut corners, instead of overbuilding based on previous experience.
Not following de facto standards. There's a bajillion ISO standards for everything you might want, and a lot of people will glibly explain "The great thing about standards is that there are so many to choose from". The problem is, those people will either go on to ignore standards in favor of their own (usually created to fight the last war), or pick one that isn't being used (usually something that is fighting a war that was already won/lost). The only thing worse than fighting the last war is fighting the last war from the losing side. JSON vs XML is a great example of this, as is RFC3339 vs ISO8601 vs <insert wrong in-house way of doing time here>. Another great example is REST vs SOAP, or Unix Way vs Not Unix Way, or ORM vs Not ORM.
Not being lazy. The best wars are never fought, right? There's nothing worse than a developer who is super industrious and never pauses to think "Wait, is the problem I'm solving just a rehash of something I've done before, or is there a deeper conflict here?". Fighting the last war happens when these people continually reiterate on the last battle ("This time I've got time right!", "This time I've got better performance!", "This time I've got the right object model!"), all the while missing the important thing ("My customer still can't do X").
Not sharing knowledge. Probably the biggest one of these other than the previous one is the problem of developers not letting other developers know about what's been tried before, and why it did or did not work. We already hate rehashing solutions--so if presented with a module that isn't explained, are you going to rewrite it, or just accept it? Almost all the developers I know would prefer to rewrite it (and only experience and wisdom holds them back from doing so). When you've got a large codebase, and you've got like three different exception handling systems built by different groups because they don't talk, you've fought the last war three times.
Here are some observed causes or environmental factors:
Using an older or low-level language. If you're doing code in asm/C/C++/Matlab/Fortran (arguably Perl, PHP), odds are you aren't in a position to easily pull in other people's solutions. That means that you are probably trying to reinvent the wheel, because you don't know of a pre-existing solution, because you don't want to pull in the massive baggage of other solutions, or because you enjoy making the wheel over again yourself. Newer, more abstract languages don't have this problem, mostly I think because they come from a time of easy collaboration via the internet. The whole gem/npm/hex ecosystems simply couldn't be possible without easy internet access and github. I'm going to call out C++ specifically here, because using that language with old code is terrible because odds are that, even if you are doing things correctly, the library author isn't (from a strict software engineering standpoint).
Not understanding your problem space. Oftentimes, being overly conservative about your design requirements (we must be webscale, fault tolerant, absolutely have to support XYZ vendor integration) means that you overdesign--and when you do that, you are more likely to be fighting old wars. If you really grok and have cleanly defined your problem space, you know where you can cut corners, instead of overbuilding based on previous experience.
Not following de facto standards. There's a bajillion ISO standards for everything you might want, and a lot of people will glibly explain "The great thing about standards is that there are so many to choose from". The problem is, those people will either go on to ignore standards in favor of their own (usually created to fight the last war), or pick one that isn't being used (usually something that is fighting a war that was already won/lost). The only thing worse than fighting the last war is fighting the last war from the losing side. JSON vs XML is a great example of this, as is RFC3339 vs ISO8601 vs <insert wrong in-house way of doing time here>. Another great example is REST vs SOAP, or Unix Way vs Not Unix Way, or ORM vs Not ORM.
Not being lazy. The best wars are never fought, right? There's nothing worse than a developer who is super industrious and never pauses to think "Wait, is the problem I'm solving just a rehash of something I've done before, or is there a deeper conflict here?". Fighting the last war happens when these people continually reiterate on the last battle ("This time I've got time right!", "This time I've got better performance!", "This time I've got the right object model!"), all the while missing the important thing ("My customer still can't do X").
Not sharing knowledge. Probably the biggest one of these other than the previous one is the problem of developers not letting other developers know about what's been tried before, and why it did or did not work. We already hate rehashing solutions--so if presented with a module that isn't explained, are you going to rewrite it, or just accept it? Almost all the developers I know would prefer to rewrite it (and only experience and wisdom holds them back from doing so). When you've got a large codebase, and you've got like three different exception handling systems built by different groups because they don't talk, you've fought the last war three times.