Skip to main content

Ethan Thornberg

Week 3: When Planning Matters

Ethan Thornberg9 min read
Deep WorkNetworkingLearningUDPSystems Design

Week 3: When Planning Matters

Week 3. Classes still packed. Showing up at 2pm.

The novelty is officially dead. Week 1 was curiosity; Week 2 was adrenaline. Week 3 was just the chair and the code.

At this point, I finished Beej's Guide. Two and a half weeks with one resource. Now, I'm reading Gaffer On Games, a blog series on game networking that assumes you know the basics.

Beej told me how to use the tools. Gaffer is telling me how to build the engine. The training wheels came off.

This week's goal: build a reliable UDP protocol. Sliding window ACKs. Bitmap tracking. Automatic retransmission.

Real complexity. The kind that punishes improvisation.

I understood the concepts from reading. Implementing them taught me something different.

Day 11: Monday

Monday afternoon I built a UDP server from scratch. No references to old code. Just memory. Had to check syntax for some functions, but the structure came from my head.

It worked.

That felt earned.

Day 12: Tuesday

Tuesday I hit a bug that took an hour to find. I was sending packets but the full message wasn't arriving. I finally realized: I was using strlen(buf) to set packet length, but buf started with a protocol ID. That ID had a \0 byte that made strlen() think the string ended early.

Binary data is an indifferent beast. It doesn't care about your 'strings' or your 'null terminators.'

I fixed it. Built a heartbeat system - sending packets every 100ms using poll(). Simple enough to debug, fast enough to test.

Tuesday ended feeling capable.

Wednesday would change that.

Day 13: Wednesday

Wednesday I started building the ACK system.

The concept was clear: server receives packets, sends back acknowledgments. Client tracks which packets got ACKed. Resends anything that times out.

The implementation required tracking state. I needed a data structure to store sent packets with timestamps.

I spent too long deciding. Circular buffer? Linked list? Dynamic array?

I was looking for the 'perfect' architecture to hide my inexperience. I realized that a flawed plan you actually build is worth more than a perfect plan you're too scared to start.

I chose a fixed array and started building.

Day 14: Thursday

Thursday I built the bitmap ACK system.

Instead of acknowledging one packet at a time, the server sends a 32-bit bitmap. Each bit represents one packet. Bit 0 = most recent. Bit 1 = second most recent. And so on.

This is how TCP works. This is how game engines work.

The logic made sense. The implementation didn't.

I needed to shift the bitmap by arbitrary amounts as new ACKs arrived. If the new ACK was 5 packets ahead, shift everything right by 5 bits. But bits don't align with byte boundaries. You have to manually handle every carry bit that spills from one byte to the next.

I spent two hours on this. Read about bit operations. Tried implementing it. Got errors I didn't understand.

Around 3pm I was stuck. Really stuck. I could see what needed to happen conceptually but couldn't translate it to code.

I opened AI and asked it to explain byte boundaries and carry bits.

It explained. I understood. I implemented it. It worked.

But I felt guilty.

There's this ideal in my head of the self-sufficient programmer who figures everything out alone. Who never needs help. Who can look at any problem and solve it through pure reasoning.

I felt like I was outsourcing my struggle. I had a romanticized image of the 'lone genius' in my head, but Thursday taught me that a lone genius who never finishes is just a guy staring at a blank screen.

Byte-boundary bit manipulation isn't something you intuit. It's something you learn once, then know. Using a tool to learn faster isn't weakness. It's pragmatism.

But the ideal of self-sufficiency is strong. The guilt lingered.

By the end of Thursday, the bitmap system was tracking ACKs. Server was sending them. Client was receiving them. Not perfect, but functioning.

Day 15: Friday

Friday was a ghost session. I was physically at the table, but the work felt hollow. I wasn't learning; I was just scrubbing the floors of a system I'd designed poorly.

I walked into Starbucks at 2pm and sat down. Opened my code. Started debugging.

Within thirty minutes I knew it was going to be a rough session.

The problems weren't hard to understand. They were tedious. I'd fix one bug - a variable not initialized correctly - and immediately see three more issues stemming from tangled function calls. I'd try to add a feature and realize the architecture didn't support it cleanly.

Every fix revealed more mess.

Around 2:45pm I realized I didn't care about any of this. Not in the "I'm too tired to care" way from Week 2's Thursday. In the "this work isn't interesting" way.

Week 2's 3:01pm was different. I was exhausted. Fighting to stay awake. But the work itself still mattered. I wanted to understand bits and bytes. I just couldn't focus.

Friday I could focus fine. I just didn't want to be doing this.

Exhaustion is a physical wall you can climb. Disconnection is a fog you have to wander through.

