In June this year, we had to make a decision around the life of our Innovations demonstration app, based on our fictional bank, Genius. The app had served us very well, having been presented at numerous FinTech conferences and to many, many clients over its lifetime. Over time, UX concepts have changed and progressed, and we felt our app was no longer representing the vision that we wanted to share with the FinTech world. We also had some legacy technical decisions that were hampering development and wanted to consider how we might improve future development effort.
The Innovations team has a relatively unique situation, where allocated developer time is very variable, and the finished code doesn’t generally get released into production. Developers can be allocated to the team for short periods of time. This makes it important for developers to be acquainted with the code as quickly as possible. As such, it benefits us to use standard architectures and patterns so that deadlines can be met, and the level of technical debt does not get out of control.
Complexity needs to be carefully considered. We need to be sure adding new technologies or techniques do not introduce accidental complexity, or even more importantly – create difficulties in debugging. Complexity does inevitably enter our code base, however this should only be in support of creating a compelling experience for end users.
Our technical aim was to create a compelling mobile experience that followed as many predefined standards as possible, utilising software best practices. Guidelines, tools and concepts have been provided by both Apple and the community – we should make the most of it.
So with all this in mind, what can we do to make Genius 5.0 relatively simple to contribute to, as well as maintain?
We decided upon using standard iOS development tools. Xcode for an IDE, Cocoapods for package management, Fastlane for making builds, TeamCity for managing builds, HockeyApp for distribution. Azure hosts our platform code.
Given we follow the GitFlow branching strategy, it makes setting up builds pretty simple. TeamCity is listening for new feature/* and release/* branches, as well as monitoring for changes on these and the develop branch.
All of these are industry standard, and should be pretty familiar to iOS devs. Also, the brilliance of them is that for the most part they Just Work™. We can create repeatable builds and spin up environments with a minimum of effort.
To Swift or Not To Swift?
In the land of iOS, we have multiple language choices. Being a team that is supposed to be forward-looking and innovative, one would expect the choice to be easy. However, weighing up the variety of developer experience levels that work with us, and the effort required to learn a new language, Objective-C won this battle.
On the positive side, we are not married to this and were able to pivot when necessary. For example, a third-party Swift-based library required us to implement a delegate, however this hadn’t been exposed to Objective-C. This gave us two options – introduce Swift for part of a feature, or modify the library. Introducing Swift made sense, and solved our problem in a pragmatic way.
Following the MVC design pattern means we are in line with existing Apple recommended practices, which means most developers should have had exposure to this. We have a thin service layer, which abstracts away any service request complications with the help of AFNetworking (another standard tool).
Trying to avoid software anti-patterns (reinventing the square wheel for example), we try to reuse third-party libraries for problems already solved by the community. Obviously we have to do this carefully, as if the innovation was to end up in production, it would have to withstand a rigorous security review.
Just in Time Development
Innovations has to follow a tight schedule, either trying to get the latest and greatest ready to show a client, or present at a FinTech event.
We have adopted lean software principles and build our innovations in a just in time fashion. This allows us to implement functionality as fast as possible, and then improve or expand it as required.
Accepting this at the time of development, results in less effort being expended over-engineering, and trying to solve every potential problem, before it exists. It is understood that the code will change over time, and we can iterate over our solution.
Take, as an example, a service request. When you create a request for the first time, you might hardcode the body and headers as strings. No one else uses it, and it is the only request made in the application. While this may seem like a contrived example, consider the effort it would require to define all the different request scenarios, and design a component that would suit. As soon as a second request is required, we can then review what is common and start to make some sensible refactoring decisions. At this point, defining different request types becomes appropriate, or some sort of request body builder warrants development.
Following standard best practices and keeping our code base as simple and clear as possible allows us to focus on more innovative parts of the app. With a lower barrier to project contribution, it means we can spend more time on the details that make Genius such a compelling banking solution.
Check out Genius 5.0 in action at Finovate Fall 2016 presenting Bank-Secured Social Funding.