It's supposedly fantasy, but it contains some of the best (fun and insightful) examinations of political systems, economics, religion, the environment, military culture and more. If it sounds like everything, it's because it is :)
Requirements will change, third party integrations will change, front end frameworks, use cases, service providers etc.
Everything will change so write (really build) code that tolerates change (IOC, interfaces, etc. Any form of loose coupling you can get away with cleanly).
There are of course situations when the one line fix is the right one. But in general, this outlook on short term vs long term changes (knowing when the concrete should become the abstract) is one of the differences between being a programmer and a software engineer (or being a senior and a junior).
> Everything will change so write (really build) code that tolerates change (IOC, interfaces, etc.
Everything might change. Not everything will change.
I think the problem I have with your principle is that many of the techniques to accommodate change (the "IOC, interfaces, etc" you mention) can have a real and immediate cost when it comes to understanding a codebase, and that's the fundamental sin when it comes to maintainability.
Abstraction and indirection have value, obviously, but on balance I'm with GP; don't add 'em until you actually need 'em. In particular, don't add an interface and a factory and whatnot until you actually have more than one implementation.
This may depend on your languages and dev environment; something like IDEA where large-scale but mechanically trivial refactorings can be accomplished quickly and safely are more suited to KISS than e.g. dynamic languages where tooling is much much weaker.
> Everything might change. Not everything will change.
True. Not everything will change. But you don't know which ones will and which ones won't. So be ready.
> I think the problem I have with your principle is that many of the techniques to accommodate change (the "IOC, interfaces, etc" you mention) can have a real and immediate cost when it comes to understanding a codebase, and that's the fundamental sin when it comes to maintainability.
Not true. The benefits of IOC and loose coupling are well established and I won't even bother relisting them here. I'll only say that those benefits usually outweigh the minor indirection you get as a result.
The question then becomes, do you loose couple from the beginning? or tightly couple everywhere and loosen as you go. I tend towards doing it from the start, for consistency sake (help those poor junior engineers out), and for all the other benefits (which again, i don't want to list, but i'll add the one biggie - unit and integration testing).
I'll also note that IOC/loose coupling is rarely the cause of the over abstraction you fear. I've seen it way more in domain models or APIs.
There are many fads, new fangled doo-dads, thingamajigs in our software world. New frameworks, seismic shifts in architecture, herd mentality etc. IOC/loose coupling isn't one of them. It's good, old engineering practice.
You underline what is essentially my biggest gripe with IOC. There seemed to have been an explosion of everyone wanting you to use a new fancy framework to make it happen.
For the most part, constructor logic that simply sets dependent fields (and, importantly, does not do anything) along with not requiring fancy initialization shakeups goes a long way to easily testable things.
That said, people that go out of their way to make a fixture for each individual method can be just as annoying as the folks that only do end to end testing.
Basically, life is easy to argue at the strawman level. Breaks down quickly when there are "boots on the ground." (Or whatever other saying works.)
I'm with you there man (or lady)...the pace of "new and shiny" is exhausting.
One of the things I like about our environment is that we're always disrupting ourselves (maybe a little too much). So while it's true that there are many heavy weight DI tools (Castle.Windsor for ex), there are also lighter-weight ones (Ninject and co). At the end of the day, we each pick our heavy we want our tools to be (we can always implement IOC without a DI tool if you want to keep things bare boned).
> Basically, life is easy to argue at the strawman level. Breaks down quickly when there are "boots on the ground." (Or whatever other saying works.)
It's an interesting discussion for sure. And some of it is philosophical. When the "rubber hits the road" (or whatever saying works) and i'm stuck in the office at 2am trying refactor a ball of mud, I wish those who came before me had thought of this stuff.
Adding a proper interface is much more expensive than removing it if it proves worthless. Designing a good interface and especially components or other means of compartmentalization removes a huge source of mistakes.
Especially the last part is critical - if there is no abstraction or some other kind of tight binding between components you've already lost.
I think we may be miscommunicating; a lot of these words mean different things to different people. Designing a good and minimal "interface" for a class or module, in the sense of how it exposes its functionality to the rest of the world, is absolutely important, yes.
Deciding that mentioning classes anywhere in a signature is Evil, and that everything must implement a separately-defined interface in the Java/C# sense, is IMHO not at all useful. Not because it's "expensive" - it just takes an already-designed class API and copypastes it into an interface - but it's pure busywork. More code (which some people treat as a plus), harder to understand (because now there's always extra ambiguity at a call site as to which implementation is being called), adds nothing. This type of thinking - taking a useful tool or rule of thumb and turning it into dogma - seems far more prevalent in Java-land than in any other language I've worked in; I've never really understood why.
Your first priority is to design your code for the inevitable day when your successor (or perhaps you yourself) dislikes it. The less effort/risk to uncouple and remove it, the better.
This helps avoid the well-meaning pitfall where you design things to be "customizable in the future", but the ways it needs to be customized aren't well known, and it turns out you've only complexity that gets in the way of the changes you eventually want.
In contrast, any code that's easy to remove implies that there's a good boundary, interface, or contract.
This. Too many people are sure of too many things. Too sure of religion, political ideology, techno purity. Too sure of the absence (or presence) of the many -isms that plague us.
HN is a perfect example. At least i think so. I'm not sure.
Does Java not have a good Micro ORMs (like Dapper, NPoco on the .Net side)? They can be a wonderful middle ground between completely custom sql and a full blown giant like Hibernate.
I remember getting physical results for SSCE and NECO exams and I would gladly have paid the $2.
I'll also add that the startups in the US/Europe can provide sample problem/solution combinations for you. You won't be able to just clone ideas, but you'll be able to evolve them for your local market.
Good luck.
PS. It makes me happy to meet fellow Nigerian engineers. I don't meet many in my day to day.
I'm reading a paper book now (Fortress Draconis, ebook is not available) and I find myself squinting for hours at a time.