I was one of those eager college grads that went and read the GOF's Design Patterns. Being pretty fresh to development in a professional setting, I used design patterns everywhere, even where they didn't belong. I've also read a ton of books on OOP and software architecture and they had a huge influence on my past projects.
Now that I've been at it for about 10 years, I've seen a sharp decline in the number of design patterns I use. In terms of UML and OOP, I barely even think about them anymore. Some of it's just that I'm so familiar with them that I don't really notice when I'm using them. The rest is because I just don't find myself reaching for them as often as before. Recently, I've noticed a sharp correlation between the use of abstract classes and increased code complexity. So, I usually rely on composition and helper classes for my shared functionality. I use interfaces mostly to support dependency injection and unit testing.
Every now and then, I whip out the Decorator or Strategy pattern. Nothing too crazy. I recently used the Visitor pattern on an open source project - I apparently misunderstood what it was all about all these years. But, overall, design patterns have become a very rare occurrence. On the other hand, I consistently use a solid set of architectural patterns.
A couple years ago, I really got into functional programming. I very quickly started using closures, partial application, filter/map/reduce, etc. in my daily coding. I went crazy with LINQ. I still on occassion write LINQ that would be completely incomprehensible to the majority of developers. Since then, I have been gradually cutting down on the amount of functional programming I put in my everyday code.
I basically made the same mistake of learning something new and immediately overusing it and abusing it.
Strangely enough, I find I repeat myself more often now than I used to, and it actually makes my code easier to understand. Just to be clear, the duplication isn't in the form of actually copying lines of code; it's in the form of doing something manually that I could try to automate. As an example, on my very first project, I spent an enormous amount of time trying to automatically generate SQL using metadata and then parse the results. The project only had about 12 tables! And most of those were lookup tables! It would have taken me a couple days to wire up that SQL manually. Instead, I spent close to a month, on and off, adding more and more functionality. Fun project, but not very productive.
If I work directly with SQL these days, I just write the SQL, write the code to extract the results and move on. I don't care about creating command classes that internally use reflection to determine column names or doing something crazy to build where
clauses dynamically. I just duplicate the code for setting parameters and iterating over the results. Big deal. It's fast and easy. If I see duplicate code, I'll write helper methods but that's about it. Honestly, though, I think a lot of people came to the same conclusion and that's why we have Dapper.
I must have realized at some point that there is a simple economics to it. If it takes you longer to automate something, then just do it manually. Even if you decide to write a utility to help you, keep it bare-bones and avoid gold-plating it. It's also important to ask how much time the on-boarding process would be for someone new to the team. I think I got tired of coming on to so many projects with lame, hand-crafted ORMs. Keeping focused on functionality might be the one and only reason why techniques like TDD and Kanban work.
On a related topic, I recently watched this video where Martin Fowler talks about software architectures that gradually pay for their increased complexity: https://youtu.be/DngAZyWMGR0?t=480. He demonstrates a graph showing that eventually a good architecture will pay for itself. He calls it a pseudo-graph because applying any real numbers would be challenging; you can't realistically predict when an architecture will pay for itself. It's a gamble. It's probability.
The only really complex thing I do anymore is try to keep the different layers of my application from knowing about each other. I use dependency injection, probably in excess, to hide everything I can. Perhaps the complexity is warranted because these projects are huge. Or maybe I'm just holding on to complexity without a good reason and it's going to be yet another learning lesson in years to come.