Emberstead started as a shared calendar. The feature that made it worth building was the one that tells you when two plans collide.
A shared family calendar sounds like a solved problem. Everyone already has a calendar app. The reason families still run on group chats and fridge notes is not that they lack a place to put events, it is that nobody finds out about a collision until someone is already in the car.
So when I started building Emberstead, I flipped the priority. The calendar grid was the easy part. The part worth engineering was the moment two plans overlap and somebody needs to know right now.
Detecting the clash is the easy half
Comparing time ranges for a single member is trivial: sort by start time, check whether the next start falls before the previous end. The hard part is deciding what counts as a clash in a real household. A parent driving one child to football and another to a dentist appointment is a clash even though the two events belong to different people. A pet's walk overlapping a work call is usually not.
That means a clash is not a property of two events. It is a property of two events plus the person who has to be in both places. Once I modelled it that way, the rule got simple: for each member, collect every event where they are an attendee or the responsible adult, then look for overlaps within that set.
Telling someone is the hard half
A warning that appears after you save is a warning you will ignore. The clash check runs while you are still filling in the time field, so the conflict appears in the same breath as the decision that caused it. And it does not just say there is a problem, it offers the fix: a single button that shifts the new event past the end of the one it collides with.
- Check on input, not on submit
- Name the specific event being collided with, not just 'conflict detected'
- Offer the resolution inline so the fix is one tap, not a re-edit
- Never block the save. Sometimes people genuinely mean to double-book.
What I would do differently
I built the detection synchronously against local state first, which made it fast and made the UI easy to reason about. It also meant that once live sync landed, two people editing at the same moment could each pass their own clash check and still create a conflict between them. Server-side validation on write closes that gap, and it is the part I should have designed in from the start rather than bolted on after the interaction felt good.