Slide 1

Slide 1 text

Being correct, quicker

Slide 2

Slide 2 text

@amlcurran Where’s the assumption? class TodoItem { var id: String? var title: String? var dueDate: Date? var tasks: [String]? var completed: Bool }

Slide 3

Slide 3 text

@amlcurran Where’s the assumption? class TodoItem { var id: String? var title: String? var dueDate: Date? var tasks: [String]? var completed: Bool } when is this nil? is this object valid without an ID? Is this property thread safe? does this change?

Slide 4

Slide 4 text

let idToDelete = todo.id!

Slide 5

Slide 5 text

@amlcurran Timeline of messing up Immediate End of the world You caught it QA caught it A user told you let idToDelete = todo.id! Merged code Released app

Slide 6

Slide 6 text

@amlcurran Timeline of messing up Immediate End of the world Unit test crashed UI test failed Crash reporting caught it let idToDelete = todo.id! Merged code Released app

Slide 7

Slide 7 text

@amlcurran How can we improve? class TodoItem { var id: String? var title: String? var dueDate: Date? var tasks: [String]? var completed: Bool } Immediate End of the world

Slide 8

Slide 8 text

@amlcurran How can we improve? class TodoItem { let id: String = "" let title: String = "" let dueDate: Date? let tasks: [String] = [] let completed: Bool = false } Immediate End of the world

Slide 9

Slide 9 text

@amlcurran How can we improve? class TodoItem { let id: String let title: String let dueDate: Date? let tasks: [String] let completed: Bool init(...) { } } Immediate End of the world

Slide 10

Slide 10 text

@amlcurran Removing guesswork • Code didn’t give us enough information, we had to assume • An assumption is either right or wrong • Removing the assumption removes the guesses • Still assuming the API is stable, but talk to your API developers ;)

Slide 11

Slide 11 text

@amlcurran Reducing a feedback loop Immediate End of the world A user told you Merged code Released app

Slide 12

Slide 12 text

@amlcurran Reducing a feedback loop Immediate End of the world QA found it

Slide 13

Slide 13 text

@amlcurran Time is money • Releases are time-expensive • The longer until an issue is found, the more work has to be done • Code review, pairing, type-safety all reduce this time

Slide 14

Slide 14 text

@amlcurran Closing a feedback loop Immediate End of the world QA found it Merged code Released app

Slide 15

Slide 15 text

@amlcurran Closing a feedback loop Immediate End of the world UI test failed Merged code Released app

Slide 16

Slide 16 text

@amlcurran • “Definite” happens more often than “maybe" • Prefer to encode expectations into code and tools, rather than knowledge • An open loop requires knowledge to close — can you guarantee this happens? Definite is better than maybe

Slide 17

Slide 17 text

Have you ever forgotten to set a dataSource or delegate?

Slide 18

Slide 18 text

@amlcurran Forgetting a delegate let pairInputView = AddPairView() class AddPairView: UIVisualEffectView, UITextFieldDelegate { var delegate: AddPairViewDelegate? func textFieldShouldReturn(_ textField: UITextField) -> Bool { delegate?.addPairView(self, didFinishCreating: request()) return true } } Immediate End of the world

Slide 19

Slide 19 text

@amlcurran Forgetting a delegate let pairInputView = AddPairView() pairInputView.delegate = self class AddPairView: UIVisualEffectView, UITextFieldDelegate { var delegate: AddPairViewDelegate? func textFieldShouldReturn(_ textField: UITextField) -> Bool { delegate?.addPairView(self, didFinishCreating: request()) return true } } Immediate End of the world

Slide 20

Slide 20 text

@amlcurran Forgetting a delegate let pairInputView = AddPairView() class AddPairView: UIVisualEffectView, UITextFieldDelegate { var delegate: AddPairViewDelegate? func textFieldShouldReturn(_ textField: UITextField) -> Bool { delegate!.addPairView(self, didFinishCreating: request()) return true } } Immediate End of the world

Slide 21

Slide 21 text

@amlcurran Forgetting a delegate let pairInputView = AddPairView(delegate: self) class AddPairView: UIVisualEffectView, UITextFieldDelegate { let delegate: AddPairViewDelegate func textFieldShouldReturn(_ textField: UITextField) -> Bool { delegate!.addPairView(self, didFinishCreating: request()) return true } } Immediate End of the world

