Programming Plainly: Encouraging the Craft & Discipline of Software Engineering

Friends & Enemies of Effective Development

How do we deliver software with more consistency and confidence? With some helpful techniques and an eye towards continuous improvement, more effective development isn't beyond reach.

All our teams at Mavenlink care a great deal about effective development workflows. Since we work daily in a pair programming environment support for refining our process is never far away. Over the last several weeks I have talked with both Salt Lake City and San Francisco colleagues about ways to keep development flowing effectively. As I’ve reflected on these conversations there are three things I believe more consistently lead to effective development.

What Is Effective Development?

The definition of effective development can vary some from team to team. For me the most important definition though is based on output. Effective development is what leads to working code, and value for those who use what I build. Anything else may be meaningful, but it’s less impactful. So, the goal with all the advice to follow is delivery of working code that represents something of value.

Keep Your Commits Close

I really like small commits, and a number of my colleagues share my affinity. Small commits provide less chance for divergence or conflict when multiple pairs are working in the same area of the code. Small commits can lead to more frequent continuous integration runs, which can allow you to catch unintended consequences sooner. For me, I like my commits to be as small as they can possibly be. I want to change the least amount of code necessary to achieve a meaningful result. The result does not need to be a finished feature, but it should be a necessary step towards that end.

When I decide whether or not to commit some work I want to gauge a number of factors. First, when was my last commit? If it has been more than 30 minutes since I last committed code, I worry that I may be chasing a rabbit. This is an arbitrary measure; but, if another developer and I can not complete some unit of work in 30 minutes, there are more potentially bad reasons for that than good reasons. We may be chasing a rabbit and doing work that is not strictly necessary. Whether we are engaged in bike-shedding, premature abstraction, premature optimization, or even premature refactoring: we are delaying being able to deliver something of value that works.

The goal is to not delay in delivering working code that can provide value to the user. Sometimes the delay may be caused by real complexity, but even that can easily turn into churning, which I will address later. For me, I don’t want to churn, and I don’t want to do work before it is actually necessary; although I will readily admit that bike-shedding, abstraction, optimization, and refactoring are all fun in their own ways. So, try to keep you commits close. But doing this you can keep yourself more focussed on delivering working code and value to your users more frequently and incrementally.

Keep Your Tests Closer

I like TDD — whether empirically verifiable or not, TDD makes me feel like a better developer. It provides discipline to how I build software and that gives me greater confidence in the correctness of what I build. I also believe that TDD gives a better structure for delivering on the goal of effective development.

Tests, when written first, provide the chance to understand what we are building early on. It forces us to justify our assumptions by spelling them out and then writing our code to fulfill the requirements we identify. When developing I like to write as few tests as possible to establish the behavior I am working on and then writing just the code necessary to get those tests passing. I don’t refactor immediately, either. I want refactoring to emerge from as a complete a picture as can be had. So, I try to wait until refactoring become obvious improvements that “write themselves,” so to say.

I also do everything possible to avoid committing code that doesn’t pass all relevant tests, because that is neither working nor able to deliver value. Because tests allow me to gauge when I am done enough they also act as great cues for when I should commit. Tests are the closer companions to code that works and delivers value, so they are indispensable to my workflow when I am focussing on effective development.

Churn Is Never Your Friend

When talking about time between commits I mentioned the subject of churn. Churn is when progress is held up, or blocked, in a way that the developer(s) working on something can not see an obvious resolution to. Churn can happen due to lack of knowledge, skill, fatigue, or any of a myriad of factors. Regardless of the cause, churn represents a work stoppage, and resolution should be sought as promptly as possible.

The dangerous thing about churn is that it can sometimes be hard to diagnose. Sometimes I feel like I am on the cusp of figuring out a problem. But, in reality I have not made meaningful progress in hours. Unfortunately, pride is often a contributing factor to churn along with the misguided belief that asking for help will disrupt the rest of your team. The fact is that being stuck, and churning on the same issue without making actual progress is a more dangerous disruption than disturbing fellow team members. Every wasted moment churning is like building up pressure that will eventually explode into something far more disruptive.

Recognizing churn is not always easy, but it is always necessary. I like applying a similarly arbitrary measure to help detect churn as I use for knowing if I have not committed recently enough. If I spend more than 45 minutes without being able to describe what I have been working on as an accomplishment, then I consider myself to be churning. Once I have recognized churn breaking through becomes a team effort.

Since I regularly am pair programming, taking a few minutes with my pair to review the problem and what we think is blocking us can sometimes be enough to break the impasse. But, if that fails, I like to turn to my other team members, either in person or via Slack, and get their input. I start by explaining the problem as best I can and explaining what I think the solution ought to be. This tends to prompt some question and answers back and forth, and sometimes some show-and-tell with the code. Ninety-plus percent of the time this collaboration will do the trick. Thankfully, in the other less than ten percent of cases we have a few dozen more talented software developers whose brains we can pick. In this way the team becomes one’s best friend and churn is recognizable as the enemy you never want to let close to you.

Final Thoughts

Finding the right tools and practices to make development work effective is always worth the investment. For myself, I find that the practices described above help me deliver working code that brings value to users very consistently. One of the other contributing practices that I believe also leads to this consistency is the practice of pair programming. Working consistently with other talented practitioners provides opportunities to learn and grow that are unparalleled in solo development. At Mavenlink we also have a lot of other elements to our process, derived from Extreme Programming, that are highly valuable. But, I believe the practices outlined here can be readily ported into teams with processes, and structures that differ from ours while still moving in a more consistent and productive direction.

Subscribe to James Thompson

Sign up now to get access to the library of members-only issues.
Jamie Larson