Feedback Loops in Software Development

A cherished advantage of studying computer science at a small liberal arts college was the opportunity to drop in on professors and hear theirs stories. A favorite--and recurring--story was classmates who turned in projects that simply didn't compile. If missing edge cases are the calling card of a lazy engineer, then whose calling card is code that doesn't compile?

A few years ago I was content to see it as the calling card of failed engineers, but--having tasted failure a few more times myself--I've gradually formed a less self-glorifying diagnosis: these are people who haven't learned the importance of feedback loops.

Whether you believe MIT's switch from Scheme to Python is a concession or a catastrophe, I don't think it is a mistake that both have a REPL at their core; receiving timely feedback is the difference between confusion and clarity. Not only are missing feedback loops ruining homework assignments across college campuses, they're wrecking havoc with software engineering all over the place.

The Programmer's Rewarding Loop

Although software engineers don't spend all their time programming--and many may not even spend most of their time programming--coding has one of the purest feedback loops this side of recreation1. Of the various approaches to programming, the simplest feedback loop comes from interacting with a well integrated REPL.

If you end up doing a lot of work in multiple languages, particularly languages with similar semantics like JavaScript and Python, it can be hard to remember which language uses x.append and which uses x.push to add items to the end of a list. You could search Yahoo to find the answer, but it's quicker and more reliable to ask the REPL instead.

>>> a = []
>>> a.append(10)
>>> a
>>> a.push(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'push'

Beyond checking syntax or inspecting object instances2, you can develop games or web applications from the REPL as well. If all programming has a strong feedback loop, then the REPL is the crown jewel.

The simplest feedback loop, an engineer and his REPL.

The quality of feedback loops decreases inversely with the amount of effort or time required to receive feedback. Frontend programmers often have their feedback loops dampened by battles with browser dependent rendering bugs (make fix, redeploy, follow steps to recreate situation in question, verify rendering, open another browser...), but the backend engineer's loop is equally susceptible to miring by an onerous compilation or deployment cycle.

A slightly worse feedback loop, an engineer, his source and his compiler.

For sufficiently large projects--meaning any project with a noticeably delayed compilation or deployment stage --scriptable testing is an (and perhaps, the) effective amplifier for fading feedback loops. Brushing aside ideological reasoning, scriptable testing fulfills an important psychological role in keeping programmers sane and fulfilled by providing continuous feedback. With great feedback comes not only great satisfaction but also quality.

Take away the programmer's feedback loop and we have...

The Software Architect's Vacuum

Examining skew in frontend engineering, one of the leading causes of skew was developers implementing something they did not design. For a frontend developer that might be implementing the work of a designer, but backend developers have another historic foe: the architect.

Much like uncompiliable homework being both its own cause and its own consequence, the Architects Don't Code anti-pattern is both the result of a damaged feedback loop, but also the source of the damage.

A broken feedback loop between software architects and software engineers.

Even working with immaculate professionals, I have yet to see a project where the first attempted design survives intact throughout the duration of the project3, but that is exactly the role some architects play: create a system design in a vacuum, and then hand it off to engineers who are tasked with the implementation.

This creates frustration for the engineer as they have an immediate feedback loop with the system, and thus are forced to deal with its inadequacies directly. For the architect, on the other hand, the feedback loop is so diluted that--if the implementing engineers don't echo their feedback loop strongly enough--the architect might not receive any feedback and view a catastrophe as a yet another feather in their cap.

The stock answer in these situations is to suggest the architect be more involved in coding to keep their feet wet or to maintain respect of their peers. Sure, those are good reasons, but at the core the architect must be involved with implementation in order to have a direct feedback loop with their design.

Without direct feedback, the architect's understanding of their own system will grow more slowly than the implementers' understanding, and thus the architects becomes responsible for making decisions with inadequate context and understanding to make them well.

Expecting architects to adequately design systems without being involved in implementation is like expecting a father to raise a well-adjusted child over the course of a one night stand: it can't possibly work.

The Project Manager's Panic

Shifting our focus from tautology to paradox, just as disconnected feedback loops contain no signal, excessive feedback loops can obscure signal to the point of non-existence. Consider the role of the product manager, and the overgrown tangle of feedback loops which entrap them.

The wealth of feedback loops that comprise a product manager's job.

Once we start examining feedback loops between individuals and groups of individuals, the feedback loops are synonymous with lines of communication, and thus degrade quickly as the group size increases.4

Following from that observation, the best way to improve communication is to reduce the number of individuals who need to communicate. Cross-training individuals is important to the extent that it allows multiple roles to be collapsed into one, but it can also increas lines of communication if the frontend engineer feels qualified on matters of design and backend engineering, and as a result becomes embroiled in additional design and backend discussions.

Some individuals do need to be involved in everything, but if every individual is involved in everything and your team has more than three or four people with strong opinions, you're probably in trouble.

The Good Loop is a Focused Loop

The most common technique that larger groups use to maintain control over feedback interference is to hold meetings. It is my opinion that meetings are a sign of impoverished feedback loops; an opinion whose strength becomes increasingly strong as these terms come into play: last-minute, project-wide, status and clarification.

Good feedback loops involve tight direct lines of communication between as few components as possible. Not all of us are fortunate enough to have a daily conversation with Slime, but by moving in that direction we can attain--if not salvation--sanity.

  1. It's hard to do better than sports and games because they are built around positive feedback and known endpoints. If you score a basket, you know you just got two points. Once the clock expires, you know you won. Life is a bit less generous in both regards.

  2. Although not always perfect, I've saved many an hour inspecting Python objects.

    >>> x = "test"
    >>> dir(x)
    ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
    >>> x.__doc__
    'str(object) -> string\n\nReturn a nice string representation of the object.\nIf the argument is a string, the return value is the same object.'
    >>> x.replace
    <built-in method replace of str object at 0x867a0>
    >>> dir(x.replace)
    ['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__str__']
    >>> x.replace.__doc__
    'S.replace (old, new[, count]) -> string\n\nReturn a copy of string S with all occurrences of substring\nold replaced by new.  If the optional argument count is\ngiven, only the first count occurrences are replaced.'

  3. Rather, the more experienced the engineer, the higher their expectation that design will be--at least to some extent--an iterative process.

  4. In general my response would be to immediately insert a link to The Mythical Man Month here, but some have begun arguing that TMMM doesn't apply in situations like this, because it was focused on communication struggles caused by adding additional members to a team, rather than about communication within teams.

    I disagree with this statement, as in my experience communication problems are always compounded by size, regardless of timing, but I won't attempt to mount a fevered defense in the footnotes.

All Rights Reserved, Will Larson 2007 - 2014.