The right time to fix it, is right before the cost of fixing it becomes exponential. 1/
If the thing works and you don't have to add anything, don't improve it.
If you add something, and the addition of N raises the cost of improving it with N, be on high alert. 2/
If you add something and the cost raises with 2N, first improve the system to get that particular impact down to N.
If you add something of N and the cost of improving raises with N², stop the work and do system-wide improvements first. 3/
I don't know for other industries, but in software, organisations get to N⁵ or so before they even start having meetings about doing an improvement. They conclude it's impossible and decide to start over. 4/
Starting over Does. Not. Work.
5/
We know this. It goes by many names, it even has a Wikipedia page. It doesn't work. The second system often doesn't make it to production. 6/
You think it's not going to be the case for your second system, because it also doesn't make it into anyone's resume, let alone the official company history. 7/
The tactical remedy is to train people on spotting N² before it happens, and learning what to do about it (such as refactoring, bounded contexts,... ).This has occupied most of my career up to a point. 8/
The strategic remedy, I thought, was agile, but there's a point where mammoth organisations are never going to move like gazelles. 9/
So the mammoths add ever more people to make ever fewer additions to the system at increasing cost. 10/
If you're spending N² money on an N² system, you get an N⁴ system. 11/
So the better remedy, is going to be to plan for N² when it doesn't look like N² yet. Assume you're going to have to shut down development to do improvement, no matter what. Design for that. 11/11
• • •
Missing some Tweet in this thread? You can try to
force a refresh
I’m having lots of conversations with @rebeccawb about Bounded Contexts in Domain-Driven Design. This is a small snapshot of some of the tensions involved in picking good boundaries.
🧵⬇️ (1/15)
Some context:
A Bounded Context is an “understandability boundary”, a boundary around a model and its language. You can understand the model and the language in isolation, without having to understand other Bounded Contexts. (2/15)
An Interface is the set of contracts or message types or APIs between Bounded Contexts. They translate from one model and language to another. (3/15)
The most important thing you can do when trying to learn Domain-Driven Design is still very much Eric's book amzn.to/3b1Uqrx People are not recommending this book enough because few have actually finished it.
It has a reputation of being hard to read, which is deserved. Read a little bit every day. Or read the bold parts first, then start over to read it thoroughly.
It also has a reputation of being too academic or too theoretical. This is undeserved: it is highly pragmatic, but it approaches software design from an angle that didn't exist anywhere else before, so it introduces many concepts that seem foreign at first.
The larger the client, the more likely they hire me because they want to "get it right the first time and avoid rework", and the more likely they end up not hiring me because before they do, they want to agree on the scope of what I will do for them.
"As small as possible" (DB partitions, message size, μsvcs, Bounded Contexts, class names, method arity, ...) is almost universally bad advice in software design. Some critical logic is going to cross those boundaries and result in poorly implemented, preventable workarounds.
But, "Whenever something is wrong, something is too big" (Kohr 1957) is also true for software. Big things are more obviously bad. Small things look simple, because the wrongness hides not inside the things, but in their connections.
Things usually tend to get bigger, rarely smaller or stable. @CarloPescio calls this gravity (things with mass acquire more mass) in the Physics of Software. Our usual reaction is to advocate smallness.
The problem is not that you shipped on Friday. The problem is that you have no way of knowing if shipping will break it. Most software is massively undermodeled and undertested.
Models (1) and tests (2) are two sides of software success: 1) do I understand this software so well that I can accurately predict the impact of a change in the system's behaviour 2) can I delonstrate confidence in its behaviour by repeatedly testing lots of scenarios
The irony is that both automated testing and modelling are crazy cheap compared to the perpetual burden and risk of undertested and undermodeled business-critical systems.
I wish we'd stop debating OOP vs FP, and started debating individual paradigms. Immutability, encapsulation, global state, single assignment, actor model, pure functions, IO in the type system, inheritance, composition... all of these are perfectly possible in either OOP or FP.
Some are better, some are worse, some are highly dependent on context, let's figure that out instead of religuously bashing umbrella terms like OOP and FP. And there may be many more paradigms to discover.
Take encapsulation: in #Haskell, you can choose for a module not to export your type constructors, and only export smart constructors and functions operating on your data structures. Sounds close enough to encapsulation to me.