Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Being correct, quicker

Being correct, quicker

Alex Curran

August 07, 2018
Tweet

More Decks by Alex Curran

Other Decks in Technology

Transcript

  1. @amlcurran Where’s the assumption? class TodoItem { var id: String?

    var title: String? var dueDate: Date? var tasks: [String]? var completed: Bool }
  2. @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?
  3. @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
  4. @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
  5. @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
  6. @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
  7. @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
  8. @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 ;)
  9. @amlcurran Reducing a feedback loop Immediate End of the world

    A user told you Merged code Released app
  10. @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
  11. @amlcurran Closing a feedback loop Immediate End of the world

    UI test failed Merged code Released app
  12. @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
  13. @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
  14. @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
  15. @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
  16. @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
  17. @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
  18. @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
  19. @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
  20. @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
  21. @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
  22. @amlcurran Immediate End of the world App released Pair with

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

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

    compile PR review “The code doesn’t match our style”
  25. @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”
  26. @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!
  27. @amlcurran class ServiceLocator { private var references = [String: Any]()

    func register<T>(_ object: T) { references["\(type(of: T.self))"] = object } func retrieve<T>() -> 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 } }
  28. @amlcurran let locator = ServiceLocator() locator.register(UserDefaults.standard) // Works let userDefaults:

    UserDefaults = locator.retreive() // Crashes let urlSession: URLSession = locator.retreive()
  29. @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
  30. @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
  31. @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
  32. @amlcurran Agile development • Reducing the time to feedback •

    Ensuring feedback is guaranteed • It is NOT about standups and retros and meetings!