Sunday, November 13, 2022

Reasons for poor quality Software Systems

I've seen issues with software system quality over the years - it wasn't just one type of organization or domain. Why is this true even with continued innovation and best practices? I would expect to see improvements over time.  There are plenty of tools to help improve software quality.

Great IDE's

  • IntelliJ
  • Eclipse
  • Visual Studio

Language and compiler improvements

  • Java 11-19

Source scanning tools

  • PMD
  • SonarQube
  • FindBugs
  • CheckStyle
  • SpotBugs
  • VeraCode & Twistlock
    • more related to security but IMO there is a relationship between security and quality

Code generation tools

  • Lombok

Additionally, features like Java annotations are leveraged heavily nowadays which simplify configuration and lower overall code quantity/complexity.

I've found two common aspects across a number of organizations which I think are related to the quality issues.  The first aspect is lack of attention to the warnings listed in common IDE's.  I've seen some applications showing over a thousand warnings when I first worked with them.  This crosses implementation languages/run-times as well - Java, Python, JavaScript, NodeJS, etc. Of course, often IDE's only show the first 100 or so warnings so you don't even have a complete list typically.  

The second aspect is the use of technology, such as general annotations and also Lombok, without paying attention to all the details regarding how each related annotation works (for default and non-default settings).   Default behavior isn't always the required behavior. An example of this is:

create a class such as:

@Data

@AllArgsConstructor

@NoArgsConstructor

public class BaseData

{

/**

* Represent identity; lombok generated equals() should

* utilize this to determine result.

*/

@NonNull

private String baseDataId;

}

 

which is fine on its own.  Note that the name-prefix "Base" easily implies that you may create sub-classes though. Let's do that here;


@Data

@RequiredArgsConstructor

public class DerivedData extends BaseData

{

/**

* Represent identity; lombok generated equals() should

* utilize this to determine result.

*/

@NonNull

private String derivedDataId; 

}

No compile error is generated.  Will this work?  It depends - how are you expecting to use this class?  If you expect that the equals() method will account for both the baseDataId and derivedDataId then you will have an unhappy surprise. 

If you pay attention to warnings in your IDE though you will note a warning is generated. 

Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.

I've not seen PMD, Sonar, etc complain about this so those tools leave you feeling like "everything is awesome".  

Add some unit tests which exercise the equals() method in the situation where you are using the DerivedData class and have instances with matching derivedDataId values but different values for baseDataId.  You'll probably write the test indicating the instances should be non-equal.  Will you be surprised when the tests you setup indicating instances should be non-equal actually fail?

The reason it fails is that the generated equals() in the DerivedData class doesn't know anything about the members of the BaseData class.  So equals() in the derived class only uses the derivedDataId member. 

If you review the warnings in your IDE, you will find that it implies how to resolve this if it wasn't intentional - add annotation '@EqualsAndHashCode(callSuper=true)'.  But notice I said "it implies" because it directly tells you how to ignore the warning about this default behavior.  So a good question is - how often is the default behavior correct versus the logic always calling superclass equal() / hashCode()?  I suspect that the current default is opposite from what would be a safer default.

And this brings us back to IDE warnings being ignored.  If you ignore / hide those warnings you easily end up missing these types of errors which can show up as "anomalous production issues" at some point.

I think that an aspect of all this is the attempt to (over)simplify everything. So many details are abstracted away that developers won't see implications of all the "defaults" and "configuration by exception", etc that occurs is so many systems.

Thanks for reading..

Scott


No comments:

Post a Comment