Hacking, Code & Open Source Reads

Separate Your Changes - The Kernel's Patch Rules Applied to LLM-Assisted Coding

Christian Lehnert2026-05-28~13 min read

Separate Your Changes

The Rule That Predates the Problem It Now Solves

The Linux kernel's "Submitting patches" document at
docs.kernel.org/process/submitting-patches.html contains a section
called "Separate your changes" that has, in various wordings, been
part of the kernel's contribution rules since approximately 1991.
The current text is direct enough to quote in full.

Separate each logical change into a separate patch.

For example, if your changes include both bug fixes and performance
enhancements for a single driver, separate those changes into two or
more patches. If your changes include an API update, and a new
driver which uses that new API, separate those into two patches.

On the other hand, if you make a single change to numerous files,
group those changes into a single patch. Thus a single logical
change is contained within a single patch.

The point to remember is that each patch should make an easily
understood change that can be verified by reviewers. Each patch
should be justifiable on its own merits.

The rule predates GitHub. It predates pull requests. It predates the
modern code-review interface. It was written for a workflow in which
patches arrive in maintainers' inboxes as plain text, are reviewed by
people who have to understand them quickly enough to make a decision,
and are applied to a tree that millions of users will run. The
constraints that produced the rule are constraints on the reviewer's
attention and on the bisectability of the resulting history.

The reason to write about this rule in 2026 is that it is currently
being violated at an industrial scale by every engineer who uses an
LLM assistant to write code. The assistants produce, by default,
exactly the kind of patch that the kernel rule forbids. A typical
session with Claude Code or Cursor or Copilot Workspace produces a
diff that touches twenty files, mixes a bug fix with a refactor with
a new feature with an unrelated formatting cleanup, and lands in a
single commit with a message that reads "Update files" or "Implement
feature X." The output is precisely the unreviewable, unbisectable
sprawl that the kernel rule was written to prevent.

The kernel's rule is the cleanest answer to the resulting mess. This
post is about how to apply it.

Why the Rule Is Correct

The case for separating logical changes is not stylistic. It is
operational, and the kernel community has worked it out across three
decades of running the largest collaborative codebase in human
history. The reasons are worth stating because they apply equally to
a one-person side project and to a fifty-thousand-engineer enterprise
codebase.

Reviewability. A reviewer has approximately ten minutes of full
attention available for any given patch before context fatigue sets
in. A patch that does one thing in twenty lines can be reviewed
correctly. A patch that does five things in two hundred lines cannot.
The reviewer will skim, miss the subtle bug in the part they did not
read carefully, and approve a worse change than they would have
approved if the patch had been split. This is not a hypothetical. It
is the dominant failure mode in code review across the industry.

Bisectability. git bisect works only if every commit in the
history builds and runs. If you bisect a regression to a commit that
did three unrelated things, you still have to figure out which of the
three caused the regression. A history of single-concern commits gives
you the answer directly. The kernel document specifies this explicitly:
"the kernel builds and runs properly after each patch in the series."

Rollback granularity. When a change introduces a problem in
production, the option to revert exactly the problematic part of the
change is valuable. A monolithic commit forces you to revert
everything, including the parts that worked. A series of separate
commits lets you revert precisely the regression and keep the rest.

Future archaeology. The git history of a codebase is the most
durable form of documentation it has. Two years from now, when an
engineer is trying to understand why a particular line of code was
written, they will run git blame on it. A commit message that reads
"Refactor authentication module" tells them nothing. A commit message
that reads "auth: switch session storage from cookie to JWT to
support cross-domain SSO" tells them everything they need to know.
The kernel's commit messages have been functioning as durable
documentation for thirty years. Most LLM-generated commit messages
will not survive the year.

What the LLM Assistant Actually Produces

The structural problem with LLM-assisted coding is that the unit of
work the assistant naturally produces is not a logical change. It is
a session output. The engineer asks for "the feature." The assistant
generates the feature, which involves modifying the database schema,
updating the API layer, changing three frontend components, adding a
test, fixing the linter complaints that appeared while editing, and
adjusting an unrelated docstring that the assistant happened to notice.
The session ends with twenty modified files and a button labeled
"Commit" that defaults to a single all-encompassing commit.

