December 1, 2020

Watch Me Refactor to MVVM in Easy, Proven Steps without Thinking

0  comments

What is refactoring? Like most of the technical agile practices, seeing the final code doesn’t help. That’s because refactoring is a process; it’s hard to grasp until you see someone do it.

So I gave this 27-minute talk showing what real refactoring can look like for iOS developers. You’ll see several refactoring practices in action that I follow mechanically, without thinking much.

In the talk, I take a view controller using Model-View-Controller (MVC) and begin refactoring it to Model-View-ViewModel (MVVM). The view controller has unit tests and is based on real iOS code I wrote for eBay. 

The talk begins the refactoring to MVVM but doesn’t complete the transformation. I show the rest of the journey in a second video. If you’d like to see yet another version where I refactor to Model-View-Presenter, subscribe to receive email notifications of new posts.

27-minute talk showing what real refactoring can look like for iOS developers. You’ll see several refactoring practices in action.

Click to Tweet

Thank you to CocoaHeads NL for inviting me to speak, and for producing the video.

The Importance of Unit Tests

The view controller behavior is covered by unit tests. These tests provide the fast feedback we need to take small steps, verifying each step.

While MVVM can make it easier to write tests, in this refactoring the view model is an implementation detail. The tests are already written and don’t care how the work gets done, as long as it gets done.

After each step, I do one of two things: build and test, or just build. I could run tests each time—and often do in the demo. But when I add new code that isn’t called yet, just building is enough feedback.

Notice how often I build or run tests, to get feedback on my latest step. The faster the feedback, the smaller the steps can be.

For Refactoring, Stay in Green

When refactoring, start in green (meaning tests pass), and end in green. Try not to go through red (build failing or tests failing).

If you do go into red, get back to green as quickly as you can. If you stay in red for more than a couple of minutes, it’s probably time to revert and start over from green. This is a technique Adrian Bolboaca calls “taking baby steps.” 

Expand—Shift—Contract

A common pattern that occurs in many refactorings is Expand—Shift—Contract. I used to describe this as “lay down the new tracks, switch over to them, then remove the old tracks.” I didn’t have a good term for it until I watched Dave Farley refactor. Dave’s term was new to me, but I immediately recognized the process:

Expand

Create a new home for some existing functionality. Nothing is calling it yet.

Shift

One at a time, switch old call sites to call the new code.

Contract

After everything has shifted, delete the old code.

After Expand, the code should still build. After each Shift, the tests should still pass. Then after Contract, tests should continue to pass.

Make Similar Expressions Identical

Another pattern that comes up is to take two lines that use similar expressions and make them identical. There are different techniques for this. In this demo, it happens by using a new method that is more general and can handle more situations.

Making the expressions identical paves the way for more interesting refactorings. In my demo, here’s what comes next…

Lift Out of Conditional

We have a conditional, with one expression on the if-side and another on the else-side. But now that we’ve made the lines identical, we can lift them out of the conditional. This works as long as it’s okay for the line to execute before the conditional check.

For example, let’s say we have:

if condition {
    x = makeResult()
    // More if stuff
} else {
    x = makeResult()
    // More else stuff
}

As long as there is no coupling between condition and x = makeResult(), we can lift those lines out of the conditional:

x = makeResult()
if condition {
    // More if stuff
} else {
    // More else stuff
}

This is a version of the Slide Statements refactoring. It shrinks the body of the if-else clauses. The statement sliding changes the order of the statements, so this isn’t one of the provable refactorings. But as long as the condition is unaffected by the change, unit tests will continue to pass.

One Refactoring Leads to More

As you can see, one refactoring can lead to more refactoring possibilities. And those new possibilities can be hard to picture until they’re the next step. For me, refactoring often happens like this:

  • Make a small refactoring.
  • Keep making small refactorings.
  • Once the “shape” of the code has changed enough, a larger refactoring becomes visible.

This happens to me over and over.

Working in Very Small Steps

Small steps bring advantages:

  • If the step doesn’t work, you can revert to get back to green. When steps are small and you commit on green, reverting doesn’t lose any progress.
  • A partially completed refactoring doesn’t block anyone. The work may not be done, but because it’s green, you can merge it to the main branch. Continuous Integration can be a reality.

Fast turnaround time to build and test is essential for working in small steps. That's not a disadvantage! It’s a forcing factor which will help everybody on your team.

Books to Help You

I have two book recommendations around refactoring.

The first is Martin Fowler’s classic Refactoring (that’s an Amazon affiliate link), which he updated in 2019. This continues to be a book I keep in arm’s reach while I work.

The other is my book iOS Unit Testing by Example. Part III of the book shows what unit testing makes possible. There are step-by-step examples of refactoring a view controller.

In a follow-up video, I finish refactoring this view controller to MVVM. If you’d like to see it, and maybe a Model-View-Presenter version, subscribe to receive email notifications of new posts.

Watch Proven Steps of Refactoring to MVVM in Swift, Part 2

Jon Reid

About the author

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 19 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

Never miss a good story!

Want to make sure you get notified when I release my next article or video? Then sign up here to subscribe to my newsletter. Plus, you’ll get access to the test-oriented code snippets I use every day!

>