---
title: Solve Problems, not Strawmen
teaser: Avoid applying solutions in a vacuum.
tags: playbook
author: Mike Burns
published_on: 2016-12-19
---

It's important to remember that solutions make little sense without problems,
and to **avoid applying solutions in a vacuum**.

Applying solutions within a vacuum -- for example, reading about VPNs and then
deciding to implement one for your team, without having a strong driving
impetus -- has a few downsides. Skipping the problem statement makes it
difficult to measure whether the solution works or how to iterate on it. It
also runs the risk of decreasing team happiness when solutions are not tethered
to reality.

We avoid applying them unless needed in an effort to [reduce bureaucracy] and
process, so that developers can concentrate on developing, designers can
concentrate on designing, product owners can concentrate on prioritization, and
so on.

To round this out we'll present [some solutions that we apply to
problems][playbook], along with some of the problems that they attack. Our
choice of the word "attack" here is meaningful: these solutions, despite their
name, do not *solve* the problems; rather, they are part of a long-running
process of dealing with the problems.  They might solve them entirely; they
might need refinement, or they might not work at all.

[reduce bureaucracy]: https://medium.com/@yanismydj/introduce-process-only-as-a-last-resort-21bd25e53eb#.vcudw7b99
[playbook]: https://thoughtbot.com/playbook

## Acceptance Criteria

Acceptance criteria is one way to attack the following problems:

- Unrelated result: the ticket says one thing, the dev does another. If you
  have another person accepting the stories, this will lead to lost time as the
  dev and QA go back and forth on solutions. Without QA, this leads to an app
  that you don't recognize.
- The banana ticket: the developer knows how to implement the solution, but
  doesn't know [when to stop], leading to an infinite refactoring of the entire
  codebase under the guise of finishing this one ticket.

To implement [acceptance criteria], when writing tickets and stories for the
team, provide a detailed description of what the solution might look like -- a
description of when the story is finished and the ticket can be accepted by the
quality assurance team. "As an unconfirmed user, I cannot message anyone," is a
quick example, but they can get bigger and more descriptive.

[acceptance criteria]: http://nomad8.com/acceptance_criteria/
[when to stop]: http://blog.teamtreehouse.com/when-is-a-user-story-done-acceptance-criteria-definition-done

## Retrospectives

Retrospectives is one way to attack the following problems:

- Unresolved conflict: the team is unable to communicate effectively during
  conflict. Anger and resentment grow instead, buried and festering inside,
  manifesting as passive-aggressive code review comments, poor collaboration,
  low morale, people quitting, or an explosion of anger.
- Fights: instead of passive-aggressive, low-level frustration, the team could
  express constant anger. Code review ends in tears.

Scheduled, frequent, recurring retrospectives are a time for the team to
reflect on their happiness and internal relationship. These happen rain or
shine: it helps to practice communication in good times so that it becomes a
normal reflex in times of need. Some teams pair it with drinks at the end of a
workday; others do it mid-day as a normal part of the workday.

## Standups

Standup is one way to attack the following problems:

- Isolation: people feel lonely working on remote teams or siloed projects.
- Othering: people on different teams have inhuman expectations of anothers'
  team. This is expressed as increasing demands via faceless platforms like
  [chat] and ticket trackers, constant rejection of solutions, or warring
  teams.

A quick check-in once a day to catch everyone up on small details: what issue or
project people are working on, whether they are blocked, perhaps something
interesting that they learned, and general announcements. They are done standing
in an effort to [keep them short][keep them short] (more fit teams may want to
do them standing on one leg instead).

[keep them short]: https://www.researchgate.net/publication/232529574_The_effects_of_stand-up_and_sit-down_meeting_formats_on_meeting_outcomes
[chat]: https://medium.com/@chrisjbatts/actually-slack-really-sucks-625802f1420a#.qf3n7xjdn

## Ticket Tracker

A ticket tracker is one way to attack the following problems:

- Duplicated work: multiple people open code reviews, only to find that another
  review exists with that exact feature implemented. This kind of simple
  miscommunication can be exacerbated by large teams, [microservices],
  distributed teams, and other communication challenges.
