MINIMIZING
DECISION FATIGUE
TO IMPROVE TEAM PRODUCTIVITY
TRY! SWIFT
MARCH, 2017
DEREK LEE
@DEREKLEEROCK
☀ 🌛
?
Class Struct
🤔
Tabs SpacesCocoapods CarthageStoryboards CodeA BUIKit ReactNative
?
A B
🤔
?
??
?
? ?
??? ?
?
? ?
?
?
?
? ??
??
??
?
?
?
??
???
?
?
?
?
??
??
?
?
?
??
???
?
??? ?? 😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A DAY IN THE LIFE @ PIVOTAL LABS
BREAKFAST!
🕣8:45am
MORNING OFFICE STANDUP
🕘9:06am
🕘9:15am
PROJECT STANDUP
PROJECT
ORGANIZATION
Open Quickly
⌘+⇧+O
Filter in Navigator
⌘+⌥+J
Reveal in Navigator
⌘+⇧+J
Find in Files
⌘+⇧+F
HOW CAN WE FIND FILES IN XCODE?
“Hunt and Peck”
HOW DO WE REALLY FIND FILES IN XCODE?
“Helpers” FolderNo organization
WHAT WE’D LIKE TO AVOID
MVC?
APPLICATION ・ COMPONENTS ・ UI
APPLICATION
COMPONENTS
UI
PAIR PROGRAMMING
🕙10:00am
PAIR PROGRAMMING - SETUP
▸ Ping-Pong
PAIR PROGRAMMING - STYLES
+
▸ Driver + Navigator
PAIR PROGRAMMING - IN ACTION
▸ We pair 99% of the time
▸ All disciplines pair: Engineering, Design, PMs
▸ Change pairs daily
▸ Regularly switch tracks of work
LUNCHTIME TECH TALK
🕧12:30pm
BACK TO PAIRING
🕜1:30pm
IMPROMPTU TEAM DISCUSSION
🕝2:30pm
SWIFT FILE
ORGANIZATION
VIEW CONTROLLER: SHOW DOCUMENT ITEMS: ^ + 6
class CountingRepeaterViewController: UIViewController {
fileprivate let repeater: Repeater
fileprivate let maximumCountValue: Int
fileprivate var counterValue: Int
let countingLabel: UILabel
init(repeater: Repeater,
maximumCountValue: Int) { ... }
required init?(coder aDecoder: NSCoder) { ... }
override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) { ... }
func addSubviews() { ... }
func addConstraints() { ... }
...
}
With “// MARK:”Without Annotations With “// MARK: —“
MARK ANNOTATION COMPARISON
VIEW CONTROLLER ORGANIZATION
class CountingRepeaterViewController: UIViewController {
// MARK: - Properties
fileprivate let repeater: Repeater
fileprivate let maximumCountValue: Int
fileprivate var counterValue: Int
// MARK: - View Elements
let headerLabel: UILabel
let countingLabel: UILabel
// MARK: - Initialization
init(repeater: Repeater,
maximumCountValue: Int) { ... }
required init?(coder aDecoder: NSCoder) { ... }
// MARK: - Lifecycle Methods
override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool)
}
// MARK: - Private Methods
fileprivate extension CountingRepeaterViewController {
func addSubviews() { ... }
func addConstraints() { ... }
VIEW CONTROLLER ORGANIZATION
class CountingRepeaterViewController: UIViewController {
// MARK: - Properties
fileprivate let repeater: Repeater
fileprivate let maximumCountValue: Int
fileprivate var counterValue: Int
// MARK: - View Elements
let headerLabel: UILabel
let countingLabel: UILabel
// MARK: - Initialization
init(repeater: Repeater,
maximumCountValue: Int) { ... }
required init?(coder aDecoder: NSCoder) { ... }
// MARK: - Lifecycle Methods
override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) { ... }
}
// MARK: - Private Methods
fileprivate extension CountingRepeaterViewController {
func addSubviews() { ... }
func addConstraints() { ... }
// MARK: - Lifecycle Methods
override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool) { ... }
class CountingRepeaterViewController: UIViewController {
// MARK: - Properties
fileprivate let repeater: Repeater
fileprivate let maximumCountValue: Int
fileprivate var counterValue: Int
// MARK: - View Elements
let headerLabel: UILabel
let countingLabel: UILabel
// MARK: - Initialization
init(repeater: Repeater,
maximumCountValue: Int) { ... }
required init?(coder aDecoder: NSCoder) { ... }
// MARK: - Lifecycle Methods
override func viewDidLoad() { ... }
override func viewWillDisappear(_ animated: Bool)
}
// MARK: - Private Methods
fileprivate extension CountingRepeaterViewController {
func addSubviews() { ... }
func addConstraints() { ... }
CREATE TEMPLATE FROM XCODE SNIPPETS
PROTOCOL CONFORMANCE
struct DefaultCustomer: Customer {
let name: String
private(set) var rentals: [Rental]
init(name: String) { ... }
mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... }
func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... }
}
PROTOCOL CONFORMANCE
struct DefaultCustomer: Customer {
// MARK: - Properties
let name: String
private(set) var rentals: [Rental]
// MARK: - Initialization
init(name: String) { ... }
mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... }
func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... }
}
PROTOCOL CONFORMANCE
struct DefaultCustomer {
// MARK: - Properties
let name: String
private(set) var rentals: [Rental]
// MARK: - Initialization
init(name: String) { ... }
}
// MARK: - Customer
extension DefaultCustomer: Customer {
mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... }
func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... }
}
PROTOCOL CONFORMANCE
struct DefaultCustomer {
// MARK: - Properties
let name: String
private(set) var rentals: [Rental]
// MARK: - Initialization
init(name: String) { ... }
}
// MARK: - Customer
extension DefaultCustomer: Customer {
mutating func addRental(rental: Rental) { ... }
func createTextStatement() -> String { ... }
func createHtmlStatement() -> String { ... }
}
// MARK: - Private Methods
fileprivate extension DefaultCustomer {
func getTotalCharge() -> Double { ... }
func getTotalFrequentRenterPoints() -> Int { ... }
}
PING-PONG BREAK
🕞3:30pm
CROSS-FUNCTIONAL PAIRING
🕓4:00pm
Engineering x Design
STYLING
UI OBJECTS
extension UIFont {
class func abcMediumFont(size: CGFloat) -> UIFont {
return UIFont(name: "AvenirNext-Medium", size: size)!
}
class func abcBoldFont(size: CGFloat) -> UIFont {
return UIFont(name: "AvenirNext-Bold", size: size)!
}
}
DEFINING FONTS
extension UIColor {
class var abcDarkSkyBlue: UIColor {
return UIColor(
red: 52.0 / 255.0,
green: 152.0 / 255.0,
blue: 219.0 / 255.0,
alpha: 1.0
)
}
class var abcBlueish: UIColor {
return UIColor(
red: 41.0 / 255.0,
green: 128.0 / 255.0,
blue: 185.0 / 255.0,
alpha: 1.0
)
}
}
DEFINING COLORS
enum UIButtonStyle {
case primary, negative
func applyTo(button: UIButton) {
switch (self) {
case .primary:
button.titleLabel?.font = UIFont.abcMediumFont(
size: 15
)
button.setTitleColor(UIColor.white, for: .normal)
button.backgroundColor = UIColor.abcDarkSkyBlue
button.layer.borderColor = UIColor.abcBlueish.cgColor
button.layer.borderWidth = 1.0
break
case .negative:
// ...
break
}
}
}
DEFINING STYLES
APPLYING STYLES
extension UIButton {
func apply(style: UIButtonStyle) {
style.applyTo(button: self)
}
}
class MyViewController: UIViewController {
let confirmButton: UIButton
let cancelButton: UIButton
...
fileprivate func applyStyles() {
confirmButton.apply(style: .primary)
cancelButton.apply(style: .negative)
}
}
RETROSPECTIVE (RETRO)
🕔5:00pm
RETROS - INGREDIENTS
🙂🙂
🙂
🙂
🙂
🙂
Core Team Members Food & Snacks
🍓🧀
🍙🍪
Drinks
☕🍵
🍷🍺
RETROS @ PIVOTAL LABS
😃
Discuss
😭
Keep
😕
Improve
RETROS @ PIVOTAL LABS
▸ Reflect → Continuous improvement
▸ Building Trust
▸ Honest communication
▸ Identify & solve problems early
▸ Team brainstorming
Kent Beck, Extreme Programming Explained
THE COURAGE TO SPEAK TRUTHS,
PLEASANT OR UNPLEASANT,
FOSTERS COMMUNICATION AND TRUST.
SUMMARY
▸ Project Standup
▸ Pair Programming
▸ Lunchtime Tech Talk
▸ Impromptu Team Discussions
▸ Cross-Functional Pairing
▸ Retrospectives
▸ Project Organization
▸ Swift File Organization
▸ Styling UI Objects
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
A B
😓
😓😓 😓
😓😓 😓
😓😓 😓
😓😓 😓
😁😃 😛
🙃😎 😅
Thank you!
try! Swift March 2017
@DEREKLEEROCK
Thank you!
try! Swift March 2017

