Oh boy, this is so true. In all my years of software engineering this is one of those ideas that has proved consistently true in every single situation. Some problems are inherently complex, yes, but even then you'd be much, much better off spending time to think things through to arrive at the simplest way to solve it. Again and again my most effective work has been after I questioned my prior approaches and radically simplified things. You might lose some potential flexibility, but in most case you don't even need all that you think you need.
Some examples:
- Now that reasonably good (and agentic) LLMs are a thing, I started avoiding overly complex TypeScript types that are brittle and hard to debug, in favor of writing spec-like code and asking the LLM to statically generate other code based on it.
- The ESLint dependency in my projects kept breaking after version updates, many rules were not sophisticated enough to avoid false positives, and keeping it working properly with TypeScript and VSCode was getting complicated. I switched to Biome.js, and it was simpler and just as effective. However, I'm recently having bugs with it (not sure if Biome itself or the VSCode extension is to blame). But whatever, I realized that linting is a nice-to-have, not something I should be spending inordinate amount of times babying. So I removed it from the build tool-chain, and neither do I even need have it enabled all the time in VSCode. I run Biome every now and then to check the code style and formatting , and that's it, simple.
- Working on custom data migration tooling for my projects, I realized forward migrations are necessary to implement, but backwards migrations are not worth the time and complexity to implement. In case a database with data needs to be rolled back, just restore the backup. If there was no data, or it is not a production database, just run the versioned initialization script(s) to start from a clean state. Simple.