In this post, we'll take a look at the effect of using implicit vs explicit logic in your code.
When developing any kind of software, you need to add various kinds of logic in your code - for loops, if statements, comparisons, you name it.
In many situations, you will have several options to choose from. Let's look at two examples.
Restricting the Email Feature for Specific Users
You are developing a website that uses the following simple User
class in the back-end.
public class User {
public enum UserType {
INTERNAL,
EXTERNAL
}
private UserType type;
private boolean isTemporary;
private String email;
// other fields hidden for clarity
}
The website has a feature to send emails, and you have just been asked to ensure that the email feature can only be used by a users who are classified as Internal.
How would you implement this request?
Implementing a Solution for Email Restriction
To restrict a user's ability to send email, your first thought might be to add a condition in the front-end to show or hide the "send email" form based on whether the user's type is Internal. After all, isn't that what the request was?
So, you add a check in the front-end to show the email form only if the user's type is Internal. You also add the same check in the back-end, in the send email API call.
Done! Right?
Well, it works, for sure. However, this solution places multiple meanings on the user's type field. Therefore, it would be a good idea to ask yourself (and perhaps also the business): what is the meaning of the Internal user type?
Does it inherently mean that this user is allowed to send emails? Will future developers and users of the system immediately understand this logic?
The answer to these questions is probably no. The implicit logic attached to the Internal user type is that it also means that the user can send emails. A better solution is most likely to make this logic explicit and add a new field, for example allowSendingEmails
.
public class User {
public enum UserType {
INTERNAL,
EXTERNAL
}
private UserType type;
private boolean isTemporary;
private String email;
private boolean allowSendingEmails;
}
This approach has a few consequences:
- You will need to add logic to the User class, to ensure the
allowSendingEmails
field is set to the correct value if the user's type changes; - It will be easier to modify, in the future, the conditions under which a user is allowed to send emails;
- It will be possible to use the new field in a query, report or list in the front-end, to filter users by whether they are allowed to send emails;
- It will be clearer to future readers of the code what this field does, reducing the likelihood of confusion.
Another Case: Orders Created in Different Systems
You are the lead developer of a custom CRM system. In this CRM, users can create orders for customers. When finalised, an order is sent to the Order Management System (OMS), which processes the order and handles the logistics and financials.
To send these orders, you created a periodic job that runs every 2 minutes, finds all finalised, unsent orders, sends them to OMS, and marks them as sent.
The Product Owner creates a new request, explaining that users can also create an order directly in OMS. These orders need to be synchronised with CRM, so that CRM users can see them when communicating with customers.
You and the OMS team quickly agree that, for each order created in OMS, OMS will perform an API call to CRM to create a copy of the order there as well.
What changes would you make to add this feature? What logic would you add to ensure that orders, synced from OMS to CRM, are not incorrectly sent back to OMS by the sending job?
Extending the system
First of all, the imported orders will need an owner. So let's create a system user for that. The OMS system can use this system user to perform the API calls to create the orders, which works out nicely.
However, without any other changes, any order created by OMS would be seen by the sending job, and sent back to OMS, creating a duplicate.
Maybe OMS can create the order with send_status = Sent
?
This would prevent the order from being sent back to OMS.
But... how would you differentiate between orders created in CRM directly, and orders that came from OMS?
You could always look at the owner of the order. If that's the system user, that means the order was originally created in OMS.
While this solution will work, it is far from elegant. Here are a few reasons why.
- The orders created by OMS have
send_status Sent
, but they were never sent; - If the system user is somehow used to create an order in CRM directly, the system will mistakenly think it was created in OMS;
- If other clients or systems later also use the create order API, the logic to determine where the created order came from becomes more complicated.
The reason this solution feels clumsy, is that there is implicit logic associated with the owner field. Not only does this field indicate who the owner is, but also whether the order was created in OMS or CRM.
There is a simple solution: add a new field, such as source
, with values crm
and oms
. By explicitly storing the source of the order, all the above problems disappear.
The only challenge left is to ensure this field is set correctly. For example, you could automatically set the source
to oms
if the order is created by the OMS system user.
Conclusion
Attaching different meanings to a single field is an example of using implicit logic. This might like a good idea in the short term - because it generally saves time - but it will cause problems in the long run.
When deciding between different solutions, always keep this in mind:
Explicit is better than implicit.
In the code that you work on, keep an eye out for logic that feels out of place or illogical. If the cause is implicit logic, try to find a way to make it explicit!