No Should be Your Default
In the last few weeks, I've had several conversations with colleagues and contributors to about how defaulting to "No, I won't do that" is a good practice. A product manager I worked with a while back explained this philosophy to me very succinctly. With software, it's very hard to deprecate or remove a feature and so your "Yes" must be closely guarded. While the wisdom of this was immediately obvious to me, the longer I've thought on this the more I realize it applies to more and more aspects of software - open source, professional development - and also life in general. I'll try to explain how this mindset change can benefit folks on many axes.
No New Features for Open Source Projects
If you don't know me, I've been maintaining Open Source software projects for over a decade. When I was younger, I often time looked for ways to improve the value the project provided to others and to myself (since almost every time I worked on a project it started out of a need for the project or a desire for a better version of what was available). That resulted in accepting feature requests that with a good amount of hindsight were truly bad ideas.
Many times, independent project owners start an open source project because it's something they need or something they're interested in learning more about or because they're looking to have fun (or some combination of that and some other needs). However, there's a thrill when you find someone who is using your project and loves it and has put in the effort to fix something, or add something new. It truly makes it hard to make a good objective decision on the actual merit of the change proposed.
Sometimes, those contributors truly believe your library needs that functionality (e.g., an API wrapper library that is missing support for some newly added or changed API surface area). Other times, those contributors are basically trying to externalize some amount of risk in their codebase. They have some code that they copy and paste around their projects and they feel like it's a good idea to push that into the library it's primarily for so they don't need to maintain it or copy it around. However, those features they feel are a good idea, often don't fit well with the whole library or are very poorly considered and a holdover from some other language that they or someone they collaborate with came from. An example of this, might be a user coming from another language with an HTTP Client library that always raises an exception for a response that doesn't have a 2xx status code. It's easy enough to make that optional for someone else, but now, imagine another user comes from a different language that assumes anything that is 4xx or 5xx is exceptional, but 2xx and 3xx are fine. They have different mental models and both want to accommodate their preferred patterns. If they submit a request to now add an optional argument to define what is exceptional or not, that could be easy enough to accept too. Now imagine someone else, integrating software from both of those earlier users together and trying to trace through some odd behaviour in the early hours after midnight after being woken up by some alert and trying to remember which thing thinks which response is exceptional and trying to parse it all through the one same library where the exception seems to be coming from unhandled by some other layer. This becomes a nightmare. Trying to make it better in the future becomes far more difficult as people have started to rely on things like the exception class, the exception message string, logging's existence or absence, etc. Truly, the gamut of things people have complained about that should have been backwards compatible is awe inspiring sometimes. If you've been or around software long enough, you know that as soon as there's a behaviour in the software you produce, it can become nearly impossible to remove it. XKCD has a famous comic about this. What's the obvious answer here? "No." If you wrote the library for yourself and you opened it up to others with a free and/or open source license you get the final say. "I won't use that feature and it's easy enough to implement on your own." is a very fair response. Also "this doesn't fit with the design I have in mind for this library".
The reality is, if you have created a free/open source piece of software, you are putting it out there for people to use as is without any requirement of you to fix anything or accept any improvement. People have become conditioned (and one might say entitled) by GitHub's "social" model of coding to believe that all bug fixes must be accepted and all feature requests are inherently reasonable and should be accepted. That's not what is required from you dearest open source author and maintainer. Maybe you wrote it, and you no longer care about it. You can communicate that clearly and allow someone else to fork it if they find it so valuable. Forks aren't evil or imperfect or anything else. They do exactly what they should - protect existing users while allowing those with different priorities to have those needs met. For a free project you're managing in your own time, you can also ground your "No" in any number of places:
- What is your motivation for working on the project in the first place?
- Have you used a similar feature elsewhere and you really saw some problematic things with it that are irreconcilable with the feature itself?
- Have you previously implemented something like this and hated the impact it had on another project?
Most users (but sadly not all) are reasonable. If you have a good reason for not adding this new feature and they understand your reasoning, it's not a problem.
One final important note for this section I want to impart is that accepting features and bug fixes does not a community build. Often times the best intentions of maintainers to build good will with others ends up in code that is not well understood and features that expand the attack surface of a project. I wonder, sometimes, if the Log4j developers accepted the code that enabled the Log4Shell (a.k.a., CVE-2021-44228) vulnerability because it seemed innocuous enough and the hope was it would lead to more people contributing, helping, and eventually becoming maintainers.
No New Features for Products
I've frequently seen the following pattern (heck, I've even been part of it):
- Internal tooling exists and gets a fair amount of traction throughout the company - or the product is actually internally focused
- Another employee at the company uses chat to talk to developers of the product for support and eventually start making feature requests, but these have never been formally accepted or added to the backlog
- One of the more junior engineers on the team is doing some related refactoring and knows about one or more of these feature requests and thinks they should just tuck in the request as part of the work
The issues here are:
- The team that owns the tooling hasn't agreed to own that functionality
- This expands the scope of the work that was already being done which can impact commitments on other products
- If the feature isn't needed by many users, you're implementing to exactly one use case but it may need to be changed and improved later after others have started trying it out. In that case, it may require breaking changes which will always upset some set of those customers. To do that in a backwards compatible way always requires more work than waiting until you have enough information to implement it correctly
- If the time is long enough between the original request and when it's implemented, the requirements may have changed, or it may no longer be necessary
The reality here is that it's always best to say something like "Thanks for telling us how we could improve! We'll add it to our backlog and see how it compares to other requests we've prioritized already."
It is important to remember that there are always priorities in any organization of any size. Those priorities, whether we like them or not, are what we need to align with. We can always try to influence or change those priorities but eventually we may need to disagree and then commit. We cannot always change things, and importantly, we may not always have the context we need in order to properly influence direction, but that's okay. As you grow in your career, you'll learn how to find that context, put it together, and understand the why behind many decisions that company leadership makes. Once you have that, you'll be able to start influencing those decisions. You'll even find yourself the ire of junior engineers wanting to be helpful and implement new features who see those feature requests.
General Life Advice
Some people tend more naturally to want to take care of others. I know that I am one of those people. Part of why I got sucked into writing so much open source software is because I put something out there for free because I was learning and found it useful for myself and someone else came along and told me how much it improved their life. I had only been programming for something less than a year. It was such a huge compliment to me that in my attempt to teach myself things I'd managed to make an improvement in someone else's life. It was also a huge hit of dopamine that really made me start to chase that sense of accomplishment (making something initially just for myself that others would find useful). Between the sense of responsibility I had for keeping that useful project alive and the desire to get more dopamine, I started saying "Yes" to too many things. At a certain point, people stopped just asking if I wanted to help maintain things and just started adding me to them and assuming I would.
Whatever you might think, companies are good at this - they have priorities (despite whatever incentive structure might inform those) and they change direction to achieve them. How many products has Google launched and killed? Yes, those shutdowns have impacted people, but, they were the right thing for the company and that's what it needs to do. You should treat your energy, your time, your mental health, your physical health, and your relationships the same way. Prioritize what you do according to your own incentive structure and say no to everything else. Leave things that aren't aligned.
This is a huge part of why I burned out so very many times and am still trying to figure out how to recover. I still struggle to leave projects and not support the many people relying on them. I still feel guilt, shame, and a sense of obligation. But that's me, and I'm working on that.
This isn't just applicable to software though. Sometimes you meet people, like them, build a relationship with them, but you may eventually notice you're doing all the work to maintain that relationship. That's a recipe for burnout and it's a point at which you need to consider if that energy you're spending is what you really want to be spending it on. This may be a person-to-person relationship, or a community you've joined. You still need to think about what you're saying yes to, and if it's something important to you.
To be clear, I'm not saying that you have to maximize what you get of relationships with others. I'm saying that you need to determine your boundaries and needs and ensure you're protecting those boundaries and getting your needs met (and every person or community you choose to engage with isn't required to fulfill your needs).
tl;dr
It's important to think about what you might say "Yes" to and when you might say "No" and whether that's a "No forever" or just a "No for now". You need to determine your boundaries and you need to ensure that you enforce them. A big part of knowing and enforcing your boundaries, is also knowing what you need. Maybe you need the thing you're working on for yourself. Do you then need to bother answering issues, or accepting feature requests? If you're doing it for yourself, you can say "No" to all of those. Others have done it. And it's good for them. It may be good for you. You may want to join a community because of a search for a spiritual practice; that doesn't mean you have to redesign their website, or anything else unless you want to. You may see an opportunity to cheaply implement something at work, but you need to decide if it fits your priorities.
Et Cetera
I've written and re-written this blog so many times since 2019. Entertainingly, I recently found that other folks I admire have since published similar writings:
- Alex Gaynor wrote "Why software ends up complex" in 2020
- Nik Kantar wrote "Solve the Problem You Have" in 2022
And I think those posts are excellent ways of phrasing what I've said here. In Alex's case, it's far more succinct.
As Alex says "One can easily say, “so reject all feature requests”, but a project that does so will eventually find itself unable to serve its users' needs at all!". That's my point above - the best idea is "No for now, but maybe later". You take the time to consider the impact, and how it can best help all of your users.
[1] | Both Snyk and Cloudflare have decent write-ups on the vulnerability if you're curious. |