Knowing when and how to say “No” is one of the most important responsibilities of a software maintainer. This applies equally to open and closed source software projects, and it’s a key skill to develop as your scope and influence grow with your seniority. Much has been written about learning to say “No” to new work as an individual who is looking to improve productivity, focus, and work-life balance, but we can also apply these same principles to the world of collaborative software development.
Software maintenance goes beyond triaging issues, fixing bugs, and publishing releases. Maintainers must also guide their project’s direction and keep it healthy by continually acting in its best interests. This is especially true for projects that attract a wide range of people who contribute a few ideas each, as is common for most open source projects. It also applies to proprietary code bases inside companies who naturally see developers come and go over the years.
A key aspect of a maintainer’s role is evaluating contributions and deciding which are a good fit for the project—and which are not.
Let’s look at some examples.
A feature request would add functionality beyond the existing scope of the project.
Software projects usually start by solving a single, specific problem and then evolve from there. This tendency is humorously captured by Zawinksi’s Law of Software Envelopment:
Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can.
Each new feature or capability can make the software more complex and harder to maintain. This can affect build times, documentation quality, and test coverage surface area. Further, it’s the project’s maintainers who will ultimately bear the responsibility for the feature; the contributor is often simply scratching their own itch and shouldn’t be expected to make a lasting commitment to the project.
To that end, you will need to decide if accepting the feature request will make the project better overall. If it doesn’t match the long-term vision of the project, or if you don’t think you’ll be able to provide a good level of support for the new feature, it is often best to say no.
A proposed change only applies to a special case.
As the Zen of Python says,
Special cases aren't special enough to break the rules.
Although practicality beats purity.
It’s up to you to decide how you want to handle these cases in your project because they often involve a lot of context, but the key message here is the combined importance of having clear principles, applying them consistently, and the willingness to forego them out of pragmatism.
A pull request introduces a new third-party dependency.
Depending on another piece of software can be a benefit, a liability, or both. It’s important to consider the dependency’s license (or cost), set of supported platforms, whether it is actively maintained, and its history of security issues.
Assuming those all check out, you will need to decide whether the ongoing maintenance burden of the dependency is good for the long-term health of the project. The author might simply have pulled in a code library because they were accustomed to using it to solve similar problems, or they might be relying on only a small part of its functionality. There are times when this clearly makes good sense, such as when cryptography is involved. However, in other cases, a little copying is better than a little dependency.
A pull request’s quality doesn’t meet the project’s standards.
The ideas behind a pull request might be great, but if its implementation falls short, it shouldn’t be accepted until it is revised to meet the project’s standards. For example:
The change lacks sufficient test coverage.
The new code doesn’t make idiomatic use of the programming language.
The source code or file organization doesn’t match the project’s style guide.
Documentation is missing or hasn’t been updated to reflect the change.
Fortunately, these expectations can be documented in the project’s contribution guidelines so that contributors are aware of them before they publish their work.
It is also useful to configure tools like linters that run automatically on every pull request. They provide immediate feedback by quickly identifying any potential issues without involving a human reviewer.
Responding
If, after consideration, you determine that the proposal isn’t a good fit for the project, that decision needs to be communicated. When it’s completely unworkable, it’s appropriate to respond accordingly and close the issue. However, if the idea has potential but simply won’t work in its current form, respond by suggesting what it would take for it to become acceptable. This hopefully leads to a productive dialogue that results in a successful contribution.
Here are some things to keep in mind:
Be polite and respectful.
You should always respond using a polite, respectful tone. You are a software maintainer, not a gatekeeper, and the way you interact with contributors reflects on both you and your project. It will also influence how potential future contributors view the project.
Acknowledge the time that the person has already invested.
A contributor might have invested a lot of time into their proposal before you first see it. It’s important to acknowledge that effort by responding in a timely fashion with thoughtful feedback.
Acknowledge the time that you’re asking the person to additionally invest.
When you ask a contributor to make changes to their submission, you should similarly be considerate about the amount of additional time you’re asking them to invest. For example, if everything looks good aside from a spelling mistake or a missing changelog entry, it might be more productive overall to make those changes yourself while it has your attention. Otherwise, you could end up taking the asynchronous feedback cycle for another loop for little net benefit.
Produce a record of your decision.
Explaining how and why you’ve reached a decision can be just as important as the decision itself, and if you’re not able to explain the reasoning behind your decision, it probably deserves some more thought or another point of view.
By providing this additional context, you are also leaving behind a documented record of your thought process. You can refer back to it later for the benefit of both yourself and future contributors.
Focus on the work.
Collaborative software development is an inherently social process, and there’s a natural human tendency to treat the contribution and the contributor interchangeably. It should go without saying, but it’s important to keep the focus on the work itself, especially when delivering critical feedback.
Be thankful.
Lastly, it’s important to express your thanks for their work, even if it’s ultimately not going to become part of the project. In its own way, the process of evaluating their contribution has made the project stronger by clarifying its goals and challenging assumptions. As Rose Judge said during her talk at the Global Maintainer Summit, saying “Thank you” is the one thing that’s always appropriate when responding to suggestions.
Moving forward
As a final thought, keep in mind that software development is a fluid process. Today’s decisions don’t necessarily need to be permanent or require heavy-weight change management processes. Systems like Semantic Versioning provide frameworks for safely and responsibly evolving software, giving us opportunities to revisit past decisions based on new information, and hopefully allowing us to say “not now” a lot more often than “not ever”.