广积深运
Build Wide, Ship Narrow

原始链接: https://adapt.com/blog/build-wide-ship-narrow

传统的工程工作流程依赖于“预先规划”边界——在编写代码之前将工作拆分为任务和 RFC——以保持代码审查的可控性。然而,这往往迫使开发人员在对问题了解最少时做出关键的架构决策。 借助 AI 助手,构建、设计以及至关重要的“拆解已完成工作”的成本已大幅降低。我们不再需要为了避免繁琐的代码梳理工作而预先划定边界。相反,开发人员应该: 1. **审视计划**:在编码前使用 AI 对设计进行压力测试并确定规范。 2. **广泛构建**:将开发分支视为“草稿纸”,频繁提交以保持存档点,而无需为了审查者生成干净的提交历史。 3. **尽早演示**:在编写任何 PR 之前,先向利益相关者验证功能。 4. **后期拆分**:使用 AI 将已完成、可运行的代码拆分为小型、逻辑清晰且可独立审查的 PR。 这种方法将结构性决策推迟到你真正理解实现方式之后,从而带来更高质量的代码审查、更安全的回滚和更高效的开发。规划依然先行,但“拆解”应是构建的最后一步,而非第一步。

Hacker News 最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 Build Wide, Ship Narrow (adapt.com) 9 分,ashumz 发布于 48 分钟前 | 隐藏 | 过往 | 收藏 | 讨论 | 帮助 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 加入 YC | 联系 搜索:
相关文章

原文

Good engineers plan before they build. The workflow I grew up seeing: write an RFC describing the feature, split it into smaller issues, then build them, each issue often blocking the next. The structure of the work was locked in before a single line of code existed.

This is reasonable. It keeps code reviews manageable and avoids big-bang merges. It also asks you to make your most critical structural decisions at the moment you know the least about the problem.

Before you've built anything, you're guessing: which pieces are separable, how complex each one will be, whether step 3 will force you to rethink step 1. Sometimes you're right. Often you're not, and step 1 gets thrown away. You learned something building it, but you'd have learned it faster by building the whole thing first.

We paid that cost for a good reason: the alternative was building everything and untangling it by hand, and untangling a week of work is harder than planning ahead. Deciding boundaries up front was never about making the build easier. It was about making review possible, and it was the only affordable way to get there. That's the part that changed.

What changed

Three things got dramatically cheaper. Building: an AI assistant turns a clear problem into working code in hours, sometimes minutes. Design: you can interrogate a plan and reshape it at conversation speed. And the one that matters most here, decomposing a finished branch: splitting a week of tangled work into a sequence of small PRs used to be the most tedious part of the job, which is exactly why we avoided it. It's now a prompt.

Two things didn't get cheaper. The first is the judgment half of code review. Agents made the mechanical half (consistency, nits, obvious bugs) nearly free, but they don't settle subtle correctness or the questions around it: does this change belong where it is, will this endpoint shape hurt six months from now. A bot approving your PR isn't the same as you understanding the code, and if you didn't type the code, reading it is how you come to own it. Narrow PRs make that reading possible.

The second is product validation. Running the thing and deciding it's the right thing to build is still slow. What's new is having the whole feature working early enough to show someone before anyone reads a line of it.

So stop pre-deciding boundaries to dodge a cost that no longer exists.

My workflow now looks like this:

  1. Grill the plan until it has real decisions in it
  2. Commit the spec before any code, when the design is novel
  3. Build wide, committing save points as you go
  4. Demo and iterate before anyone reads the code
  5. Split into PRs along the boundaries the code revealed
  6. Merge, cleanup last: pure deletion in its own final PR

Design still goes first

To be clear: this isn't "skip planning and start coding." Before I touch the editor, I have a plan, and every feature starts with an interrogation. I run grill-me, a skill that interviews you about your idea in adversarial rounds until it has real decisions in it. What's the fallback if the API call fails? I run it on everything, including small changes, and it keeps surfacing gaps I didn't know were there.

When the design is novel, the plan becomes a spec committed before any code. On a different project, the first PR was a document: what the feature was, how it would work, where the trust boundaries sat. It merged days before any implementation existed, so the team could push back first.

What never gets committed is a decomposition into PRs. Decide what to build before you build it; decide how to slice it after.

Build wide

A crumpled scratch-paper build branch on one side and a clean fan of five pull requests on the other

Once the design is settled, I build. I often work in steps, but I don't stop at each one to open a PR and wait for review. Everything stays on one branch until it works end to end, across whatever files are in the way.

Commits happen, but they aren't milestones for anyone else. They're save points: a concept is proven, or I'm about to try something risky and want a rope to pull back to. A refactor I finished recently ran to a dozen-plus commits in a single day across dozens of files, with operational messages. Waypoints so I can see where I've been, not a story for a reviewer.

