One way of thinking about software is as a collection of decisions made at a certain point in time. When collaborating with others on software projects, every line of code is a decision we make on behalf of someone else. Whether it’s our current teammates, future ones, or even our future selves, as we build up lines of code, we’re trusted to make choices to build towards the common goal we consider “working software.”
The complexity of our decisions depends on many variables, including the number of people who these decisions will impact. When we write a program by ourselves, we have to worry about convincing the computer to do what we want it to do. However, as soon as we start collaborating with other humans, we step into social skill territory. For example, sometimes we have to convince or influence others to go along with our implementation, especially when we’re not building on a previously agreed-upon approach.
Whether we’re working on a program at work, school, or on the internet with friends and strangers, achieving outcomes with our code requires that we coordinate with others so that the code evolves in the direction we desire. At work, we have tools to facilitate decision making, including project briefs, user stories, libraries, architecture discussions, code reviews, and even well-defined teams and roles to clarify ownership and responsibility. Even when we have these tools, converging on decisions can be tricky, leading to passionate discussions and long threads of arguments on the path to take. In a corporate environment, at some point, someone will make the call. Decisions about software don’t get easier when working in public.
Building software in the open increases the stakes. Increased adoption of open-source software brings more users, more use cases, and more complicated trade-offs to consider. Compared to proprietary software, open source seems easier to change. But facilitating decision-making in the open is a challenging endeavor. As Nadia Eghbal discusses in her book Working In Public, while open source is frequently characterized as participatory, in practice, it’s not blindly open to every person who wants to change it.
Building the Internet in the open required collaborators to converge on a mechanism to agree on proposals and make decisions as different pieces of the world wide web came together. The first Request For Comments (RFC) was published by Steve Crocker in 1969 to the Network Working Group, seeking comments on a proposal for Host Software. Today, there are more than 9,000 RFCs published in the Internet Engineering Task Force RFC index covering everything from protocols to meeting notes and even humor. RFCs evolved the Internet (and with the Internet) as a vehicle to facilitate decision-making across time and space between people who may not have met each other before.
Today, many popular open source projects use RFCs or a variation of them to facilitate their evolution. For example, Go uses Project Design Documents, Python uses Python Enhancement Proposals (PEPs), and Rust uses RFCs. Structured processes like RFCs give users a space to propose changes that align projects with their needs while balancing impact on existing users. When these proposals are successful, projects evolve by incorporating these collective decisions into their codebases, managing compatibility, and increasing or improving functionality. And while having a tool makes decision-making more accessible, using RFCs doesn’t mean decisions are easy. Sometimes, open-source discussions can be contentious, making the process of building software even harder.
We’re lucky to have the direct experience of two prominent Open Source contributors and Rust Core Team Members Ashley Williams and Steve Klabnik. Ashley and Steve shared their experience with RFCs in Rust, and this is what we learned.
RFCs, the good parts
Both Ashley and Steve agree that their favorite part of RFC contributions is the ability to refer back to previous decisions. Steve likens it to having a superpower. “Knowing what was decided and why, and being able to figure that out easily, is super key,” Steve says. “Chat gets lost to time; emails get lost to time. With RFCs, you can build up a record of the decisions and the process that got you there.”
Ashley concurs. “A history of decisions that you’ve made and how and why you’ve made them is the type of context that is difficult to capture after the fact,” she says.
The advantages that Ashley and Steve highlight, among others, are why I use the RFC structure for my distributed engineering teams. Writing prose before writing code can also help prevent confusion or poor decision-making, which is harder to undo once it gets to the pull-request stage. Finally, teams, whether volunteers or co-workers, can work at a distance when they adopt asynchronous tools like RFCs, setting them up for what I consider is the future of work.
Steve also shared a few favorite RFCs with us:
RFC #230 is one of my favorites. There’s not a ton of discussion on it; this RFC is early enough that we hadn’t gotten in a great habit of doing all discussion on the RFC thread itself yet. However, I love this RFC because of the artifact it produced: the RFC itself. I refer back to this constantly when people ask about why this particular decision was made.
RFC #2, which defines the RFC process. Self-referentiality is a fun thing.
In a similar vein, RFC #1068 re-did the entire governance for the project. It’s neat to see a process be able to self-adjust over time.
RFCs, the not so good parts
When we consider RFCs as a tool for new contributors, Steve highlights that while getting an RFC started is technically very easy, you can quickly get overwhelmed by a barrage of comments and notifications on a proposal. Ashley adds that while writing an RFC may be easy, she considers that the process is unapproachable for beginners. “You don’t see a lot of new folks filing RFCs, and if you do, you rarely see them get attention or response from teams,” she says. “I think this was a bit different early on. Still, the level of context required to write a meaningful RFC, wrangle the attention of the important eyes, and then the sheer will and time to withstand the consequences of that attention are a huge barrier to entry. There’s work needed to grow the RFC process alongside the project, and we haven’t done that work yet.”
Sustainability is another crucial aspect to consider in RFCs; after all, tools should make maintainers’ lives easier. The original question posed to Ashley and Steve assumed that RFCs are sustainable, but it turned out to not always be the case.
“I think one of the key aspects of RFC sustainability thus far has been that most decisions do not attract that much attention,” Ashley says “The system is extremely well designed for a conversation of around 10 folks, and for many decisions, it doesn’t grow beyond that. Because the Rust project is large and distributed, we’re organized on the principle that it is no one’s job to care about everything; that being said, there are still some things that everyone *does* care about. It’s overwhelmingly clear that we’ve outgrown our process by a long shot for those particular decisions, and in those scenarios, the RFC process is a liability. In some cases, the process has caused actual harm, and it is my personal belief that the project has an ethical imperative to improve it, urgently.”
Ashley’s comment reminds us of Julia Evan’s post "Figuring out how to contribute to open source,” regarding “things that are still hard about open source”:
“In open source, I need to send code reviews to total strangers. At work, I generally send code reviews to the same 10 people or so, most of whom I’ve worked with for a year or more, and who often already know exactly what I’m working on.”
The ugly side of RFCs
Like many sides of the internet, open-source decision-making exposes contributors and maintainers to toxicity. “Never read the comments” is popular advice, and yet open source maintainers and contributors do the opposite so they can learn about others’ needs or perspectives. The internet can be brutal, and Steve agrees as he shares one of the few times he’s been brought to tears due to a GitHub discussion of a particular Rust RFC that collected hundreds of comments. “In the end, the story of this RFC is positive because we learned a lot, but at the time, it was *brutal*,” he says. “People got extremely heated, and what seemed like a fairly low-stakes decision became an INCREDIBLY high stakes one.”
Conclusion
We agree with Klint Flinley when he says, “it’s time to stop taking open source infrastructure for granted.” RFCs are part of the infrastructure by facilitating decisions that shape our code. The social activity of proposing, negotiating, influencing, learning, and sometimes conceding is an aspect of modern software development that goes mostly unseen. On its best days, open source collaboration is a fantastic example of what we can do as humans when we work together. Distributed negotiations made by groups of possibly total strangers become load-bearing decisions in the modern world thanks to processes like RFCs. The evolution of these tools may very well lead us to make more and better collective decisions reflected in open-source software. Maybe open source collaboration will lead us to a future where we enjoy reading the comments.
This post wouldn’t have been possible if it weren’t for the dedicated attention of Ashley and Steve; I dedicate this post to them and all the dedicated open-source maintainers who make FOSS happen. May your comments be constructive.