The 10/100 Principle – Following this one development rule will improve your code

There is a simple guideline that every developer can follow that will make their code easier to manage and easier to test, or essentially, make your code S.O.L.I.D. (You should be familiar with S.O.L.I.D. principles of software engineering. If not, look it up now.)

The 10/100 Principle

Question: What is the 10/100 principle?

The 10/100 Principle is liked training wheels for writing code that follows S.O.L.I.D. principles.

Answer: It is a simple rule that developers should follow to keep their code concise and clean. It involves keeping in mind two warning signs for bad code.

  • Warning #1 – If a method reaches 10 lines, you need to stop and consider refactoring the method.
  • Warning #2 – If a class reaches 100 lines (comments and brackets included) you should stop and consider refactoring.
  • Warning #3 – If an interface reaches 10 combined methods and properties, you need to stop and consider refactoring the interface to be multiple interfaces.

Like me, you have probably heard both rules before. I see the rule to keep methods short written over and over again in other blogs and other software engineer’s guidelines. But I rarely see this touted as the #1 most important rule that all developers should learn first.

Why should developers learn the 10/100 principle first?

Because it is the single most effective rule to improve code that any developer can learn in less than a minute. No other rule can be learned in other a minute and improve a developers code as much.

It is hard to know how to write S.O.L.I.D. code, especially as a new developer. However, just like when learning to ride a bike, you start with training wheels, in order to write solid code, you should start with the 10/100 Principle. It is your training wheels for solid code.

I gave this rule to a first year developer (not college educated) and after following this rule, he wrote “proof of concept” code. As many more experienced engineers know, proof of concept code sometimes becomes release code. It is usually a horrible tragedy when this happens. The code is usually rough and untestable and filled with scripting or linear processing. However, in this case, that issue didn’t occur. All the classes were under 100 lines (most considerably under 100 lines). All the method were easily testable and under 10 lines. Sure there were a few that needed to be broken up. The code wasn’t perfect. But the most important and amazing thing about his code were the design patterns he used without even knowing them. He used the proxy and bridge patterns. He used the builder pattern. He used a rudimentary version of the Factory Pattern. He had small and easily manageable singletons. I even found the chain of responsibility pattern in his code. He also had a number of “Extension methods” since he was using C# which further increased the testability and the readability of his code.

When a developer asks me if they should study design guidelines and learn the many design patterns common to the industry I say, “Yes, of course study them.” However, if a developer follows the rules above, they will naturally find themselves using many of these patterns without even knowing them. It is the perfect rule to keep your code inline until you are an expert.

Why does the 10/100 principle work?

Look at the first year developer’s experience mentioned above. His code wasn’t perfect, but just the effort he took to keep the classes under 100 lines the methods under 10 lines forced him to create other objects that made more sense for the task at hand. These other objects naturally fell into the realm of well-known design patterns. It works because all the classes are kept small.

The 10/100 principle helps with the Keep it super simple (KISS) rule.

The 10/100 principle helps with the Single Responsibility Principle (SRP),  though it doesn’t guarantee the SRP, it helps users naturally stay close to that rule. Sure they may have C# class do two things instead of one because two things fit in the 100 lines allowed for a class. However, how many times have you touched someone else’s code and found a class doing a dozen things in a class that is more than 500 or 1000 lines. To fix this code you have to redesign and break this class into a dozen or more other objects. I think you would be more than happy to only have a class doing two things. Sure you need to break it still, but you only need to break it in half, which is much easier.

Summing Lines in a Method

If you call MethodA from MethodB, and MethodB is not abstractable (isn’t a method on an abstraction or interface), then the methods add together.

Look at the following class.

public class SomethingDoer
{

private readonly ISomeDependency _someDependency;

public Example(ISomeDependency someDependency)
{
_someDependency = someDependency;
}

private DoSomething1()
{
DoSomething2();
DoSomething3();
_someDependency.DependencyWork();
}

private DoSomething2()
{
// ... 8 lines of code
}

private DoSomething3()
{
// ... 9 lines of code
}
}

How many lines is DoSomething1()? Is it 3?

No. this is a 20 line method.

How? Since it calls methods that are not abstracted, those lines sum together. DoSomething2() which is 8 lines, and DoSomething3() which is 9 lines, makes 17 lines, plus its own three lines.

3 + 8 + 9 = 20

Why? When you go to test this, can you mock or fake DoSomething2() or DoSomething3()? No, you can’t. You have to include them in Unit Tests because if the code calls DoSomething1(), the code is also calling DoSomething2() and DoSomething3(). This isn’t just a test thing. Tests just reveal the truth. The code is breaking the single responsibility principle. DoSomething1() is not performing a single responsibility. If the method is doing the correct responsibility, then the multiple responsibilities might be at the class level. You probably need to break this class up into 3 classes, and the other two methods would be abstracted into sub classes.

It is a guideline that can be broken

The 10/100 principle is a guideline. When a class reaches 100 lines you stop and think. When a method reaches 10 lines, you stop and think. However, thinking is the most important part. Should this method take 10 lines? Maybe it should. Should this class be longer than 100 lines? Maybe it should.

There are always exceptions

  • Generated code – it is as big as the generator makes it.
  • Interface implementation –  You may have to implement an interface (such as IList) and when you are done, your class is already over 100 lines and you haven’t even added any methods other than the interface’s methods. Well, some might argue that the developers of your interface could have used a smaller interface (the I in S.O.L.I.D.) but there is usually nothing you can do about that, as IList is not your code. This class isn’t going to be able to follow that 10/100 principle. That is OK.
  • Unit Tests – I don’t always follow the 10/100 Principle, however, keeping your unit tests testing one thing is still important, and you will find your unit tests natural stay close to this rule when testing code that follows this rule.
  • Algorithm – Sometimes implementing an algorithm in one method will result in a method much more than 10 lines. You could break it up, but maybe that has performance issues.

