← All posts

The Layer Underneath · Post 3

The rewrite I didn't ask for.

SparkLink chapter · July 2026 · ~6 min read

Last time, the story ended on a small win: the call connected. Raw WebRTC, my own signaling, a call that came up most of the time. I was proud of it. This is what happened next — and it starts with me being furious.

I came back to a backend I didn't recognise

I'd built the calling client. It worked. Then, without a word to me, one of the seniors rebuilt the entire backend — off raw WebRTC and onto the LiveKit SDK. Not "let's talk about moving to LiveKit." Not "here's why." I came back and the ground had moved: the server my app talked to was a different server now, speaking a different language, and my client had to be redone to match.

Why would I throw away something that already worked?

I got on a call and lost it. Full rage. I'd done exactly what was asked — the call connected, I'd earned that the hard way — and now it was being swept aside without so much as a heads-up, and the redo landed squarely on me. That's the person I was then: the work was mine, and someone had thrown it out.

The reasons I couldn't see yet

Here's the part that stings a little to admit: the reasoning was good. There were two things raw peer-to-peer WebRTC simply could not give the product, and I couldn't see either of them from where I was standing.

The first was recording. Personal one-to-one calls can stay peer-to-peer — the video flows straight between two phones, nothing in the middle. That's private and it's cheap, but it also means there's nothing in the path to capture. This product needed professional calls to be recorded and their reports handled correctly — monitored, kept, accounted for. You can't cleanly record a stream that never touches your servers. LiveKit put a server back in the path that could.

The second was group calls. A one-to-one call is nearly free on every platform because it's just two peers. A group call over pure peer-to-peer means everyone connects to everyone else — and those connections pile up fast with each person you add, until phones simply can't keep up. The fix is a media server — specifically an SFU, a Selective Forwarding Unit: every participant sends one stream up to the server, and the server fans it back out to the room. That server is a big part of why Zoom gets to charge you, and why one-to-one stays basically free everywhere. LiveKit is that server, managed for you.

Peer‑to‑peer Two phones, nothing in between. The video goes straight across. Private, cheap, almost free — but there's nothing in the middle to record, and it won't scale past a couple of people.
Media server An SFU in the path (LiveKit). Each person sends one stream up; the server fans it out to the group and can record the calls that need recording. That server is the part you pay for.

So the senior wasn't wrong. He was right for reasons the product needed and I hadn't been told about. He just decided it over my head — and that was the part I actually had a right to be angry about. Being right and being respectful aren't the same thing. But the technical call? Sound.

The damage was done — my half was left

None of my anger changed the facts. The backend was already on LiveKit. What remained was the mobile client, and that's where my weakest ground came due all at once. I didn't own an iOS device yet. My iOS knowledge was thin. And we were on Expo's managed workflow — which is lovely until you need the native layer, and calling needs the native layer for everything: camera, microphone, permissions, staying alive in the background. The moment the SDK reached for those, managed Expo started pushing back. With that setup, the iOS-side friction was almost guaranteed.

No manual — just the source

And LiveKit, at the time, didn't hand me a tidy guide to follow. No step-by-step for what I was doing. So I did the only thing I knew how to do: I read the source, watched how the pieces actually talked to each other, and went on instinct. It was the same reflex from the WebRTC build — jump in the water, start swimming — except this time my brain was already trained for it. So I just did it.

I built two calling types — the personal one-to-one and the professional one — each with real connection states: connecting, connected, failed. States sound trivial until you've stared at a frozen screen wondering whether the call is dead or just slow; making the app tell the truth about what the connection is doing is half of what makes it feel trustworthy. I'll be honest about the gap, too: we still had no idea how to properly manage the background state — keeping a call alive when the app leaves the foreground. That one was still ahead of us.

The TURN server that finally beat Wi-Fi

There's a thread from the last post I got to tie off here. Raw WebRTC — and Kabootar before it — kept dying on restrictive Wi-Fi, because STUN alone can't punch through some networks. The answer was always TURN: a relay that carries the media when a direct path isn't possible. With LiveKit we stood up our own TURN server. On the backend that was real work; on my side it came down to swapping a few URLs. But it worked — the calls that used to fail on Wi-Fi came up. The exact thing the last post ended stuck on, finally unstuck.

What being overruled taught me

The lesson here isn't technical. I was right that the call worked. The senior was right that "it works" was never the requirement — recording, compliance, calls that scale past two people were, and I couldn't see any of it from where I sat. That gap has a name I only understood later: seniority is often just being able to see the requirement you don't yet have the vantage point to see.

I'd handle the call differently now — less heat, more questions. The rage was ego, plain and simple: my work, discarded. But the rewrite is what turned a demo that connects into a product that records, scales, and holds up on a hotel Wi-Fi. The layer underneath this one wasn't in the code. It was in the decision I wasn't in the room for — and being junior enough to think that made it wrong.

Next in The Layer Underneathnobody noticed, and that was the point: four hours on a fifteen-minute tab switch.
← Back to all posts

The story and the experiences are mine — the writing is Claude's.