Debugging my own poorly planned code didn't feel like learning. It felt like cleanup. Necessary, but not engaging. Not meaningful. Just draining.

I kept working. Solved a bitmap ordering bug. Found another one. Fixed a retransmission issue. Discovered the timeout logic wasn't quite right.

Around 3:30pm I thought: "I could just leave. Go home. Work on something else. Something I actually care about right now."

I didn't leave.

Not because I felt motivated. Not because I was curious what would happen next. Not because the work was pulling me forward.

I stayed because I'd made a commitment.

That's what Week 3 taught me that Week 2 didn't. Week 2 taught me I could push through exhaustion. Week 3 taught me I could push through disconnection.

Exhaustion has a cause. Sleep, food, rest fix it. There's a clear reason you feel bad and a clear solution.

Disconnection is harder. The work isn't rewarding right now. You're not learning new things. You're fixing problems you created. There's no quick fix for that. You just have to do the work.

I'd told people I was doing this. Blogged about it. Made it public. Committed to six months. Fifteen days down. I wasn't going to quit because one session felt meaningless.

So I stayed. Kept debugging. Kept fixing small issues. Made incremental progress on a system I wasn't proud of.

At 4:30pm I packed up.

I felt relief mixed with disappointment. Relief that I was done and could do something else. Disappointment that I was feeling that way. Disappointed I hadn't ended the week engaged and satisfied.

Disappointed that commitment, not curiosity, got me through the day.

The disappointment had another layer too. I understood the concepts well. Gaffer's articles made sense. The theory was clear. Sliding windows, bitmaps, ACKs, retransmission - I got it.

But getting it conceptually and executing it cleanly are different skills. I understood the what. I couldn't execute the how.

That gap - between understanding and implementation - is frustrating in a way that pure confusion isn't. If I didn't understand the concepts, I'd just need to study more. But I did understand. I just couldn't translate that understanding into clean code.

That feels worse somehow.

I know that's not quite true. The problem is experience. I haven't built enough complex systems to know how to structure them well. That comes with practice.

But Friday it felt like: "I'm smart enough to understand it. Not skilled enough to build it well."

That's a hard feeling to sit with.

What Week 3 Proved

Looking back at the five days, I can see the trajectory clearly.

Monday I was confident. Building from memory. Solving problems independently.

By Friday I was disconnected. Debugging my own mistakes. Staying out of obligation.

What changed wasn't the difficulty. Thursday's bitmap shifting was genuinely hard. But it was interesting hard. I was learning something. Building something I'd never built before.

Friday was tedious hard. Untangling functions I'd written poorly. Fixing bugs I'd created by not planning. No new concepts. Just cleanup.

I'm starting to understand what kind of work energizes me versus what drains me. Building new things energizes me. Understanding new concepts. Even when it's difficult. Even when I need help. The difficulty is engaging when I'm learning.

Debugging problems I created drains me. Fixing architecture I didn't plan. Solving problems that exist because I was careless or rushed. The difficulty is exhausting when it's just cleanup.

The hard part is that learning requires both. You have to build messy first versions to understand what clean looks like. You have to debug your own mistakes to understand why planning matters.

Friday sucked. But Friday taught me something I couldn't have learned by just reading about architecture and planning. I had to feel the tedium of poor planning to really understand why it matters.

Complex systems need architecture before code. I knew this intellectually. Now I know it experientially.

I didn't plan this protocol. I made up functions as I went. Added features when I thought of them. Solved problems as they appeared.

That works for small programs. It doesn't work when multiple pieces need to coordinate.

Poor planning creates tedious work. And that tedium doesn't just slow you down - it kills engagement. Friday wasn't hard because the concepts were difficult. It was hard because I was fixing problems I'd created by not thinking through the design first.

The work became meaningless. Just tedious.

About using tools: Using AI to learn faster isn't cheating. It's pragmatic. But the ideal of self-sufficiency is strong. The guilt lingers.

I'm starting to separate "learning" from "figuring it out alone." They're not the same thing. Learning means understanding. The source of that understanding matters less than I thought.

About commitment: Week 2 taught me I could push through exhaustion. Week 3 taught me I could push through disconnection.

Those are different skills.

Friday at 3:30pm, I had no reason to stay except commitment. Not curiosity. Not engagement. Just: "I said I'd be here until 4:30pm. I'm staying."

Disconnection is harder than exhaustion. Exhaustion has a fix. Disconnection doesn't. You just do the work anyway.

I don't know if that's sustainable long-term. But I can do it.


Fifteen days. Zero missed.

The system mostly works. Not perfectly. Not cleanly. But functionally enough to prove I understand how reliable protocols work.

Week 4 starts Monday. One more week to finish Month 1.

Same table. 2pm. I'll be there.