Keep studying

Remember the 10/100 principle takes less than one minute to learn. It is the single most effective rule a developer can learn in 1 minute to improve their code. It is not the end.

Sure, this rule is always good to follow, even for senior developers, however, this rule doesn’t solve everything. It doesn’t teach interface-based design and good decoupling. It doesn’t make developers follow the open/closed principle or the substitution principle. However, it usually prevents code from becoming spaghetti code. The 10/100 principle keeps the blocks small and easily to work with and easy to fix. So when you see the code and you have to revamp it to include dependency injection and decouple it, you will have a much easier time.

Senior Developers Benefit too

There are two reasons senior developers should follow this rule.

  1. They have never really been taught to write solid code and still need to learn
  2. They are more skilled and have more tools to help follow the 10/100 Principle.

Never Learned to write solid code

It isn’t always a developers fault that they didn’t have great leads. Often they are led astray by their first teams. Or they are thrown to the wolves without a team.

Adopting the 10/100 pattern for all new code is easy for a senior dev to do to immediately change their mindset to write solid code.

More tools in your toolbox

Senior engineers should make larger efforts to the follow the 10/100 Principle because the can. New developers aren’t going to have any idea about more complex development tools. One such example of this is Aspect Oriented Programming (AOP) to handle cross-cutting concerns. New developers will accept that some instance where the 10/100 principle is broken that maybe a senior developer shouldn’t accept. Senior developers should have heard of cross-cutting concerns and should have at least heard of AOP. Often when a method is probably kept in scope, but still reaches over 10 lines, it may be using a try/catch block.

Check out this code found in a single method. The stub code alone is 35 lines. With the missing log, the method was over 50 lines. Is this acceptable just because it is doing a try/catch block?

public StreamReader TryReadFile(string path)
{
    try
    {
        return File.OpenText(path);
    }
    catch (UnauthorizedAccessException ane)
    {
        // handle exception
    }
    catch (ArgumentNullException ane)
    {
        // handle exception
    }
    catch (ArgumentException ane)
    {
        // handle exception
    }
    catch (PathTooLongException ane)
    {
        // handle exception
    }
    catch (DirectoryNotFoundException ane)
    {
        // handle exception
    }
    catch (FileNotFoundException ane)
    {
        // handle exception
    }
    catch (NotSupportedException ane)
    {
        // handle exception
    }
}

Well, if you don’t accept this as OK, (well, the cyclomatic complexity is high why would you accept this as OK?) and you seek out how to resolve this, you are going to run across AOP solutions. With AOP, this method could look like this:

[HandeFileReadExceptionsAspect]
public void TryReadFile(string path)
{
    File.OpenText(path);
}

The HandeFileReadExceptionsAspect is now a separate class that is reusable for every instance where you read a file. You now have common code handling the File.Read exceptions and your method that was 50 lines is now 5 lines. Your HandeFileReadExceptionsAspect class will most likely be under 100 lines. So your codes is now cleaner, more decoupled, easier to read, easier to use, and follows the 10/100 Principle.

Conclusion

I preach the 10/100 principle to all developers, new and old. I am not sure that anyone else calls it the 10/100 principle. If you call it something else, let me know.

2 Comments

  1. professorjava says:

    I agree with most of what you advocate here. However, I disagree that extracting exception handling into a separate class then using AOP to inject it back in is a good solution. First of all, you haven't actually reduced the size of the code. You have simply hidden most of it from view. You still have the same cyclomatic complexity, but now your analysis tools can't see it. That is very different from actually reducing complexity. My biggest object, however, is that you have now hidden vital code from future maintainers of your method.

    Unlike many cross-cutting concerns, exception handling isn't orthogonal to the task being coded, it is integral. Despite how different the syntax appears, exception handling is as much a part of the logic as any "if-then" or switch statement. Extracting it into a separate, reusable class may be a great option. But make the use of that class obvious. Don't use AOP to sneak it in by the back door.

    • Rhyous says:

      Thanks for the comments. Your are correct. In our instance, the file IO exceptions are orthogonal to our code. This code existed dozens of times in our project. At first it may appear that the cyclomatic complexity is just moved to a different class. But dozens of methods existed with this same cyclomatic complexity (cc). With n methods, cc * n, where n is greater than 1 is always worse that just cc * 1. Also, once a complex piece of code becomes a separate tested class, I'm sure you are not surprised how a developer can suddenly see how to do this more generically and nearly eliminate the cyclomatic complexity altogether. With an exception-to-logstring dictionary and a single method that passes in the exception and returns the log string, this suddenly becomes a small handful of unit tests covering what is now a code path with little to no branching. Once the code is abstracted into a single class (in our case an aspect), these changes are easier to see.

      Also, the AOP aspect class is tested in a separate Unit Test. Perhaps in Java test tools AOP is not include in code coverage and CC reports, but in Visual Studio, the aspect code still shows up as tested or untested and still has its own CC.

      Also, your comment almost implies that AOP is only for orthogonal code. That is the easiest way to teach AOP and the easiest examples are orthogonal, sure. But AOP is also quite useful for accomplishing the single responsibility (SRP) and don't repeat yourself (DRY) needs of integral code.

Leave a Reply

How to post code in comments?