Vous pensiez que la dette technique était le pire ?
Voici la dette de conception !
@julientopcu.com

@josianchevalier.bsky.social















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

Croisières chevauchantes sélectionnables
Bug

[Ancillary]
Connection Pigeon Voyager sans fil
Feature
JIRA



Bateaux du passé affichés dans la Recherche

Bug

Ajouter l'assurance annulation

Feature
TODo
Current Quest
Done

Croisières chevauchantes sélectionnables
Bug

[Ancillary]
Connection Pigeon Voyager sans fil
Feature
JIRA




Bug

Ajouter l'assurance annulation

Feature
Bateaux du passé affichés dans la Recherche

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


Recherche
(Search)
Réservation
(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 regression historique de réservation #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 "Croisières des jours précédents" #2099
Josian merged 57 commits into
cruising:main

+ 1278
- 843














Le Vaillant
Code Spaghetti
a.k.a Big Ball of Mud





Besoin Métier
Recherche de Croisière




guide
Exploration
&
Expérimentation
Besoin Métier
Recherche de Croisière
Concepts
Relations
Comportements
Croisière

Réserver
Destination

Tarif

Définir
sélectionner




guide
Exploration
&
Expérimentation
Besoin Métier
façonne
Modèle Métier
Recherche de Croisière
Concepts
Relations
Comportements
Croisière

Réserver
Destination

Tarif

Définir
sélectionner




doit prendre en compte un nouveau
Sélection d'une classe tarifaire
guide
Exploration
&
Expérimentation
Besoin Métier
façonne
Modèle Métier




guide
Exploration
&
Expérimentation
Besoin Métier
façonne
Itérative
doit prendre en compte un nouveau
Sélection d'une classe tarifaire
Modèle Métier
Modélisation




guide
Exploration
&
Expérimentation
Besoin Métier
étend
doit prendre en compte un nouveau
Modèle Métier
Itérative
Modélisation




guide
Besoin Métier
Exploration
&
Expérimentation
Itératif
Sabordage
Modèle Métier
étend
doit prendre en compte un nouveau




guide
Exploration
&
Expérimentation
Besoin Métier
Modèle Métier
doit prendre en compte un nouveau
étend




guide
Exploration
&
Expérimentation
Besoin Métier
Obsolète
Modèle Métier
étend
doit prendre en compte un nouveau




$$$
guide
Exploration
&
Expérimentation
Besoin Métier
Modèle Métier
doit prendre en compte un nouveau
façonne




Aucun Modèle (code métier) n'est extensible en soi !





guide
Exploration
&
Expérimentation
Besoin Métier
Modèle Métier
façonne
de Modèles
Exploration
doit prendre en compte un nouveau




challengé par un nouveau
Pas un biais de confirmation
de Modèles
Exploration
guide
Exploration
&
Expérimentation
Besoin Métier
Modèle Métier
doit prendre en compte un nouveau
façonne




Besoin Métier
en façonnant
Modèle Métier
Nouveau Modèle
peut
déprécier
Exploration
&
Expérimentation
challengé par un nouveau
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€",
}]
}]
}
Recherche
(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€",
}Recherche
Selection
(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,
"booked" : true,
}]
}],
"selectionComplete" : true,
"totalPrice" : "230€",
"paymentMean": {
"type": "CREDIT_CARD",
"number": "***********"
},
"travelInsurance" : "40€",
}
Réservation
Recherche
Selection
(Search)
(Booking)














Dette de Conception
L'accidentelle




Dette Technique
- Ward Cunningham
Choix délibéré de livrer un code partiellement inadapté, reflet d'une compréhension encore incomplète du problème, afin d'avancer plus rapidement




de Conception
Dette Technique
Choix délibéré de livrer un code partiellement inadapté, reflet d'une compréhension encore incomplète du problème, afin d'avancer plus rapidement




de Conception
Dette Technique
concevoir un modèle
Choix délibéré de livrer un code partiellement inadapté, reflet d'une compréhension encore incomplète du problème, afin d'avancer plus rapidement




Dette de Conception
Accidentelle
Modèle
génère via des décisions inadéquates
se manifeste en complexité dans
Dette de Conception
sabote la cohésion
augmente la complexité
& érode la fiabilité des modèles !
introduit du couplage




Des bugs qui apparaissent dans
des endroits inattendus ?




Un changement simple qui oblige à modifier le modèle partout ?
Des bugs qui apparaissent dans des endroits inattendus ?




Des fonctionnalités qui prennent
de plus en plus de temps ?
Des bugs qui apparaissent dans des endroits inattendus ?
Un changement simple qui oblige à modifier le modèle partout ?














