Lessons learned from building an MVP
We needed to quickly build a prototype of a mobile app and a website, get them in front of our users and get feedback. Sound familiar? Yep, it’s MVP time.
CargoMate is a port call optimisation platform that enables container ship owners to have real-time visibility of their fleet in port. Using machine learning, we predict when a ship is ready to depart and we let voyage planners know when their ship is ready to leave. The app we built is used by crew onboard containerships to help them track cargo operations in port. Here are a couple of lessons we learned during our first year of building.
As we were giving our users a new and–by our own account–better method of doing their day to day work, it was important to us that they had a good experience using our product. Fortunately, our users have been very helpful and have provided great feedback.
The first thing we did was make it clear to our users that it’s ok if the app crashes or if they encounter issues. No, it’s not your fault, you haven’t done anything wrong. No one will chastise you. Reid Hoffman’s famed saying “If you’re not embarrassed by your first product you’ve waited too long to launch” is a truism in startup circles. By making it clear to our users that we need to build quick enough that they may encounter issues, they’re able to provide honest feedback on what needs improving. On the plus side, they get to see their feedback implemented quickly and get to be a part of making the product they’re using better.
At the same time, we couldn’t move too fast as too many issues with the app would likely cause the crew to put the mobile device aside–if not overboard–and pick up their trusty pen and paper again. Lesson: Build your product together with your users. The end result will be better than doing so in isolation and your users will get a feeling of ownership, which will help with adoption.
Speed or quality
There’s a constant trade-off between speed and quality. No need to spend a lot of time building a feature only to realise that you were building something users didn’t need and discarding it. Better to spend less time building something coarse, get it in front of users and see if you’re moving in the right direction. Feedback is crucial, especially as our users have plenty of domain knowledge with many edge cases.
Our first version was complex and had advanced features. My Co-founder, Chris, tested it onboard multiple container ships. He has the domain expertise required and it worked great. The mistake we made was assuming that all our users would have the same alacrity he had. Turns out Chris was willing to put in the effort and time the complexity of the app required. Our real users weren’t as interested; they found it cumbersome and time-consuming and stopped using it. Back to the drawing board. A weekend later we had redesigned and simplified how users input data. Our users then eagerly used the app as they understood from a glance how things worked.
You won’t be able to create a beautiful user interface or a great user experience if you don’t know what features will be in the app. That’s part of the fun of building a prototype. As you add more features, the UI gets more bloated. That’s fine. The initial prototype is to make sure you’re building the right thing. Once you know that you’re on the right track, redesigning the UI to look better is a satisfying endeavour. Until then, just keep iterating. Lesson: Simplify. Simplify. Simplify.
Communication is hard
But it can be alleviated by being clear about your intentions. Telling our users beforehand that we’re doing a trial and that the app may crash prepares them for running into issues. As a user, it’s easy to believe that it was your fault that something went wrong and sweeping a problem under the rug is an easier option than letting someone know their app crashed. Fortunately, by managing our users’ expectations they’ve been great at letting us know what’s gone wrong, how features could be improved and what they feel is missing. We also had software in place that let us replay a user session locally on our development machines. When something went wrong in the app, a crash log was created with the necessary information required to replay all user actions in the safety of our development environment. This saved us from trying to discern what went wrong based on user feedback in the form of an email–a difficult thing to pull off on the best of days. Lesson: manage expectations and do yourself a favour by helping users explain what happened when things go wrong.
Everything as a service
As a part of going through Entrepreneur First we got a whole bunch of AWS credits. This has been crucial to getting up and running quickly. When we were building our first prototype, there wasn’t time to build a proper backend so we had the app communicate directly with AWS. DynamoDB lets us store all the data we logged, control who can access what using IAM and manage authentication using Cognito. The wheel has already been invented many times over and it’s quite round by now. By using software as a service we were able to focus on building a great product for our users instead of worrying about managing infrastructure. Until you have the resources and team required to create custom software, reuse what’s already available.
That said, there’s a difference between creating a prototype and building a robust platform. After building the first prototype we hired an exceptional engineer as our Head of Engineering. He has built a backend that lets us move a lot of our business logic there instead of having it in the app. This helps us immensely as pushing updates to an AWS EC2 instance is much easier than pushing an app to a device, with intermittent connectivity, on the other side of the globe. Having a proper backend also lets us use machine learning to estimate, in real-time, when ships are ready to depart from a port. Lesson: Reuse and move quickly.
Automate your build pipeline
I’ve worked on projects where development is going fine, until someone pipes up “Two weeks until the deadline, should we start thinking about how we deploy this?”. Organising a deployment pipeline from the start of the project helps you do less manual work during development and avoids one more nasty surprise at the end of it.
We’re using CircleCI for continuous integration, Travis would likely have worked equally well. When code is merged into our master branch of our app’s GitHub repo, CircleCI builds a debug apk. It then waits for one of us to tell it whether or not to build a release apk. We will soon have our test suites run automatically to catch any bugs that have slipped through. Android APK lets you build a debug apk but we found that running it is extremely slow. So instead we build a release version that pushes data to our development database. We lose a bit of debugging capability when something goes wrong, in exchange for a better user experience, but crash logs are created so we can easily reproduce the issue. Lesson: logging when things go awry and spending time on error handling from the start will save you time in the long run.
A word on quality
Creating a great product isn’t just about good UI and UX, it also requires you to write high quality code. That means maintaining and adding more features doesn’t break other parts of the app and developers don’t pull their hair out while building.
Never merge your own code. I’ve done it more than a few times and the price of being wrong is high. What you believe to be a quick fix likely isn’t. Have one of your team members look over your code and see if they can catch any mistakes. Make sure the focus of your code reviews isn’t only about pointing out your team members’ mistakes. Code reviews are also a great source of learning about the product you’re building and ways of writing code.
Beware of Broken window theory. Once you start allowing quick fixes and “I’ll just add this now but will fix it as soon as I have some time”, the quality of your codebase will start to deteriorate. The next developer looking at the code sees that someone before them added a quick fix or broke the agreed upon code convention and will feel that it’s alright for them to do so as well. Before you know it, you have software that’s hard to maintain as you can’t do changes without unintended side effects. Lesson: Fast is sometimes slow.
If you agree with your team that it’s alright to leave “TODOs” in your code, make sure to have a plan to handle them. They’ll otherwise grow stale faster than the standard internal company wiki page.
That said, there’s a difference between the quality needed for software used in e.g. financial products and software written to prototype an app. One needs to work every time and the other needs to work most of the time. If you spend too much time and effort creating a robust prototype, you won’t be able to build quickly enough.
Once you know what your users want, start working on a long-term version of your product. Take your time, manage tech debt continuously and enforce best practices. Lesson: Slow is sometimes fast.
Dennis Sandmark – CTO CargoMate
If you enjoyed this post you should know that CargoMate is hiring. We’re looking for software engineers and statisticians who are passionate about their craft and want to tackle big issues in an important industry. Check out our live roles by clicking here.
Co-founder and CTO of CargoMate. Full Stack Software Engineer. Previously external advisor for clients like McKinsey and Spotify.