Your app is an onion: Why software projects spiral out of control
You start with the best of intentions. You hire a developer to build out your startup idea. But almost every week, it feels like the project needs tweaking. Features start creeping in, and the scope slowly expands.
It’s as if the project has a life of its own, and is trying to destroy your life.
How did this happen? Did you hire a bad developer? Did you fumble the project management? Or was the idea just terrible all along?
Possible. But there is one core misunderstanding that often dooms a project from the start.
We assume that our product is defined by a set of features, written on a sheet of paper. Occasionally features get added; occasionally features get cut. But you can see the scope of the project at a glance.
This assumption is wrong.
Your project is not two dimensional, like a sheet of paper. It has depth.
Each feature can be peeled back to reveal layer after layer. In fact, if I were inclined towards creating clickbait titles, I’d say that your app is an onion. Let me show you what I mean. Let me tell you why, as you peel each layer back, your app makes you cry.
The library to end all libraries
Let’s look at something concrete. Pitch me an idea.
People often want books that nearby libraries don’t have. Other people in the area might. So this app lets people send out book requests, and people who have the book will respond. We make money off each transaction.
Genius.
I know, right? The feature list is straight forward:
Time to send the spec off to the developer and start thinking of killer startup names. kaBooki? lib.rari.ly? reddit?
Alright, hang on. Let’s take a closer look. Also, reddit is already a thing.
Let’s probe the idea with some questions and see where it takes us.
How do users pay each other? Do they just chuck cash at each other when they meet, or do they pay through the app?
Oh, they should pay by credit card through the app, so we can take a cut.
How do they actually request a book? I mean, what do they literally do on the app? Do they fill up an open-ended form with the book title and author?
Oh, hmm. They should search for their book and pick the one they want.
Okay, so we need a database of books.
I guess.
Actually, what is a book request? Do book sellers see a list of all requested books? Or do we send out notifications to sellers, like Uber or Thumbtack do?
That one. We send out requests to other people who have the book.
So, sellers need to populate the app with all the books they are selling?
Um, yes.
How does the book actually get across? Do users send it themselves, or do we handle the delivery?
We handle the delivery.
So you’ll need a system that tells you the books that need picking up and where they need to go. I imagine your drivers will want to see the shortest route that connects these, right?
Okay, no. How about this? Users mail the books to each other.
So we show the recipient's address to the sender? How do we handle shipping charges?
Actually, you know what. These users have to be near each other. They can just go pick it up, and they can make a new friend, okay?
So, we need to match users who are close by. How do they find each other? Does the app have a real-time map so they can arrange a meet-up?
Sure. Fine. Whatever.
Do we set the book’s price? Or do they negotiate the price themselves?
They talk it out and decide on a price.
How do they decide? Do we need to build chat into the app?
They can just call each other on the phone.
Alright, so do we want to verify that number with a SMS text message? What about… you get the point. We really could go on forever.
Please don’t.
Let’s have a look at what the feature list looks like now:
Every single one of those line items can be expanded multiple times. We just peeled the onion back one layer, and look what happened.
Now now, don’t cry.
What happened?
Let’s be clear about what the reason for the feature explosion is not.
This is not what is commonly known as feature creep.
That’s when new functionality keeps getting added to a product. If you look at our final list of features, each item exists to support the original pitch.
This was not because of technical considerations.
For example, what is the most suitable chat system architecture? What payment platform is easiest to integrate?
We are not talking about those here. A good developer can talk you through various technical options. But a developer can’t decide for you what features need to be built.
This was also not because of market considerations.
Do people want to use such a product? What incentivizes early users to sign on when there are few other users? Are users willing to pay for this service?
These questions have to do with validating your idea, identifying product-market fit, and pricing strategy. Again, these are important issues. But this is not why your feature list blew up.
Yes and it’s also not because of global warming, or the tooth fairy. Are you going to tell me the reason for the onion, or go through every single non-reason?
Oh, you’re no fun. Here’s what happened.
We fundamentally misunderstood how complexity works.
We thought each feature is as complex as the description we started with. Hey, maybe it takes time to write code or whatever it is engineers do, but the feature can be fully described with a few words framed in business terms. “User does x with y”. Right?
This view is wrong.
Software features are more like fractals
The closer you look, the more details make themselves apparent.
Onions are fractals?
No, I’ve switched metaphors. Keep up.
We started with a big picture description of what the feature is. However, when the app is built (by the developer) or used (by users or yourself), it is at a much smaller scale. It is done by going through the app step by step. At each step, you might encounter problems. To solve the problem, you may need to redesign that step, add new steps, or add entirely new features.
It’s like planning a journey from your home to your office. Looking at the map, the journey might look like a straight line.
When you get down to a smaller scale, you’ll see that your straight line doesn’t fit in the city grid. You encounter traffic stops, and elevation, and obstructions. Depending on whether the directions are for walking, biking, or driving, you need different routes.
Building software is much the same. The closer you look, the more detailed you realize your solution is going to have to be.
Great. Details all the way down. Just what I needed. Does this mean my product will never be finished?
Well, yes and no. The complexity showed up because we started with a feature description, which is usually an objective. We then zoom in and build every step of the solution to achieve that objective. The more we zoom in, the more complexity we will uncover.
The complexity isn’t the problem, though. The problem is the way we choose to uncover it. We hand the feature spec to the developer. Weeks later, we get the first version of the app and try it out. For the first time, we are walking through the software step by step, and we see massive holes. We write down our revision notes, and send it off to the developer to build version 2.
At the end of this whole process, after doing this a few times, we have a pretty good solution. The trouble is that the development cycle is really long. Each loop takes weeks or months.
While discovering the inherent complexity of your app is a necessity,
Going back and forth with a developer is an expensive and time-consuming way to discover this complexity.
What we want to do is this:
We want to be able to discover as much of this complexity before we write a line of code. We want to peel back the layers to discover a more robust feature list, and we want to do this in a quick and inexpensive way.
Instead of iterating on the app, we want to iterate on the spec.
We want to go from this process:
To this process:
To do this effectively, we need two things. One, we need a way to represent the feature list, so that it can be iterated on. Two, we need a way to interrogate this feature list effectively.
Let’s talk about how to do this.
Mapping out the features
We want to stop thinking of this in business terms and start thinking of this like a user. That is the level at which we will have to deal with the complexity of the software.
First, make a list of user objectives. What does the user want to achieve using your app?
Next, you need to map out the user flow. Walk through every step that a user takes to achieve each of your objectives. Record those steps.
What we are building is a map of how users flow through the app.
There are many ways to do this. You could just make a simple outline of each step taken. You could draw up mockups of each screen the user goes through. You could draw a flowchart, either on paper or using software.
Whichever you pick, make sure to prioritize speed. Unless you’re an expert at Photoshop, put it away and pick up pen and paper.
Make sure every screen is represented in your outline. For example, if a particular step goes through multiple screens in the app, split it up. We want to tease out more details, not less.
This process alone should start surfacing some of the complexity of your app. Now, we drill deeper.
Question everything
Asking questions like we did above can be quite open-ended, like we did above. It can be hard to know where to begin.
To start you off, I’ve put together a list of common questions that apply to most software. These questions are arranged into four broad categories that you can use as a framework when attacking your outline.
Look at each step of your outline, and ask yourself every question on this list. If the answers reveal new steps, add them to your outline.
Here’s the list:
User inputs
These questions are about information that goes from the user to the product.
- What information does the user input?
- Is this input open ended or interactive?
- Open ended examples: free-form text input, choosing an image to upload, recording a video.
- Interactive examples: text input into a search form that displays results to pick from, choosing an address on a map, picking from a predefined collection of options.
- Is there any input that would be considered invalid, and how does the app deal with it?
- Are there any passive inputs? Most common example: user location via GPS.
- What information does the product need from a new user? Is there a way for the user to change this later?
Information presented to the user
If the previous section was inputs, you can think of this one as outputs.
- What information is shown to the user on this screen?
- How is this information shown? Examples: text, images, maps, lists, graphs.
- Does the information need to be sorted in some way? Examples: recency, distance from user, relevance.
- Does the product send any active outward communications? Examples: emails, push notifications, SMS messages.
Interactions between the pieces
These questions are about how different pieces of the experience (users, the product, other services) interact with one another.
- Do users interact with each other? Examples: messaging, adding as friend, “liking” or tagging of other users’ content.
- Do the users interact with external services? Examples: payment services, shipping tracking, social login.
- Does the product interact with external services? Examples: location lookup services, weather APIs, social networks (“Your friend X just joined! Check out their profile!”).
Business owner functionality
Questions about what you, the business owner, need from the product.
- Do you need to be sent alerts or summaries, via email or some other medium?
- Do you need a specialized system for yourself or employees? Examples: driver app for your delivery staff, real-time order management interface for your kitchen staff.
- Do you need an interface for manually approving users/content?
- Do you need a content moderation system? A way for your admins to easily remove user content, disable user accounts.
That’s… a lot of questions.
Yep! And it’s really just the common ones that people often forget early in the process. There are many more that will be specific to your product, and that you’ll uncover over time.
Bringing it all together
This process of building an outline and interrogating it will tell you what’s missing. Repeat this process a few times, and you should have a pretty solid feature list that you are much more confident about.
You know, I was really hoping you had this one weird trick to fix my project. This sounds like a lot of work.
It really is. But this is work you would have done at some point in the process anyway.
Remember why we are doing this. Software features have hidden complexity that you will need to uncover no matter what. You just get to choose when you peel the onion.
Essentially, you have two choices:
- You can discover user objectives, build a mockup or flowchart, question your own assumptions, and peel back the layers like we did above. Or,
- You can get a developer to start building your app and uncover these issues with each version, one week at a time. After several months, you will discover the exact same things you could have figured out in a week with pencil and paper.
This is why software projects blow up and try to eat you alive.
So, go and discover your features. Build your outline and question your assumptions. Uncover the bulk of your problems quickly and cheaply.
Save yourself the stress and pain, release your product out into the world, and let it bloom.
Do onions even bloom? Your metaphors are stupid.
Oh touché.
If you found this article useful, you can help me out by sharing it! You can also subscribe to my mailing list to be sure not to miss my next article.