The document discusses the author's experience with optional types in software design. It describes the author's shifting views on types over their career from enthusiast to professional to advocate for optional types. The author details how optional types can lead to cleaner, smaller code that is easier to change and requires fewer tests. Deep questions are also posed about the cognitive costs of abstractions, whether strong typing enables strong coupling, and how important names and contracts are in minimalist design. The conclusion is that minimalism exerts positive pressure on design through smaller code, better names, clear responsibilities and contracts, and clear tests.
15. Personal History
Code Designer (40 - now)
• what can I use to solve problems as quickly as possible?
Stance on types: optional types have distinct advantages
17. Minimalism in Code - Reasoning
• Custom code is expensive: to build, to test, to change, to refine until it
works, to run, to maintain
• The more lines of code, the higher the probability to have bugs
• Less code => smaller cognitive pressure
19. Rules
• Type not specified unless it’s important
• No casting
• No thinking about the types used, only about their capabilities
20. What I’ve expected
• bugs
• weird problems
• need to write more tests
• code that’s more difficult to understand
21. What I’ve got from optional types
• cleaner & smaller code
• easier to change
• fewer tests than I expected
• fewer bugs than I expected
NB: Some things were enabled by the language (groovy + grails), others are
strongly related to the idea of optional types
22. Project
• eventrix.co
• web platform for conference organizers
• groovy on grails (JVM language)
Not perfect!
• too much custom code - learned the hard way
23. Cleaner and smaller code
// Typical Java version
User user = new User(); // Duplication: 3 * User + ';'!!!
var user = new User(); // Optional type: 2 * User + ';'
// Groovy version
def user = new User() // 2 * User
// GORM
def user = User.get(userId)
24. Cleaner and smaller code
// Functional programming
// C++ lambda
auto increment = [](int value){ return first + 1;};
// C++ lambda with optional type
auto increment = [](auto value){ return first + 1;};
25. Cleaner and smaller code
// Functional programming
// C++ lambda
auto increment = [](int value){ return first + 1;};
// C++ lambda with optional type
auto increment = [](auto value){ return first + 1;};
// Simplest Groovy lambda
def increment = {it + 1} // optional type
// also some magic: what is 'it'?
26. Cleaner and smaller code
// C++ get list of names of users
auto names = transform(
users.begin(), users.end(),
[](auto user){ return user.getName(); }
);
27. Cleaner and smaller code
// C++ get list of names of users
auto names = transform(
users.begin(), users.end(),
[](auto user){ return user.getName(); }
);
// groovy get list of names of users
def names = users*.name
28. Cleaner and smaller code
// Do the same, assuming User class has firstName and lastName
// C++
auto names = transform(users.begin(), users.end(),
[](auto user){
return user.getFirstName()
+ ” ”
+ users.getLastName();
}
);
29. Cleaner and smaller code
// Do the same, assuming User class has firstName and lastName
// C++
auto names = transform(users.begin(), users.end(),
[](auto user){
return user.getFirstName()
+ ” ”
+ users.getLastName();
}
);
// groovy
def names = users.collect{it.firstName + ” ” + it.lastName}
30. A taste of optional types
• Tests using Spock
• Query
• Controller
• Command
36. Strong Coupling
What if I change the type from int to UserID?
(e.g. my company buys another company and we need to merge the systems)
• change User
• change UserDbService
• and change everywhere where int userId is used
37. Optional Type => looser coupling
class UserDbService {
boolean confirmEmail(userId) {
def user = User.get(userId)
user.emailConfirmed = true
user.save()
}
...
}
class User{
int userId;
...
}
38. Q3: How Important Are Names?
// a design pattern we use for
// transactional classes that
// interact with database
class UserDbService {
def confirmEmail(userId) {
def user = User.get(userId)
user.emailConfirmed = true
user.save()
}
...
}
39. Q4: How Important Are Contracts?
TEST_CASE(”X wins”){
Board board = {
{'X', 'X', 'X'},
{' ', 'O', ' '},
{' ', ' ', 'O'}
};
CHECK(xWins(board));
}
40. Let’s see the code!
Contract: Coordinates have two values
42. Beyond Types: Design Entities
We use clear, well defined design entities:
• Model: class defining the entity structure and mapping to database
43. Beyond Types: Design Entities
We use clear, well defined design entities:
• Model: class defining the entity structure and mapping to database
• Query: class that encapsulates database queries
44. Beyond Types: Design Entities
We use clear, well defined design entities:
• Model: class defining the entity structure and mapping to database
• Query: class that encapsulates database queries
• DbCommand: class that saves data to the database
45. Beyond Types: Design Entities
We use clear, well defined design entities:
• Model: class defining the entity structure and mapping to database
• Query: class that encapsulates database queries
• DbCommand: class that saves data to the database
• DbService: class that is called by Command or BusinessService to
make multiple operations in the database, usually with the help of
DbCommands
46. Beyond Types: Design Entities
We use clear, well defined design entities:
• Model: class defining the entity structure and mapping to database
• Query: class that encapsulates database queries
• DbCommand: class that saves data to the database
• DbService: class that is called by Command or BusinessService to
make multiple operations in the database, usually with the help of
DbCommands
• BusinessService: class that is called by Command to facilitate a
business process
47. Beyond Types: Design Entities
We use clear, well defined design entities:
• Model: class defining the entity structure and mapping to database
• Query: class that encapsulates database queries
• DbCommand: class that saves data to the database
• DbService: class that is called by Command or BusinessService to
make multiple operations in the database, usually with the help of
DbCommands
• BusinessService: class that is called by Command to facilitate a
business process
• Command: class that is called by Controller to validate specific HTTP
requests and delegate to the right Services the execution.
49. Minimalism exerts a positive pressure on design
We still need to understand the code. Without types, how do we understand
it?
• smaller code
50. Minimalism exerts a positive pressure on design
We still need to understand the code. Without types, how do we understand
it?
• smaller code
• better names
51. Minimalism exerts a positive pressure on design
We still need to understand the code. Without types, how do we understand
it?
• smaller code
• better names
• clear responsibilities
52. Minimalism exerts a positive pressure on design
We still need to understand the code. Without types, how do we understand
it?
• smaller code
• better names
• clear responsibilities
• clear contracts
53. Minimalism exerts a positive pressure on design
We still need to understand the code. Without types, how do we understand
it?
• smaller code
• better names
• clear responsibilities
• clear contracts
• clear tests
54. Always Optional Types? NO
Clear problem domain, clear solution, not expecting change, used by
specialists => strong types, validation at compile time
Expecting change, evolving problem and solution domain => minimalism,
changeable code, fewer constraints
56. Let’s think differently!
“I want the compiler to do as much checking for me as possible”
vs.
“I want to write the code that makes sense, and the compiler / interpreter to
figure it out”