- Hurry up and wait: the marketing department waits until the feature is
  shipped, and then hurries to advertise it (meanwhile they sat around
  waiting).
- Surprise changes: the support team first learns that the UI has changed when
  complaints roll in about the redesign; the CEO hears about the removal of her
  favorite feature during a Q&A session at a conference.

Ticket trackers are common, though often using different vocabulary. Trello
uses cards; Trajectory uses stories; Pivotal Tracker uses bugs, chores, and
stories.  Jira does all of that plus provides visibility into metrics -- some
of which are projections, and others that are correct.

[microservices]: http://basho.com/posts/technical/microservices-please-dont/

## Story or Jobs-To-Be-Done Format

Story format is one way to attack the following problems:

- Mysterious business: the developer will happily implement a feature, lacking
  the understanding of how it fits into the product or how it might be used.
  Long term this leads to a disconnect between the code and the product -- the
  domain-specific wording and language used throughout the app bears no
  relation to the reality that it must model -- causing frustration among the
  users and confusion when onboarding new developers.
- Unfollow-through: the developer implements the letter of the ticket, but not
  the spirit, leading to situations where the feature is done but nothing
  links to it; the JSON API functions but sends useless data; the user can
  receive the password reset email but Gmail marks it as spam.
- Inflexibility: when the dev runs into complications, she pushes through
  instead of re-evaluating for time sensitivity. This one solution is the only
  one, and no compromises are entertained, regardless of how long this takes
  and how it affects the user or the company.

The story format phrases tasks in terms that the end user cares about, with an
explanation for [why] the user might want the task done; similarly, the [jobs
story format] puts the user's context first and the [motivation] right in the
middle. This is all in contrast to the traditional task format, that focuses
only on what the developer must change in the code, with no explanation or
motivation.

[motivation]: http://www.kitchensoap.com/2014/11/14/the-infinite-hows-or-the-dangers-of-the-five-whys/
[jobs story format]: https://thoughtbot.com/blog/converting-to-jobs-stories
[why]: http://www.toyota-global.com/company/toyota_traditions/quality/mar_apr_2006.html

## Test-Driven Design

TDD is one way to attack the following problems:

- Blind fixes: the bug is fixed &hellip; or is it? No one is quite sure, but
  the new commit sure has a great description of how it could have fixed the
  bug.
- Runtime whimsy: return values go unchecked, from [`nil`][nil] to missing
  files to failed credit card payments, leading to errors at runtime.
- Fear of deployment: the development workflow is running efficiently until it
  comes time to actually merge the branch. Hesitation, followed by asking for
  review after review, followed by begging others for reviews because no one
  wants the responsibility of saying that it will work in production.

Test-driven design (TDD) uses programmatic tests to drive the design and
architecture of the codebase. An incidental side effect is that the major
codepaths are tested, including error flows. Running the test suite exercises
all parts of the application, finding regressions in paths where bugs have been
found and fixed.

[nil]: https://thoughtbot.com/blog/why-do-rubyists-test-so-completely

## Refactoring

Refactoring is one way to attack the following problems:

- Unworkable feature: adding the new feature requires its own separate app or a
  fragile connection through the database. What could be a simple button on a
  Web page that performs a common activity takes days, weeks, or months to
  implement.
- Hidden bug: you've traced the crash to one method, but that method was
  written by a developer from two generations of coworkers ago, is 127
  lines long, and the commit message was "it works". The twenty code paths,
  including liberal use of [`return`][return], obscure the source of the error.

Refactoring is the process of shuffling code around without adding any features
or fixing any bugs. It is often the first step to implementing a new feature or
bug fix, carving a more clear path through the system, as part of the
[red-green-refactor] workflow.

[red-green-refactor]: http://www.virtuouscode.com/2012/06/25/every-day-in-every-way/
[return]: https://docs.racket-lang.org/reference/cont.html#%28def._%28%28quote._~23~25kernel%29._call-with-escape-continuation%29%29

