@julientopcu.com
@josianchevalier.bsky.social
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "AISLE", "WINDOW", "PANORAMIC_VIEW", "LOUNGE" ],
"deckOptions": [ "ANY", "UPPER_DECK", "MIDDLE_DECK", "LOWER_DECK" ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
},
{
"comfortClass" : "SECOND",
"price": "50€",
"seatOptions": [ "ANY", "AISLE", "WINDOW" ],
"deckOptions": [ "ANY", "MIDDLE_DECK", "LOWER_DECK" ],
"selected": false,
"booked" : false
}
]
},
{
"number": "B",
"bound": "outbound",
"departureDate": "2025-10-06T15:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "80€",
"selected": false,
"booked" : false
}
]
},
{
"number": "C",
"bound": "inbound",
"departureDate": "2025-10-16T12:00",
"fares": [
{
"comfortClass" : "FIRST",
"price": "90€",
"seatOptions": [ "ANY", "AISLE", "WINDOW", "PANORAMIC_VIEW" ],
"deckOptions": [ "ANY", "UPPER_DECK", "LOWER_DECK" ],
"preferences": {
"seat": "PANORAMIC_VIEW",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked": true
}
]
},
{
"number": "D",
"bound": "inbound",
"departureDate": "2025-10-16T14:00",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "AISLE", "WINDOW", "PANORAMIC_VIEW" ],
"deckOptions": [ "ANY", "UPPER_DECK", "LOWER_DECK" ],
"selected": false,
"booked" : false
},
{
"comfortClass" : "SECOND",
"price": "50€",
"seatOptions": [ "ANY", "AISLE", "WINDOW" ],
"deckOptions": [ "ANY", "LOWER_DECK" ],
"selected": false,
"booked" : false
}
]
}
]
}
}{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "AISLE", "WINDOW", "PANORAMIC_VIEW", "LOUNGE" ],
"deckOptions": [ "ANY", "UPPER_DECK", "MIDDLE_DECK", "LOWER_DECK" ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
},
{
"comfortClass" : "SECOND",
"price": "50€",
"seatOptions": [ "ANY", "AISLE", "WINDOW" ],
"deckOptions": [ "ANY", "MIDDLE_DECK", "LOWER_DECK" ],
"selected": false,
"booked" : false
}
]
},
{
"number": "B",
"bound": "outbound",
"departureDate": "2025-10-06T15:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "80€",
"selected": false,
"booked" : false
}
]
},
{
"number": "C",
"bound": "inbound",
"departureDate": "2025-10-16T12:00",
"fares": [
{
"comfortClass" : "FIRST",
"price": "90€",
"seatOptions": [ "ANY", "AISLE", "WINDOW", "PANORAMIC_VIEW" ],
"deckOptions": [ "ANY", "UPPER_DECK", "LOWER_DECK" ],
"preferences": {
"seat": "PANORAMIC_VIEW",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked": true
}
]
},
{
"number": "D",
"bound": "inbound",
"departureDate": "2025-10-16T14:00",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "AISLE", "WINDOW", "PANORAMIC_VIEW" ],
"deckOptions": [ "ANY", "UPPER_DECK", "LOWER_DECK" ],
"selected": false,
"booked" : false
},
{
"comfortClass" : "SECOND",
"price": "50€",
"seatOptions": [ "ANY", "AISLE", "WINDOW" ],
"deckOptions": [ "ANY", "LOWER_DECK" ],
"selected": false,
"booked" : false
}
]
}
]
}
}public record Search(UUID id, Criteria criteria, SearchType searchType, List<Ship> ships) {
public Search {
+ if(!hasBookedShips()){
assert ships.stream()
.noneMatch(ship -> ship.departureDate().isBefore(now()))
: "Some ships are departing in the past";
+ }
}
}cruising:main+ 532
- 385
public record Search(UUID id, Criteria criteria, SearchType searchType, List<Ship> ships) {
public Search {
- if(!hasBookedShips()){
- assert ships.stream()
- .noneMatch(ship -> ship.departureDate().isBefore(now()))
- : "Some ships are departing in the past";
}
}
}cruising:main+ 1278
- 843
drives
Exploration
&
Experimentation
Business Need
drives
Exploration
&
Experimentation
Business Need
shape
Model
drives
Exploration
&
Experimentation
Business Need
shape
Model
needs to account for new
drives
Exploration
&
Experimentation
Business Need
shape
needs to account for new
Model
drives
Exploration
&
Experimentation
Business Need
expand
needs to account for new
Model
drives
Business Need
Exploration
&
Experimentation
Model
expand
needs to account for new
drives
Exploration
&
Experimentation
Business Need
Solution-Push
focuses on
Model
needs to account for new
expand
drives
Exploration
&
Experimentation
Business Need
Solution-Push
focuses on
Obsolete
Model
needs to account for new
expand
drives
Exploration
&
Experimentation
Business Need
shape
Model
needs to account for new
Exploration
&
Experimentation
Business Need
Model
Business Need
Model
Not a confirmation bias
Exploration
&
Experimentation
Business Need
by shaping
Model
New Model
may
depreciate
Exploration
&
Experimentation
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
}
]
}
]
}
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
}
]
}
],
"selectionComplete" : true,
"totalPrice" : "230€",
}{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"ships": [{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true,
}]
}],
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
}
Model
Accidental
Design Debt
generates through
misguided decisions
manifests
as complexity in
lower their cohesion and flexibility,
increase their complexity,
and erode their correctness.
introducing coupling in models,
Model
Accidental
Design Debt
generates through
misguided decisions
manifests
as complexity in
Model
Accidental
Design Debt
generates through
misguided decisions
manifests
as complexity in
Model
Accidental
Design Debt
generates through
misguided decisions
manifests
as complexity in
low coherency, consistency, cohesiveness
unclear outcomes
unexpected regressions
dissipates the structure
and clarity of
Model
Entropy
produces through accumulation
Model
Accidental
Design Debt
generates through
misguided decisions
dissolves the model into a
BBOM
Model
Entropy
produces through accumulation
dissipates the structure
and clarity of
Model
Sclerosis
hardens into
locking the model into
Model
Accidental
Design Debt
generates through
misguided decisions
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
manifests as
KISS
DRY
YAGNI
Refactoring
Habits
KISS
DRY
YAGNI
Refactoring
Habits
Sunk Cost
Fallacies
Deadlines
Pressure
Intrinsic
Coupling
{}
fosters
fosters
Intrinsic
Coupling
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
Intrinsic
Coupling
manifests as
strong
Accidental
Design Debt
Responsibilities
Coupling
may induce
"Spork Effect"
"Spork Effect"
causes
Model
Fragility
Intrinsic
Coupling
manifests as
strong
Accidental
Design Debt
Responsibilities
Coupling
may induce
Model
Rigidity
leads to
Model
Entropy
increases
produces
through accumulation
{}
Intrinsic
Coupling
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
is a structural stress within a model that signals it is being stretched beyond its conceptual integrity.
conceptual integrity
Model
Tension
is the result
of
Accidental
Design Debt
Intrinsic
Coupling
may
induce
is the degree to which a model maintains clarity, coherency, cohesion
and its purpose.
is the result
of
Accidental
Design Debt
Model
Tension
Conceptual
Integrity
erodes
prevents
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
Model
Tension
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
Model
Tension
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
Model
Tension
compromises
"fibrosing"
left unresolved
leads to
Model
Tension
Conceptual
Integrity
erodes
Crystallized
Model Tension
Model
Entropy
raises
Model
Sclerosis
hardens into
shapes
left unresolved
leads to
Model
Tension
Crystallized
Model Tension
Model
Obsolescence
signals
visual elements © Alberto Brandolini
Search
Bound
Select
a Fare on a Ship
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Check Selected Fares
Completion
visual elements © Alberto Brandolini
Search
Bound
Select
a Fare on a Ship
Ship + Fare Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Check Selected Fares
Completion
Fares Selection
COmpleted
Selected Fares
Fares Selection Found Incomplete
Selected Fares
visual elements © Alberto Brandolini
Search
Bound
Select
a Fare on a Ship
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Check Selected Fares
Completion
visual elements © Alberto Brandolini
Search
Bound
Select
a Fare on a Ship
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Check Selected Fares
Completion
A cognitive bias that predisposes people to focus on information that are more prominent or visible
visual elements © Alberto Brandolini
Search
Bound
Select
a Fare on a Ship
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Check Selected Fares
Completion
Search
Bound
Select
a Fare on a Ship
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
Ancillaries
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Selected Fares +
Ancillaries
Selected Fares &
Ancillaries
Selected Fares &
Ancillaries
Selection
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
Selected Fares
Ancillaries
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Price
Selection
Total Price Computation
Selection
Priced
Check
Completion
Fares Selection
COmpleted
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
Selection
Priced
Selection
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Check
Completion
Fares Selection
COmpleted
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
Price
Selection
Total Price Computation
Selection
Priced
Priced
Selection
Selection
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Check
Selection
Completion
Check
Selection
Completion
Selection
COmpleted
Selected Fares
Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
Incomplete
Selection
complete
Selection
Price
Selection
Total Price Computation
Selection
Priced
Priced
Selection
Selection
(Selection)
visual elements © Alberto Brandolini
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Check
Selection
Completion
Check
Selection
Completion
Selection
COmpleted
Selected Fares
Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
Incomplete
Selection
complete
Selection
Price
Selection
Total Price Computation
Selection
Priced
Priced
Selection
Selection
(Selection)
visual elements © Alberto Brandolini
Search
Bound
Select
a Fare on a Ship
Ship + Fare
Selected On Bound
Compute Total Price
Total Price Computation
Total Price Computed
Check
Completion
Fares Selection
COmpleted
Selected Fares
Selected Fares
Fares Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Incomplete
Selection
complete
Selection
Priced
Selection
visual elements © Alberto Brandolini
Check Selected Fares
Completion
Ancillary
Ship
Select
Ancillary
Ancillary
Selected
Search
Bound
Ship + Fare
Selected On Bound
Check
Selection
Completion
Check
Selection
Completion
Selection
COmpleted
Selected Fares
Selection Found Incomplete
Ancillaries
Retrieval
List
Ancillaries
Ship
Fare
Ancillaries
Listed
Ancillaries
Select
a Fare on a Ship
Incomplete
Selection
complete
Selection
Price
Selection
Total Price Computation
Selection
Priced
Priced
Selection
Selection
(Selection)
visual elements © Alberto Brandolini
Select
Ancillary
Check
Selection
Completion
Selection
COmpleted
Selection Found Incomplete
Price
Selection
Selection
Priced
Select
a Fare on a Ship
Ancillary
Selected
Ship + Fare
Selected On Bound
SELECTION
visual elements © Alberto Brandolini
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"selected": true,
"booked" : true
{
"search": {
"id": "XXX",
"ships": [
{
"number": "A",
"bound": "outbound",
"fares": [
{
"comfortClass": "FIRST",
"price": "100€"
},
{
"comfortClass": "SECOND",
"price": "50€"
}
]
},
{
"number": "B",
"bound": "outbound",
"fares": [
{
"comfortClass": "FIRST",
"price": "80€"
}
]
},
{
"number": "C",
"bound": "inbound",
"fares": [
{
"comfortClass": "FIRST",
"price": "90€"
}
]
},
{
"number": "D",
"bound": "inbound",
"fares": [
{
"comfortClass": "FIRST",
"price": "100€"
},
{
"comfortClass": "SECOND",
"price": "50€"
}
]
}
]
}
}{
"selection": {
"id": "YYYY",
"isComplete": true,
"totalPrice": "190€",
"outbound": {
"number": "A",
"fare": {
"comfortClass": "FIRST",
"price": "100€"
}
},
"inbound": {
"number": "C",
"fare": {
"comfortClass": "FIRST",
"price": "90€"
}
}
}
}
class Selection {
TripType tripType;
Map<Bound, Fare> selectedFares = new HashMap();
void selectFare(Bound bound, Fare selectedFare) {
selectedFares.put(bound, selectedFare);
}
Price totalPrice() {
return selectedFares.values().stream()
.reduce(Fare::add)
.get();
}
boolean isSelectionComplete() {
if (tripType == ONEWAY){
return selectedFares.size() == 1;
} else {
return selectedFares.size() == 2;
}
}
}public record Search(UUID id, Criteria criteria, SearchType searchType, List<Ship> ships, PaymentMean paymentMean) {
public Search {
assert ships.stream().noneMatch(ship -> ship.departureDate().isBefore(now()))
: "Some ships are departing in the past";
}
/*...*/
}
public record Search(UUID id, Criteria criteria, SearchType searchType, List<Ship> ships, PaymentMean paymentMean) {
public Search {
if (!hasBookedShips()) {
assert ships.stream().noneMatch(ship -> ship.departureDate().isBefore(now()))
: "Some ships are departing in the past";
}
}
}
public record Search(UUID id, Criteria criteria, SearchType searchType, List<Ship> ships, PaymentMean paymentMean) {
public Search {
if (!hasBookedShips()) {
assert ships.stream().noneMatch(ship -> ship.departureDate().isBefore(now()))
: "Some ships are departing in the past";
}
}
public boolean hasBookedShips() {
Map<Bound, Long> bookedShipsPerBound =
ships.stream()
.flatMap(ship -> ship.fares().stream().filter(Fare::booked).map(_ -> ship))
.collect(groupingBy(Ship::bound, counting()));
if (searchType == SearchType.ONEWAY) {
return bookedShipsPerBound.get(OUTBOUND) == 1;
} else { //RoundTrip
return bookedShipsPerBound.get(OUTBOUND) == 1 && bookedShipsPerBound.get(INBOUND) == 1;
}
}
public List<Ship> getBookedShips() {
var bookedShips = ships.stream().filter(ship -> ship.fares().stream().anyMatch(Fare::booked)).toList();
if (bookedShips.isEmpty()) {
return Collections.emptyList();
}
List<Ship> cleanedShips = new ArrayList<>();
for (var bookedShip : bookedShips) {
Fare bookedFare = getBookedFare(bookedShip);
Ship cleanedShip = new Ship(bookedShip.number(), bookedShip.bound(), bookedShip.departureDate(), asList(bookedFare));
cleanedShips.add(cleanedShip);
}
return cleanedShips;
}
private Fare getBookedFare(Ship bookedShip) {
Fare bookedFare = null;
int i = 0;
while(i < bookedShip.fares().size()) {
Fare fare = bookedShip.fares().get(i);
if(fare.booked()){
bookedFare = fare;
}
i++;
}
return bookedFare;
}
public void book() {
if(paymentMean == null) {
throw new IllegalStateException("Payment mean is missing");
}
if (hasBookedShips()) {
throw new IllegalStateException("Ships are already booked");
}
var selectedFaresPerBound = new ArrayList<Entry<Bound, Fare>>();
for (Ship ship : ships) {
for (Fare fare : ship.fares()) {
if (fare.selected()) {
selectedFaresPerBound.add(entry(ship.bound(), fare));
}
}
}
if (selectedFaresPerBound.isEmpty()) {
throw new IllegalStateException("Ships must be selected before booking");
}
var numberOfSelectedFarePerBound = selectedFaresPerBound.stream().collect(groupingBy(Entry::getKey, counting()));
if (searchType == SearchType.ONEWAY) {
assert numberOfSelectedFarePerBound.get(OUTBOUND) == 1 && numberOfSelectedFarePerBound.get(INBOUND) == 0 : "Inconsistent number of selected fares";
} else { //RoundTrip
assert numberOfSelectedFarePerBound.get(OUTBOUND) == 1 && numberOfSelectedFarePerBound.get(INBOUND) == 1 : "Inconsistent number of selected fares";
}
for (Entry<Bound, Fare> selectedFare : selectedFaresPerBound) {
selectedFare.getValue().book();
}
}
public boolean isSelectionComplete() {
Map<Bound, Long> selectedFaresPerBound = ships.stream().flatMap(ship -> ship.fares().stream().filter(Fare::selected).map(_ -> ship)).collect(groupingBy(Ship::bound, counting()));
var selectedOutBounds = selectedFaresPerBound.get(OUTBOUND);
var selectedInBounds = selectedFaresPerBound.get(INBOUND);
if (searchType == SearchType.ONEWAY) {
//This should never happen 👇
assert selectedOutBounds <= 1 && selectedInBounds <= 0 : "Too many fares are selected";
return selectedOutBounds == 1;
} else { //RoundTrip
//This should never happen 👇
assert selectedOutBounds <= 1 && selectedInBounds <= 1 : "Too many fares are selected";
return selectedOutBounds == 1 && selectedInBounds == 1;
}
}
public Price getTotalPrice() {
return ships.stream().flatMap(ship -> ship.fares().stream().filter(Fare::selected).map(Fare::price)).reduce(Price::add).orElse(Price.ZERO);
}
public void selectFareOfShip(Bound bound, String shipNumber, ComfortClass comfortClass) {
var fareToSelect = ships.stream().filter(ship -> ship.number().equals(shipNumber) && ship.bound() == bound).flatMap(ship -> ship.fares().stream().filter(fare -> fare.comfortClass() == comfortClass)).findFirst().orElseThrow();
var previousSelectionOnBound = ships.stream().filter(ship -> ship.bound() == bound).flatMap(ship -> ship.fares().stream()).filter(Fare::selected).findFirst();
previousSelectionOnBound.ifPresent(fare -> fare.setSelected(false));
fareToSelect.setSelected(true);
}
}
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"selected": true,
"booked" : true
public record Search(UUID id, Criteria criteria, SearchType searchType, List<Ship> ships) {
public Search {
assert ships.stream().noneMatch(ship -> ship.departureDate().isBefore(now()))
: "Some ships are departing in the past";
}
/*...*/
}
public record Booking(UUID id, List<Ship> ships, PaymentMean paymentMean, Boolean finalized) {
public Booking {
if(!finalized){
assert ships.stream().noneMatch(ship -> ship.departureDate().isBefore(now()))
: "Some ships are departing in the past";
}
}
boolean isFinalized(){/**/}
}
CRUISING
POST-BOOKING
Text
works around
Sclerosed
Model
Outgrowth
Model
Cruising
Post-Booking
POST-BOOKING
CRUISING
Search
Booking
Selection
POST-BOOKING
CRUISING
Booking
Selection
Search
POST-BOOKING
CRUISING
Search
Booking
Selection
POST-BOOKING
CRUISING
Search
Booking
Selection
noDepartureInThePast()
noDepartureInThePast()
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
POST-BOOKING
CRUISING
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Cancellation
Booking
Exchanged
Booking
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Cancelled
Booking
POST-BOOKING
CRUISING
Booking
Exchanged
Booking
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Cancelled
Booking
Completed
Booking
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Created
Completed
Exchanged
Cancelled
POST-BOOKING
CRUISING
SHOPPING
ORDER
DELIVERY
POST-BOOKING
CRUISING
POST-BOOKING
CRUISING
fix ship departing in the past
POST-BOOKING
CRUISING
MOBILE APP
Determining current booking status
check booking exists
check if exchange or cancellation exists
Completed
POST-BOOKING
CRUISING
MOBILE APP
check if exchanged/cancelled
check booking exists
Determining current booking status
BOOKING
can be both
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Cancellation
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Cancellation
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
POST-BOOKING
CRUISING
Cancellation
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Cancellation
POST-BOOKING
CRUISING
breaks logic in
Every model evolution breaks collaborating bounded-contexts.
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Text
Sclerosed
Model
Cruising
works around
Outgrowth
Model
Post-Booking
Sclerosed
Model
Outgrowth
Model
works around
Model
Fragmentation
becomes a fragment of
is a fragment of
causes
Extrinsic
Coupling
Cruising
Post-Booking
Business
Logic Leak
favorises
induces
exacerbates
is a form of
Extrinsic
Coupling
Outgrowth
Model
Model
Fragmentation
Synced
Evolutions
Model
Fragmentation
Extrinsic
Coupling
is a form of
contrains
systems to
Synced
Evolution
results in
Synced
Failures
leads to
System
Entropy
raises
binds the fragments into
Distributed
BBOM
entangles
into
crystallizes
into
Fragmented
Model
Rigidity
causes
Fragmented
Model
Fragility
POST-BOOKING
CRUISING
Booking
Release 463
Release 52
POST-BOOKING
CRUISING
ignores
Release 464
Release 52
Booking
Insurance
Policy
Insurance
Policy
Cancellation
POST-BOOKING
CRUISING
ignores
Release 464
Release 52
Booking
Insurance
Policy
Insurance
Policy
Incompatible
Cancellation
POST-BOOKING
CRUISING
Insurance
Policy
Release 464
Release 53
Deploy Together
Booking
Insurance
Policy
Insurance
Policy
Cancellation
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Process Tension
Org. Tension
Governing
Constraint
POST-BOOKING
CRUISING
Release 465
Release 57
Incompatible
Special Meal
POST-BOOKING
CRUISING
Release 465
Release 57
Compatible
Special Meal
Toggle: OFF
POST-BOOKING
CRUISING
Release 465
Release 58
Compatible
Toggle: OFF
Toggle: OFF
Special Meal
Special Meal
POST-BOOKING
CRUISING
Release 465
Release 58
Toggle: ON
Toggle: ON
Special Meal
Special Meal
Compatible
POST-BOOKING
CRUISING
Special Meal
Special Meal
Interface
Toggle: OFF
Toggle: OFF
POST-BOOKING
CRUISING
Special Meal
ready?
Special Meal
Interface
ready?
Toggle: OFF
Toggle: OFF
POST-BOOKING
CRUISING
Special Meal
ready?
Special Meal
Interface
ready?
Toggle: OFF
Toggle: OFF
POST-BOOKING
CRUISING
Special Meal
ready?
Special Meal
Interface
ready?
Toggle: OFF
Toggle: OFF
Extrinsic
Coupling
leads to
Synchronized
Releases
leads to
InterTeam
Work Coupling
Extrinsic
Coupling
Synchronized
Releases
InterTeam
Work Coupling
leads to
leads to
buffered by
buffered by
Palliative Roles
handled by
Band-aid processes
Extrinsic
Coupling
leads to
leads to
buffered by
buffered by
offset
offset
Synchronized
Releases
InterTeam
Work Coupling
Palliative Roles
handled by
Compensatory
Counter Measures
Band-aid processes
Extrinsic
Coupling
Tests Envs Proliferations
Synced
plannings
& backlogs
Feature Flags
Overuse
Multi
Versionning
Shared & Distributed
DataSets
leads to
leads to
buffered by
buffered by
offset
offset
never address
Compensatory
Counter Measures
Synchronized
Releases
InterTeam
Work Coupling
Extrinsic
Coupling
Tests Envs Proliferations
Synced
plannings
& backlogs
Feature Flags
Overuse
Multi
Versionning
Shared & Distributed
DataSets
leads to
leads to
buffered by
buffered by
offset
offset
never address
Compensatory
Counter Measures
Synchronized
Releases
InterTeam
Work Coupling
Tests Envs Proliferations
Synced
plannings
& backlogs
Feature Flags
Overuse
Multi
Versionning
Shared & Distributed
DataSets
leads to
leads to
buffered by
buffered by
offset
offset
never address
Compensatory
Counter Measures
Extrinsic
Coupling
Synchronized
Releases
InterTeam
Work Coupling
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Process Tension
Compensations
System
Org. Tension
GoverningConstraint
Crystallized
Extrinsic Model Tensions
Booking
Exchange
POST-BOOKING
CRUISING
Cancellation
Seat Preferences
Seat Preferences
When a need for feature debugging or development arises, debates often break out to determinate which team should be in charge
ToDo
ToDo
ToDo
In Progress
When the work of a stream-aligned team is systematically on the critical path of another team
GoverningConstraint
Synchronized
Releases
InterTeam
Work Coupling
Compensations
Systems
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Org. Tension
Org. Tension
Catalyst
Model
Fragmentation
Organizational
Fragmentation
Synced
Evolution
Process
overhead
???
A capability varies in scope, maturity, or behavior across the system, causing gaps and inconsistencies.
Business
Impact
Socio-technical system entropy
Model
Fragmentation
Organizational
Fragmentation
Synced
Evolution
Process
overhead
Sclerosed
Model
Outgrowth
Model
Sclerosed
Model
works around
Outgrowth
Model
Historical
Team
New Team
offloads the
work of
Historical
Team
Sclerosed
Model
works around
Outgrowth
Model
offloads the
work of
New Team
Sclerosed
Model
works around
Outgrowth
Model
Organizational
Fragmentation
Historical
Team
New Team
Sclerosed
Model
Organizational
Fragmentation
Historical
Team
New Team
Model
Fragmentation
Conway's Law
Outgrowth
Model
Sclerosed
Model
Organizational
Fragmentation
Historical
Team
New Team
Model
Fragmentation
Conway's Law
Outgrowth
Model
Historical
Team
Sclerosed
Model
works around
Outgrowth
Model
offloads the
work of
New Team
Structure eats Strategy
Jan Bosch - 2017
Organizational Tensions
produce
Architectural
Tensions
structure
Process
Tensions
compensate
Business
Tensions
reflects as
Model Tension
Heuristics
Model
Tension
spot
prevents
Model
Entropy
Model
Tension
Model Tension
Heuristics
spot
Socio-Technical
Entropy
prevents
Detect signals
across
the sociotech system
Highlight
Extrinsic Coupling
noDepartureInThePast()
noScheduleOverlap()
noDepartureInThePast()
noScheduleOverlap()
Use heuristics
to find symptoms
Collect a body of evidence to explore possible correlations
Assess its location, depth & Spread
... but are only heuristics. Not surefire rules.
SHOPPING
ORDER
DELIVERY
...do not always reflect tensions.
Once you have a body of evidences, act on the signals by running limited, safe to fail experiments to test your heuristics
POST-BOOKING
CRUISING
Exchange
Cancellation
Search
Booking
@julientopcu.com
@josianchevalier.bsky.social
orders
POST-BOOKING
CRUISING
Exchange
Cancellation
Search
Booking
POST-BOOKING
CRUISING
Booking
Exchange
Cancellation
Search
Shared
Kernel
POST-BOOKING
CRUISING
Booking
Exchange
Cancellation
Search
SK
POST-BOOKING
CRUISING
Booking
Exchange
Cancellation
Search
BOOKING
CRUISING
Booking
Exchange
Cancellation
Search
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Cancellation
POST-BOOKING
CRUISING
drives
Exploration
&
Experimentation
Business Need
shape
Model
needs to account for new
drives
Exploration
&
Experimentation
Business Need
shape
Model
needs to account for new
drives
Exploration
&
Experimentation
Business Need
shape
Model
needs to account for new
Model
Accidental
Design Debt
Model
Entropy
produces through
accumulation
generates through
misguided decisions
manifests
as complexity in
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
Intrinsic
Coupling
Accidental
Design Debt
Responsibilities
Coupling
may induce
manifests as
strong
{
"search": {
"id": "XXXX",
"criteria": {
"outBoundDate": "2025-10-06T12:00",
"inBoundDate": "2025-10-16T11:00"
},
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
"ships": [
{
"number": "A",
"bound": "outbound",
"departureDate": "2025-10-06T13:30",
"fares": [
{
"comfortClass" : "FIRST",
"price": "100€",
"seatOptions": [ "ANY", "..." ],
"deckOptions": [ "ANY", "..." ],
"preferences": {
"seat": "LOUNGE",
"deck" : "UPPER_DECK"
},
"selected": true,
"booked" : true
Intrinsic
Coupling
Accidental
Design Debt
Responsibilities
Coupling
may induce
manifests as
strong
"Spork Effect"
Intrinsic
Coupling
{}
Refactoring
Habits
fosters
Sunk Cost
Fallacies
fosters
Deadlines
Pressure
KISS
DRY
YAGNI
is the path to
BBOM
"Spork Effect"
Intrinsic
Coupling
{}
BBOM
leads to
Model
Entropy
dissolves the model into a
"Spork Effect"
Intrinsic
Coupling
{}
BBOM
Model
Entropy
dissolves the model into a
leads to
locking the
model into
hardens into
Model
Sclerosis
Entropy
Model Rigidity
Entropy