At Adevinta, our data platform teams are deeply committed to continuous deployment, ensuring we can move fast and experiment frequently to drive innovation. At the same time, we must guarantee high availability and reliability, as we serve as the foundation for the excellence of our marketplace products.
Several years ago, a coworker ranted about colleagues sneaking “ninja-commits” into the project he was leading. We laughed a bit about the term, but it was clear he was referring to code changes made outside the scope of the previously agreed requirements — unfocused changes that silently sabotaged their speed in a continuous delivery environment.
From Urban Dictionary, the definition of ninja-commit:
When you commit source code to the repository without telling management.
I did a ninja-commit yesterday so it should be working since the installation this morning!
My interpretation
Ninja-commits go beyond just not telling management. Ninja-commits refer to code changes that deviate from the agreed-upon scope, often sneaking in unrelated improvements or refactoring without proper discussion or prioritisation. In other words, ninja-commits are the extra changes we decide to add outside of what could reasonably be expected or was previously agreed.
Avoiding ninja-commits does not mean avoiding large LOC (Lines of Code) changes nor changes that took more than a couple of days to be written. (Note: smaller, fresh changes do make a reviewer’s life easier). Avoiding ninja-commits is about focusing on one agreed change at a time and treating everything else as being out of scope for the current changeset.
Why do we ninja-commit?
Here are some common motivations for developers to bundle code changes into a single pull request:
Convenience
It’s more convenient (for the code author) to create one pull request, then release and monitor the changes all at once.
Boy Scout rule
“I’m leaving the codebase better than I found it.” That’s great! But it could be even better if you extract those improvements into smaller, separate changes.
Improve delivery time due to slow CI/CD
A slow CI/CD process undermines delivery speed. Grouping changes together decreases the delivery time by X where X is the number of changes packed together, assuming all of these changes will go well.
Pair programming / Mob programming
Pair programming (or mob programming) involves several team members working together on a code change simultaneously. Whatever is done, it’s agreed during the pairing session. This dynamic can feel like carte blanche to relax code reviewing practices and the delivery process because code is being reviewed interactively as it is written.
You only live once (YOLO)
Sometimes, talented developers push the limits, making brilliant changes that others will struggle to follow. Anyone following the new changes may also be occupied fixing similar incidents from previous rule-breaking code updates.
While this point is made in jest, who hasn’t gone YOLO during their early programming days?
The benefits of avoiding ninja-commits
When we avoid ninja-commits, we reframe problems in a way that isolates and constrains the changes. Working to scope is one of the foundations of stable continuous deployment (Work in Small Batches). Here are some key benefits:
Easier monitoring
Monitoring, tracking and interpreting production metrics requires special care. Monitoring your production deployment by one change at a time will simplify the assumptions and correlate observations.
Easier rollbacks
Let’s say you integrated three isolated changes into the codebase, all of which are now in production. If one of those changes causes trouble, you can easily revert that specific change without affecting the other two.
Traceability / Clean Version Control History (VCS) logs
Commit messages and pull request descriptions are valuable sources of knowledge. Smaller changes make it easier to trace the decisions made and the rationale behind them.
Faster feedback loop (from peers)
The sooner you attempt to integrate a change, the sooner you’ll get feedback from your colleagues and automated systems.
Faster feedback loop (from production)
Every time you deploy to production, you learn something new. These learnings might raise new challenges or reveal simpler solutions
Consequences of Ninja-Commits
Ninja-commits may seem harmless, but they introduce hidden risks to the development process. Here are some of the notorious consequences:
Misdirection
Imagine you submit a flawless pull request that introduces outstanding performance improvements. However, within the same PR, you also refactor some error descriptions. A colleague disagrees with the new text, leading to a lengthy debate that directs resources away from the primary goal—improving service performance.
Mixing priorities
Different needs have different priorities. Bundling a hotfix with a non-priority feature could lead to delays in urgent updates.
Elevating risks
A ninja-commit can turn a trivial, safe change into a potential incident, as the additional, unrelated changes might introduce unexpected issues.
Increase cognitive load
Humans have a limited capacity to retain information. When a large, complex change is proposed, it increases the chances that you or the reviewers will overlook important details.
The probability increases when some of the changes are new, unrelated and unexpected.
Techniques to disassemble ninja-commits
As we work on a task, we often make changes that deviate slightly from the original scope. As we dig deeper into the code, we gain insights that may change the assumptions we started with. Eventually, we may arrive at a changeset that includes refactoring, small fixes and perhaps some adjustments to the original task—all bundled together with the main task itself. At this point, we know we have several ninja-commits in the local changes. What can we do?
Technique 1: Embrace the ninja-commit
Organise a team meeting, start a Slack thread, or include a detailed PR description. Be transparent and vocal about why these changes are tied together. If your colleagues agree with the trade-offs, go ahead and ship it! The ninja-commits are no longer covert, as they become “expected” changes.
In cases where you want to provide more structure to the changes while still keeping everything bundled, you can break down your work into sequential commits.
Technique 2: Disassemble into sequential commits within a single pull request

