"Outside-in" by Namogoo

When Namogoo Conquered Sparta

When we started developing Namogoo’s code, a while back, one of the most important goals we set to ourselves was to keep it light, minimal, ascetic, open, fast, yet powerful and effective. This goal was critical for the mission we set out to do. To achieve these goals, we decided to adopt some of the main principles of Spartan Programming.

“There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare.

Complex code tends to be challenging due to the inability to see all the code at once (long methods, many variables and method arguments). Its complex state (mutability and hidden side effects), and complex controls (nested IF statements, complex loops and vague conditional expressions) are also quite the challenge for developers trying to build an effective system.

We made a conscious decision to design our code differently. Spartan programming strives for simultaneous minimization of all of the following measures of code complexity:

Horizontal complexity – the depth of nesting of control structures, just as the total line length
Vertical complexity – module length in lines
– Token count
– Character count
Parameters – the number of parameters to a function or a generic structure
– Variables
Loops – the number of iterative instructions and their nesting level
Conditionals – the number of if and multiple branch switch statements

It sounds very ambitious, and it really is. Step by step we started to adopt the Spartan principles, trying to understand each one of them, learning its advantage and disadvantage. It helped us stay focused and pick what worked best for our solution.

After a joint team effort, here are the key principles that Namogoo decided to adopt. It may not work for all developers but it made the most sense to us:

Frugal Use of Variables

The main goal here is to minimize the number of variables: inline what is possible, minimize what cannot.

But remember, not all in-lining opportunities should be exploited.
For example:

Which may have resulted from in-lining of a more explicit statement of the iteration boundaries:

Tips:

  • Minimize the lifetime of variables, and minimizing the visibility of variables and other identifiers: define these at the smallest possible scope, prefer local variables to fields and scoped variables to local.
  • Minimize the accessibility of variables, by preferring greater encapsulation, e.g., private variables, to public variables.
  • Minimize mutability and minimize side-affecting parameters (parameters that can be mutated, or mutate the object accepting them).
  • Minimize the number of method parameters. Avoid flags parameters, use a method with a different name, open() and close() are better than setOpened(boolean b).

Spartan takes it to an even higher level and recommends to use $ sign (or other signs if you are using jQuery) for a “special” variable, e.g. a return value of a function, object undergoing tests and temporary variable.
We decided not to use this approach: we started using generic names whenever possible, we have shortened ours generics values names, e.g. res instead of result. Also, if a method can accept any value, there’s no need for a long name. isPalindrome(String possiblePalindrome) vs. isPalindrome(String s), or isPrime(int number) with isPrime(int n). We use a qualified name if there are expectations: connectTo(String urlAddress), but we prefer not to have any expectation!
If you can’t use a generic name, always ask yourself “why?”

Small Interfaces

Use short method names: they are easier to remember and understand. Prefer a single-word verb method names, e.g. map, filter, reduce, and use two words to differentiate methods e.g. map, flatMap; fold, foldLeft, foldRight;. Methods that return a modified copy should be in the passive voice, e.g., sorted, reversed. Add question modifier for methods returning Boolean values, e.g. isEmpty, hasValues, and use class context to cut words Url.getUrlAddress() vs. Url.getAddress().

Minimal Use of Control

Prefer declarative code over imperative code, use the Ternary operators (?:), avoid conditionals, avoid “else” as much as possible, avoid complicated Boolean expressions, avoid two or more connectors, and if possible, avoid if altogether.

if (visible === true) looks somewhat stupid.

Always omit the else of an if statement, if the main branch ends with a return statement or any other statement that does not let control carry through to the else branch.

We designed our code to check the inputs as soon as possible, and if they are not valid we end the function immediately and we don’t use exceptions as a control mechanism.
We simplify the control flow by early exits from a method (via return) or from a loop (via break or continue).

Careful Use of Screen Space

We use K&R indentation style, eliminate unnecessary parenthesis/braces, and yes, it’s okay to remove them if the statement is only one line. In fact, you should strive to make it only a single statement. Fluent API helps immensely here also.

You must have seen this construction before…

Even comments after braces don’t help much. Avoid that. Don’t encase the ‘main processing’ code into preconditions – checking nested-if blocks – check for preconditions and return when it fails to fulfill. Don’t encase a single operation into braces. It is not better than indentation. It’s worse.

Are you happy or Spartan?

The code below is called the Happy Path (which is not very happy at all…):

This is Sparta!

Tips:

  • Refactor code into helper methods, shortens the primary method, but try to avoid short, single-use methods.
  • A line should never span more than 80 characters. If you find that a line is too long, you should reduce its complexity, rather than break it into two distinct lines.
  • Use the “Hand Rule”: You should be able to completely cover a method by placing your hand on the screen.
  • Know your standard library: many common patterns are already implemented; there’s rarely a need to reinvent the wheel.
  • Know your IDE, it doesn’t matter which IDE you are using, just pic one, and learn his abilities. IDEs have many refactoring features, don’t be afraid to learn the keyboard shortcuts, mastering what your IDE has to offer will make your life easier.

Minimize Comments

Your code should be understandable without reading the comments. If it isn’t, make it so; if you can’t, ask yourself why. You should still use docs to document all accessible modules and functions that are part of your API. Docs explain what, code explains how and comments explain why.

Functional Programming

Spartan doesn’t really talk about Functional Programming, but while using Spartan and trying to understand the main Spartan principals, we realized that it’s natural to use Spartan Programming alongside with Functional Programing.
So we adopted the Functional Programming principles as well. Almost all loops constructs can be achieved using lambdas, so we use the find, filter, map and reduce functions, and try to write fluent APIs.

“Minimalism isn’t always the right choice, but it’s rarely the wrong choice” – Jeff Atwood

References:
https://github.com/SpartanRefactoring/spartan-refactoring/wiki/Spartan-Programming
https://blog.codinghorror.com/spartan-programming/
http://www.cranked.me/2008/07/spartan-programming-real-man-way-to-do.html
https://webcourse.cs.technion.ac.il/236700/Spring2016/ho/WCFiles/06-Spartan%20Programming.pdf

Leave a comment
Subscribe for Blog updates