This is the part that makes some engineers uncomfortable, and I understand why: git history is supposed to be the record. But this history never becomes the record. The PRs at the end are cut fresh off main, and the build branch is scratch paper you throw away. Two audiences separated in time, me and the reviewers, and keeping them apart lets you optimize for both.

Demo before anyone reads the code

Once the work is in a good place, I stop and show it. Not as a PR or a code review, but a short video in Slack, or a preview deployment if the change needs clicking. Feedback on working software from the people who will use it, before a single code review starts.

Review is expensive now that the cheap half is automated. Finding out at review time that you built the wrong thing (confusing UI pattern, an endpoint shape that doesn't fit how the frontend uses the data) burns a reviewer's time and your own. A demo catches it while changes are cheap. If it surfaces something that needs rethinking, I run grill-me on the feedback first, so the iteration isn't just vibes.

Ship narrow

One tangled build branch cut into five narrow pull requests, with the deletion PR last

The build order is only a rough draft of the split: the steps get regrouped and re-cut. The deletion PR is the clearest example, a unit that only exists at the end, once the new path is in place.

I run one prompt:

Split the current work into the smallest set of independently reviewable PRs, each safe to merge on its own, and each delivering value to the user whenever the work allows. Create a git worktree and branch off main for each. Stack only where a dependency is real; otherwise branch from main. Any removal of the code being replaced goes in its own final PR. Show me the proposed split before creating anything.

How long it takes depends on how tangled the work is, from a few minutes to a few rounds of back-and-forth. Either way, no manual cherry-picking.

That refactor came out as five PRs: two backend endpoints as siblings off main, two frontend views each sitting on top of the backend PR whose data it needs, and a final PR that was pure deletion of the old path. The deletion removed several hundred more lines than the whole feature added. That's the shape of a refactor done in this order.

Two rules I've arrived at by doing this repeatedly:

Stack only when the dependency is real. A frontend view PR depends on its backend endpoint PR, and the branch reflects that. Everything else comes from main. Stacking for convenience creates a rebase chain you'll regret the moment the bottom PR gets feedback.

Cleanup ships last. The old code dies in its own PR, after the new path is live. Mixing deletion with creation confuses reviewers, makes rollback ambiguous, and buries the cleanup in the noise of the feature.

The split is also when I read my own work. Going through the diff PR by PR, at a size I can hold in my head, is the difference between having shipped AI-written code and understanding it. I'd rather find my own problems there.

One practical note: managing a stack burns context fast, so hand the split work to subagents that report back to a main agent. In Cursor, the split-to-prs skill packages this.

What it bought, what it costs

The heavy PRs stay heavy, and they should. The backend ones carried the real architectural risk (new data models, new API surface, trust boundaries), which is where reviewers should spend their attention. The frontend PRs that consume them read in minutes. Small, focused PRs flow. Large ones sit.

The agent review loop is faster at this scale too. At Adapt, we use Adapt itself as a reviewer, an agent with business context from previous work. It comments within minutes of a PR opening, and the exchange (questions, clarifications, a small fix) resolves in under ten. That only works when a PR is small enough to read quickly; a 2,000-line PR mixing several concerns doesn't get that treatment from a human at all.

Merging incrementally also makes deployments easier to manage. If something breaks, you revert one focused change, and your error tracking points at that change instead of an entire merged stack.

Those are the gains. Two costs come with them.

Rebasing. When a reviewer asks for a change on a PR that another sits on, every branch above it needs rebasing. It doesn't happen often, since reviewers usually touch leaf PRs, but when it does you feel it. Keep the chat session that produced the split open: it still holds the split, so you can re-prompt it to update the branches above instead of doing it by hand.

Splitting is not shipping. As I write this, all five PRs from that refactor are still open. A good split makes each PR easy to review and worth merging on its own. An agent does the first for me; the second is my call when I decide what goes in each PR, and my team's when they decide what to merge. Mine sat because I only did the first. Even if all five merge on the same day I keep the review benefit and a clean revert target per change; what I lose is incremental delivery, since nothing reached a user earlier and the deploys land as one batch.

When to reach for it

Good fit: multi-surface features crossing backend and frontend, refactors where you don't know the final shape until you've done it, any work where you'd otherwise be guessing at issue boundaries.

Harder fit: migrations and schema changes that must be sequenced in production, where the ordering is real and you should plan it first. Work that has one obvious place to cut. Features where step 1 cannot ship alone, ever; if everything lands at once, late decomposition buys you incremental review and nothing more.

The heuristic: if you're writing an RFC and guessing at how to break it into issues before you've built anything, that time is probably better spent building. You'll have better answers at the end. I've been testing this across a few projects and it has held up.

The structural decision doesn't go away. It just gets much cheaper when you make it with the code already in front of you.

联系我们 contact @ memedata.com