The problem is knowing when to do it and when not to do it.
If you're even the slightest bit unsure, err on the side of not thinking a few steps ahead because it is highly unlikely that you can see what complexities and hurdles lie in the future.
In short, it's easier to unfuck an under engineered system than an over engineered one.
And they followed the alternative with Itanium, and look how that turned out.
Apart from that, IPv6 _is_ IPv4 with a bigger address space. It's so similar it's remarkable.
The problem quickly becomes "how do you route it", and that's where we end up with something like today's IPv6. Route aggregation and PI addresses is impratical with IPv4 + extra bits.
The main changes from v4 to v6 besides the extra bits is mostly that some unnecessary complexity was dropped, which in the end is net positive for adoption.
Also maybe simplicity is sometimes achieved AFTER complexity, anyway. I think the article means a solution that works now... target good enough rather than perfect. And the C2 wiki (1) has a subtitle '(if you're not sure what to do yet)'. In a related C2 wiki entry (2) Ward Cunningham says: Do the easiest thing that could possibly work, and then pound it into the simplest thing that could possibly work.
IME a lot of complexity is due to integration (in addition to things like scalability, availability, ease of operations, etc.) If I can keep interfaces and data exchange formats simple (independent, minimal, etc.) then I can refactor individual systems separately.
1. https://wiki.c2.com/?DoTheSimplestThingThatCouldPossiblyWork
The most fundamental issue I have witnessed with these things is that people have a very hard time taking a balanced view.
For this specific problem, should we invest in a more robust solution which takes longer to build or should we just build a scrappy version and then scale later?
There is no right or wrong. It’s depends heavily on the context.
But, some people, especially developers I am afraid, only have one answer for every situation.
The trouble is by the time you get there you will discover the problem isn't what you expected and it will all have been wasted effort.
As is IPv4s simplicity got us incredibly far and it turns out NAT and CIDR have been quite effective at alleviating address exhaustion. With some address reallocation and future protocol extensions, its looking entirely possible that a successor was never needed.
"could possibly work" is clearly hyperbole as it would only exclude solutions that are guaranteed to fail.
But even under a more plausible interpretation, this slogan ignores the cost of failure as an independent justification for adding complexity.
It's bad advice.
We had a project which is supposed to convert live objects back into code with autogenerated methods. The initial design was using a single pass over the object graph and creating abstractions of HDL and combining method blocks in the same pass.
That is a big hairy code with lot of issues. Simpler would be to handle one problem at a time - method generation in one pass and then convert the methods to HDL. But, getting approval for a deployed app is so hard. Particularly when it is a completer rewrite.
Do you handle one "everything is perfect" happy path, and use a manual exception process for odd things?
Do you handle "most" cases, which is more tech work but shrinks the number of people you need handling one-off things?
Or do you try to computerize everything no matter how rare?
Bring the problem back to our primary contact and they've got no clue what to do. They're on like year 2 of a 7 year contract and they've just discovered that their payroll department has been interpreting the ambiguous rules somewhat randomly. No one wants to commit to an interpretation without a memorandum of understanding from the union, and no one wants to start the process of negotiating that MoU because it's going to mean backdating 2 years of payroll for an unknown number of employees, who may have been affected by it one month but not the next, depending on who processed their paystub that month.
That was fun :D
If you overpay someone… getting that money back is a challenge.
To make it more complicated still, there was an element of “we’re not sure if we overpaid or underpaid” but there was also an element of “we gave person X an overtime shift but person Y was entitled to accept or deny that shift before person X would have even had an opportunity to take it”. That’s even harder to compensate for.
There’s no reason to leap to ‘stealing’ (which requires intent) from ‘ambiguous rules.’ Those ambiguous rules, incidentally, may have been agreed to — or even insisted on — by the union!
Not strictly for technical reasons, but definitely for political reasons. The client was potentially the largest organization in my province (state-run healthcare). Outsourcing payroll and scheduling with the potential of breaking the rules in the contracts with the multiple union stakeholders was a completely non-starter. Plus the idea of needing to do lay offs within the payroll department was pretty unpalatable.
Hell I just spent a week doing something which should've taken 5 minutes because rather then a settings database, someone has just been maintaining a giant ball of copy+pasted terraform code instead.
Adding the runtime complexity and maintenance work for a new database server is not a small decision.
Don't worry, the second half of the title has this covered:
> ... that could possibly work
In the scenario you've described, the technology is not working, in the complete sense including business requirements of reasonable operating costs.
Perhaps it really did work at first, in the complete sense, when the number of users was quite small. That's where the actual content of the article kicks in: it suggests you really do use that simple solution, because maybe you'll never need to scale after all, or you'll need to rewrite everything by then anyway, or you'll have access to more engineering talent by then, etc. I'd tend to agree, but with the caveat that you should feel free to break the rule so long as you're doing it consciously. But none of that implies that you should end up in the situation you described.
This is where I am arguing nuance. These decisions are contextual; and the superficially more complicated solution may be solving inherent complexity in the problem space that only provides benefit over a time period.
As an example, some team might decide to forgo a database and read/write directly to the file system. This may enable a release in less time and that might be the right decision in certain contexts. Or it could be a terrible decision as the externalised costs begin to manifest and the business fails because of loss of customer trust.
My point is that you cannot only look at what is right in front of you, you also need to tactically plan ahead. In the big org context, you also need to strategically plan ahead.
>In the scenario you've described, the technology is not working, in the complete sense including business requirements of reasonable operating costs.
In the parent comment's reasaonble premise, they wouldn't be sure of what they would need.
The programmer's mind is the faithful ally of the perfect in its war waged against the good enough.
The "best" solution for most people that have a problem is the one they can use right now.
The one you can use right now in order to get feedback from real world use, which will be much better at guiding you in improving the solution than what you thought was "best" before you had that feedback.
Real world feedback is the key. Get there as quickly as feasible, then iterate with that.
> As an example, it is possible to solve very many business or operational problems with a simple service sitting in front of a database.
If this is the simplest approach within the problem space or business's constraints, and meets the understood needs, it may indeed be the right choice.
> At scale, you can continue to operate, but the amount of man-hours going into keeping the lights on can grow exponentially. Is the simplest thing possible still the DB?
No problem in a dynamic human system can be solved statically and left alone. If the demands on a solution grows, and the problem space or business's needs changes, then the solution should be reassessed and the new conditions solved for.
Think of it alternatively as resource-constrained work allocation, or agile problem solving. If we don't have enough labor available (and we rarely do) to solve everything "best," then we need to draw a line. Decades of practice now have shown that it's a crap shoot to guess at the shape of levels of complexity down the road.
Best case you spend time that could go into something else valuable today, to solve a problem for a year from now; worst case you get the assumptions wrong and fail to solve that second "today" problem as well as still needing to spend future time on refactoring.
When this is accounted for, “the simplest thing” approaches “the best solution”.
I think the nuance here is that “the simplest thing possible” is not always the “best solution”. As an example, it is possible to solve very many business or operational problems with a simple service sitting in front of a database. At scale, you can continue to operate, but the amount of man-hours going into keeping the lights on can grow exponentially. Is the simplest thing possible still the DB?
Complexity is more than just the code or the infrastructure; it needs to run the entire gamut of the solution. That includes looking at the incidental complexity that goes into scaling, operating, maintaining, and migrating (if a temporary ‘too simple but fast to get going’ stack was chosen).
Measure twice, cut once. Understand what you are trying to build, and work out a way to get there in stages that provide business value at each step. Easier said than done.
Edit: Replies seem to be getting hung up over the “DB” reference. This is meant to be a hypothetical where the reader infers a scenario of a technology that “can solve all problems, but is not necessarily the best solution”. Substitute for “writing files to the file system” if you prefer.