Features taking longer to develop?
More bugs in unrelated parts of the model?
Simple change causing widespread model updates?
L'implacable
Entropie de Modèle














Features taking longer to develop?
More bugs in unrelated parts of the model?
Simple change causing widespread model updates?
Le niveau de Zbeul
dans le design
a.k.a




Entropie de Modèle
L'énergie se dissipe à force de lutter contre la complexité accidentelle
a.k.a le niveau de Zbeul
Haute Entropie
moins de cohérence, consistence & de cohésion
résultats imprévisibles
régressions inattendues




Entropie de Modèle
génère par accumulation
Dette de Conception
dissipe la structure et la clarté de
Modèle
génère via des
décisions inadéquates




Code
Spaghetti
dissout le modèle en
Entropie de Modèle
génère par accumulation
Dette de Conception
dissipe la structure et la clarté de
Modèle
génère via des
décisions inadéquates
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




Pression des Deadlines
Biais des coûts irrécupérables
{}
Couplage intrinsèque
favorisent
favorisent
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
Couplage intrinsèque
effet cuichette




{}
se traduit par
Dette de Conception
Couplage de Responsabilité
Couplage intrinsèque
peut causer
"Effet Cuichette"




"Effet Cuichette"
cause
Fragilité de Modèle
Couplage intrinsèque
se traduit par
peut causer
entraine
Rigidité de Modèle
Entropie de Modèle
augmente
produit par
accumulation
{}
Dette de Conception
Couplage de Responsabilité




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
Modèles Distincts !
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














Tension de Modèle
La Redoutable




est le résultat de
Dette de Conception
Accidentelle
Couplage Intrinsèque
provoque
est un smell de design qui signale que l'on tord le modèle au delà de son intégrité conceptuelle
Tension de Modèle
Tension
de Modèle
intégrité conceptuelle




Intégrité Conceptuelle
Chaque concept métier reste fidèle à ce qu'il représente, sans être tordu pour faire autre chose ni surchargé de responsabilités.




Intégrité
Conceptuelle
érode
évite
Intégrité Conceptuelle
Tension
de Modèle
est le résultat de
Dette de Conception
Accidentelle




