Ekklesia is, by a decent margin, the project I've been the most wrong about along the way, and I mean that as a compliment to how it turned out, not a complaint. It started as the smallest, most unambitious thing I could imagine building: a way for my own church to stop taking attendance with a paper register that someone had to manually tally after every service. That's it. That was the entire plan. I built it, my church used it, and I figured that would basically be the end of the story.
It was not the end of the story. And the way it kept not being the end of the story is what actually taught me something.
The part I got right, mostly by instinct
Here's the one decision I'm genuinely proud of, and I want to be honest that it wasn't some brilliant foresight; it was closer to a gut feeling I'm glad I listened to. Even while I was building the very first version, the one meant for exactly one church with exactly zero ambition of ever serving a second one, I scoped every single table (churches, members, services, attendance records, groups, follow-ups, all of it) to a church_id. There was no second church. There was no plan for a second church. I did it anyway, mostly because something about hardcoding "there is only one church" into the shape of the data felt wrong in a way I couldn't fully articulate at the time.
I'm very glad I listened to that feeling, because I've since watched what happens when you don't. "One of something" and "many of something" are not the same schema with a filter clause bolted on afterward; they're a structural decision, and if you get it wrong at the start, fixing it later isn't a migration, it's closer to a rewrite wearing a migration's clothing. Multi-tenancy is one of the few things I now believe genuinely has to be right from day one, even (especially) when day one only has one tenant in it. It's cheap to add when nothing depends on its absence yet. It's brutally expensive once real data, real relationships, and real assumptions have calcified around "there's only ever one of these."
The part where reality quietly proved me wrong
But the schema wasn't actually the humbling part of this story. The humbling part was the visitor pipeline, and it humbles me a little just retelling it.
In the first version of Ekklesia, there was no such thing as a "visitor." There was just the member list: one flat table of people who come to church. It seemed completely reasonable at the time. Someone shows up, you add them to the list, done. It worked fine for a good while, right up until the exact moment it very obviously didn't: a flat list of names has absolutely no concept that a particular person is three weeks into being gently followed up with, or that they specifically need one particular person from the care team checking in on them, or that "someone who has come twice and might be deciding whether this is their church" is a fundamentally different category of person from "a member who's been here eleven years." None of that distinction existed anywhere in the data. It couldn't, because I hadn't imagined it needed to, until real church leaders, using the real thing, on a real Sunday, ran straight into that wall and told me about it.
I want to sit with how uncomfortable that moment was, honestly, because I think the discomfort is the actual lesson, more than the fix itself. My instinct, in the moment, was almost defensive: I'd built this whole system, and it had a hole in it that I hadn't seen coming, and someone else had to point it out to me by hitting it face-first. That's a specific flavor of humbling that I don't think you can get any other way than by putting something real in front of real users and watching, a little wounded, as they find the thing you missed.
What I chose not to do, which mattered just as much
Here's the part I think is actually the harder lesson, and the one I have to consciously remind myself of on every project since: I did not, in that first version, try to anticipate the visitor pipeline speculatively. I didn't sit down at the start and think "well, we'll probably need some kind of visitor-tracking thing eventually, let's build a flexible person-status system now just in case." I built the plain, boring member list, because that was the actual problem in front of me, and I let the visitor pipeline not exist until a real gap, hit by real people, made its shape obvious.
I think that restraint mattered every bit as much as the eventual fix did. If I'd guessed at the visitor pipeline up front, I would have guessed wrong in some way: designed it around what I imagined a church's follow-up process looked like instead of what it actually is, which I only learned by watching the flat list fail. The version that exists now (stage, priority, a due date, a specific assigned owner, a pipeline instead of a list) only looks obvious in hindsight. It absolutely was not obvious to me the day I sat down to build the first version, and I don't think it would have been, no matter how hard I'd tried to think ahead.
So the two lessons sit right next to each other, a little in tension, and I think that tension is the actual point: bake in the structural things (multi-tenancy, the stuff that's genuinely expensive to retrofit) from day one, even before you can picture the second customer who'll need it. But everything else, including the feature that feels completely obvious once a real user has shown you the gap it fills, earns its place only when that real gap actually shows up. Not a day before. Building it earlier doesn't make you look prescient. It mostly just means you built the wrong version of it, confidently, and now have to unbuild that too.