Reuse Code, Not Objects

We all know of the importance of code reuse and DRY code. It is important to remember that this applies to code, and not to objects!
A common anti-pattern I see in stateful classes is something along the following lines:

class SomeStatefulClass {
  private ResponseType responseType;
  private final ErrMessages errMessages;
  
  public SomeStatefulClass() {
    this.ResponseType = null;
    this.errMessages = new ErrMessages();
  }
  
  public void init() {
    errMessages.clear();
  }
  
  public Response process(Request request) {
    try {
      // Process the request.
      // Update stats.
      // Update otherState.
      Response response = computeResponse();
      responseType = computeResponseType();
      return response;
    } catch (Exception e) {
      this.errMessages.append(e);
    }
    return null;
  }
  
  public LogMessages getErrMessages() {
    return this.errMessages;
  }
  
  public ResponseType getResponseType() {
    return responseType;
  }
}

This design pattern is a major code smell. Ironically, such classes are prone to be misused through reuse. A common example of this is reusing the object within a loop:

public void process(List<Request> requests) {
  final SomeStatefulClass statefulObject
      = new SomeStatefulClass();
  Response response;
  for (Request request: requests) {
    statefulObject.init();
    response = statefulObject.process(request);
    appendResponse(response, statefulObject.getResponseType());
    
  }
}

The issue here is subtle, but dangerous. Consider what happens if one of the requests in the list of requests passed to process() causes statefulObjectto throw an exception inside computeResponse(). Dutifully, this exception is caught by process() and it returns null. However, note that the value of responseType in statefulObject was never modified by processing of this request, and therefore, it still hold the ResponseType of the previous request! Therefore, the line appendResponse(response, statefulObject.getResponseType()); is now passing in a null response and the response type of the previous request!
These types of bugs are subtle and a pain to track down.
And this happened because we chose to reuse the statefulObject. If we were to use a new instance each time, this would not really be an issue.
Moral: If feasible, do not reuse objects; create new instances and throw them away when done!

Code reuse can prevent bugs

I am as surprised as you are at the title. The statement does seem like “Duh!”, but much to my embarrassment, I have seen enough code in many code bases to need to reiterate this.

The latest example was the following bug that I spotted. First some background. The caller to this function can choose any subset of a given set of ‘modes’ or ‘features’. The caller represents this via a bit-mask. So 1 represents feature 1, 2 represents feature 2, 4 represents feature 3, 8 represents feature 4, and so on. So, if the caller requests with bit-mask 3, then it represents features 1 and 2, and bit-mask 10 represents features 2 and 4.

The following piece of code is support to filter out requests depending on the requested set of ‘modes/’features’.

if ((bitMask & ModeEnum.FEATURE_FOO.getValue()) == 0) {
  // FEATURE_FOO has already taken care off the by some other module. So do nothing here.
  return null;
}

/* Some more code here */

if ((bitMask & ModeEnum.FEATURE_BAR.getValue()) == 0) {
  // For now, we process the request only if caller explicitly specified FEATURE_BAR.
  return null;
}
/* more code to process the request with FEATURE_BAR. */

The code above has a bug because the comment inside the first if-check does not match the if-check’s logic. The intent was to skip processing the request if FEATURE_FOO is enabled. Instead, it does the exact opposite.

The naive way to fix it would be to replace

if ((bitMask & ModeEnum.FEATURE_FOO.getValue()) == 0) {

with

if ((bitMask & ModeEnum.FEATURE_FOO.getValue()) != 0) {

However, this misses the more important point of why this bug occurred in the first place. The simple answer to that question is that this bug occurred because the author ignored the principle of code reuse. By putting that principle into practice, a cleaner way to write this code (and therefore fix this bug and prevent similar bugs in the future) is as follows.

We first encapsulate the logic to detecting various modes via this function

boolean hasRequestedFeature(int bitMask, ModeEnum feature) {
  return (bitMask & feature.getValue()) != 1;
}

With that function in place, the new code looks as follows

if (!hasRequestedFeature(bitMask, ModeEnum.FEATURE_FOO)) {
  // FEATURE_FOO has already taken care off the by some other module. So do nothing here.
  return null;
}

/* Some more code here */

if (hasRequestedFeature(bitMask, ModeEnum.FEATURE_BAR)) {
  // For now, we process the request only if caller explicitly specified FEATURE_BAR.
  return null;
}
/* more code to process the request with FEATURE_BAR. */

This make the code more readable, and as long as we reuse the hasRequestedFeature() function, such bitwise operation fragility will not reoccur in the code.

Is this obvious? I think so. Was it necessary to belabor the point? Empirical evidence seems to scream “YES!”.

Git may not be the best for SaaS companies

Yes, I realize that this is going to be pretty controversial 🙂 Let’s dive in, shall we?

In the past half decade or so, Git has sky rocketed in popularity and is becoming the defacto choice for version control. While I understand its popularity, I have found it to be a poor fit for one specific, but popular, environment: SaaS development in a medium to large size enterprise.

Let’s take a quick look at what developing software in a SaaS enterprise is like. For the most part, it involves a significant number of developers concurrently committing code to a single master branch with frequent releases to prod and no long lived branches or versions. In a services environment, it also includes coordinated deployment of multiple services that have complementary server and client API spec.

While Git has been used in such environments with varying degrees of success, I have seen teams really trying to work around Git rather than Git just work for them. I have seen Git get in the way when teams get large, when teams get siloed into their own branches, when teams starts working with junior developers, and when having to develop across multiple services. While there are mechanisms in Git workflows and tools to mitigate this, it only adds additional complexity to developing software instead of taking it away.

Continue reading “Git may not be the best for SaaS companies”

When should you build for survival?

source: http://beaconbusinessmarketing.com/success-vs-survival/

Previously, I wrote about building for survival vs. success. Briefly, when building for survival, your only goal to get the product working for the specific usecase, and in contrast, when building for success, you are building to solve a bigger class of problems within the broader context of your solution space. In this post, I will talk about when you should be build for survival, and when for success.

Continue reading “When should you build for survival?”

Are you building for survival or Survival?

Source: https://www.youtube.com/attribution?v=oW2i6QpnmyY

In my experience, the approach to building a software artifact often falls into one of two types: building for survival, or building for success.

When building for survival, your only goal to get the product working for the specific usecase(s) that will save your skin. In contrast, when building for success, you are building to solve more than just the immediate problem; you are building to set up building blocks that is incidentally used to solve the immediate problem, but can also be adapted to solve a larger class of problems within the same context.

This post is not about when to choose what approach. Instead, it is about what each of the two approaches look like, and what purposes they serve. A subsequent post will talk about when I think each approach is appropriate.

Continue reading “Are you building for survival or Survival?”