{
"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
Tension
de Modèle




{
"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
Tension
de Modèle




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
Tension
de Modèle














Tension Cristallisée
L'inflexible




compromet
"fibrose"
non résolue
mène à
Intégrité
Conceptuelle
érode
Entropie
Modèle
augmente
Tension
de Modèle
Tension
Cristallisée




Obsolescence
Signale
non résolue
mène à
Tension
de Modèle
Tension
Cristallisée






comme signal de dette de conception accidentelle








Tension de Modèle
La Redoutable














invisible
comme signal de dette de conception accidentelle
Tension de Modèle
La Redoutable










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














Tension Linguistique
Biais de Saillance
Un biais cognitif qui prédispose à se concentrer sur les informations les plus proéminentes ou visibles.




Tension Linguistique
Biais de Saillance












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














Tension de relation de concepts
Effet de liaison cognitive

Selected
Fares +
Ancillaries
L'association de concepts apparemment indépendants masque un concept qui les contient





Selected
Fares +
Ancillaries
Tension de relation de concepts
Effet de liaison cognitive





Selection
L'association de concepts apparemment indépendants masque un concept qui les contient
Tension de relation de concepts
Effet de liaison cognitive













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














Tension de cycle de vie
Cycle de vie Implicite
Le cycle de vie d'un concept est dérivé des états d'autres concepts




Tension de cycle de vie
Cycle de vie Implicite













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
décharge
Modèle Spaghetti
Nouveau
Modèle
Cruising
Post-Booking
Piste Explorée
Investigation

Nos Systèmes respectifs forment un monolithe distribué












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














Tension d'Architecture
Fuite de Logique Métier
Les logiques métier, cycles de vie, etc. sont éparpillés entre le code de plusieurs domaines métier voire de services
Tension d'Architecture
Fuite de Logique Métier




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












Panier
SHOPPING
Commande
ORDER
Colis
DELIVERY
Cycle de vie du Booking
Created
Completed
Exchanged
Cancelled
POST-BOOKING
CRUISING












Exchanged
Cancelled
POST-BOOKING
CRUISING












correction des croisières qui partent dans le passé
Created
Completed
Cycle de vie du Booking














Tension d'Architecture
Cycle de Vie Fragmenté
Quand des domaines métiers différents gèrent les états d'un même concept, tout en partageant plusieurs concepts, personae et règles.
Tension d'Architecture
Cycle de Vie Fragmenté




POST-BOOKING
CRUISING












APPLICATION MOBILE
Booking
Exchange
Cancellation
Déterminer le statut actuel d'un booking
vérifie si le booking existe
vérifie si l'échange ou l'annulation existe
Completed
Booking
Created
Completed
Exchanged
Cancelled
POST-BOOKING
CRUISING












vérifie si l'échange ou l'annulation existe
vérifie si le booking existe
BOOKING
potentiellement les deux
APPLICATION MOBILE
Déterminer le statut actuel d'un booking














Tension d'Architecture
États Distribués
Connaitre l'état d'un concept demande de consolider de la donnée provenant de plusieurs domaines métier voire des services
Tension d'Architecture
États Distribués




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
Modèle Fragmenté












Cancellation
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Couplage Extrinsèque












Cancellation
POST-BOOKING
CRUISING












Changement Fonctionnel
Remédiation
casse de la logique dans
Tension d'Architecture
Changements Cassants Fréquents




Toutes les évolutions de modèles d'un domaine métier
cassent les domaines qui collaborent avec lui.
Tension d'Architecture
Changements Cassants Fréquents





Investigation
Tensions d'Architecture

Fuites de Logique Métier
Cycle de Vie Fragmenté

États Distribués

Changements Cassants Fréquents

noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Couplage Extrinsèque

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


















Text
décharge
Cruising
Post-Booking
Modèle Spaghetti
Nouveau
Modèle




Text
contourne
Cruising
Post-Booking
Modèle Spaghetti
Modèle
Excroissant




Cruising
Post-Booking
contourne
devient un fragment de
Modèle
Fragmenté
est un fragment de
Modèle Spaghetti
Modèle
Excroissant
engendre
Couplage
Extrinsèque




Fuite de
Logique Métier
Modèle
Fragmenté
induit
Modèle
Excroissant
favorise
aggrave
est une forme de
Couplage
Extrinsèque




Evolutions
Synchronisées
Couplage
Extrinsèque
contraint le système à des
Échecs
Synchronisés
engendre
Monolithe
Distribué
signalent
signalent




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














Évolutions Synchronisées
Contrainte Structurante














Compensations
System

Nos process et notre organisation sont contraints de compenser les évolutions synchronisées de notre modèle qui s'est fragmenté
Piste Explorée
Investigation
POST-BOOKING
CRUISING
Booking












Release 464
Release 52
Insurance
Policy
Insurance
Policy
POST-BOOKING
CRUISING












ignore
Release 464
Release 52
Booking
Insurance
Policy
Insurance
Policy
Cancellation
POST-BOOKING
CRUISING












ignore
Release 464
Release 52
Booking
Insurance
Policy
Insurance
Policy
Incompatibles
Cancellation
POST-BOOKING
CRUISING












Insurance
Policy
Release 464
Release 53
Déploiements simultanés
Booking
Insurance
Policy
Insurance
Policy
Cancellation














Tension de Processus
Livraisons Synchronisées
Nécessité de se synchroniser fortement autour des releases et des évolutions de modèles entre les différents systèmes
Tension de Processus
Livraisons Synchronisées


















Tension Organisationnelle
Équipes
Interdépendantes
Les équipes doivent constamment synchroniser leurs plannings et priorités, malgré leur périmètre d'autonomie
Organizational Tension
Inter-Team Work Coupling




Investigation

Évolutions
Synchronisées

noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Modèle Fragmenté
Tension Process
Livraisons
Synchronisées
Tension Org.
Équipes
Interdépendantes
Constrainte
Structurante

POST-BOOKING
CRUISING












Release 465
Release 57
Incompatibles
Meal
POST-BOOKING
CRUISING












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












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












Release 465
Release 58
Toggle: ON
Toggle: ON
Meal
Meal
Compatibles














Tension de Processus
Processus Rustine
Les problèmes de conception non résolus sont compensés par des processus tels que le multi-versionning, le double run et les feature flags, afin de limiter l'impact sur les autres équipes
Tension de Processus
Processus Rustine




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
prêt ?
prêt ?
POST-BOOKING
CRUISING












Meal
Meal
Interface
Toggle: OFF
Toggle: OFF
R
T
E
prêt ?
prêt ?
POST-BOOKING
CRUISING












Meal
prêt ?
Meal
Interface
prêt ?
Toggle: OFF
Toggle: OFF
Readiness
Toggle
Engineer














Tension Organisationnelle
Rôles Palliatifs
Quand la complexité croissante des processus et des cérémonies rustines nécessite un poste à temps plein
Tension Organisationnelle
Rôles Palliatifs








Couplage
Extrinsèque
mène à
Livraisons
Synchronisées
Équipes
Interdépendantes
mène à




Équipes
Interdépendantes
Livraisons
Synchronisées
compensées par
compensées par
gérés par
Couplage
Extrinsèque
mène à
mène à
Processus
Rustines
Rôles Palliatifs




Couplage
Extrinsèque
mène à
mène à
Livraisons
Synchronisées
compensées par
compensées par
Équipes
Interdépendantes
Processus
Rustines
Rôles Palliatifs
Contre-Mesures
Compensatoires




Palliative Roles
handled by
Band-aid processes
Tension Process
Tension Org.
Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Couplage
Extrinsèque
mène à
mène à
Livraisons
Synchronisées
compensées par
Contre-Mesures
Compensatoires
compensées par
Équipes
Interdépendantes
repousse le problème de
repousse le problème de




Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Couplage
Extrinsèque
mène à
mène à
n'adresse jamais
Livraisons
Synchronisées
repousse le problème de
repousse le problème de
compensées par
Contre-Mesures
Compensatoires
compensées par
Équipes
Interdépendantes




Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Couplage
Extrinsèque
mène à
mène à
n'adresse jamais
Livraisons
Synchronisées
compensées par
Contre-Mesures
Compensatoires
compensées par
Équipes
Interdépendantes
Cristallisation de Tension
Cristallisation de Tension
repousse le problème de
repousse le problème de




Système de Compensations
Shared & Distributed
DataSets
Tests Envs Proliferations
Feature Flags
Overuse
Multi
Versionning
Synced
plannings
& backlogs
Couplage
Extrinsèque
mène à
mène à
n'adresse jamais
Livraisons
Synchronisées
compensées par
Contre-Mesures
Compensatoires
compensées par
Équipes
Interdépendantes
repousse le problème de
repousse le problème de
Investigation

noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Couplage
Extrinsèque
Tension Process


Contre-Mesures
Compensatoires
Système de
Compensations
Tension Org.
Processus
Rustines
Rôles
Palliatifs


Évolutions
Synchrones
Contrainte
Structurante
Cristallisation
Tensions de Modèle














Socio-Technical
Entropy

Notre couplage extrinsèque se manifeste par des dysfonctionnements organisationnels
Piste Explorée
Investigation
Booking
Exchange
POST-BOOKING
CRUISING
Cancellation












Seat Preferences
Seat Preferences
Update Booking?
Create Exchange?














Tension Organisationnelle
Ownership Flou
Tension Organisationnelle
Ownership Flou
Lorsqu'un besoin de debug ou de développement se présente, des débats éclatent souvent pour déterminer quelle équipe doit s'en charger









Bug
Ships departing in the past are bookable
ToDo


Bug
ToDo
Cruising BackLog
Bateaux du passé sont réservables




Post-Booking BackLog
Cruising BackLog


Bug
In Progress


Bug
ToDo
Bateaux du passé sont réservables
Bateaux du passé sont réservables














Tension Organisationnelle
Détournement
de Backlog
Quand le travail d'une équipe produit se retrouve systématiquement sur le chemin critique d'une autre équipe
Tension Organisationnelle
Détournement de Backlog




Investigation

Modèle Fragmenté
Contrainte Structurante


Évolutions Synchrones
Synchronized
Releases
InterTeam
Work Coupling
Tensions de Modèle Cristallisées


Contre-mesures
Compensatoires
Système de
Compensations
Processus Rustines & Rôles Palliatifs
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()

Fragmentation Organisationnelle
Ownership
Flou
Tension Org.
Détournement de Backlog
Tension Org.

Catalyseur




Model
Fragmentation
Organizational
Fragmentation
Synced
Evolution
Process
overhead
Socio-technical system entropy














Results






???














Tension Business
Désalignement Fonctionnel
Tension Business
Désalignement Fonctionnel
Une fonctionnalité varie en scope, maturité ou comportement selon les parties du système, provoquant des écarts et des incohérences








Business
Impact
Socio-technical system entropy
Model
Fragmentation
Organizational
Fragmentation
Synced
Evolution
Process
overhead














Interpretation




Modèle Spaghetti
Outgrowth
Model




Modèle Spaghetti
contourne
Modèle Excroissant
Historical
Team
New Team
offloads the
work of




Équipe Historique
décharge
Nouvelle Équipe
Modèle Spaghetti
Modèle Excroissant
contourne




Fragmentation
Organisationnelle
Historical
Team
New Team
Modèle Excroissant
contourne
Modèle Spaghetti




Historical
Team
New Team
Loi de Conway
Fragmentation
Organisationnelle
Fragmentation de Modèle
Modèle Excroissant
contourne
Modèle Spaghetti














Loi de Conway
Les organisations produisent des systèmes qui reflètent leurs structures de communication




Le management conçoit l'architecture du système inconsciemment via des décisions organisationnelles
Manager as Shadow Designer
Tension Organisationnelle




Historical
Team
New Team
Fragmentation
Organisationnelle
Loi de Conway
Fragmentation de Modèle
Modèle Excroissant
contourne
Modèle Spaghetti




Équipe Historique
décharge
Nouvelle Équipe
Modèle Spaghetti
Modèle Excroissant
contourne




L'organisation vous fera payer les intérêts de votre dette de conception !!!
Extrinsic coupling signals are mainly socio-technical


















Structure eats Strategy
Jan Bosch - 2017
B.A.P.O Model
Organizational Tensions
produce
Architectural
Tensions
structure
Process
Tensions
compensate
Business
Tensions
reflects as




Les symptômes du couplage extrinsèque sont principalement socio-techniques
Conclusion


















Vous pensiez que la dette technique était le pire ?
Voici la dette de conception !
Et vos bonnes pratiques ne vous sauveront pas...














comme signal de dette de conception accidentelle
Tension de Modèle
La Redoutable














pour prévenir la dette de conception
Heuristiques de Tension de Modèle
Les Heuristiques












Panier
SHOPPING
Commande
ORDER
Colis
DELIVERY
...n'indiquent pas toujours des tensions.
Ce n'est pas un cycle de vie fragmenté !
µDétourer la Dette de Conception

Pourquoi ?

Comment ?

Détecter les frictions
à travers les strates du système
Mettre en évidence
les couplages

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

Constituer un faisceau d'indices pour trouver des corrélations

Évaluer leur localisation, profondeur & étendue

Identifier les causes possibles avec les heuristiques




Expérimentez sur le système
Une fois un faisceau d'indices constitué, agissez sur les signaux en menant des petites expériences réversibles pour tester vos hypothèses et vos solutions potentielles
POST-BOOKING
CRUISING
Exchange












Selection
Booking
Search
Cancellation
Search
Booking
POST-BOOKING
CRUISING
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()
Booking
Exchange
Couplage Extrinsèque












Cancellation
Investigation

Modèle Fragmenté
Contrainte Structurante


Évolutions Synchrones
Synchronized
Releases
InterTeam
Work Coupling
Tensions de Modèle Cristallisées


Contre-mesures
Compensatoires
Système de
Compensations
Processus Rustines & Rôles Palliatifs
noDepartureInThePast()
noDepartureInThePast()
noScheduleOverlap()
noScheduleOverlap()

Fragmentation Organisationnelle
Ownership
Flou
Tension Org.
Détournement de Backlog
Tension Org.

Catalyseur




Couplage Extrinsèque
Heuristiques
détectent
Tensions de Modèle
préviennent
Entropie Organisationnelle







SHODO


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

Vous pensiez que la dette technique était le pire ?
Voici la dette de conception !
Vous pensiez que la dette technique était le pire? Voici la dette de conception!
By Julien Topçu
Vous pensiez que la dette technique était le pire? Voici la dette de conception!
Dans nos métiers, la dette technique est sans doute la chose la plus frustrante, éprouvante et coriace contre laquelle nous devons lutter. Bien sûr, il existe des bonnes pratiques : refactoriser, viser la simplicité avec KISS, éviter de trop anticiper avec YAGNI afin de limiter cette dette au maximum. Et pourtant, on n'y vient jamais vraiment à bout. Pire encore, elle continue de s'accumuler même lorsque nous les appliquons, comme une fatalité. Et si ces mêmes bonnes pratiques ne faisaient parfois qu'empirer le problème au lieu de le résoudre? La dette technique peut n'être que la partie visible de quelque chose de plus profond : la dette de design accidentelle (dette de conception). Un problème bien plus complexe, donnant lieu à des logiciels spaghettis et à des microservices en monolithe distribué, dont nous sommes souvent à l'origine sans même nous en rendre compte. Dans cette conférence, nous explorerons les mécanismes de la dette de design et ses impacts sur notre architecture, nos processus, notre organisation, et même notre business. À travers des exemples concrets, nous verrons comment la détecter, analyser sa profondeur et agir avant qu'elle ne s'ancre durablement.
- 224