The temptation, with the work already done and the session already
finished, is to press the button and move on. The temptation is
universal. The discipline of resisting it is rarer than it should be.

There are three specific patterns where this goes most wrong.

The first is mixing functional changes with formatting changes.
The LLM "helpfully" reformats code it touches. The diff now contains
the actual feature mixed with hundreds of lines of whitespace and
brace-style changes that have nothing to do with the feature. The
reviewer cannot see the feature through the noise. The kernel rule on
this is explicit: code moves and reformatting should be in their own
patch, separate from any functional edit to the same code.

The second is mixing the new feature with the refactor that enables
it
. The LLM, asked to add a new endpoint, decides that the existing
endpoint structure is the wrong shape and refactors it on the way to
adding the new one. The refactor might be correct. The feature
addition might be correct. As a single commit, the combination is
impossible to review because the reviewer cannot distinguish the
refactor's behavior changes from the feature's. The kernel
prescription is two patches: refactor first (with its own
justification), feature second (built on the refactored base).

The third is mixing the fix with the cleanup. The LLM, asked to
fix a bug, also "fixes" five other things that looked suspicious to
it on the way. The bug fix that should have been three lines is now
ninety lines. Whether the other fixes are correct is unknown because
nobody reviewed them as the focused changes they should have been.

All three patterns are produced by LLM workflows that do not impose
the kernel rule. All three are eliminated by workflows that do.

Applying the Rule to LLM-Assisted Sessions

The translation from kernel discipline to LLM-assisted workflow is
mechanical. It involves three changes to how the session is
conducted, none of them difficult, all of them resisted by the
default tooling.

Plan the logical changes before generating the code. The first
prompt in a session should produce a plan, not a diff. Ask the
assistant to enumerate the logical changes the work requires. Refactor
the existing module to support pluggable backends. Add the new backend.
Wire the new backend into configuration. Update tests. Update
documentation. Five logical changes, each justifiable on its own,
each producing a separate commit. The assistant is perfectly capable
of producing this plan when asked. The default workflow does not ask.

Generate one logical change at a time. After the plan is agreed,
the second prompt asks for the first logical change only. Not the
feature. Not the whole work. The first item on the plan. The assistant
produces a smaller diff that does one thing. The diff is reviewed,
committed with a precise message, and the session continues to the
next item on the plan. The session is a series of small focused
exchanges, not one large indiscriminate one.

Use git add -p aggressively when the diff escapes the plan. If
the assistant produces changes that span multiple logical concerns
despite the planning, the rescue tool is git add -p, which lets you
stage hunks selectively. The hunks for the bug fix go into one commit.
The hunks for the unrelated cleanup go into another. The hunks for
the formatting changes go into a third, or get discarded entirely if
they were not requested. The kernel rule survives even when the
assistant fails to respect it, as long as the engineer uses the tools
git has provided for exactly this case.

The result of these three changes is that the LLM-assisted session
produces a series of small commits that each do one thing, each have
a precise commit message in the imperative mood the kernel format
prefers, and each could pass code review on their own. The same
amount of work is done. The product is structured differently. The
durable artifact (the git history) is substantially more valuable.

A Worked Example

Concretely, consider a task that an engineer might hand to Claude
Code or Cursor. "Add rate limiting to the public API endpoints."

The default workflow produces a single session that touches the
middleware, adds a Redis connection, configures the rate limit values
in the application config, updates the OpenAPI spec, adds tests,
fixes a typo the assistant noticed in an unrelated docstring, and
reformats two files because the assistant has opinions about
formatting. One commit. Message: "Add rate limiting."

The disciplined workflow produces the following commit series:

1. config: add rate_limit section to application config schema
2. infra: add redis client initialization for rate limit storage
3. middleware: add rate limiting middleware (no-op by default)
4. middleware: enable rate limiting on public API endpoints
5. test: add tests for rate limiting middleware
6. docs: update OpenAPI spec to document rate limit headers

Six commits. Each one does one thing. Each one has a precise message.
Each one would pass review individually. The history that results is
the history a kernel maintainer would accept. The same LLM produced
the same code in both cases. The difference is the structure imposed
on top.

The cost is roughly fifteen minutes of operator time, spent planning
and breaking the work into separate prompts. The benefit is a
codebase that remains reviewable, bisectable, and archaeologically
legible for as long as it exists.

