Some Process Thoughts July 7, 2015
In an Era of Distributed Teams
Over the last bunch of years, I've worked with a fair number of distributed teams, both doing open source and contracting. Over this time, I haven't ever been in the same room as half the people I've worked with. I've used a ton of tools from AIM to Slack.
Here are a bunch of thoughts on process.
Almost Always in the Light of Day
Almost all team communication should be "in the light of day"... available for the whole team to see... and now with near infinite storage and excellent search, almost all discussions should happen where they are visible to the whole team and where there's a group-visible historic record. The Apache Foundation does a great job of this.
The only private communications should be related to either reviews or dealing with personal issues and negative feedback.
Things that should be visible to every team member:
- Design discussions
- "Stupid Questions"
- Off-topic discussions
- Scheduling discussions
- Customer feedback discussions (especially negative customer feedback)
- "Stupid Questions"
- Bug reports
- Team members joining/leaving
- Post Mortems
Note, "Stupid Questions" is on the list twice. They are important. It's important to create an environment where people are encouraged to ask questions and discuss issues.
Discussing the above privately means that somebody is out of the loop... somebody is lacking information that may be valuable to them. That somebody may be a current team member. That somebody may be a future team member. That somebody may be a future version of you.
Also, I use the word "team" loosely. Sometimes team means "management team." Sometimes team means developers. Sometimes team includes customers and clients.
There's also an important part of having communications preserved... it reminds people that they should always be on good behavior because their behavior will always be visible.
If you're on a team and someone tries to communicate directly, move the discussion to a public forum. Yes, the person may be embarrassed that they will "look stupid" in front of the team. This is a great teaching moment for everyone. If people can feel comfortable asking "stupid questions" it helps the team bond... because at least one other team member has the same question.
Tools like Slack and Flowdock are great for discussions.
It's also important to have a reasonably up-to-date set of documentation for
every project. I like to keep documentation in the
/info directory at the
top of a projects. I used to keep it in a
/doc directory, but that
seems to conflict with Docker related stuff.
Things that should always be in the
- A "Getting Started" guide for new project members
- A dev-ops guide with a special section on "oh crap, it's 3am and things are down, what should I check?"
- Key architecture decisions... this is likely copied/pasted from whatever discussion tool the team uses
- A list of key technologies used and why they were chosen. This may seem stupid, but coming onto a project that's using Struts may lead to a huge "WTF???!!?" but seeing the decision in the 2004 context can change things.
- A database schema or E/R diagram
- If the project is in a dynamic language, so pointers to key data structures
Basically, it should take no more than a day for a new developer to get up and running on a project, understand the key decisions about the project, and understand where to look for more information.
Ideally, a developer will spend the last 15-20 minutes of his/her workday writing documentation for the work he/she did that day.
Whether it's asking a question of co-workers, opening a ticket, or explaining things to a customer/user, always be reproducible.
What does this mean? Explain the steps it takes to reproduce an issue.
- What is the system configuration? What commands did you type or what URL did you browse to?
- What was the system's behavior (browser output, browser console, terminal output, etc.)
- What did you expect the system's behavior to be?
Be ritualistic about the above formula. Why?
- Give the person you're asking to address the issue every tool that person needs to address the issue you've raised.
- Give future versions of the reader (including you) the tools and context to understand the issue.
- The act of putting together all the information often leads the author to the answer.
There are two corollaries:
- When possible, ask questions by showing data (e.g., JSON) or data structures/types rather than using English.
- When possible, create a Git branch that demonstrates an alternative approach and use that actual code as a tool for discussion.
Keeping a schedule
Software projects are complex interactions among many skilled people. In most cases, people rely on each other. The schedule is a reflection of that reliance.
And schedules are always fluid.
The first key thing about a schedule is building it based on business goals. Except for pure hobby projects, there are people who have business needs and money associated with software projects. So, figure out what the business drivers are and schedule based on those business drivers.
The second thing about a schedule is to show each team and each team member where the other team members are relying on them. Use the social component of this reliance to help motivate the team to communicate early if there's going to be trouble with a deadline. Get feedback on a schedule. Keep the feedback loop open so that everyone knows as early as possible about a slip.
Third, keep the cycles short. One to three week cycles are short enough that most people can figure out if the particular task can fit into the particular chunk of time.
Fourth, reward early notifications of slip. The earlier a team stands up and says, "we're not going to make X," or, "we're stuggling with Y, which we didn't anticipate, can we have some help?" the more time the other teams have to correct and work around things. On the other hand, people who notify me of schedule slips on the due date (or after the due date) typically don't work for me for very long.
Fifth, done means done. Code should be completed 25% before the due date. That gives time for testing and feedback and seeing if it works on other machines, etc. Checking code in at 11:59pm on the due date that "works on my machine" but lacks documentation and tests is not meeting the deadline. Done means done. Done means it works in a wide variety of cases. Done means tests. Done means documentation and code comments. Done means feedback on visuals and UI/UX. Done means getting feedback from other teams on APIs and data structures. Done means you're proud of it... not because you worked hard, but because the code is beautiful.
A corollary is that when possible, test spikes and exploratory iterations, and "throwing stuff against the wall" tickets all need to be explicitly scheduled. But, unless a ticket is marked as exploratory, then done means done.
Have open discussions about decisions on the project. Solicit input. Encourage the quiet team members to chime in. Work through issues.
Then make a strategic decision and stick to it. Keep the tactics fluid. What's a tactic? Adding/removing a field from a data structure. What's strategic? Using a messaging/event based design.
Don't revisit strategic decisions unless there's data that was available at the time the decision was made that materially impacts the quality of the decision.
Know the time horizon of your decision. For example, if you're a 2 person start-up, your decisions are all about surviving until you've got enough users/revenue to go to the next step. If you're a 2 person start-up, you don't have a 5 year, Twitter scale set of decisions to make.
And bugs... every piece of software has bugs. But not all bugs are equal.
A bug that hits a user during initial use will color the user's perception of the overall system. For example, I saw a demo of a system that had a bug in the initial login screen... so the first thing a user saw was a stack trace... this bug doomed the project even though it only took 30 seconds to fix.
The earlier a cycle is finished, the better chance a developer/team has to write tests, do run-throughs, perform smoke tests, give demos to other team members, etc. This keeps the bugs to a minimum because the common use patterns are run through over and over.
In my book "unboxing" bugs are complete failures. The first set of experiences a user/customer has with the system should be excellent. Weight development and QA on these experiences.
As much as possible, implementations should be hidden behind easy to comprehend interfaces. Maybe the interface is a Java Interface. Maybe it's a REST API and a JSON schema. And yes, maybe the interface is a thin veneer over the functionality. But the interfaces are important.
The interfaces are the things that different teams can discuss. The implementation is up to a team and should not be the subject of comment from outside that team.
Interfaces are super-useful because they can be mocked and tested. Early interface design means that each team can code against mocked implementations on the interfaces and get their parts of the system working.
Pieces of the system should be isolated and should be able to run in isolation. This means easier development because a developer doesn't have to run the whole system... just mocks of the stuff they interact with. This means parts of the system can be upgraded, swapped out, etc. without breaking the rest of the system.
From the Front End, Back
I'm a back-end developer. I tend to design and develop from the persistence layer forward. This is bad.
Design from the ui back. Test the UI with users as early as possible. Make as much run in the browser/mobile device by mocking REST calls as early as possible.
We are building stuff for users. Build and test with users as early and as often as you can.
Facts vs. Opinions
Facts are things that can be measured. It's perfectly fine to be direct about things that can be measured. Failing to meet a deadline is a fact that can be measured. Having a lot of user-reported defects in code is a fact that can be measured. Performance of code against a benchmark is a fact that can be measured. Not including tests with a checkin is a fact that can be measured.
Being direct and even confrontational about facts is okay. "You failed to make a deadline," "You're code is not fast enough as measured by this benchmark," "There's a memory leak in your code, here's the retention graph," "This code is not complete because there are no tests," are all reasonable things to say.
Then there are opinions. Like, "this design is weak" or "this UI is ugly" or "that's impossible."
Know the difference between fact and opinion.
Be direct about facts. Be direct about how the facts impact the project and the team. For example, "you failed to deliver your code on time and failed to notify the rest of the team that you were going to be late. This means that other people have to do extra work to compensate and that's unacceptable."
Be a lot more kind about opinions: "I find that I have to click through 8 steps to enter information, and that seems like a lot of steps. Is there a way to reduce it to 5 steps?" This can lead to a constructive discussion about improvement.
It doesn't matter how good someone is, if they are an asshole, they have to go. Assholes spout anger on social media. Assholes make baseless pronouncements about other team members, third parties, code, whatever. Being direct and critical is not being an asshole (see Facts.)
If a team member tries to assert authority or domination over other team members when there's no reporting hierarchy, fire them. They will cause friction and resentment on the team.
The asshole might engage in a series of little micro-aggressions like talking over people in meetings or summarily dismissing ideas during the pre-decision exploration. The asshole will engage in "look at me" behavior, often at the expense of other team members. The asshole might expect a lot of slack from the team but give little in return.
There is almost no case that a disruptive team member is worth keeping around. Identify them and get rid of them.
Discuss things in the light of day. Be open about schedules and business drivers and reward those that keep to the schedule or notify everyone that there's going to be a slip. Make strategic decisions and stick to them. Focus on interfaces and isolate parts of the system. Work from the user back. Deal with facts directly, but opinions in a constructive manner. Fire assholes.
And this is all that much more important when you're not looking into the eyes of your co-workers.