The Agile Architect
Finding the 'Simple' in Agile
Beauty may be in the eye of the beholder, but simplicity takes hard work to create. Our Agile Architect gives his take on "simple."
- By Mark J. Balbes, Ph.D.
In my last column, I talked about misinterpretations of the idea of simple, giving several examples of instances where people thought they were creating a simple solution to a problem but were, in fact, not. This time, I'd like to discuss my own ideas on simplicity.
What Is Simple?
The Agile Manifesto says "Simplicity--the art of maximizing the amount
of work not done--is essential."
This is an amazingly simple statement with many implications. What is the work we are trying not to do? How do we maintain a high quality system by not doing things? If I do more work now to save work later, is that agile?
Simple Takes Time
When solving a problem in an agile way, we try to break it down into small pieces, e.g. stories, and tackle them one by one. It comes back to the old joke, "How do you eat an elephant? One bite at a time."
When building a solution to a problem bit by bit, the result becomes increasingly complex as we add more bites of the elephant. By the time we've eaten the whole elephant, we often end up with a very complex solution that solves the complete problem. This is by necessity since we learn more about the problem space and the solution space with each bite. It's tempting to stop at this point and declare victory. By doing this, we lose the opportunity to simplify our solution at the very time when we know the most about the problem.
Simple Is Understandable
Simple is defined through the eye of the developer who has to read and understand your code.
I will often find myself solving a technical problem, creating what I think is a simple solution, and then finding out that no one else on the team understands what the heck I did or how they should use or modify it. This has taught me to spend time looking at my code through the eyes of someone else. Am I using the right metaphors and clear terminology? Are my implementation details bleeding through into my interface? Are my tests providing good examples? If the code needs to be modified or extended, is it clear how that should be done? Of course, I can't know for sure that my code is easy to understand until someone else tries. In the agile world with pair programming, this happens every time I switch pairs.
As a non-technical example, imagine putting together a Kanban board for your team. It's easy to set up the queues that define the workflow. As the person that built the board, you know what your intentions are, but how do other people learn how to work the board? Is it self-describing? Is it intuitive? When a new person joins the team in two months, how are they going to know what to do? Simple is making a board that anyone can walk up to and know how it works without a lot of training and hand-holding.
Simple Is Extensible
Extensibility allows you to solve today's problem today and leave tomorrow's problems for another time. My personal favorites to guarantee extensibility in a technical solution are:
- Obey the Single Responsibility Principle
- Obey the Open-Closed Principle
- Obey the Dependency Inversion Principle
Test-driven development will pretty much guarantee that your code will follow these principles.
Simple Is Solving the Right Problem
There are times when we, as human beings, get so wrapped up in the problem we are trying to solve that we lose perspective and do not realize that we are solving the wrong problem. By looking at the problem from a different perspective or by discussing it with others, we have an opportunity to recognize this.
Let me give you a non-technical example: I was recently talking to another team lead about the problem his team had with retrospectives. They weren't effective and were wasting the team's time without providing benefit. The problem the other team lead was trying to solve was how to minimize wasted time. His obvious solution was to cut down on time spent in retrospectives, either by shortening the meeting or having it less often.
He is, of course, solving the wrong problem, which leads him to the wrong conclusion. The real problem for the team is that they are not reflecting effectively on the workings of their team and are losing out on the opportunity to improve. The problem to solve, therefore, is how to make the retrospectives more effective. This solution is harder to implement. It's easy to cancel a meeting. It can be very hard to figure out how to do something better.
What does this have to do with simplicity? Canceling or minimizing time spent in the retrospective robs the team of the opportunity to improve. As problems arise, instead of being able to address them effectively, the team is now without a mechanism to deal with them. Therefore, the problems accrue creating even more problems. This simplistic, obvious, but wrong conclusion leads the team down a path to increased problems and increased complexity. By solving the right problem "How can we make the retrospective more effective", the solutions lead the team to being more effective at solving problems, thus increasing simplicity in future team interactions.
Simple Is Solving Only the Right Problem
I have a phrase I like to use when describing how to write software in an agile way, "Production quality. Limited scope." Simplicity is often lost in a solution by over-complication due to trying to solve too many problems, many of which are not in scope or are not actually problems.
Here's a technical example, albeit somewhat trivial. We are building an iPhone app that has a requirement to put 35,000 entries in a list. During our estimation meeting, we included a lot of hours for performance optimization. When we started implementing the story, the team began a long discussion about how to feed new entries to the list as the user scrolls, knowing that at some point there will be too many entries and performance will start to degrade. One of the team members asked how they knew this would be true for 35,000 entries. So the team challenged themselves to find out in a spike. It turns out that the list handles 35,000 entries just fine. Our complicated, big story turned into a trivial one because we only solved the problem "How do you put 35,000 entries in a list," not "How do you put enough entries in a list such that performance degrades and then manually optimize performance?"
Simple Is Breaking One Big Problem into Independent, Smaller Problems
When looking at a large, intractable problem, I often find simplicity by breaking it down into smaller, orthogonal problems. Orthogonal meaning problems that may combine to create a larger effect but aren't really dependent on one another. (Sorry, it's the physicist in me.) This gives me the opportunity to solve each smaller problem independently with a simple solution for each rather than needing to find one large, complex, all-encompassing solution.
For technical problems, design patterns often offer viable ways to break up a large problem. For example, suppose you are trying to build a drawing program that offers a lot of different tools to manipulate your drawing. Trying to code all of the different possibilities for what can happen when the user drags the mouse on the image is daunting. Are they drawing something new? Are they moving an existing object? Are they resizing something? The list of possibilities goes on. However, the State (or Strategy) design pattern breaks this almost intractable problem into a series of small, independent problems that are easy to solve one by one, e.g. the act of the user choosing a particular drawing tool puts the system into a single well-defined State where each mouse gesture is mapped to one specific capability.
I confess that this column was not easy to write. I've edited, modified, deleted and rewritten it multiple times as I've tried to distill out my own thoughts on simplicity.
If you've understood and enjoyed this column, it proves my point that simple is hard. And if you didn't, well, I guess it proves it just that much more.