Slide 22

Slide 22 text

@amlcurran Forgetting a delegate let pairInputView = AddPairView(delegate: self) class AddPairView: UIVisualEffectView, UITextFieldDelegate { let delegate: AddPairViewDelegate func textFieldShouldReturn(_ textField: UITextField) -> Bool { delegate!.addPairView(self, didFinishCreating: request()) return true } } Immediate End of the world

Slide 23

Slide 23 text

@amlcurran Using the compiler • A compile-time error is guaranteed to happen • A runtime crash is not (and is worse for the user!) • Flexible is not guaranteed to be safe

Slide 24

Slide 24 text

@amlcurran Compile time vs runtime Compile time Compile Runtime Compile Launch Navigate Hit issue

Slide 25

Slide 25 text

@amlcurran Swift (generally) helps • Stronger typing in Swift vs Obj-C makes compiler more important • Look at your optionals, mutables, and your implicitly unwrapped optionals • IUOs you’ve written can often be written in a less-crashy way • Don’t make things mutable/optional if it doesn’t make sense • Verify (and fail) early

Slide 26

Slide 26 text

@amlcurran Strong typing helps • Use generics for common behaviour, not convenience • Don’t circumvent the compiler using Any • Think whether a primitive type really makes sense • Forcing a strong type can prevent bugs

Slide 27

Slide 27 text

Quicker feedback is not always about crashes

Slide 28

Slide 28 text

@amlcurran Tools to reduce feedback • SwiftGen and R.swift reduce runtime failures or undefined behaviour to compile errors — much faster! • SwiftLint makes compile time warnings from style guides

Slide 29

Slide 29 text

@amlcurran “The code doesn’t match our style” Immediate End of the world App released Code review

Slide 30

Slide 30 text

@amlcurran Immediate End of the world App released Pair with dev “The code doesn’t match our style”

Slide 31

Slide 31 text

@amlcurran Immediate End of the world App released Swiftlint + compile “The code doesn’t match our style”

Slide 32

Slide 32 text

@amlcurran Immediate End of the world App released Swiftlint + compile PR review “The code doesn’t match our style”

Slide 33

Slide 33 text

@amlcurran Immediate End of the world App released Swiftlint + compile PR review Not open to interpretation Newer members of the team get neutral feedback “The code doesn’t match our style”

Slide 34

Slide 34 text

@amlcurran Preventing accidents • Removing ability to undo a decision that was already made unconsciously • You can still change the rules, but not accidentally • If you have rules that you want people to follow, make it as easy as possible • Don’t add rules around things you can accept changing!

Slide 35

Slide 35 text

@amlcurran class ServiceLocator { private var references = [String: Any]() func register(_ object: T) { references["\(type(of: T.self))"] = object } func retrieve() -> T { let identifier = "\(type(of: T.self))" guard let object = references[identifier] as? T else { preconditionFailure("Didn't register an object of \(type(of: T.self))") } return object } }

Slide 36

Slide 36 text

@amlcurran let locator = ServiceLocator() locator.register(UserDefaults.standard) // Works let userDefaults: UserDefaults = locator.retreive() // Crashes let urlSession: URLSession = locator.retreive()

Slide 37

Slide 37 text

@amlcurran class Dependencies { lazy var userDefaults: UserDefaults = .standard lazy var urlSession: URLSession = .shared } let dependencies = Dependencies() // Works let userDefaults = dependencies.urlSession // immediately doesn't compile let application = dependencies.application

Slide 38

Slide 38 text

@amlcurran When is this stuff useful? • Old codebase, with weak coding practises • Teams with inexperienced developers • Teams with opinionated developers • Teams with high turnover • But really, its always useful

Slide 39

Slide 39 text

@amlcurran Summing up • Reducing feedback loop time reduces the time to realise a mistake • Closing open feedback loops guarantees you feedback • Third-party tools can close Apple’s feedback loops • Think about aiming for certainty, not convenience • If something doesn’t make sense, make it impossible to happen

Slide 40

Slide 40 text

@amlcurran Agile development • Reducing the time to feedback • Ensuring feedback is guaranteed • It is NOT about standups and retros and meetings!

Slide 41

Slide 41 text

Thanks