I have noticed most people pin my lack of enthusiasm for iOS development on the Apple ecosystem, and while there is an element of truth in that statement, it doesn’t tell the whole story. Those that work with me know that I’ve made it no secret that I’m not too fond of iOS development. Today I hope to explore the importance of a very tight feedback loop when writing code and its impact on my motivation.
I’ve worked as a software engineer for more than six years now. I spent the first three years working with ASP.NET technology with an opinionated architectural pattern. The basis of our architecture was to follow Onion Architecture to some extent. During this time, what stood out for me was my ability to iterate on ideas and quickly invalidate various approaches.
Within our system’s solution, we had various projects1 that denoted the multiple layers of our architecture. Our domain layer sitting at the centre of our onion left me with quite a clear path to implementing new features. I’d pick up a ticket and open the user story. Jumping directly to the acceptance criteria, I’d be able to understand how the feature should function, and I’d start highlighting which of the items are the all-important domain rules.
From there, I’d use Test-Driven-Development to build out all of the various domain rules I needed before even having to think about touching any UI code. This entire process allowed me to engross myself in the problem domain, but what stood out was the iteration time. I had a test project that only depended on the domain project, so running those tests meant only compiling those two projects before starting up the test runner.
Getting a test to show red/green takes but a few seconds. Each time I go through a red-green-refactor cycle, I’d get these small dopamine hits, and it kept me coming back to what I’m doing instead of getting distracted with other stuff.
I’d then work my way towards the outer layers once I feel the inner layers are stable enough to continue, still following TDD wherever it made sense. I prefer working with this approach to software development, and it’s ultimately one of the reasons I struggle with game development. High coupling between gameplay systems makes it very difficult to follow TDD. Add restarting the game to that might mean you’ll end up spending more time waiting before testing it’s easy for me to understand why my motivation drops off so quickly.
I still believe Microsoft has put together a very robust toolset in Visual Studio. Many people complain that it’s a very bulky “jack of all trades & master of none” piece of software, but it’s tightly integrated into the .NET ecosystem, making it a great joy to use. I’m also a big fan of the tools Jetbrains have developed. I stick to Rider for personal projects, and I only really do so because I’ve found it more productive when collaborating on a personal project with my cousin, who’s predominantly been using Jetbrains IDEs for the work in Java.
I coupled Visual Studio with ReSharper and had gotten quite proficient at using the two. Many people took issue with ReSharper slowing down their machines by quite a bit, but I wasn’t finding myself frustrated with what we were doing. I found it easier to navigate our solution and work in it, even with some hiccups caused by ReSharper.
The big tool that I view as helping me the most was ReSharper Build. It better parallelised building our solution locally, but more importantly, if a project’s public API did not change, it wouldn’t require rebuilding projects that depended on that project. ReSharper Build is extremely useful when working on just the domain layer because I can make small changes there that would allow me to rebuild quickly. I can then run the entire solution without waiting the full 30 seconds to a minute it would’ve usually taken.
The last piece of tooling was that I liked setting our project up to run in IIS on my local machine instead of IIS Express. Working in the webspace during that time meant that I could keep a specific page open and make code changes in the background, recompile, go back to the browser and all I’d have to do is refresh the page. It didn’t often happen that I needed to navigate a complex set of pages to test issues as I’d get back to testing as soon as I had refreshed the page.
iOS Development Iteration Times
Our approach to the app I’m working on started as a “single module” application. A single-module app has a rather significant impact on iteration times, in my view, because your computer can only dedicate a single core to the compilation process. Add to that the sheer number of code files a project like this would generate, and you’re bound to have something with a slow iteration time. While I’d like to give numbers, it’s not fair comparing the compilation of an iOS app to that of an ASP.NET app. At present, it’s orders of magnitude when compared to what I was doing in ASP.NET.
A “Making App”
I recently saw a tweet by Michael “GeePaw” Hill outlining the concept of a “making app” 2. He’s taken the thread of tweets and turned it into an excellent blogpost that I’d highly recommend reading. I’m sad to say that I’ve fallen into the trap of believing something like this won’t be worth the effort, but I think it’s something I have to reconsider seriously.
One of the other things that kill iteration time for me is app navigation. App navigation will be an issue on Android and desktop apps because if your only way to test a feature is to go through a very deep navigation graph, you’re bound to spend a lot of wasted time debugging issues. Some of my colleagues get around this by “hacking” something into our login flow, but it still means you have to remember to go back and fix that before finally committing your code.
I’m not blaming iOS as a platform, but rather my muscle memory hasn’t gotten accustomed to ensuring I have the tooling built upfront to improve my iteration time. I have a few uncertainties on how to achieve this, but I should let go of the fear of wasting time and realise that I’m NOT doing this.
Back to Compilation
I mentioned we have a “single module” architecture. I think what’s complicated about this is the projects I’ve previously worked on either had smart people bootstrapping a proper architecture from the start, or we were working on something that already had the bulk of things figured out.
I was one of the parties responsible for bootstrapping the current project and didn’t quite know how to approach creating modules. It doesn’t help that I’ve found creating modules a very confusing process, and most of my experimentation did not yield the fruit I had hoped it would. Poor adoption rate on a pattern had meant it’s just not something that had proven to “work”.
I’m also of the opinion that there are other benefits to having multiple modules. If your module structure supports and encourages people to follow your broader architecture, it allows people to experiment and see the “recommended” approach. We have a hodgepodge of various patterns we’ve tried, and someone new joining cannot distinguish our recommended approach from some experiments we’d tried. That said, I’m not confident that Xcode will make this as easy as Visual Studio has made it.
There is one other benefit: a modular architecture better supports a “making app”. If your making app doesn’t need the bulk of the app, it can be nice and lightweight, reducing iteration times.
TDD is a pain
While some of this information is a little dated, the following was true the last time I tried it. Previously Xcode would run tests in the simulator. Running tests in the simulator means that any modules I was trying to test individually STILL required compilation of our entire app! Red-green-refactor had a much longer feedback loop than I expected, and it destroyed my productivity.
I’m starting to believe I better understand why people tend to crap on TDD as a practice, and it’s difficult if they’re having problems tightening up their feedback loop, which causes their frustration with TDD. In our well-architected, at least in my view, ASP.NET app, my feedback loop was incredibly tight.
I wrote this piece to try and reflect a little and put down some thoughts on why I’m struggling to stay motivated with the work I’m currently doing. There’s much more to unpack that isn’t just related to the technical work that’s also having an impact, but the technical work is something that I still have a level of control over.
I have plenty of reasons I like to dunk on iOS development, but without making sure I’m at least putting effort into optimising my workflow, I can’t expect to cry wolf and expect people to listen. More importantly, the way I’ve been going about this is not sustainable, and I need to make sure I first give things a fair chance.
I’ll close out with an inconclusive yet thought-provoking message my cousin sent me after reading an initial draft of this piece:
Iteration times are crucial! Let’s say a bit of averagely complex functionality takes 10 iterations to build… Maybe a senior could get it right in 5, maybe a junior takes 20.
Let’s take 2 scenarios:
- Quick feedback: Feedback on iteration takes 1 minute
- Slow feedback: Feedback on iteration takes 5 minutes
So, just the time spent testing each iteration (not creating it):
- Senior: 5m; Intermediate: 10m; Junior: 20m
- Senior: 25m; Intermediate: 50m; Junior: 100m Now take into consideration that the make-up of the average team will probably be something like 20% senior, 30% intermediate and 50% junior…
The team members you have the most of are the hardest hit. Time spent by a senior to cut 50% off the iteration times could be a huge force-multiplier for the rest of the team, making that larger part of the team so much more productive!
Juniors also often have shitty hardware because companies are stupid, so they tend to be even harder hit on account of running on slower hardware…
I’m referring to the solution & project term common in .NET projects.
Projects are equivalent to a single module of the broader deployment in a DLL generated for each project.
The solution being the combined collection of projects that mesh together to form the deployment. ↩︎
A making app is something you as a developer/tester would use to test your application while working on it. My current space would be a separate iOS application that directly enters into the flow I’m currently building.
Say you’re building a very complex sign-up form for life insurance. As a developer, you’d not be filling in every single detail of your 5-page wizard every single time you wanted to test something on the final screen. Ideally, you’d like to quickly skip all of that and jump directly to that screen to see what impact your changes had.
You could also think of your tests as forming part of your making app, and the concept shouldn’t be limited to just these, but instead, form part of a suite of tools that accelerate your development time. ↩︎