Few months ago I've published a short article about what is a Model Debt & why it matters. Today I'd like to provide you with two nice, crispy examples that illustrate the idea. Both are coming from the Domain I currently work on with my teams - booking appointments in Beauty & Wellness industry.
Booking an appointment initially was very straightforward. Apart from knowing what (particular service) you were booking & whom (which stylist) you were booking, the only challenge was to make sure that no-one has booked the same time-slot (of a given stylist).
We've achieved it in the most obvious way: after checking the general working hours, we've looked for already made appointments (with a given stylist) in the requested time-frame - whatever we've found, was excluded from available time-slots. A simple query. Piss easy.
However we've kept adding features - our appointments involved shared, limited resources (like massage tables), all employees (incl. stylists) were able to not only adjust their general working hours, but also to mark flexible work-time (short absences during the day) and their schedules got filled with things other than appointments (e.g. cleaning time for equipment). That has tremendously increased the complexity of the bespoken query - now we were subtracting not only appointments related to stylists, but also resources (equipment) and also other time-absorbing activities performed by the stylists.
And that's not the end of it - apparently not whole appointment time involved stylists - e.g. when your hair is drying, stylist could do something else - in other words: could be bookable for another service ... This has blown our "availability query" out to several screens of sophisticated SQL querying logic. We also had to subject it to several optimizations, because it was not only complex, but also it referred to our biggest transactional tables in the booking sub-system.
Needless to say, complexity itself was already a big issue. It took a lot of time to "parse" the query conceptually to reverse-engineer its logic. And each change was a massive pain in the ... neck.
The enlightenment came with a question: "HOW can it be so complicated if explaining the EXPECTED logic to another person takes a single breath?"
Timeslot is available for booking if requested stylists and at least a single resource (element of equipment) in each group required for the service is AVAILABLE.
Yes, that's all.
100% of expected logic.
There's no simple freaking word about past appointments BECAUSE person/resource availability is a product of past appointments. A derived variable. What we needed was concept of AVAILABILITY & we've omitted it, because the complexity was growing too slow for us to spot an issue. We were using this word ("availability") on the daily basis, it should have been an important element of our UL, but it was not.
All we had to do was to introduce the concept of "Availability" in the model. For people (stylists), resources & locations there were several events which were impacting their availability (e.g. appointments, cleaning, time off, cancellation) - all of them were reflected by adjusting "Availability" (either of a person or equipment or location).
YES, it's some sort of redundancy - because the very same knowledge can be deduced from appointments like before, BUT what we're achieving by introducing the explicit concept of "Availability" is:
- extremely simple query logic of our most frequent query (trying to book an appointment)
- more testable code (because we can mock particular aspects of availability & test the availability adjustment logic in separation)
- decoupling of concerns (reserved appointments with logic determining the best conditions for another appointment)
- flexibility of simple adding new conditions for appointment booking logic (whenever it's needed)
See? One simple adjustment to the model which has MASSIVELY improved the most vital part of the whole platform (!). That's why design matters so much - making code reflect the reality in a more accurate manner has made all the difference.
In the next post in the series I'll tell you how Georgian restaurant owner inspired me to fix the way we do pricing - to learn more, read here.