When using an open source project, you may find it lacks some important feature you need to work with it effectively. When you are in this situation, you can either request the feature you need and implement it yourself, or just look for another project to use that has something closer to what you need. While most of the time people pick the second option, requesting and implementing the feature yourself can have a lot of benefits.
- Maintenance work on the feature can be shared by all of its users
- Working within the project exposes powerful internals that can give you exactly what you are looking for
- Implementing the feature can give you deep knowledge of the project that can be shared within your organization
If you always choose to look for another project when it lacks a feature, you are missing out on one of the main benefits of using open source software: the fact that you have access to the source and are able to change it. This is an enormous amount of power to have, and the top technology companies take advantage of it. When time and budget allow, it should be considered as an option for your important dependencies.
Making good feature requests is an essential skill to master to be productive with open source projects. As an open source maintainer, I’ve seen a lot of variation in the quality of feature requests to my projects over the years. Making a good feature request is much more difficult than people realize. It’s part creative, part sales, and part technical. But when you get it right, it’s one of the most rewarding experiences I’ve had as a developer.
It may seem intimidating at first, but it’s much easier when you know the rules. In this article, I’d like to share some things I’ve learned about making good feature requests to help you create contributions that are able to get the attention of maintainers so they can be accepted into the project.
Creating an Issue
Once you have something in mind to work on, the first step is always to make sure you have an issue to work from. The most common beginner mistake I see is to start coding right away. This works for simple bugs when you fix something that’s obviously wrong, but any nontrivial feature will require some discussion before it’s ready to be implemented. You want to get people involved in the design process as soon as possible. The amount of discussion you generate with your proposal is a great way to gauge how interested other people are in the feature. Someone who is involved early in the process will have the best understanding of your goals and will provide valuable feedback that will guide your development. Treat anyone who gets involved early in the discussion as a potential user of the feature. Even if they seem adversarial or the feedback is negative, taking the time to respond at all should be taken as a sign of respect and an indication that a conensus is possible.
If the project is active, chances are someone may have already thought of your feature and proposed it on the issue tracker. Spend five to ten minutes searching for your issue with different wordings and see if you can find something similar to avoid creating a duplicate issue. If an issue already exists, read through the discussion carefully because it can save you a lot of time by not duplicating a discussion that has already happened or not trying an implementation strategy that is known to have problems. Check if someone is already working on the feature. If you see that someone is actively working on it, you can still contribute by adding your opinion to the discussion, testing the active branch, and reviewing the code. However, don’t get discouraged if you see someone who claims to be working on the feature who doesn’t have an active branch they are working on. In my experience, about eighty percent of the people who start working on something never actually finish it. Ask if you can pick up the work where they left off and try to credit them the best you can in your work. The best thing to do would be to use their commits directly in their branch, but that’s not always possible so at least give some thanks in your commit message.
Once you have an issue to work from, now it’s time to explain what you want to do. A good feature request always has at minimum these three components:
- The use case
- The approach
- The test
The Use Case
Coming to an agreement on the use case is the most important part of the discussion. If the maintainers agree that the use case is important to support, the rest is just implementation details. If the maintainers believe the use case is not valid, then nothing else is important. Don’t try to sell a beautiful approach for an invalid use case. The three most effective ways to justify a use case are 1) appeal to the project’s mission, 2) appeal to similar features, 3) demonstrate user demand.
The project’s mission is often best expressed in the description which is usually in the form of e.g., “a library to do X”. Justify your use case by explaining how your feature facilitates the user to more effectively accomplish X with this library. A more detailed description of the project mission is usually included in the project overview or the README. You may even find your feature is explicitly requested or blacklisted within the documentation. You can use all of these sources of information to support your case.
Additionally, look for features in the project that are similar to yours. This sort of appeal is extremely efficient because you get to reuse all of the justification that was used to justify the similar feature, which by default is assumed to be valid or otherwise the similar feature would be deprecated. On a related note, keep in mind that this sort of justification is so powerful, that maintainers may be wary of accepting even small features that may expand the scope of the project in unpredictable ways. To accept a certain feature with a certain justification is to implicitly accept all future features proposed within the same scope. If the project doesn’t have the resources to support the whole class of features, this is a good justification by the maintainer to reject the smaller feature even if it doesn’t add a lot of complexity by itself.
Finally, it is important to demonstrate user demand. Most often the user of the feature will be yourself or another project you are involved with. If you can, demonstrate a concrete use case with issues from other projects. Explain how adding the feature will help to fix those issues on the other projects. High demand for a feature means a larger pool of developers who can potentially come fix bugs when things break, as well as more influence within the project space.
The Approach
Discussion of the approach should come after everyone has come to a rough consensus on the nature and validity of the use case. Give a general overview of the changes you will need to make to implement the feature in a few sentences. For example, explain whatever new classes you might need to add or how the existing functions need to be modified. The amount of complexity a maintainer will allow in an approach usually relates to the strength of the use case, with a stronger use case warranting a greater amount of complexity. Changes that break backwards compatibility and need a major version bump need the most justification, so don’t propose these kinds of changes unless it’s clear to everyone why it’s important to do so.
Explaining the approach is an important step because people who know more about the project will often have valuable feedback on what you’ve proposed. What might seem simple to you may not be extensible enough to accomidate future planned work, or there might be unwritten conventions to follow so your code fits better with the project’s style. Knowing these things up front will save you a lot of time in code review.
The Test
Finally, you should include a test with your feature request. These don’t have to be formal tests, just an example snippet that demonstrates what the important part of the API will do when you are done. Including a short test tends to bring about a good discussion of details you might not have thought of like error handling. You need to have a test in mind during development anyway so you might as well post it on the issue. Be honest about any edge cases and bring up any problems you find in the issue as early as possible.
Issue Discussion Etiquite
These sorts of discussions are what give open source projects their reputation for being unfriendly places. Please keep in mind that discussions on open source issue trackers have very little resemblence to what is commonly called “normal human interaction” and a different set of rules tends to apply. They can sometimes resemble a game unto themselves much like poker with lots of posturing and bluffing. You may even sometimes feel like a lawyer in a court room. The most important thing to keep in mind is to always begin your thoughts from a place of respect and do not take things personally. The goal of the discussion is to always move forward towards a consensus. If you sense that no progress is being made, do not repeat your points. Wait a few days for others to chime in with a fresh opinion. Be willing to accept that not every idea you propose can come to a consensus in the project, and have a backup plan such as forking or starting a new project that can accomidate your use case.
Now Start Coding
Congratulations, your feature request was accepted. With all you’ve been through, this might seem like the easy part. It is very rare to have a feature rejected at this phase, but I have regretably seen it happen before. There’s a lot more that can be said after this point such as how to make a good pull request and how to respond to feedback during code review, but I’ll leave those topics to another post.