You thought technical debt was the worst?
Meet design debt!
@julientopcu.com

@josianchevalier.bsky.social















CHAPTER I
When model exploration
Whirlpool
turns into model sinking
TODo
Current Quest
Done

Selectable Overlapping Cruises
Bug

[Ancillary]
Wireless Carrier Pigeon Connection
Feature
JIRA



Past Ships Displayed in Search

Bug

Add Cancellation Insurance

Feature
TODo
Current Quest
Done

Selectable Overlapping Cruises
Bug

[Ancillary]
Wireless Carrier Pigeon Connection
Feature
JIRA




Bug

Add Cancellation Insurance

Feature
Past Ships Displayed in Search

{
"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
Booking

{
"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";
+ }
}
}Fix booking history regression #2097
Josian merged 10 commits into
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";
}
}
}Fix "Flexible Dates" Feature #2099
Josian merged 57 commits into
cruising:main

+ 1278
- 843














The Mighty
Spaghetti Code
a.k.a Big Ball of Mud





Business Need
Search Cruises




guides
Exploration
&
Experimentation
Business Need
Search Cruises
Concepts
Relations
Behaviors
Cruise

Book
Destination

Fare

Define
select




guides
Exploration
&
Experimentation
Business Need
shapes
Domain Model
Search Cruises
Concepts
Relations
Behaviors
Cruise

Book
Destination

Fare

Define
select




must account for a new
Selecting a fare
guides
Exploration
&
Experimentation
Business Need
shapes
Domain Model




guides
Exploration
&
Experimentation
Business Need
shapes
Modeling
must account for a new
Selecting a fare
Domain Model
Iterative




guides
Exploration
&
Experimentation
Business Need
extends
must account for a new
Domain Model
Modeling
Iterative




guides
Business Need
Exploration
&
Experimentation
Sinking
Model
Domain Model
extends
must account for a new




guides
Exploration
&
Experimentation
Business Need
Domain Model
must account for a new
extends




guides
Exploration
&
Experimentation
Business Need
Obsolete
Domain Model
extends
must account for a new




$$$
guides
Exploration
&
Experimentation
Business Need
Domain Model
must account for a new
shapes




No Model (business code)
is inherently extensible !





guides
Exploration
&
Experimentation
Business Need
Domain Model
shapes
Exploration
Model
must account for a new




challenged by a new
Not a confirmation bias
guides
Exploration
&
Experimentation
Business Need
Domain Model
must account for a new
shapes
Exploration
Model




Business Need
by shaping
Domain Model
New Model
can
deprecate
Exploration
&
Experimentation
challenged by a new
Accidental Design Debt:
the invisible engine of BBOMs














One does not simply
challenge the model