Minimizing Decision Fatigue to Improve Team Productivity

  • 1.
    MINIMIZING DECISION FATIGUE TO IMPROVETEAM PRODUCTIVITY TRY! SWIFT MARCH, 2017 DEREK LEE @DEREKLEEROCK
  • 2.
    ☀ 🌛 ? Class Struct 🤔 TabsSpacesCocoapods CarthageStoryboards CodeA BUIKit ReactNative
  • 3.
    ? A B 🤔 ? ?? ? ? ? ???? ? ? ? ? ? ? ? ?? ?? ?? ? ? ? ?? ??? ? ? ? ? ?? ?? ? ? ? ?? ??? ? ??? ?? 😓
  • 4.
  • 5.
    A B 😓 A B 😓 AB 😓 A B 😓 A B 😓 A B 😓
  • 6.
    A DAY INTHE LIFE @ PIVOTAL LABS
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
    Open Quickly ⌘+⇧+O Filter inNavigator ⌘+⌥+J Reveal in Navigator ⌘+⇧+J Find in Files ⌘+⇧+F HOW CAN WE FIND FILES IN XCODE?
  • 12.
    “Hunt and Peck” HOWDO WE REALLY FIND FILES IN XCODE?
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
    ▸ Ping-Pong PAIR PROGRAMMING- STYLES + ▸ Driver + Navigator
  • 22.
    PAIR PROGRAMMING -IN ACTION ▸ We pair 99% of the time ▸ All disciplines pair: Engineering, Design, PMs ▸ Change pairs daily ▸ Regularly switch tracks of work
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
    VIEW CONTROLLER: SHOWDOCUMENT ITEMS: ^ + 6 class CountingRepeaterViewController: UIViewController { fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int let countingLabel: UILabel init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... } override func viewDidLoad() { ... } override func viewWillDisappear(_ animated: Bool) { ... } func addSubviews() { ... } func addConstraints() { ... } ... }
  • 28.
    With “// MARK:”WithoutAnnotations With “// MARK: —“ MARK ANNOTATION COMPARISON
  • 29.
    VIEW CONTROLLER ORGANIZATION classCountingRepeaterViewController: UIViewController { // MARK: - Properties fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int // MARK: - View Elements let headerLabel: UILabel let countingLabel: UILabel // MARK: - Initialization init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... } // MARK: - Lifecycle Methods override func viewDidLoad() { ... } override func viewWillDisappear(_ animated: Bool) } // MARK: - Private Methods fileprivate extension CountingRepeaterViewController { func addSubviews() { ... } func addConstraints() { ... }
  • 30.
    VIEW CONTROLLER ORGANIZATION classCountingRepeaterViewController: UIViewController { // MARK: - Properties fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int // MARK: - View Elements let headerLabel: UILabel let countingLabel: UILabel // MARK: - Initialization init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... } // MARK: - Lifecycle Methods override func viewDidLoad() { ... } override func viewWillDisappear(_ animated: Bool) { ... } } // MARK: - Private Methods fileprivate extension CountingRepeaterViewController { func addSubviews() { ... } func addConstraints() { ... } // MARK: - Lifecycle Methods override func viewDidLoad() { ... } override func viewWillDisappear(_ animated: Bool) { ... } class CountingRepeaterViewController: UIViewController { // MARK: - Properties fileprivate let repeater: Repeater fileprivate let maximumCountValue: Int fileprivate var counterValue: Int // MARK: - View Elements let headerLabel: UILabel let countingLabel: UILabel // MARK: - Initialization init(repeater: Repeater, maximumCountValue: Int) { ... } required init?(coder aDecoder: NSCoder) { ... } // MARK: - Lifecycle Methods override func viewDidLoad() { ... } override func viewWillDisappear(_ animated: Bool) } // MARK: - Private Methods fileprivate extension CountingRepeaterViewController { func addSubviews() { ... } func addConstraints() { ... }
  • 31.
    CREATE TEMPLATE FROMXCODE SNIPPETS
  • 32.
    PROTOCOL CONFORMANCE struct DefaultCustomer:Customer { let name: String private(set) var rentals: [Rental] init(name: String) { ... } mutating func addRental(rental: Rental) { ... } func createTextStatement() -> String { ... } func createHtmlStatement() -> String { ... } func getTotalCharge() -> Double { ... } func getTotalFrequentRenterPoints() -> Int { ... } }
  • 33.
    PROTOCOL CONFORMANCE struct DefaultCustomer:Customer { // MARK: - Properties let name: String private(set) var rentals: [Rental] // MARK: - Initialization init(name: String) { ... } mutating func addRental(rental: Rental) { ... } func createTextStatement() -> String { ... } func createHtmlStatement() -> String { ... } func getTotalCharge() -> Double { ... } func getTotalFrequentRenterPoints() -> Int { ... } }
  • 34.
    PROTOCOL CONFORMANCE struct DefaultCustomer{ // MARK: - Properties let name: String private(set) var rentals: [Rental] // MARK: - Initialization init(name: String) { ... } } // MARK: - Customer extension DefaultCustomer: Customer { mutating func addRental(rental: Rental) { ... } func createTextStatement() -> String { ... } func createHtmlStatement() -> String { ... } func getTotalCharge() -> Double { ... } func getTotalFrequentRenterPoints() -> Int { ... } }
  • 35.
    PROTOCOL CONFORMANCE struct DefaultCustomer{ // MARK: - Properties let name: String private(set) var rentals: [Rental] // MARK: - Initialization init(name: String) { ... } } // MARK: - Customer extension DefaultCustomer: Customer { mutating func addRental(rental: Rental) { ... } func createTextStatement() -> String { ... } func createHtmlStatement() -> String { ... } } // MARK: - Private Methods fileprivate extension DefaultCustomer { func getTotalCharge() -> Double { ... } func getTotalFrequentRenterPoints() -> Int { ... } }
  • 36.
  • 37.
  • 38.
  • 39.
    extension UIFont { classfunc abcMediumFont(size: CGFloat) -> UIFont { return UIFont(name: "AvenirNext-Medium", size: size)! } class func abcBoldFont(size: CGFloat) -> UIFont { return UIFont(name: "AvenirNext-Bold", size: size)! } } DEFINING FONTS
  • 40.
    extension UIColor { classvar abcDarkSkyBlue: UIColor { return UIColor( red: 52.0 / 255.0, green: 152.0 / 255.0, blue: 219.0 / 255.0, alpha: 1.0 ) } class var abcBlueish: UIColor { return UIColor( red: 41.0 / 255.0, green: 128.0 / 255.0, blue: 185.0 / 255.0, alpha: 1.0 ) } } DEFINING COLORS
  • 41.
    enum UIButtonStyle { caseprimary, negative func applyTo(button: UIButton) { switch (self) { case .primary: button.titleLabel?.font = UIFont.abcMediumFont( size: 15 ) button.setTitleColor(UIColor.white, for: .normal) button.backgroundColor = UIColor.abcDarkSkyBlue button.layer.borderColor = UIColor.abcBlueish.cgColor button.layer.borderWidth = 1.0 break case .negative: // ... break } } } DEFINING STYLES
  • 42.
    APPLYING STYLES extension UIButton{ func apply(style: UIButtonStyle) { style.applyTo(button: self) } } class MyViewController: UIViewController { let confirmButton: UIButton let cancelButton: UIButton ... fileprivate func applyStyles() { confirmButton.apply(style: .primary) cancelButton.apply(style: .negative) } }
  • 43.
  • 44.
    RETROS - INGREDIENTS 🙂🙂 🙂 🙂 🙂 🙂 CoreTeam Members Food & Snacks 🍓🧀 🍙🍪 Drinks ☕🍵 🍷🍺
  • 45.
    RETROS @ PIVOTALLABS 😃 Discuss 😭 Keep 😕 Improve
  • 46.
    RETROS @ PIVOTALLABS ▸ Reflect → Continuous improvement ▸ Building Trust ▸ Honest communication ▸ Identify & solve problems early ▸ Team brainstorming
  • 47.
    Kent Beck, ExtremeProgramming Explained THE COURAGE TO SPEAK TRUTHS, PLEASANT OR UNPLEASANT, FOSTERS COMMUNICATION AND TRUST.
  • 48.
    SUMMARY ▸ Project Standup ▸Pair Programming ▸ Lunchtime Tech Talk ▸ Impromptu Team Discussions ▸ Cross-Functional Pairing ▸ Retrospectives ▸ Project Organization ▸ Swift File Organization ▸ Styling UI Objects
  • 51.
    A B 😓 A B 😓 AB 😓 A B 😓 A B 😓 A B 😓
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.