|
The longer I've worked in software development, the less I'm convinced that any of this matters, at all. What has mattered more to me is choosing the right architecture, technologies and dependencies. Understanding the fundamentals, especially at least a vague understanding of what the computer is doing with the code I am writing. Basic understanding of algorithms and techniques like state machines. I've grown almost disdainful of things like SOLID principles, after taking them more seriously in the past. |
If you can't follow the logic by just reading the code, then maintenance will be slow and cumbersome. New development will eventually taper off as more and more time is spent fixing issues. I've written production systems, still in operation today decades later, that hold true to the simplicity principle. I wouldn't do it any other way. But, the battle I frequently face is with upstart devs who want to use some new pattern or principle because they read that it was "better" than straight-up code. And yes, they can implement some new feature quick, but 2 years down the road all hell breaks loose.
Some of my pet-peeve anti-patterns:
* "Plugin" systems only used by the primary development team. There's power in being able to follow the logic for any call stack by just reading the code; plugins make the flow much more challenging, and provide little value.
* Large (and complicated) run-time configuration IOC systems which make any debug session a dreadful process.
* Rules Engines. 'nuff said.
* Piles of abstractions, one atop the other, because teams had no time to find commonalities and/or sensible use-cases.
* 25+ new source code files to respond to a /heartbeat endpoint.
* Fixing a problem in 3 lines of code is not fixing anything if it complicates the debug process, forces a re-write of the unit test drivers, enables mysterious build failures or taxes the DevOps team.
And some of my favorite simplifying patterns:
* AOP for logging; but NOT (dear god) for business logic.
* 3 levels of abstraction, and no more. If you can't figure out a way to do it in 3 levels of abstraction, then something else is wrong.
* 3 systems of state: config (global), current request and persistence (database). If you need more than that, then something is wrong with the architecture. (Ok, compilers may not be able to get away with this, but most applications can, in my experience.)
* If you use a pattern (MVC, for example) apply it to the whole system. E.g. doing IOC only in the upstream API system when the rest of the code doesn't, just makes a mess.