{
"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




{
"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
Selection




{
"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€",
}
Booking
Search
Selection














Design Debt
The accidental




Technical Debt
- Ward Cunningham
Deliberate choice to ship code that does not fully fit, reflecting a still incomplete understanding of the problem, in order to move faster




Design
Technical Debt
Deliberate choice to ship code that does not fully fit, reflecting a still incomplete understanding of the problem, in order to move faster




design a model
Design
Technical Debt
Deliberate choice to ship code that does not fully fit, reflecting a still incomplete understanding of the problem, in order to move faster




Design Debt
Accidental
Model
generates through misguided decisions
manifests as complexity in
Design Debt
undermines cohesion
increases complexity
& erodes model reliability!
introduces coupling




Bugs that appear in unexpected places?




A simple change that forces you to modify the model everywhere?
Bugs appearing in unexpected places?




Features that take more and more time?
Bugs appearing in unexpected places?
A simple change that forces you to modify the model everywhere?














Features taking longer to develop?
More bugs in unrelated parts of the model?
Simple change causing widespread model updates?
The Unyielding
Model Entropy














Features taking longer to develop?
More bugs in unrelated parts of the model?
Simple change causing widespread model updates?
The WTF level
of the design
a.k.a




Model Entropy
Energy gets burned fighting accidental complexity
a.k.a the WTF level
High Entropy
less coherence, consistency & cohesion
unclear outcomes
unexpected regressions




Model Entropy
generates through accumulation
Design Debt
dissipates the structure and clarity of
Model
generates through
misguided decisions




Spaghetti
Code
dissolves the model into
Model Entropy
generates through accumulation
Design Debt
dissipates the structure and clarity of
Model
generates through
misguided decisions
Spork Effect:
unveiling the roots of model fragility and rigidity















{
"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



Booking
Search
manifests as




Refactoring
KISS
DRY
YAGNI




Deadline Pressure
Sunk cost Fallacies
{}
Intrinsic coupling
fosters
fosters
Refactoring
KISS
DRY
YAGNI




Booking
Search
{
"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
Booking
Search
Booking
Intrinsic coupling
spork effect




{}
translates into
Accidental Design Debt
Responsibility Coupling
Intrinsic coupling
may induce
"Spork Effect"




"Spork Effect"
causes
Model Fragility
Intrinsic coupling
translates into
may induce
leads to
Model Rigidity
Model Entropy
increases
produced by
accumulation
{}
Accidental Design Debt
Responsibility Coupling




Booking
Search
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
Booking
Search
Booking




Search
Booking
Distinct Models!
Search
Booking
Booking
Model tension as signals of accidental design debt


















{
"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
The Fearsome




is the result of
Accidental
Design Debt
Intrinsic Coupling
may
induce
is a design smell that signals that we are stretching the model beyond its conceptual integrity
Model Tension
Model
Tension
conceptual integrity




Conceptual Integrity
Each business concept stays true to what it represents, without being stretched to do something else
or overloaded with responsibilities.




Conceptual
Integrity
erodes
prevents
Conceptual Integrity
Model
Tension
is the result of
Accidental
Design Debt




{
"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




Booking
Search
{
"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
Booking
Search
Booking
Selection
Selection
Model
Tension














Crystallized
Model Tension
The Unstoppable




compromises
"fibrosis"
unresolved
leads to
erodes
Model
Entropy
raises
Model
Tension
Crystallized
Tension
Conceptual
Integrity




Obsolescence
Signals
unresolved
leads to
Model
Tension
Crystallized
Tension






as a signal of accidental design debt








Model Tension
The Fearsome














invisible
as a signal of accidental design debt
Model Tension
The Fearsome










Exploring (intrinsic)
model tensions heuristics
















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

Fares Selection Found Incomplete

Check Selected Fares
Completion

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














Linguistic Tension
Salience Bias




Linguistic Tension
Salience Bias
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














Concept Relationship Tension
Cognitive Binding Effect

Selected
Fares +
Ancillaries
The association of seemingly independent concepts hides a concept that contains them





Selected
Fares +
Ancillaries
Concept Relationship Tension
Cognitive Binding Effect





Selection
The association of seemingly independent concepts hides a concept that contains them
Concept Relationship Tension
Cognitive Binding Effect













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

Selection
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

Selection

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
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

Selection

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
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














Lifecycle Tension
Implicit Lifecycle
The lifecycle of a concept is derived from the states of other concepts




Lifecycle Tension
Implicit Lifecycle













Ancillary
Ship

Select
Ancillary

Ancillary
Selected



Search
Bound


Ship + Fare
Selected On Bound

Check
Selection
Completion

Check
Selection
Completion

Selection
COmpleted


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
visual elements © Alberto Brandolini
Selection













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 {
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) {
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";
}
}
}














CHAPTER 2
When model sclerosis
turns into model
Fragmentation












CRUISING
POST-BOOKING




Text
offloads
Spaghetti
Model
New
Model
Cruising
Post-Booking
Pursued Lead
Investigation

Our respective systems form a distributed monolith












POST-BOOKING
CRUISING
Search
Exchange
Cancellation
Search
Booking
Selection












POST-BOOKING
CRUISING
Search
Exchange
Cancellation
Booking
Selection
Search












Selection
Booking
POST-BOOKING
CRUISING
Search
Exchange
Cancellation
Search
Booking
Selection












Selection
Booking
POST-BOOKING
CRUISING
Search
Exchange
Cancellation
Search
Booking
Selection












Selection
Booking
Search
Exchange
Cancellation
noDepartureInThePast()
noDepartureInThePast()
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
POST-BOOKING
CRUISING












Selection
Booking
Search
Exchange
Cancellation














Architectural Tension
Business Logic Leak
Business logic, lifecycles, etc. are scattered across the code of several business domains or even services
Architectural Tension
Business Logic Leak




POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange












Selection
Booking
Search
Exchange
Cancellation
Cancellation
Booking
Exchanged
Booking
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Cancelled
Booking












Selection
Booking
Search
Exchange
Cancellation
POST-BOOKING
CRUISING
Booking
Exchanged
Booking
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Cancelled
Booking
Completed
Booking












Selection
Booking
Search
Exchange
Cancellation
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Created
Completed
Exchanged
Cancelled












Selection
Booking
Search
Exchange
Cancellation
POST-BOOKING
CRUISING












Cart
SHOPPING
Order
ORDER
Parcel
DELIVERY
Booking Lifecycle
Created
Completed
Exchanged
Cancelled
POST-BOOKING
CRUISING












Exchanged
Cancelled
POST-BOOKING
CRUISING












fix ship departing in the past
Created
Completed
Booking Lifecycle














Architectural Tension
Fragmented Lifecycle
When different business domains manage the states of the same concept, while sharing several concepts, personae and rules.
Architectural Tension
Fragmented Lifecycle




POST-BOOKING
CRUISING












MOBILE APP
Booking
Exchange
Cancellation
Determining the current status of a booking
checks if the booking exists
checks if the exchange or cancellation exists
Completed
Booking
Created
Completed
Exchanged
Cancelled
POST-BOOKING
CRUISING












checks if the exchange or cancellation exists
checks if the booking exists
BOOKING
MOBILE APP
Determining the current status of a booking
can be both














Architectural Tension
Distributed States
Knowing the state of a concept requires consolidating data coming from several business domains or even services
Architectural Tension
Distributed States




POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Cancellation












Selection
Booking
Search
Exchange
Cancellation
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Cancellation












Selection
Booking
Search
Exchange
Cancellation
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
POST-BOOKING
CRUISING
Fragmented Model












Cancellation
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Extrinsic Coupling












Cancellation
POST-BOOKING
CRUISING












Remediation
breaks logic in
Feature Change
Architectural Tension
Frequent
Breaking Changes




All model evolutions of a business domain
break the domains that collaborate with it.
Architectural Tension
Frequent
Breaking Changes





Investigation
Architectural Tensions

Business Logic Leaks
Fragmented Lifecycle

Distributed States

Frequent
Breaking Changes

noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Extrinsic Coupling

Model fragmentation (extrinsic coupling):
the insidious builder of distributed BBOMs


















Text
offloads
Cruising
Post-Booking
Spaghetti
Model
New
Model




Text
works around
Cruising
Post-Booking
Spaghetti
Model
Model
Outgrowth




Cruising
Post-Booking
works around
becomes a fragment of
Fragmented
Model
is a fragment of
Spaghetti
Model
Outgrowth
Model
causes
Extrinsic
Coupling




Business Logic
Leak
Fragmented
Model
induces
Outgrowth
Model
favorises
exacerbates
is a form of
Extrinsic
Coupling




Synchronized
Evolutions
Extrinsic
Coupling
forces
the system into
Synchronized
Failures
causes
Distributed
Monolith
signal
signal














Synchronized Evolutions
Structuring Constraint














Compensations
System

Our processes and our organization are forced to compensate for the synchronized evolutions of our model that has become fragmented
Pursued Lead
Investigation
POST-BOOKING
CRUISING
Booking












Release 464
Release 52
Insurance
Policy
Insurance
Policy
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
Booking
Insurance
Policy
Insurance
Policy
Cancellation
Deploy Together














Process Tension
Synchronized Releases














Organizational Tension
Inter-Team
Work Coupling
Investigation

Synchronized
Evolutions

noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Fragmented Model
Process Tension
Synchronized
Releases
Org. Tension
Structuring
Constraint

InterTeam
Work
Coupling
POST-BOOKING
CRUISING












Release 465
Release 57
Incompatible
Meal
POST-BOOKING
CRUISING












Release 465
Release 57
Compatible
Meal
Toggle: OFF
POST-BOOKING
CRUISING












Release 465
Release 58
Compatible
Toggle: OFF
Toggle: OFF
Meal
Meal
POST-BOOKING
CRUISING












Release 465
Release 58
Toggle: ON
Toggle: ON
Meal
Meal
Compatible














Process Tension
Band-aid Processes
Unresolved design problems are compensated by processes such as multi-versioning, double run and feature flags, in order to limit the impact on other teams
Process Tension
Band-aid Process




POST-BOOKING
CRUISING












Meal
Meal
Interface
Toggle: OFF
Toggle: OFF
R
T
E
POST-BOOKING
CRUISING












Meal
Meal
Interface
Toggle: OFF
Toggle: OFF
R
T
E
ready?
ready?
POST-BOOKING
CRUISING












Meal
Meal
Interface
Toggle: OFF
Toggle: OFF
R
T
E
ready?
ready?
POST-BOOKING
CRUISING












Meal
ready?
Meal
Interface
ready?
Toggle: OFF
Toggle: OFF
Readiness
Toggle
Engineer














Organizational Tension
Palliative Roles
When the growing complexity of band-aid processes and ceremonies requires a full-time position
Organizational Tension
Palliative Roles








Extrinsic
Coupling
leads to
Synchronized
Releases
InterTeam
Work Coupling
leads to




InterTeam
Work Coupling
Synchronized
Releases
buffered by
buffered by
handled by
Extrinsic
Coupling
leads to
leads to
Band-aid Processes
Palliative Roles




Extrinsic
Coupling
leads to
leads to
Synchronized
Releases
buffered by
buffered by
InterTeam
Work Coupling
Patch
Processes
Palliative Roles
Compensatory
Counter-Measures




Palliative Roles
handled by
Band-aid processes
Process Tension
Org. Tension
Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Extrinsic
Coupling
leads to
leads to
Synchronized
Releases
Compensatory
Counter-Measures
offset
offset
buffered by
buffered by
InterTeam
Work Coupling




Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Extrinsic
Coupling
leads to
leads to
never addresses
Synchronized
Releases
offset
offset
buffered by
buffered by
InterTeam
Work Coupling
Compensatory
Counter-Measures




Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Extrinsic
Coupling
leads to
leads to
never addresses
Synchronized
Releases
Tension Crystallization
Tension Crystallization
offset
offset
buffered by
buffered by
InterTeam
Work Coupling
Compensatory
Counter-Measures




Compensations System
Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Extrinsic
Coupling
leads to
leads to
never addresses
Synchronized
Releases
offset
offset
buffered by
buffered by
InterTeam
Work Coupling
Compensatory
Counter-Measures
Investigation

noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Extrinsic
Coupling
Process Tension


Compensatory Countermeasures
Compensations
System
Org. Tension
Band-aid
Processes
Roles
Palliatives


Synced
Evolutions
Structuring
Constraint
Crystallization
Model Tensions














Socio-Technical
Entropy

Our extrinsic coupling manifests itself through organizational dysfunctions
Pursued Lead
Investigation
Booking
Exchange
POST-BOOKING
CRUISING
Cancellation












Seat Preferences
Seat Preferences
Update Booking?
Create Exchange?














Organizational Tension
Unclear
Ownership
Organizational Tension
Unclear Ownership
When a debug or development need arises, debates often break out to determine which team should handle it









Bug
Ships departing in the past are bookable
ToDo


Bug
ToDo
Cruising BackLog
Past ships are bookable
Past ships are bookable




Post-Booking BackLog
Cruising BackLog


Bug
In Progress


Bug
ToDo
Past ships are bookable
Past ships are bookable














Organizational Tension
Backlog Takeover
When the work of one product team systematically ends up on the critical path of another team
Organizational Tension
Backlog Takeover




Investigation

Model Fragmentation
GoverningConstraint


Synced
Evolutions
Synchronized
Releases
InterTeam
Work Coupling
Crystallized Model Tensions


Compensatory
Countermeasures
Compensations
Systems
Band-Aid
Processes & ORg
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()

Organizational
Fragmentation
Unclear
Ownership
Org. Tension
Backlog
TakeOver
Org. Tension

Catalyst














Interpretation




Spaghetti Model
Outgrowth
Model




Spaghetti Model
works around
Outgrowth Model
Historical
Team
New Team
offloads the
work of




New Team
Spaghetti Model
Outgrowth Model
works around
offloads the
work of
Historical
Team




Historical
Team
New Team
Outgrowth Model
Spaghetti Model
works around
Organizational
Fragmentation




Historical
Team
New Team
Model Fragmentation
Outgrowth Model
bypasses
Spaghetti Model
Conway's Law
Organizational
Fragmentation














Conway's Law
Organizations produce systems that mirror their communication structure.




Management designs the system architecture unconsciously through organizational decisions
Manager as Shadow Designer
Organizational Tension




Historical
Team
New Team
Model Fragmentation
Outgrowth Model
bypasses
Spaghetti Model
Organizational
Fragmentation




New Team
Spaghetti Model
Outgrowth Model
works around
offloads the
work of
Historical
Team




The organization will make you pay interest on your design debt !!!
Conclusion


















You thought technical debt was the worst?
Here is design debt!
And your best practices won't save you...














as a signal of accidental design debt
Model Tension
The Fearsome














to prevent design debt
Model Tension Heuristics
The Heuristics












Cart
SHOPPING
Order
ORDER
Parcel
DELIVERY
...don't always indicate tensions.
This is not a fragmented lifecycle!
µOutline the Design Debt

Why?

How?

Detect frictions
across the layers of the system
Highlight
the couplings

noDepartureInThePast()
noScheduleOverlap()
noDepartureInThePast()
noScheduleOverlap()

Build up a body of evidence to find correlations

Assess their location, depth & scope

Identify possible causes with the heuristics
POST-BOOKING
CRUISING
Exchange












Selection
Booking
Search
Cancellation
Search
Booking
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Extrinsic Coupling












Cancellation
Investigation

Model Fragmentation
Synchronized
Releases
InterTeam
Work Coupling

Synced
Evolutions

Compensatory
Countermeasures

Compensations
Systems
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()

Organizational
Fragmentation
Unclear
Ownership
Org. Tension
Backlog
TakeOver
Org. Tension

Catalyst

GoverningConstraint
Crystallized Model Tensions
Band-Aid
Processes & ORg







SHODO


Julien Topçu
Josian Chevalier
Tech Coach
CTO / Coach
@julientopcu.com
@josianchevalier.bsky.social

You thought technical debt was the worst?
Meet design debt!
You thought technical debt was bad? Meet design debt!
By Julien Topçu
You thought technical debt was bad? Meet design debt!
In our job, technical debt is probably the most frustrating, draining and tenacious thing we have to fight against. Of course, there are best practices: refactor, aim for simplicity with KISS, avoid over-engineering with YAGNI in order to keep this debt as low as possible. And yet, we never really manage to get rid of it. Worse, it keeps piling up even when we apply those principles, as if it were inevitable. But what if those very best practices sometimes made the problem worse instead of solving it? Technical debt may be only the visible part of something deeper: accidental design debt. A far more complex problem that leads to spaghetti software and microservices that are really just a distributed monolith, and that we often create ourselves without even realizing it. In this talk, we will explore the mechanisms of design debt and its impact on our architecture, our processes, our organization, and even our business. Through concrete examples, we will see how to detect it, assess how deep it goes, and act before it becomes entrenched for good
- 9