#remote, #nomeetings, #noestimates, #nobacklog. #storytelling
How we work at feenk
As I am writing this, billions of people are urged to socially distance themselves from others. Temporary hospitals are erected. Medical personnel face deadly situations without proper equipment. Factories change to produce ventilators. As physical places shut down, the economy takes a hard hit, too. Unemployment sky rockets. Production drops. People worry. And rightfully so.
We are all asked to do our part and stay home. But, some of us that are more privileged than others: we can work and produce value while being at home. This article is addressed to them. In particular, to those creating software-based solutions.
Many people are experiencing working from home full time for the first time. And for many, it’s not a pleasant transition. Routines are shattered. Frustration grows. Productivity seems to go down.
It does not have to be like this. Well, let’s not talk about productivity right now. We are not going through a normal situation. It’s already a stressful time. At the very least, work should not make it more stressful. Still, we should be able to produce some value. And, perhaps, in the process, we can also bring some joy into our lives.
To help with this conversation, I’d like to offer our experience at feenk as a case study. Ever since we started in 2015, we work in a rather peculiar way: we work exclusively remotely, we work when it fits, we have no scheduled meetings, we do not rely on estimations, we have no centrally ordered backlog, and we guide our effort through the stories we tell each other.
According to the typical organizational and development process literature this sounds like we should not be able to produce anything of meaning. Whether we do or not, we leave it up to you to decide.
Perhaps a bit of background. We are a consulting company, but we work internally as a research lab. Our goal is to make the internals of systems explainable. We do this by rethinking the development experience:
- we invented moldable development, a new approach to programming that makes the system explainable through custom tools, and
- we develop Glamorous Toolkit, the environment that makes moldable development practical.
Let’s assume you consider what we produce as being not entirely meaningless. I am not suggesting you necessarily do what we do, but at the very least our experience could be considered as a case study to unearth assumptions that should not be cast in stone.
People that work are not resources. They are humans. We see well-being having priority over work. We apply this as a matter of principle, not as a result of a crisis.
What does that mean? For one, well-being is personal. If we follow this to its natural conclusion, it means that it is the organization that should adapt to the individual, not the other way around. Every individual has contextual needs starting with availability. A person with family and kids will have different needs than a single person that wants to travel the world. Neither is better. They are just different.
We accept our friends to be different and those relationships are better and stronger for it. Why should this not apply for the people we want to work with?
Moreover, the individual needs likely vary in time. For example, for many of us, the individual needs during this pandemic are significantly different than they were a mere couple of months ago. Just consider the sudden transition to homeschooling that many families have to go through. This is dramatically disruptive. It’s likely that people no longer have long contiguous blocks of time to allocate to work.
We are humans first, workers second. Work should be embedded in the current reality of life, and only the individual knows how that should happen best. There are exceptions, of course. Right now, medical and support personnel throughout the world will have little choice but to work in terrible circumstances. But, those are exceptions. The majority of us can still choose.
This is particularly relevant when you set out to do work that has never been done before. When you want to create, it’s best to be in a position to be able to create. Yes, some external pressure can sometimes work, but not for an extended period of time, and not when it’s not voluntarily accepted. In general, creativity requires lateral thinking, and anxiety tends to inhibit it.
When we can work in our terms, we have more chances to enjoy our work. When we can decide what is important, we are more likely to explore a step further than usual, and by doing so, we increase the chance of uncovering value that otherwise would remain hidden.
As we like what we do, we sometimes tend to work more than we should. This is a tendency we actively try to counter balance. While we do not want to control how much people work, we do try to keep track of when people are explicitly on holidays. The contract stipulates a minimum amount of mandatory holidays. People are encouraged to take more than that, but at least the minimum must be taken. This seems to work reasonably well, but we likely still can improve in this regard.
Remote. No prescheduled synchronization
We regard the organization as a distributed system made of collaborative actors. There are multiple ways to design such a system. We chose the view that relies on asynchronous communication and on eventual consistency. This implies that the organization should not rely on any prescheduled synchronization mechanisms.
The most obvious is to not have to be in a certain place. We work remotely. We are currently a team of 11 spread over 5 countries in 3 continents. While some of us actually live in the same city, we do not meet in person to work. We do meet occasionally on company trips, conferences, or visits to our customers (during the COVID-19 crisis, we cancelled all of those). Still, our functioning does not depend on anyone being in a certain place.
Another synchronization mechanism is the work schedule. If everything is asynchronous by default, it does not quite matter when one works. This turns out to be rather convenient when someone is on a different timezone, and it goes hand in hand with having no scheduled meetings, too. A scheduled meeting requires someone to be available at a certain time, which is at odds with the asynchronous work principle. So, we have no such meetings for any of our internal work. That means we have no standup, no fix planning session. The only scheduled meetings we have is when we have to comply with an external factor, such as a customer, but we fight hard to not have many of those.
A meeting is a way to bring different parties into sync. However, when we rely on eventual consistency, we do not have to have everyone be on the same page all the time. We can be out of sync for a while and we are fine with that.
Yet another mechanism that induces synchronization is the ordered backlog. An ordered backlog implies that there is a single queue of work. So, we do not have one of those. We do have an issue tracker, but it is not ordered. In fact, everyone in the team can do what they want at any time.
That sounds like chaos. So, let’s look at what we put in place.
Organizing communication around conversations instead of topics
We do not schedule meetings, but we still meet. In fact, we like to pair often. Only we do it when it fits everyone involved. How do we do that? We simply ask. If I want to pair with someone, I’ll ask kindly if they are available. If they are available, we pair. If not, we do not.
Our communication setup is simple. Almost simplistic. We mostly use a chat tool, but more relevant is how we use it. The vast majority of communication (>90% of messages) are exchanged on public channels. The channels do not denote topics, but are named after pairs of people, such as
#personA-personB. The goal is to connect people first and have work follow the connection, rather than the other way around. We find that this works reasonably well in our case, in particular given that the same persons often end up tackling rather distinct problems.
As all these channels are open, they can be seen by anyone and they can also be contributed to by anyone. It is not unusual for a conversation to start with two people and then have one or two join for a short while with various inputs.
A worry is that this leads to too much noise. That can happen. There are days of agitation when everything seems to be moving at once, but in most days, it is manageable. One interesting side-effect of having everything in the open is that there is less anxiety of missing out.
Beside chatting and pairing, we also communicate through narratives and through code. When we want to convey a larger wish, we might describe it in a longer internal blog post. But, when we want to convey what is in the system, we make it the job of the system to carry that narrative forward, in an executable manner.
Remote communication does not have a good reputation. We hear people complain about the quality of connections, or about the lack of physical feedback during calls. This can happen when we assume that we need to replicate the same communication the happens when co-located. That’s a misguided comparison. Remote work is another new medium that offers other opportunities.
First, the lead time: How long does it last between the moment you want to do something and the moment you actually get to do it? If for talking with someone, I need to move to a room, there is a delay involved. If to move to that room, I first have to book it, there is another delay involved. These can take many minutes. In a remote environment, it can take seconds. This matters because shorter lead times can enable finer grained communication.
When remotely, we can pair for just a few minutes meaningfully, something that is not so convenient when co-located. This is just one example. Look for the shift in costs, and adjust accordingly.
There is at least one more thing to consider when it comes to communication costs. If we get the cost of communication down, say an order of magnitude, you get access to a whole new set of options.
Consider this conversation sample.
Orange has a question. Blue answers in brief plain text.
Orange answers back with a picture showing the inheritance tree of the class that
Blue pointed out, essentially saying that the indication does not seem to map to the system. This is not unusual. These back and forth are often necessary, but can be quite expensive.
Blue then sends a script with a picture that exemplifies the message concretely.
Orange commits the code a couple of minutes later without asking any further questions.
Being able to show what you mean in a way that the other can immediately relate to without much further explanation can bring the cost of communication down significantly. When the cost is large, we might want to have synchronization points with status/planning meetings and with associated ceremonies. But, if you can convey the same problem in depth through a picture that you can put together in a few minutes and have the person on the other side say “ahh, yes, I see it”, you have new options.
We explicitly and continuously optimize our communication cost. It is not always as easy, but when it does happen (and our chat is full of pictures we send back and forth) we literally rejoice. It’s a thing we aim for. In our case, constructing concise narratives about a system is at the core of moldable development and is enhanced by our development environment.
Theory says that we should be doing the most important work first. That might work well in a factory. Or when everything else is equal. But, in creative work we see two problems with it.
First, someone would have to define what important is. Second, someone would have to order the items linearly. That’s not trivial in a space that was not tackled before. We often hear that the ordering is the prerogative of the business. But, how often does it happen for the business people to order the backlog only to find the technical tasks be pushed at the bottom of the list?
Over the years, we have experienced many times how seemingly unimportant issues unravel opportunities that would have otherwise be missed. In our team, there is no one that tells others what to do. Anyone can choose what they want to work on.
If we look at the core of it, a backlog holds a set of wishes. Some of those wishes come from some existing pains. Others might come from dreams of new possibilities. And they might come at various levels of abstractions.
When you set out to create a new kind of value, one that a customer cannot yet quite define, all you are left with is an inner compass. Only when you work as a team on a large enough problem, there are potentially many compasses, each working in a certain area.
Software is multi-dimensional. Flattening all those dimensions in a single list is rather limiting, and we choose to not do that.
Planning around goals. No estimations. No handovers
Because nobody tells anyone what to do, nobody can expect that someome does something specific either. In practice, they do listen to one another and they do coordinate.
To coordinate, we do employ some informal planning. Our planning happens in pairs or small groups, and might take minutes. And we might do it multiple times a day, or just a few times a week.
We never plan too specific tasks. We mostly agree on the goal and on how to describe a problem. And we think of the demo we want to show at the end.
We might engage in some short term estimations mostly to evaluate whether we want to go one way or another, but nothing else depends on that estimation. There are no promises. There are no handovers. We do not have to wait for others to finish something. If it’s not finished we might continue it, or we might go a different route.
We should also mention the issue of trust. When people choose when and how to work, we must trust them. We might disagree with the path or outcome, but all debates should happen from a trust perspective. Trust is essential and it goes both ways: When we trust the source of feedback, we are less likely to perceive it as personal and focus on improving the work.
Throwing code away
When we describe what we do, we hear often that given that there is no upfront coordination, it might happen that there will be overlap in what different people work on. In practice, it happens very rarely. In most cases, people naturally work together.
Nevertheless, it can happen that two people work on the same thing at the same time, and we are more than fine with it. That’s because we throw away most of the code we write. If we do not like the story we can tell with the code, we throw it away. Even if it works. It’s rather liberating, really.
Just consider the alternative: if you never throw away code that works, it means that you got it right the first time. That’s unlikely. We find that we need some 3–5 variations of the same thing to reach a reasonably satisfying solution.
So, having two competing solutions is actually positive because it means we can learn faster. Throwing code away is only a waste when you think of programming as a process of construction. Instead, we regard programming as capturing our ideas in a larger, coherent, but yet-to-be-defined puzzle. In this context multiple perspectives are essential and throwing code away is almost mandatory.
Trunk-based development. Continuous delivery
We do synchronize eventually. In code, directly on trunk. We do not use development branches or pull requests, and we do not require code reviews. We simply commit, integrate and deploy intensely.
We might review code during pairing, or we might do it after a commit while working on the code. When we do find something we do not like, we transform it into automatic detections either in the form of tests, when the problem is decomposable functionally, or of structural checks, when the problem is better suited for some static analysis. In other words, we use reviewing for exploratory purposes, but the regression checking is always the job of the machine.
From a technical perspective, this way of working enables continuous delivery. The largest cost in software development is integration and we want to see problems as soon as possible. This constraints how we work and naturally pushes us to modularize our work both internally and from a user perspective.
From an organizational perspective, a pull request that requires a review is a synchronization point. It often builds a queue and one’s work waits for someone else. We want to avoid relying on synchronization mechanisms that are not necessary.
And, there is another perspective to it. We look at the systems we construct not as something we implement, but something that expresses our understanding of the problem. Each of us has an individual understanding from our own perspective. Given the multi-dimensional nature of software, it is not reasonable to expect a complete understanding to fit in a single head. We need to externalize it and the code is the ultimate place in which we get to synchronize our perspectives. When a mismatch occurs, we see it as an opportunity to learn from and adjust our understanding. This synchronization mechanism is mandatory, and we want to get to this point in the fastest possible way.
Storytelling glues our development together. When we want to convince a colleague about a particular direction, we tell a story. When we design, we think of the story the system will tell. When we document our system, we make the system show the story. And we release only when we deem that the story is beautiful.
Stories come in many shapes. A document can tell a story. A blog post can tell a story. A comment to an issue can tell a story. A tweet can tell a story, too. Each medium enables different kinds of stories.
We utilize all of these, but we spend most of our time inside software systems. That is a medium that lends itself to new opportunities. Most of our stories are executable and visual and are created and delivered right in the development environment. We want our system to tell a story. And we want these stories to be told at all level of abstractions.
Let’s look at some examples. We start from a single object. In this case, we see a price object constructed by the code on the left being inspected through a custom view on the right. The view tells a story of how the object is formed in a way that a non-technical person can relate to.
Once we can show individual objects in interesting ways, we can weave these representations into larger narratives. For example, take a look below at an example of how we went about documenting the implementation of an algorithm: the environment allows us to explore the detection of a neighbor in a circular treemap through views specific for the different steps in the algorithm.
And here is another similar example documenting another algorithm. This time, we assemble the object views into a larger narrative presented in a live document.
We went through thousands of scenarios like these, and we learnt that every piece of our systems hides at least one exciting story, regardless of how technical and dry appears at first sight. But, these stories are more than just excitement. They are engineering tools, too, as they enable us to reason with ease about otherwise impenetrable problems.
Just consider this story of a bug, a highly technical low-level bug, about how when pressing certain keys on a real keyboard (bottom left) we get a wrong signal internally (top right). The video depicts rather literally how we approached the debugging of the problem. We built a whole tool just to be able to reproduce this problem in a way that was pleasant for our brain. This then allowed us to tackle the problem effectively. And then, perhaps unexpectedly, it ended up as a tweet and became a marketing tool, too.
A good story can make us both be productive and smile at the same time. It can be both an engineering tool and a marketing tool at the same time.
Storytelling is a powerful skill. Like any skill, it can be learnt. We make it a focus to train it, and we optimize all our work around it. By doing so, we find that most other things are secondary.
No “I am sorry”. Just “thank you”
We do have one rule though: we can only say “I am sorry” if we hurt someone, but we can say “thank you” as many times as we want.
We trust each other to do the best we can, and we assume mistakes are normal, be it in code or otherwise. As such, making a mistake should be associated with focusing on finding a solution, and not with being sorry for what could have been different.
At the same time, whenever we do get something we enjoy, we like to celebrate it. We noticed that a “thank you” does wonders, so we use that abundantly.
This article does not put forward a recipe. It also does not describe an ideal solution. This is merely our story with its specific caveats.
As we develop the tools we use, we are our first customers, and this puts us in the strong position of understanding the domain rather deeply.
We are not a large team. Although we were told before that this approach will not work beyond a handful of people, 11 people still does not constitute a large team. Then again, many larger companies are made up of teams around this size.
We work for the long term. We never promise a specific outcome until it’s done. As such, we do not have to run after promises. Others might not be in this position, although we do believe that one should avoid depending on promises, especially in our field and especially when the promises are not made by the team.
In any case, this approach worked for us and for our values. We wanted to have a resilient organization that can mold around the individual. We did not follow a rule book. We borrowed some pieces from others, such as working remotely or focusing on the team members first, but we wrote our book the way it worked for us.
Resilience is hard to judge in normal conditions. In fact, resilience comes at an apparent cost whose utility is not obvious in normal times. Resilience pays off when you meet an unusual event. The current crisis is such an event. We noticed that while there were many changes for the individuals, there was little change for the organization. And we learnt that even in these times, there needs not be a tradeoff between individual well-being and group productive work.