| ▲ | pmg101 an hour ago | |
Can you give an example of a pattern that was considered common practice in a book 10 to 15 years ago that would be considered low-quality code today? My experience is pretty much the reverse: that we seem to just go round and round relearning the lessons that had already been learned in the past. | ||
| ▲ | jdw64 22 minutes ago | parent | next [-] | |
And the 'monad' that functional programmers always talk about—same story. In C#, they encourage monadic composition by chaining errors. But about 15 years ago, that wasn't the case. Back then, if there was no value → null. Now, even the absence of a value is modeled with Option, Maybe, or nullable types. Failures are handled not with throw/try-catch but with Result, Either, or Try. Overall, we've shifted toward a style where we preserve the computation context and then compose the next computation. Instead of manually branching every time, now we use map, bind, or LINQ-style operations that define the composition rules. Things that used to be used only by a small group of people eventually become embedded into the language itself. For example, callbacks have evolved into Task, Promise, and async/await. So honestly, I don't think quality has declined at all. I think there's just more context to keep track of now. I've seen around 40 different company codebases. I have no idea about US code, but I've seen code from Korean and Chinese large corporations, and the difference between past and present code is quite significant. | ||
| ▲ | jdw64 26 minutes ago | parent | prev | next [-] | |
Right, there were examples like Singleton and baseController. We moved from ambient/global dependency access to explicit constructor injection and Composition Root, didn't we? Application entry points use Composition Root now, but old general programming books didn't. Errors were handled differently too. In the past, you'd throw new Exception() and wrap it in try/catch at a higher level, but these days we use Result<T, E> type modeling. Of course, C# still uses try/catch, but the guidance in .NET has long been to return predictable failures as type contracts. Old .NET documentation used to encourage global access to common features and dependencies. Now it's shifted toward composition at the entry point. Node.js used to use nested error-first callbacks as a standard pattern, but that's no longer the case, right? It moved to promise/async-await, and these days, as you know, type-based modeling is trending. C# books also used to return null if something was missing, but now we use nullable reference types to express nullability more explicitly. Even inheritance—these days it's often advised to avoid inheritance, but it used to be very common. Just off the top of my head, I can think of quite a few examples. Record keywords and immutability features started appearing about 10 years ago, and Java has started adopting them too. These are all outdated patterns now, with clearly defined use cases. More precisely, the contexts in which they should be used have become much more granular. I could probably give about 30 more examples of this. | ||
| ▲ | ModernMech an hour ago | parent | prev [-] | |
I feel like the industry has learned a lot of good lessons from functional programming, and that has been reflected by functional features being baked into almost every popular imperative language. | ||