6-Month Technical Retrospective: Architecture Decisions That Paid Off
Posted: November 20, 2026 · 5 min read
Six months in, what held up?
When you launch a product, you make dozens of architectural bets. Some are deliberate. Some are accidental. All of them age differently under the pressure of real users, real data, and real edge cases. Six months after launch, we are looking back at our biggest technical decisions with the benefit of hindsight.
This is an honest retrospective. Some decisions look brilliant in hindsight. Others need work. We are sharing both because engineering retrospectives that only celebrate wins are not retrospectives. They are press releases.
Decision: Local storage instead of cloud. Verdict: Paid off.
Storing all calendar data locally in the browser (IndexedDB and chrome.storage) was our most fundamental architectural choice. Every other decision flows from it. No server means no server costs, no uptime concerns, no data privacy liability, and no dependency on network availability.
Six months in, the benefits have exceeded our expectations. We have had zero data breaches (there is no server to breach). We have had zero downtime incidents (there is no server to go down). Our support queue has zero tickets about data loss, sync errors, or account issues. These are entire categories of problems that simply do not exist in our architecture.
The cost is real but manageable: users cannot access their unified calendar from a device that does not have the extension installed. Data does not roam across machines automatically. These limitations are accepted by our users because they understand the trade-off. Privacy and reliability in exchange for portability. For most multi-client professionals, that is a trade worth making.
Decision: Tab Sync as a first-class data source. Verdict: Paid off, with maintenance costs.
Tab Sync lets manyCalendars read calendar events from open browser tabs, which means it works with any web-based calendar even when ICS feeds are unavailable. This was a differentiating feature from day one, and it remains the primary reason many users choose manyCalendars over alternatives.
The technical reality is that scrapers are fragile. Google Calendar has updated its DOM structure three times in six months. Outlook Web has changed twice. Each update required a sync engine patch, usually within 24 to 48 hours. We have gotten faster at patching, but the fundamental brittleness remains.
What we did right was designing the sync engine architecture modularly. Each calendar provider has its own sync engine module with a standardized interface. When Google changes their layout, we update one file. The rest of the system does not know or care. This modularity has saved us countless hours.
What we would improve is our detection of sync engine breakages. Right now, we rely on user reports to learn that a sync engine has broken. We are building automated smoke tests that run against live calendar UIs and alert us before users notice. That investment will pay for itself within a month.
Decision: Vanilla JS instead of a framework. Verdict: Paid off, barely.
We covered this in our earlier browser extension post, but the six-month perspective adds nuance. Vanilla JS kept our bundle small, our startup fast, and our memory footprint low. Those are real wins that users feel every day.
But the codebase has grown. At launch, we had roughly 4,000 lines of JavaScript. We are now closer to 12,000. The patterns that were manageable at 4,000 lines are starting to strain at 12,000. State management, in particular, is getting complex. We have event data flowing through multiple modules, and tracking where a particular piece of state was last modified requires more archaeology than it should.
We are not rewriting in React. The performance benefits of vanilla JS are too valuable to give up. But we are introducing more structure: a lightweight event bus for state changes, clearer module boundaries, and better documentation of data flow patterns. The framework is not the answer. Discipline is.
Decision: Sweep-line algorithm for conflict detection. Verdict: Absolutely paid off.
This is the decision we are most proud of. Conflict detection is the feature that makes manyCalendars essential rather than merely convenient. Getting it right technically was critical.
The naive approach to conflict detection compares every event against every other event. That is O(n squared) complexity. With 50 events visible, that is 1,225 comparisons. With 200 events (a busy month view), it is 19,900. Performance degrades noticeably.
We used a sweep-line algorithm instead. Sort all events by start time, then sweep through them with a pointer, maintaining a set of "active" events (those whose end time has not passed). Each new event only needs to be compared against the active set, not against every event. This gives us O(n log n) complexity, which scales gracefully even with hundreds of events.
In practice, conflict detection runs in under 5 milliseconds for a typical week view. Users never perceive a delay. The algorithm runs on every data change (new events, updated events, deleted events) and produces results instantly. This is the kind of technical decision that users never notice, which is exactly how it should be. The feature just works. Fast.
What needs revisiting
Recurring event handling. Our initial approach was to expand recurring events into individual occurrences at parse time. This works but creates storage overhead for events that repeat far into the future. It also makes exception handling (single-occurrence rescheduling or cancellation) more complex than it needs to be. The H2 rework will store recurrence rules and evaluate them on demand instead of pre-expanding.
ICS parsing edge cases. The ICS specification is large and inconsistently implemented. We have encountered ICS feeds with non-standard timezone definitions, malformed RRULE values, and encoding issues that our parser did not handle. Each one became a bug report. We have fixed the ones we have seen, but the long tail of ICS parsing edge cases is longer than we expected.
Extension popup performance. The popup opens quickly, but on machines with many calendars and many events, the initial render can take a noticeable fraction of a second. We need to virtualize the event list (only render visible items) and defer rendering of off-screen events. This is straightforward work, just not done yet.
Six months of compounding decisions
Architecture is not a single choice. It is a series of choices that compound. Good early decisions make later decisions easier. Bad early decisions create friction that slows everything downstream. Six months in, our big bets, local storage, Tab Sync, vanilla JS, sweep-line conflict detection, have compounded positively. The product is fast, private, reliable, and maintainable. Not perfect, but the foundation is solid.
If you appreciate software that is built deliberately, try the result. manyCalendars is free to install, and every architectural decision described above is running in your browser the moment you set it up. The engineering is invisible, which is the highest compliment we can give ourselves.