The Most Important Lesson I Have Learned About Coding

Paul Heintzelman
Paul Heintzelman
5 min readMar 21, 2021

--

I have learned a lot in over 10 years in software. But if someone asked me what is the number 1 thing I have learned, I wouldn’t have to think too hard.

It is to keep state and business rules separate.

Photo by Timon Klauser on Unsplash

State is all your data, flags, properties, whatever it is that you want to keep track of.

Business rules is the code that controls all the operations in your application. It dictates how your code should transition from one state to another.

Which is exactly why these things should be kept separate. The rules about how and when one state becomes another shouldn’t get tangled up in the very state we are mutating.

Photo by Paul Steuber on Unsplash

Let’s say we have a User Object

class User {
id,
name
}

Now we want our user to be able to login so we add a login method.

class User {
id,
name,
login(password) {...}
}

But this isn’t the only thing we want our user to do, I don’t know, let’s say our Users can also get married to another User.

class User {
id,
name,
login(password) {...}
marry(user) {...}
}

But that’s not all, we also want to be able to save User to our database.

class User {
id,
name,
login(password) {...}
marry(user) {...}
save() {...}
}

If you are wondering what the hell happened to single responsibility principle, you would be right.

Photo by alexey turenkov on Unsplash

Everything that we add to our User is just creating more responsibility. Another ball to keep up in the air as we try to juggle it all.

And this is just the start, if you take every part of your code that has to do with a user and cram it into this one single class you are creating a giant mess.

But there is something else not quite right here.

A strange coupling between the state and our methods.

Let’s say one user wants to marry another.

user1.marry(user2)

but why not?

user2.marry(user1)

Our code design has created an ambiguity and, although in this case it doesn’t matter much, it doesn’t feel right. Does marry need to mutate both users?

Let’s fix our User class by separating our business rules from our state. We’ll do this by taking the methods out of User.

class User {
id,
name
}

Ahh… much better. But we still want users to be able to login.

class AuthService {
login(user)
}

And Marry

class OrdainedService {
marry(user1, user2)
}

And Save

class UserRepository {
save(user)
}

That’s it. By separating our business rules and our state we clean up the code. We can now extend the functionality of our user as much as we want without creating clutter.

Photo by Sigmund on Unsplash

The overall structure of our code becomes classes which hold state and classes that hold business rules. This is sometimes called entity service separation, with entities representing our classes that hold state. And services house all of the business rules.

It’s funny, really, this is something that C got right but that is a story for another day.

Entity Service Separation is base principle that can be used with any programming language. Class can be interchanged with module or package, or gem, or whatever.

And in some cases, like if you are using TypeScript with JavaScript, then this principle is baked in.

Benefits of Entity Service Separation

  1. We don’t violate the single responsibility principle
  2. Our code is extendible without creating clutter
  3. It is easier to write pure methods which have no side effects (as there is no state to mutate)
  4. Methods are much easier to test
  5. We increase code portability and reusability since the services are not coupled to our state
  6. Our entities can be very simple, no need for getters and setters
  7. Our entities are serializable

Cons

none.

There is a reason this is the most important thing that I have learned. I promise if you do this your code will be much much cleaner.

Exceptions (but not really)

There are two cases which are sorta exceptions but not really. Data structures.

If you are making a data structure it absolutely should have state and methods. But these methods should contain no business rules, they should only be used to manipulate the state within the data structure. For example, if you make a cache (see how to write a cache), you want methods that let you add data to your cache but you should never add a method that calls your api. That would be adding business rules to a class which holds state.

Sometimes functions are state. This is ok (although there is likely a better design), once again this doesn’t actually violate our principle because these functions shouldn’t hold any business rules.

Conclusion

If you are going to make one change to the way you code, it should be this. Keep your state and business rules separate and you will find that the quality of your code gets much better. I promise.

Like this? Check out some of my other posts.

--

--