You're still shipping bugs in 2020? 😱
Slides : https://bit.ly/2zDQlXR
@JoGrenat
Morning chit chat
and coffee...

You will never believe what the new guy did!

Don't you have code review?

Yes, but...

Maybe there's a deeper problem...
The new guy is always a pain in the a**!
Except sometimes it's me...

Jordane Grenat
@JoGrenat

And sometimes it's you
account.sendTransaction("John Doe", Date.now(), 100);account.sendTransaction(100, Date.now(), "John Doe");
¯\_(ツ)_/¯

account.sendTransaction();What needs
special attention
will eventually fail
What needs
special attention
reveals a flaw in conception
Production
IDE
Compilation
Where do we find bugs?
Code review
CI/CD
Compilation Error == Impossible Error
fetchUser(userId).then(user => {
        console.log(user.name);
});fetchUser(userId).then(user => {
    if(user && user.name) {
        console.log(user.name);
    }
});function fetchUser(userId: string): Promise<User | null> {
    // ...
}

with strictNullChecks
fetchUser(userId).then(user => {
    console.log(user.name); // Compilation Error!
}); 
export class Account {
    sendTransaction(recipient: string, 
                    amount: number, 
                    date: number): string {
        // ...
    };
}account.sendTransaction("John Doe", Date.now(), 100);account.sendTransaction(100, Date.now(), "John Doe");account.sendTransaction();export class Account {
    sendTransaction(recipient: string, 
                    amount: number, 
                    date: Date): string {
        // ...
    };
}account.sendTransaction("John Doe", Date.now(), 100);account.sendTransaction(100, Date.now(), "John Doe");account.sendTransaction();private void sendEmail(String email, 
                       String subject,
                       String content) {
    // ...
}sendEmail("john@doe.com", "Hello!", content);sendEmail("Hello!", content, "john@doe.com");Primitive Obsession

