Make illegal states unrepresentable


This really great quote by Yaron Minsky mentioned in this talk made me to think about it a bit more. While there are some great examples in the talk and I strongly suggest to watch it, in this post we will try to think about two ways to achieve this:

###Language support

It is true that programming language is just a tool and one could argue that you can write good code in any programming language, still it can provide us nice features to express our problems in a better way.

One example of that are Swift enums. They are really powerful in a lot of ways but let’s consider one appropriate example. Imagine we have a Person class:

enum PersonType {
	case Staff
	case Client
}

class Person {
	let type: PersonType
	let isManager: Bool
	
	init(type: PersonType, isManager: Bool) {
		self.type = type
		self.isManager = isManager
	}
}

Now, a person can be either Staff or Client but we have a business rule that only Staff person can be a manager.

If we want to create manager we can simply do it:

let manager = Person(type: .Staff, isManager: true)

but the problem is that we can to do this as well:

let client = Person(type: .Client, isManager: true)

which is violation of our business rule. While this might look like minor issue and it can be avoided with proper documentation and discipline, it would be nicer that compiler doesn’t even allow us to do this.

Swift enums with associated values allow us to represent this in a really nice way:

enum PersonType {
	case Staff(isManager: Bool)
	case Client
}

class Person {
	let type: PersonType
	
	init(type: PersonType) {
		self.type = type
	}
}

So Person type can be either Staff or Client, but we can specify isManager only on Staff person. The way to create Staff manager is:

let manager = Person(type: .Staff(isManager: true))

and there is no way to create Client manager so we have our business rule nicely represented, we don’t have to write additional documentation and our code is self explanatory.

###Domain modelling

While it is important what features programming language has to offer, I think it is even more important to properly model problem our software is trying to solve.

It is important to recognise domain objects and make them explicit. One nice and simple example of making things explicit in domain is specification pattern. For example, our domain object User can have password. Password has some rules that it needs to satisfy. One way to deal with this is to validate user input in UIViewController. But if we think more about it, this is just a domain rule. User has password and that password has to satisfy specific set of rules. If password business rule is changed for some reason, developer should not search for password checks across the project.

So lets extract it in its own object:

protocol Specification {
	func isSatisfiedBy(object: String) -> Bool
}

protocol PasswordSpecification: Specification {
	let minLength: Int
	init(minLength: Int) {
		self.minLength = minLength
	}

	func isSatisfiedBy(object: String) -> Bool {
		return self.minLength < object.length
	}
}

We can then instantiate our rule:

let passwordSpecification = PasswordSpecification(minLength: 6)

and then pass it to wherever we need it and simply check it:

let userInputPassword = "password"
passwordSpecification.isSatisfiedBy(userInputPassword)

This is very simple example of specification pattern. We could improve Specification with AND, OR and NOT operators and then we can compose domain rules.

To conclude, we should spend more time thinking about what is the most appropriate representation of the problem we are trying to solve. Programming language can help us, but understanding problem and making domain objects explicit is more important.