3 Things I Learned During My Summer at LaunchDarkly

Published September 8, 2026

by Maya King, Intern at LaunchDarkly

AI makes architecture more important, not less

As code generation gets easier, deciding how a system should be structured becomes an even bigger part of a software engineer’s job. A first pass can work and still leave the code harder to understand, extend, or reuse.

During my internship, I had the opportunity to use different frontier AI models from Claude, Cursor, and OpenAI, but found that, despite their capabilities, all of them needed guidance when it came to well designed code for complex problems. At an early stage of moving several product pages onto a shared frontend component, I made the decision to have each page responsible for managing shared state based on AI recommendation. However, with reviewer feedback I realized that there was better design. If the shared component owned the state and the page supplied the information specific to it, the next page did not have to copy the same state machine. AI can help get working code on the screen, but it will not decide which responsibilities should be shared or notice when a complete solution is creating more complexity than it removes.

With AI deciding where to spend your brain power and where to let the agent do the work becomes an essential part of an engineer’s job, and I’ve learned that code architecture should be a focal point and isn’t something that can just be delegated to AI. More specifically, well written code has one owner per fact and a small public surface, so you change shared behavior in one module instead of repeating the same edit at every call site.

For example, there is a huge difference in the outcome of a prompt something like:

Add the shared filter bar to the segments list. Keep it in sync with the URL.

versus adding something simple, like:

The filter bar owns the query string. Do not put URL read/write, stale-write cancel, or token walking in the page.

The latter will save so much time when debugging the AI output.

Every change to shared state is a writer, but only one should own it

Every way a value can change is a writer, but only one of those writers should own the value. I learned this while connecting an interface to a URL because the interface could update immediately while the URL update waited until typing paused, so a write from an earlier keystroke could run after browser history or another control had changed the same value and put the old state back. At the same time, letting the interface and URL update each other turned “keep them in sync” into a separate protocol with rules for ownership, ordering, and disagreement. A simpler design made the interface authoritative, treated the URL as a projection of its committed state, and cancelled a pending URL update when the value changed somewhere else.

if (urlValue === lastWrittenRef.current) {
return;
}
if (writeTimerRef.current !== null) {
clearTimeout(writeTimerRef.current);
writeTimerRef.current = null;
}
lastWrittenRef.current = urlValue;
setValue(urlValue);

This experience showed me that debouncing and two-way synchronization can fail for the same reason. A second author writes after it no longer has permission to do so.

Developing my own workflow

There is no single workflow that works for every engineer, so part of becoming more effective is figuring out which tools and habits fit the way you work. Throughout my internship, I was managing many subtasks in the same repository without a standardized process for using AI, so I built a personal workflow tool in my free time. That gap gave me an opportunity to experiment with how I organized tasks, separated work, and reviewed changes instead of trying to copy someone else’s process. As I built the workflow, I also began to understand how its different pieces fit together. The harness controlled what the agent could do, the terminal user interface (TUI) gave me a way to interact with it, and the other tools each handled a distinct part of the process. My first version had the server drive Claude, but as I used it, I realized I wanted to remain the person steering the work, so I changed it to store state and let me control the flow from my own session.

Before, the server spawned Claude and ran the loop itself:

const response = query({
prompt: focusedPrompt,
options: {
cwd: workDir,
allowedTools: READ_TOOLS,
disallowedTools: ["Bash", "NotebookEdit", ...WRITE_TOOLS],
canUseTool,
maxTurns: 16,
},
});

After, the server only stored coordination state. My terminal session ran the model:

// The Claude Code session is the conductor. The server doesn't run AI —
// it tracks which goals are open (per terminal), subtask owners, each
// session's live activity, and a small activity log the dashboard renders
// as a feed.

The absence of a standard is not always a problem to solve by finding the “right” workflow, but instead it can be an opportunity to test different approaches and develop one that supports how I work.

About me

Hi! I’m Maya! I love reading long fantasy books with too much lore, although I’ve recently been getting more into sci-fi (I’m currently reading Red Rising, if anyone is familiar). I admittedly spend a lot of my downtime watching reality TV—my favorite show of all time is Survivor, but I can’t watch it with anyone but my girlfriend because I like pausing every 10 minutes to talk about it. I’ve had a great time at LaunchDarkly this summer and am so thankful for the opportunity!