Dustav.com

Essays

Build it in, don’t bolt it on

Why pal.fun is a destination, not an integration layer — and the day we ripped out our own calendar sync.

There's a moment in the life of every software product where someone says "we should integrate with Google Calendar." It's such an obvious thing to say that saying no to it feels almost irresponsible. Of course you should sync. Everyone wants their stuff in one place. Meet the user where they are.

I had the sync built. Then I ripped it out. This essay is about why, and about the stance underneath it — one of the most important product decisions in pal.fun: we are a destination, not an integration layer. We build natively. We don't build bridges.

The calendar that synced

Your pal keeps a calendar. A dentist appointment, your brothers' birthdays, the stuff a friend who's paying attention would remember for you. Early on, I made that calendar exportable as a standard feed — the kind you can subscribe to from Google Calendar or Apple Calendar, so the pal's events would show up in whatever calendar app you already use.

It worked. It was even kind of elegant. And it was wrong, and I couldn't articulate why until I said the thing out loud: we don't sync. That ain't our thing.

Here's the reasoning that crystallized after.

You come here for this

When you put a birthday into your pal's calendar, you're not doing data entry. You're telling a friend something, and the value is that the friend holds it and brings it back to you at the right moment — "hey, your brother's birthday is Saturday, want to do something." The reminder, in the pal's voice, surfacing at the right time, is the product.

The moment that birthday becomes a row in Google Calendar, it's just a row in Google Calendar. The pal-ness evaporates. You've turned a relationship into a feed. The thing that made it worth putting here is gone the instant it's also there.

So the calendar isn't a database the pal happens to write to. It's a place you come to, because of who's keeping it. Making it sync outward would be quietly admitting that the real calendar lives somewhere else and we're just a satellite. We're not a satellite. The whole bet is that there's a reason to come here.

Integrations are a tax disguised as a feature

There's a more practical layer to this too, and it generalizes well beyond calendars.

Every integration is a permanent liability. The other side's API changes, their auth breaks, their rate limits bite, their data model doesn't quite match yours and you spend forever reconciling the seams. An integration is never "done" — it's a maintenance commitment that lasts as long as the integration does. For a solo builder, that's a tax I pay forever in exchange for a feature that, as we just established, subtracts from the thing I'm actually building.

And integrations have a way of slowly redefining your product as the glue between other people's products. You start as a destination and you end as middleware — the place data passes through on its way to the apps that matter. That's a fine business. It's not this one.

The native version, by contrast, is something I fully own. The pal's reminders are my notification layer — the pal tells you, in its voice, at the right time. I don't depend on anyone's API staying stable. I don't reconcile anyone's data model. The capability is mine end to end, which means I can make it good in ways an integration never can, because an integration can only ever be as good as the lowest common denominator of the two systems it bridges.

"But what about my data?"

The honest objection to all of this is: isn't refusing to sync just lock-in? If I can't get my calendar out, you've trapped me.

This is the line that matters, and it's worth being precise about: data custody is not the same as live sync. You have an absolute right to your data — to take it and leave. That's a promise the product has to honor, and it does: the deterministic way out is account deletion that actually deletes, and portability is a request we fulfill. What you don't get is a live, always-on bridge that keeps a copy of everything mirrored into someone else's app in real time. The first is a right. The second is an architecture decision, and it's one that quietly dissolves the product.

You can leave with your stuff. You just can't make us into plumbing while you stay.

When the rule flips

I want to be careful not to oversell this into dogma, because "build natively, never integrate" is wrong as an absolute. The rule isn't "no external services ever." It's "don't build bridges — don't make your product's identity depend on being the connector between other products."

There's a clean test for the difference. Web access is a good example of where I did reach outward: the pal can search and fetch from the web. That's not a bridge — it's a capability the pal owns, where the outside world is an input the pal reasons over, not a destination it's trying to stay in sync with. The pal isn't mirroring its state into the web; it's using the web to do its job here. That's native-with-reach, not integration-as-identity. The line is: does the outside system become part of what the product is, or is it just something the product uses? Calendar sync was the former. Web fetch is the latter.

The stance, stated plainly

So here's the principle, and it's a good one to carry into any product that's trying to be a place rather than a pipe:

Build the thing natively, all the way down, and make people come to you for it — because the coming-to-you is the value. Don't build the bridge that lets the value leak out to where it's generic. Honor data custody as a right; refuse live sync as an architecture. And when you catch yourself adding an integration, ask whether you're adding reach to your own thing or slowly turning your thing into glue for everyone else's.

We don't sync. We're the destination. That ain't a limitation — it's the whole point.