
The Scope of Machine Learning
Clients Leveling Up Machine LearningA lot of this confusion surfaces around the scope of machine learning. While there is a lot of hype around deep learning, it is...
Responding to change is a key tenant of the Agile Manifesto. Whether or not you prescribe to agile, change is undeniably inevitable and predicting that change is hard, if not impossible.
Every software project of sufficient age will undergo change. We cannot predict how it will change, but accepting that change will happen can improve our decision-making process. We have to ship, we have to make money – it’s a business, after all. But resources like time, money, and effort are finite; thus it’s better to be wiser and more efficient in how we spend those resources. There are ways to develop (write) software and architect (plan) code that facilitates responding to change and strengthens stewardship with stakeholders.
This article series explores a coding approach we use at Big Nerd Ranch that enables us to more easily respond to change. It will start by laying out an example and explaining why some “traditional” approaches make change-response difficult. Part 2 will introduce the approach, and Part 3 will complete it.
To help illustrate and understand how we can manage change, let’s look at a simplified Contacts app. I have chosen to use the common Master-Detail style interface, with a TableViewController
showing the master list of Persons. Selecting a Person shows a ViewController
displaying the Person’s details. The implementation is typical and likely familiar:
import UIKit /// My `Person` model. struct Person { let name: String } /// Shows a single Person in detail. class PersonViewController: UIViewController { var person: Person? // Imagine it has functionality to display the Person in detail. } /// Shows a master list of Persons. class PersonsViewController: UITableViewController { let persons = [ Person(name: "Fred"), Person(name: "Barney"), Person(name: "Wilma"), Person(name: "Betty") ] override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selectedPerson = persons[indexPath.row] // `instantiateFromStoryboard()` is poetic convenience for this example let personVC = PersonViewController.instantiateFromStoryboard() personVC.person = selectedPerson navigationController?.pushViewController(personVC, animated: true) } // Other TableView delegate and data source functions omitted for brevity }
When my app is launched, the PersonsViewController
 is the initial ViewController
 loaded and displayed. A user can tap on a row/cell of a Person, and the app will navigate to display the details of that Person. This is a fairly common scenario in mobile apps.
As it is now, the app has a single view showing a list of Persons; tap a Person and the Person’s detail is shown. The product stakeholders want to expand the app to support a new feature: Groups. To support this new feature, they want a view showing a list of Persons, and when you tap a person it shows a list of the Person’s Group memberships. The app should change from a single-view UI to a tabbed-UI, with the first tab for Persons feature and the second for the Groups feature. How can we implement this change request in a manner that provides good stewardship of time, money, and resources, and also leads to a more robust, more maintainable code base?
Consider the commonalities: both tabs start by showing a list of Persons. Our PersonsViewController
 shows a list of Persons, so we can use it to implement both tabs, right? While it does show persons, it’s not able to satisfy our requirements: the data to display is hard-coded within the PersonsViewController
, and the tight coupling to the PersonViewController
 doesn’t support showing Group membership.
How can we solve this?
I’ll immediately disqualify three approaches.
First, duplication. This is creating a new class, such as PersonsGroupViewController
 that replicates PersonsViewController
 in full (perhaps by select-all, copy, paste) and edits tableView(_:didSelectRowAt:)
 to transition to a GroupsViewController
 instead of PersonViewController
. Duplicating code in such a manner might work but will become a maintenance nightmare to maintain essentially the same code in multiple places.
Second, subclassing. This is creating a common base class which then PersonsViewController
 and PersonsGroupViewController
 inherit from, varying just in how the didSelect
 is handled. This isn’t necessarily a bad option (and in some cases may be the right approach), but in addition to creating additional maintenance overhead, it’s also not quite the correct model. The display of a list of persons is the same regardless of the action taken when tapping a cell, so to subclass just to change the tap-action is a little much.
Third, to expand PersonsViewController
with some sort of “behavior” or “configuration” enum such as:
class PersonsViewController: UITableViewController { enum SelectionMode { case personDetail case groupMembership } var selectionMode: SelectionMode override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch selectionMode { case .personDetail: let personVC = PersonViewController.instantiateFromStoryboard() navigationController?.pushViewController(personVC, animated: true) case .groupMembership: let groupVC = GroupsViewController.instantiateFromStoryboard() navigationController?.pushViewController(groupVC, animated: true) } } } // when filling in tab 1… let personsVC = PersonsViewController.instantiateFromStoryboard() personsVC.selectionMode = .personDetail // when filling in tab 2… let personsVC = PersonsViewController.instantiateFromStoryboard() personsVC.selectionMode = .groupMembership
This approach is problematic because, in addition to making the couplings tighter and more complex, it does not scale well when more modes are added. You might be tempted to think “Oh, it’s just two cases,” but remember we are trying to position the codebase to be to respond to future, and unknown, changes. The reality of codebase maintenance is that, once you establish a pattern, future developers are likely to maintain that pattern. If more changes are required, a future developer is more likely to expand the enum and lead the ViewController
 down the road to the ill-fated “Massive View Controller” problem.
There is a better way, which I’ll introduce in part 2.
A lot of this confusion surfaces around the scope of machine learning. While there is a lot of hype around deep learning, it is...
Agile methodologies are now mainstream. But what about the principles of the Agile Manifesto that spawned these methodologies? Here's how Big Nerd Ranch thinks...
There are three versions of the React Native story. One says that React Native is a silver bullet that allows you to ship two...