## Feature Flags

Feature flags is one way to attack the following problems:

- Market timing: the feature is implemented but the rest of the company still
  isn't ready for it. The support team needs to be trained on it, the
  promotional announcement needs to be sent out, or the CEO needs to be
  convinced that it's a good idea.
- Questionable code: an isolated chunk of code -- a new file system, for
  example -- is ready to be evaluated by willing and able participants, but
  is not ready for public consumption until all the initial bugs have been
  shaken out.

Feature flags refers to hiding parts of the app behind a toggle, only shown to
some specific users or only enabled by an admin. These feature flags typically
differ from A/B testing in that they're less about measurement and more about
hiding.

## Code Review

Code review is one way to attack the following problems:

- Siloed development: developers work in isolation on specific categories of
  projects -- one person on payment, another on API, a third on advertisement
  targeting -- but on the same codebase. Features change around them and cruft
  grows without any clear communication channel between devs.
- Poor code quality: the developers learn from blog posts and Web search
  results instead of from each other, furthering their isolation. Coding styles
  vary, and the same solution exists [multiple times] in the same repo.

Reviewing code is the [process] of reading a diff: comparing a new commit with
what exists in the system. Often there is a focus on maintainability,
consistency, or knowledge transfer. Since it typically works on a diff, there
are fewer considerations for big picture harmony.

[process]: http://www.geraldmweinberg.com/Site/Programming_Psychology.html
[multiple times]: http://lists.llvm.org/pipermail/llvm-dev/2016-October/106079.html

## Git Workflow

A consistent git workflow is one way to attack the following problems:

- Unexplainable code: you find a strange line of code, the test explains
  nothing, the commit message only says "initial commit", the ticket tracker
  was replaced twice since the project started, as was the project manager. Why
  is this solution the right one, and why does the mysterious test enforce it?
- Bus factor one: the developer works alone, deploys a JAR, and leaves no
  comments. Then she quits and the new dev is onboarded. I hope you enjoyed
  this short horror story.

Git provides enough plumbing to hang yourself. Maybe that's not the expression.
Regardless, there's no one way to use git, and multiple right ways. From branch
names to merge strategies to [commit message] content, it's possible to have
[a unified version control process].

[commit message]: https://thoughtbot.com/blog/5-useful-tips-for-a-better-commit-message
[a unified version control process]: https://github.com/thoughtbot/guides/tree/master/protocol/git

## Deployment Process

A deploy process is one way to attack the following problems:

- Expensive downtime: each second of downtime costs more in lost sales than
  each second of development time costs in salary.
- External failures: the deployment depends on a set of external services
  providing DNS, caching and content delivery, uptime monitoring, error
  reporting, and so on, each of which has their own failure modes.
- Regulations: strict compliance with the law requires that very few people
  have access to the database or the production servers.

Going from development to production takes a few steps, which means it can be
scripted. The runnable script can live near a human-readable script, separately
describing who can deploy, what steps to take when it fails, what to do about
downtime, and how to announce it. Combined, the program and documentation
around it make up the deployment process.

## Horizontal Scaling

Scaling is one way to attack the following problems:

- Concurrent users: the service is immediately more popular and needs to handle
  twice as many users as before. They don't necessarily need to use the full
  service, but at least need to have enough working to get their job done.
- Concurrent processing: the algorithm can be subdivided into smaller,
  independent problems, but each problem would take its own computer to solve.

Scaling horizontally presents as spinning up more servers to handle the load;
this is in comparison with vertical scaling, where the CPU is sped up or the
RAM increased. The new servers come up quickly ("instantly"), run the same
software, and can be reduced when needed.

## And more

This is just a tiny selection of the solutions that we have seen to real
problems over the years. Some are for fancy ideas -- feature flags and story
format are not common to every team -- and some are sacred cows, like TDD and
refactoring.

Your homework, dear reader, is to think of what problems you are solving and
what solutions are not carrying their weight. Let us know what you learn while
reflecting; we are always looking for [processes we can drop][playbook]!