void sendEmail(String, String, String);void sendEmail(Email, Subject, Content);Primitive Obsession
private void sendEmail(Email email, 
                       Subject subject,
                       Content content {
    // ...
}sendEmail(new Email("john@doe.com"), 
          new Subject("Hello!"), 
          new Content(content));sendEmail(null, null, null);Primitive Obsession
private void sendEmail(@NotNull Email email, 
                       @NotNull Subject subject,
                       @NotNull Content content {
    // ...
}Primitive Obsession
Only checked by the IDE in Java
sendEmail(new Email("wrongEmail.com"), 
          new Subject("Hello!"), 
          new Content(content));private void sendEmail(Email email,     
                       Subject subject, 
                       Content content) {
    if(email.isValid()) {
        // ...
    }
}sendEmail(new Email("wrongEmail.com"), 
          new Subject("Hello!"), 
          new Content(content));class Email {
    public Email(String emailString) {
        if (!isValid(emailString) {
            throw new IllegalArgumentException();
        }
        // ...
    }
}An Email argument is
always a valid email!
sendEmail(new Email("wrongEmail.com"), 
          new Subject("Hello!"), 
          new Content(content));Still catched at runtime...

class Email {
    public Email(String emailString) 
            throws InvalidEmailException {
        // ...
    }
}
class InvalidEmailException extends Exception {}Better
try {
    Email email = new Email("wrongEmail.com");
} catch (InvalidEmailException e) {
    // Fallback
}class Email {
    private Email(String emailString) {}
    static Either<Reason, Email> fromString(String email) {}
}Even Better
Either<Reason, Email>
Reason
Left
Right
Either<Reason, Email> email = Email.fromString(wrongEmail);
email.map(email -> sendEmail(email, subject, content));class Email {
    static Either<Reason, Email> fromString(String email) {
        if (!isValid(email) {
            return Either.left(BAD_EMAIL);
        }
        return Either.right(new Email(email));
    }
}Input filtering
Type cardinality
boolean
true
false
number
-23.4
-3
5489
π
2345.45
0
...
string
""
"😱"
"@&34"
"Œ"
"Hello"
"Goodbye"
...
Card(bool) == 2
Card(number) == ∞
Card(string) == ∞
Type cardinality
Boolean
true
false
Number
-23.4
-3
5489
π
2345.45
0
...
String
""
"😱"
"@&34"
"Œ"
"Hello"
"Goodbye"
...
null
null
null

Type cardinality
boolean
true
false
number
-23.4
-3
5489
π
2345.45
0
...
string
""
"😱"
"@&34"
"Œ"
"Hello"
"Goodbye"
...
null
null
null
undefined
undefined
undefined

 function setDieValue(value: number) {
     // ...
 }∞
 function setDieValue(value: DiceValue) {
     // ...
 }
 enum DiceValue { 
     ONE, TWO, THREE, FOUR, FIVE, SIX 
 }6
if (value < 1 || value > 6) {}
Card(modelisation) == Card(business)
Modelisation
Business


- Pending
 - Received
 - Cashed
 
- Pending
 - Validated
 
5
Study Case 1: Payment
class Payment {
    method: string;
    status: string;
}Card(Payment) = Card(string) * Card(string)
= ∞ * ∞ = ∞
class Payment {
    method: Method;
    status: Status;
}Card(Payment) = Card(Method) * Card(Status)
= 2 * 4 = 8
(Card, Received)
(Card, Cashed)
(Cash, Validated)
Study Case 1: Payment
("cb", "unknown")
enum Method { Card, Cash }
enum Status { Pending, Received, Cashed, Validated } Study Case 1: Payment
class Payment {
    private method: Method;
    private status: CardState | CashState;
    private constructor(m: Method, s: CardState | CashState) {};
}Card(Payment) = 1 * Card(CardStatus) + 1 * Card(CashStatus)
= 1 * 2 + 1 * 3 = 5
    static cardPayment(state: CardState): Payment {}
    static cashPayment(state: CashState): Payment {}Study Case 1: Payment
enum Payment {
    Card(CardState),
    Cash(CashState),
}
enum Payment {
    Card(CardState),
    Cash(CashState),
    Free,
}Study Case 2:
Natural Numbers
enum NaturalNumber {
    Zero,
    Succ(NaturalNumber),
}
Succ(Succ(Succ(Succ(Zero))))4
Study Case 3: Random Die

type DieValue = 
    One | Two | Three | Four | Five | Six
Random.uniform [One, Two, Three, Four, Five, Six]Random.uniform []???
Random.uniform One [Two, Three, Four, Five, Six]Not empty list = an element + a list
hand = Ace :: King :: Queen :: Jack :: Ten :: Nil
fifthCard = index 4 hand
sixthCard = index 5 hand
    hand : Vect 5 Card
hand = Ace :: King :: Queen :: Jack :: Ten :: Nil
fifthCard = index 4 hand
sixthCard = index 5 hand
    hand : Vect 5 Card
hand = Ace :: King :: Queen :: Jack :: Ten :: Nil
fifthCard = index 4 hand
sixthCard = index 5 hand
index : Fin n -> Vect n e -> e
    
hand = Ace :: King :: Queen :: Jack :: Ten :: Nil
fifthCard = index 4 hand
sixthCard = index 5 hand
    hand : Vect 5 Card
hand = Ace :: King :: Queen :: Jack :: Ten :: Nil
fifthCard = index 4 hand
sixthCard = index 5 hand
    hand : Vect 5 Card
hand = Ace :: King :: Queen :: Jack :: Ten :: Nil
fifthCard = index 4 hand
sixthCard = index 5 hand
index : Fin n -> Vect n e -> e
    Dependent types
Study Case 4: Vector
interface UserRepository {
    private User findUserById(String id);
}interface UserRepository {
    private Optional<User> findUserById(String id);
}Optional<User>
User
∅

connection.connect(function() {
  connection.query(myQuery, callback);
});connection.query(myQuery, callback);
connection.connect(otherCallback);Temporal coupling
MySQL connector in Node.js

connector.connect(function(connection) {
  connection.query(myQuery, callback);
});Temporal coupling
const passwordInput = new PasswordInput();
// ...
if (passwordInput.isValid()) {
  saveForm(passwordInput);
}
type Unvalidated = {_type: "Unvalidated"};
type Validated = {_type: "Validated"};

class PasswordInput<T> {
  
  
  
  
  
  
  
}  static create(): PasswordInput<Unvalidated> {
    return new PasswordInput();
  }  validate(): Error | PasswordInput<Validated> {
    // ...
  }saveForm(input: PasswordInput<Validated>) {
  // ...
}Phantom type
Make your code more explicit to avoid errors
Implicit is error-prone
upload:
  destinationPath: ./upload/images
  maxFileSize: 1024upload:
  destinationPath: ./upload/images/
  maxFileSizeInMo: 1024Paths.get(uploadPath, fileName);
Encapsulation
User user = new User();
user.setId(userId);
user.setName("Jordane");
user.setPicture(picture);User user = new User(userId, name);
user.setPicture(picture);

class UserService {
    @Autowired
    private UserRepository userRepository;
    public UserService() {
        // ...
    }
}class UserService {
    @Autowired
    public UserService(UserRepository userRepository) {
        // ...
    }
}Honesty

getName : User -> String
getName user = 
    user.name    user.password = 'p0wn3d';function getName(user: User) {
    return user.name;
}
    sendLoveLetterToMyEx();
getMessage : User -> String
getMessage myUser = 
    "Hello " ++ (getName myUser)Prevent mistakes before they can occur
Poka-Yoke
Avoid
Mistakes






 _______ _    _ _____  _____   _____  _____            
|__   __| |  | |_   _|/ ____| |_   _|/ ____|   
   | |  | |__| | | | | (___     | | | (___     
   | |  |  __  | | |  \___ \    | |  \___ \   
   | |  | |  | |_| |_ ____) |  _| |_ ____) |  
   |_|  |_|  |_|_____|_____/  |_____|_____/ 
 _____  _____   ____  _____  _    _  _____ _______ _____ ____  _   _ 
|  __ \|  __ \ / __ \|  __ \| |  | |/ ____|__   __|_   _/ __ \| \ | |
| |__) | |__) | |  | | |  | | |  | | |       | |    | || |  | |  \| |
|  ___/|  _  /| |  | | |  | | |  | | |       | |    | || |  | | . ` |
| |    | | \ \| |__| | |__| | |__| | |____   | |   _| || |__| | |\  |
|_|    |_|  \_\\____/|_____/ \____/ \_____|  |_|  |_____\____/|_| \_|
admin@server /project/build: - Why did the error occur?
 - Can I make it impossible?
 - If not, can I catch it earlier?
 - Can I make it more difficult to happen?
 
Technical ? Human ?
Fail loudly (Option, Either, Exception)
Refactoring, focus on types
Feedback cycle, unit tests
Final thoughts
- Get an IDE / linter
 - Reduce your domain (ADT)
 - Kill null and undefined
 
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. [...] This has led to innumerable errors, vulnerabilities, and system crashes [...]
– Tony Hoare –
Final thoughts
- Get an IDE / linter
 - Reduce your domain (ADT)
 - Kill null and undefined with 🔥
 - Make tests
 - Be honest – Modelize uncertainty
 
When you start to modelize your uncertainty, you quickly realize it's everywhere in your codebases
Final thoughts
- Get an IDE / linter
 - Reduce your domain (ADT)
 - Kill null and undefined with 🔥
 - Make tests
 - Be honest – Modelize uncertainty
 - Automatize everything
 - Use a langage that helps you
 
¯\_(ツ)_/¯


17K+ LoC
200K+ LoC
Links
- Making impossible states impossible by Richard Feldman
 - Fear, Trust and JavaScript by Nicholas Kariniemi
 - Bug Free. By Design by Johan Martinsson
 - Software: Designing with types by Mark Seemann
 - Parse, don't validate by Alexis King
 
Slides : https://bit.ly/2zDQlXR
@JoGrenat
Thank you!
Slides : https://bit.ly/2zDQlXR
@JoGrenat
Bug-free by design
By ereold
Bug-free by design
- 3,638