Skip to main content

7 posts tagged with "software-architecture"

View all tags

Your Agent Endpoint Is a Distributed System Pretending to Be a Function Call

· 9 min read
Tian Pan
Software Engineer

The most dangerous line of code in a modern AI application looks completely innocent:

result = await agent.run(user_query)

It reads like a function call. It has a name, it takes an argument, it returns a value. Your IDE autocompletes it. Your type checker is satisfied. And that single await is hiding a remote, multi-hop, partially-failing distributed system behind the syntax of a local procedure. The gap between what the code looks like and what it actually does is where most production agent incidents live.

Your Internal API Became a Public API the Day an Agent Called It

· 10 min read
Tian Pan
Software Engineer

Internal APIs survive on a quiet arrangement: nobody writes the contract down because everybody already knows it. The fields that happen to be there, the error you throw that a caller secretly parses, the endpoint that returns 200 with an empty list instead of 404 — these are load-bearing behaviors held together by the fact that you can name every caller and Slack them before you change anything. That arrangement works right up until it doesn't.

It stops working the day you wire an agent to that API. Not because the agent is malicious or careless, but because the agent is a caller you cannot reach. It has no Slack handle. It did not read your migration note. It depends on response shapes it absorbed from an example payload or a schema snapshot, and it will keep depending on them long after you've moved on.

The uncomfortable truth is that "internal" was never a property of the API. It was a property of the caller list. Shorten that list to people you know and the API is internal; add one participant you can't coordinate with and the API is public — with all the discipline that word implies, and none of the infrastructure you'd have built if you'd known.

The Copy-Paste Contagion: How AI-Assisted Development Spreads Architectural Anti-Patterns

· 11 min read
Tian Pan
Software Engineer

Your codebase has the same authentication logic implemented three different ways, and nobody on the team wrote any of them. A quick git blame shows the same engineer on all three files, but ask that engineer and they'll tell you they just accepted what the AI suggested and it "looked right." The anti-pattern didn't spread because someone was lazy. It spread because an AI model with no memory of your existing auth module generated plausible-looking implementations every time someone opened a new file and asked for help.

This is the copy-paste contagion, and it's structurally different from the classic copy-paste problem you already know how to fight.

The Expertise Cliff: Why AI Coding Agents Fail in Mature Codebases

· 8 min read
Tian Pan
Software Engineer

A 2025 controlled trial gave experienced developers access to AI coding tools and measured whether they got faster. The developers predicted a 24% speedup. After completing the study, they reported feeling roughly 20% faster. Objective measurement showed they were actually 19% slower.

This isn't a story about AI hype. It's a story about tacit knowledge — the undocumented "why" that lives inside every mature codebase and cannot be recovered by reading the code alone. AI agents are remarkably productive in greenfield systems precisely because there is little tacit knowledge to violate. They degrade in mature codebases for exactly the same reason.

LLM-as-Compiler Is a Metaphor Your Codebase Can't Survive

· 10 min read
Tian Pan
Software Engineer

The pitch is seductive: describe the behavior in English, the model emits the code, ship it. Prompts become the source, artifacts become the target, and the LLM sits between them like gcc with a friendlier front-end. If that framing held, the rest of software engineering — review, refactoring, architecture — would be downstream of prompt quality. It does not hold. And the codebases built on the assumption that it does start failing in a pattern that is now boring to diagnose: around month six, nobody can explain why a particular function looks the way it does, and every incremental change produces a wave of duplicates.

The compiler metaphor is the root cause, not vibe coding, not model quality, not prompt skill. It is a category error that quietly excuses teams from doing the work that keeps a codebase coherent over years. When you believe the model is a compiler, the generated code is an implementation detail, the same way assembly is an implementation detail of a C program. When you are actually running a team of non-deterministic, context-limited collaborators, the generated code is the asset — and the prompts are closer to Slack messages than to source.

The Post-Framework Era: Build Agents with an API Client and a While Loop

· 8 min read
Tian Pan
Software Engineer

The most effective AI agents in production today look nothing like the framework demos. They are not directed acyclic graphs with seventeen node types. They are not multi-agent swarms coordinating through message buses. They are a prompt, a tool list, and a while loop — and they ship faster, break less, and cost less to maintain than their framework-heavy counterparts.

This is not a contrarian take for its own sake. It is the conclusion that team after team reaches after burning weeks on framework migration, abstraction debugging, and DSL archaeology. The pattern is so consistent it deserves a name: the post-framework era.

Brownfield AI: Integrating LLM Features into Legacy Codebases Without a Rewrite

· 9 min read
Tian Pan
Software Engineer

Every AI demo starts with a blank slate. A fresh repo, no dependencies, no legacy authentication system, no decade of business logic encoded in stored procedures. The demo works beautifully. Then someone asks: "Can we add this to our actual product?"

That's where brownfield AI begins — and where most teams get stuck. The gap between a working prototype and a production integration inside a ten-year-old monolith is not a matter of scaling up. It's a fundamentally different engineering problem, one that requires adapter patterns, careful boundary design, and a deep respect for the existing system's constraints.