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

Being correct, quicker

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Being correct, quicker

Avatar for Alex Curran

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!