Be a Rockstar with STONE code

Are you looking for a solution to crush bugs, make your life easier, and be more productive? STONE has you covered.

STONE principles are an easy way to help you become more productive and create amazing projects. The principles are quite easy, and may be familiar. They are:

  • Single Responsibility Principle

  • Tell don’t ask

  • Organization and good naming

  • Never skip tests!

  • Extract as needed

Single Responsibility Principle

This is one of the foundations of SOLID principles from Robert (Uncle Bob) Martin and his amazing work with Object Oriented programming. Whenever I trained a new or junior developer I would always tell them if they were to remember anything from SOLID principles this is the best principle to remember.

Robert Martin defines a responsibility as: “A reason to change”, and that a class or module should have “one, and only one, reason to be changed (e.g. rewritten).” That sums up the Single Responsibility principle really nicely.

For a simple example think of a common third party API integration using the sandwich pattern that divides the integration into three parts:

  • The MessageBuilder — The only responsibility is to create the message that will be sent. This generally is transforming local domain specifics into the API domain specifics.

  • The Client — The only responsibility is HTTP related needs such as authentication and sending the actual message created by the MessageBuilder.

  • The ResponseHandler — The only responsibility is to handle responses by either returning the success or handling errors from the Client.

When the API integration is split into the three single modules above then testing becomes super easy. All you need to test with each module is their one responsibility.

What’s even better is when changes are required those changes too become easy. For example if the message changes it does not affect the other parts of the integration. Each module has its single responsibility and knows all it needs to do its job which leads to the next principle.

Tell don’t ask

Code flows and reads best when the logic flows in a similar direction. Too many questions and stops with “if” this and “if” that and you wind up with a mess that is hard to follow. Usually too many questions also means you’re missing an abstraction or violating the first principle of Single Responsibility.

If the module or class you are in does not have the proper responsibilities or information needed to make the decisions then really stop and think about whether this is the best place for this logic or not.

In a previous article, with more detail and code examples, there is a good analogy using children. If you’ve ever had to go somewhere with a bunch of children then you may know this can be difficult or easy depending on how well you trust the children.

You could ask the children one by one if they have their food, jackets, been to the bathroom, and have their shoes on.

With two or three children that’s easy but as the number of children grows then so does the difficulty and time it takes to get everyone ready. However, if you could just simply tell them to get ready and they get ready on their own because they know what they need to know in order to get ready to go, then going somewhere gets a lot easier.

That’s the basics of tell don’t ask. Design and build your modules or classes to have the right information and logic to be able to help your code flow without having to stop and ask a bunch of questions.

You should be able to tell your code to perform tasks such as get a users balance or send an email and the modules should know what messages to prepare, who to send it to, and how to handle any responses without having to stop the flow along the way to check the status of the process or errors.

Organization —Stay organized and adopt standards

If you want to be efficient then you need to be organized and have standards. Even though the code base you are working in may be private to your organization, the code is community property within your company or team, and many people need to use it.

Imagine a restaurant chain where every store is exactly the same (as is now) it is easy to start working in a new restaurant at a totally different location and be productive. The menus are the same, the floor plans are the same, the store rooms are all the same. It also makes going into any store and finding problems really easy.

If every restaurant were completely different then anyone joining, even with many years experience, has to start from a base of zero and relearn almost everything all over again. Standards and organization are critical to being productive.

Standards for development are anything from file organization to naming conventions. For example one standard, I try to follow with all projects, is all functions in a file should be alphabetical with public functions at the top and private functions at the bottom (again in alphabetical order). Tests should also follow this same convention.

Naming conventions of functions should adopt community standards whenever possible such as in Elixir fetch_something should return an ok tuple or error tuple where a get_something should return the struct or nil.

Very often I have seen developers spend a longer than needed amount of time on just working out naming and placement of functions in a file. I have also seen duplicate or similar code lead to bugs and confusion because files have almost no organization of the code.

With good organization and naming standards as new developers come into the project or when you need to go back to visit code again then those standards will pay off. With good organization you can easily adapt to the code around you. You can be more productive faster and so can anyone joining your project.

Having good file organization also means ensuring the code has proper test coverage is really easy and we all should follow the next principle.

Never forget to test!

We all make simple mistakes, sometimes our linters/compilers can catch those but logic mistakes will slip thru and ruin everything.

Quite often bugs are simple mistakes in plain sight. Therefore: All public functions should have edge case tests at a minimum. Its hard to stress how valuable tests can be, but all the effort will be worth the time.

All successful projects change over time for various reasons and when those changes occur it cannot be stressed how a well tested code base can help someone determine the effects of the changes and, most importantly, where to make fixes so the new changes can deploy successfully.

Provided the tests are written properly. A good test should follow a simple three step flow:

  • Setup the system state and verify if needed

  • Run the function that will affect the state

  • Verify the new state is correct

In previous projects I have seen tests that were getting “lucky” or basically the tests were simply “self fulfilling promises”. In other words the tests were passing green but the code was failing in the real world.

Part of the problem was the tests were not checking the system was in the correct state from the start of the test.

A good example of setting up the system state would be a simple get function that would look for a user based on an email. If you have only one user in the database then only that user would be returned from the database so almost any query could succeeded. There should be a few other users with different emails in the database so you know the user that was returned was in fact due to a good query. The system state before you run the function to be tested should be a simulation as close to real life as reasonably possible.

Another example, using Elixir or Ruby, is when a function returns a list. I have seen many tests passing green yet the list was empty. The person who wrote the test wrote assertions to verify the data in the list was accurate but since the list was empty then the assertions of the retuned list values were never ran. Worst part is no errors or failures were thrown either. A simple fix is to verify the list is not empty and contains values before you check the values returned are correct.

Tests should be easy to write, however if they are not then maybe the next principle can be helpful.

Extract when needed

Interface Segregation is also one of the SOLID principles and is very similar to this principle. With Elixir and most OO languages extraction and abstraction is very easy to do. Create a module and encapsulate the logic with public functions that should be easy to test and make development easier.

You will discover that you just made your life easier with a good new set of tools that you now know will work because they are well focused and tested. However, understanding when to extract logic into a new module or class can take time, but like all things over time you will improve. There are a few basics to look for.

For an easy start any logic that violates the above principles are a good sign your missing and abstraction.

Another simple way to know if your missing an abstraction is if your having a hard time writing the tests or the tests are overly complex. Tests should be easy and not require too much setup.

Finally lacking of tools. A good example is a recent project where there are special roles for certain users depending on which group they belong to. Instead of having a bunch of complex logic around the users it was easier to build a simple module to handle the logic of the roles. The new Roles module simplified testing and made the code easier to extend because we could test those roles and the logic around them extensively directly and then the functions in the new module were very helpful for sending the users notifications, authentication, and permissions.

STONE code summary

In summary most of these principles are probably being applied (hopefully) in your projects. Some of them are possibly familiar, but hopefully all make sense.

From past experience the two principles that are most often missing or skipped are testing and organization and they tend to hurt the most when absent in a project. Invest the time to STONE your code and be a “Rock Star!”

Previous
Previous

One small step for devs, one giant leap for software