Going fast: developing software when requirements are ambiguous
by Tim Kallady, Code for Australia Fellow
Tim Kallady is the Founder and CEO of Piccard.ai, and one of the Code for Australia Fellows helping us to develop Ask Izzy Plus — the exciting evolution of Ask Izzy. This article originally appeared on the Code for Australia blog.
For the past four months, Sindhu, Mustafa and I have been doing a Fellowship with Infoxchange — a not-for-profit social enterprise that has been delivering technology for social justice for over 40 years. You can read more about our Fellowship here.
We have been tasked with building Ask Izzy Plus, a brand new website that helps social service workers and volunteers to find help for their clients. For example, a volunteer at a homeless shelter who is helping someone fleeing family violence could go on our site and search for family violence support services nearby.
A key challenge from an engineering perspective is finding the right balance between working fast, and building something robust and maintainable.
Some of the areas where this tradeoff exists include:
- Time spent planning vs time spent building
- Test coverage
- Code readability and structure
- Where and how we deploy
In our case we’ve tried to get the best of both worlds where we can, and prioritised speed over robustness.
When building a new product with a lean mindset, the goal should be to find product-market fit as soon as possible. This means prioritising activities that maximise the rate of learning and iteration. There’s no point spending ages gold plating your code if it’s not actually the product your customers want and need.
A few ways this mindset has played out for our project are listed below.
Test coverage
Some developers think of comprehensive testing as a non-negotiable aspect of writing code, and that skimping on tests is lazy and bad practice. However, there’s an alternative (slightly controversial) opinion that under some circumstances, you don’t need Test Driven Development or even many tests at all.
When building a brand new product with lean startup methodology, the cost of writing tests is higher (because they need to be re-written more often as the product changes) and the benefits are lower (fewer users means lesser consequences if a minor bug makes it through). This calls for a slightly different approach to testing.
Firstly, we focus on the core functionality of the product by prioritising solid end-to-end tests. This gives us peace of mind that the core functionality of the website is working, which is the most important thing. Only a small number of tests are needed to cover most things a typical user might want to do. These tests are written after the features are implemented because this is faster and requirements are constantly changing.
For visual/user interface (UI) testing, we use Storybook (with Chromatic), which has the simultaneous benefits of generating a design system for the user experience (UX) team and automatically detecting visual changes in the UI.
Code readability and structure
There is a difference between writing clear and readable code (a.k.a. writing good code) and obsessing over details that don’t really matter. Sure, it would be nice to have a perfectly consistent code base with strict adherence to a style guide, and this is important in some circumstances, but when speed is the priority, this is hardly a good use of time.
We get by with the "prettier" VSCode plugin to format our JavaScript, which does most of the formatting work for us automatically. We might have some minor inconsistencies with naming conventions and folder structures, but these are easily fixed prior to handing over the finished product.
Planning vs building
The team working on the original Ask Izzy work on a six-week iteration cycle, with two weeks planning and four weeks building. In contrast, for our work on Ask Izzy Plus, we’ve adopted a shorter three-week sprint cycle, with detailed feature planning occurring during a single sprint planning session, and higher-level planning occurring on a more ad-hoc basis as we feel it is needed.
Working in this way has forced us to make tough product decisions on the fly with limited information, and can be more stressful because there is not a well-defined roadmap. It also runs the risk of having us build the wrong thing, because certain assumptions were wrong.
The upside, however, is that we can progress at a very fast pace, which means we get the product into the hands of users sooner and start validating the assumptions we made in planning.
There’s a fine line between just enough planning and not enough planning, and we’re still working out the best way for us, but our focus on building has enabled us to make great progress and ship a working minimum viable product (MVP) much sooner than we would have otherwise.
Deployment
We have chosen to use Platform as a Service wherever possible to greatly reduce time spent on DevOps. Platform as a Service (e.g. Heroku, AWS Elastic Beanstalk, Azure Container Instances) and Database as a Service remove the need to configure servers, and allow deployment of a new app in minutes, not days. They can be scaled up with the click of a button and are typically either free or very inexpensive at small scale.
There are two main downsides to Platform as a Service: they typically cost more at scale than self-managed infrastructure, and there is less control over server configuration. These issues may mean that it will be necessary to transfer to self-managed infrastructure at a later stage. In the case of our project, we need to transition to on-premises hosting at some stage.
Fortunately, because we are using Docker, the process of moving our production servers will be relatively straightforward and painless.
Final thoughts
When developing new software, there are many decisions to be made that come down to a trade-off between more work upfront and more work later. In most cases the conventional wisdom holds true: it’s better to spend time upfront to save a lot of headaches later. With a new product, the cost of upfront work is higher (it delays launching and receiving feedback from users) and the cost of rework is less important (there may not even be a product to rework!) This calls for careful consideration of the costs and benefits when making decisions about how we build the product, and sometimes using approaches that might not be considered "best practice", but will get the right product built faster.