Commit Messages, Imperative Mood, and the LLM Default

The kernel document also specifies the form of the commit message
itself. The summary line should be "no more than 70-75 characters,
and it must describe both what the patch changes, as well as why the
patch might be necessary." The body should be in imperative mood,
"as if you are giving orders to the codebase to change its behaviour."

LLM-generated commit messages routinely violate both rules. They are
often longer than seventy-five characters. They are often in
declarative mood ("This change adds...") or past tense ("Added...").
They are often vague to the point of uselessness ("Update files,"
"Implement feature," "Fix bug").

The fix is to ask explicitly. Every LLM I have tested produces
correctly-formatted commit messages when prompted to do so. The
prompt is short: "Write a commit message for this change in the
Linux kernel style: imperative mood, subject under 72 characters,
body wrapped at 75 columns, explaining what changes and why."

The assistant will produce something like:

ratelimit: enforce per-endpoint limits on public API
 
The public API endpoints were vulnerable to abuse from bots
hammering the search endpoint, which dominated database load
during incidents. Add Redis-backed rate limiting middleware
enforced on /api/v1/public/* with configurable per-route
limits. Defaults match the production-observed legitimate
traffic patterns plus 2x headroom.

This is a usable commit message. It explains what changed, why the
change was needed, and what the configuration implies. Six months
from now, when someone is debugging a rate-limit-related incident,
the git blame on the middleware will surface this message, and the
debugger will have everything they need to understand the context.

The default LLM commit message would have been "Add rate limiting."
The difference is a single explicit instruction in the prompt. The
ratio of value to effort is exceptional.

The Deeper Observation

The kernel community spent thirty years working out what commit
discipline looks like under real production pressure. They wrote it
down. They documented it. They made it the explicit precondition for
contribution. They built the tooling around it: git format-patch,
git send-email, git bisect, git am. The entire workflow assumes
the rule.

LLM-assisted coding tools have arrived as if this work did not exist.
The default mode of operation in Claude Code and Cursor and similar
tools is to produce session outputs, not logical changes. The default
commit interfaces collapse the session output into a single commit.
The default commit message is whatever the LLM happens to generate
in two-second response time, with no opinion about format.

This is not the LLM's fault. The LLM does what it is asked. The
problem is that the tools that wrap the LLM have not internalized the
discipline that thirty years of kernel development worked out. The
engineer using the tool, who is responsible for what lands in the
repository, has to impose the discipline themselves.

The discipline is not difficult. It requires planning the work
before generating the code, generating one logical change at a time,
using git add -p when the changes escape the plan, and writing
commit messages in the kernel style by explicit prompt. Four
practices. None of them takes long. All of them produce a codebase
that will still be intelligible to its maintainers five years from
now.

The codebases that adopt this discipline will be measurably better
than the codebases that do not. The history will be readable. The
regressions will be bisectable. The blame will be informative. The
ability to revert exactly the change that caused a problem, without
collateral, will exist.

The codebases that do not adopt this discipline will accumulate the
LLM-generated technical debt that is currently building in repositories
across the industry. Eventually, the cost of operating those codebases
will exceed the productivity benefit the LLM provided in the first
place. The reckoning is coming. The kernel rule is the cleanest way
to avoid it.

Closing

The "Separate your changes" rule is one paragraph in a document at
docs.kernel.org. It was written by people who had to deal with the
consequences of not having it. It is correct, and it has been correct
for three decades, and the arrival of LLM-assisted coding tools has
made it more relevant rather than less.

If you are using an LLM to write code, read the rule. Apply it to
your sessions. Plan before generating. Generate one change at a time.
Use git add -p when reality diverges from the plan. Write commit
messages in the kernel style.

The tools will catch up eventually. The default workflow will improve.
Cursor or Claude Code or some yet-unnamed successor will eventually
ship with kernel-style commit discipline as the default rather than as
a manual override. Until then, the discipline lives in the operator's
hands.

The kernel's rule is short. The benefits of applying it are long.
Read the section. Internalize it. The repository you maintain will
thank you for it, and so will every engineer who has to maintain it
after you.

Tagged:
#git #kernel #llm #linux
← Back to posts