Needless to say, creating meaningful commits is a good practice. The repository version history stays clean, documents the intention behind code changes and simplifies rollbacks. Also, commits needing further discussion can easily be split into separate PRs for quicker integration.
Many developers start with this approach and then strip out some commits into independent or dependent PRs.
Technique 3: Disassemble into non-dependent pull requests

If the code changes are sufficiently independent, create separate pull requests for each. Doing this makes it easier to naturally prioritise each change according to its impact.
On the other hand, tracking them to the task/issue DoD (Definition of Done) becomes essential. You may split the ongoing task to align with the way the PRs were divided, to become your new way of framing the problem.
However, in cases where changes can’t be easily separated, consider using dependent pull request sagas.
Technique 4: Disassemble into dependent pull requests sagas

Changesets may grow large as multiple things—like refactoring, improvements or feature implementations—happen simultaneously. When this happens, it’s tough to strip changes independently, requiring an iterative approach instead. In these cases, partial code integration and release feature toggles stand out.
As an example of partial code integration, let’s say we need to implement a new endpoint for listing users. This could be broken down into four pull requests:
1. Pull Request 1: add UserRepository class with a getAll method.
2. Pull Request 2: add GetAllUsersUseCase class with a getAll method (depends on 1).
3. Pull Request 3: add the endpoint implementation with its documentation. Note that the endpoint is not yet instantiated in the application (depends on 2).
4. Pull Request 4: instantiate get users endpoint (depends on 3).
As you may notice, releasing PRs 1, 2 and 3 have no impact on the ongoing application. They are never instantiated nor referenced in the production code. However, they could be safely integrated and released. The feature will be completed and released only when 4 is finally integrated.
There are cases where features need even larger developments in several modules and applications. Then, release feature toggles will help with integrating smaller chunks sooner: basically the new feature will be disabled and controlled by one or several “switches”. The integrated code will only be enabled once this switch/es is on.
Feature toggles are a separate topic as they are also used for different use cases, such as running experiments or performing operations. Please refer to Martin Fowler’s Feature Toggles (aka Feature Flags) article for a more in-depth understanding.
A downside of this technique is that the reviewers will need to be able to see the full picture beyond the specific code changes. A request to review a repository implementation without knowing what it is for will unlikely be approved by skilled reviewers. Adding some context with a diagram or a link to the final state documentation to the pull request description will help override these concerns.
Techniques summary

Deciding how to split changes—by code size, context or any criteria—is an art in itself. The key is to combine these techniques thoughtfully to maximise impact while minimising interruptions for your team. It’s not about creating one-liner PRs, but about leveraging reviewer feedback effectively and optimising delivery time.
Final words
Thinking outside the box, proposing new ways of doing things, and challenging the status quo are what make us great developers. On the flip side, narrowing the scope and focusing discussions directly impact delivery time. When possible, isolating changes will pay off in terms of delivery time, disaster recovery and maintaining a healthy repository change log.
While these points might seem obvious, I encourage you to reflect on how many ninja-commits you’ve made in the past week. If your PRs are stalling because discussions veer off course, ask yourself if that could have been avoided by splitting the changes into independent or sequential parts.
Finally, remember that everything in life involves trade-offs where context, common sense and team agreements prevail. Challenging yourself to consider whether ninja-committing is the best option can turn accidental behaviour into a powerful, deliberate tool. So, what kind of code ninja do you want to be?