Practical Advice To Whip Complexity And Develop Simpler Software

Albert Einstein famously said, “Everything should be made as simple as possible, but not simpler.” Agile development guru Venkat Subramaniam has a knack for taking that insight and illustrating just how desperately the software development process needs the lessons of Professor Einstein.

As the keynote speaker at the Oracle Code event in Los Angeles—the first in a 14-city tour of events for developers—Subramaniam describes the art of simplicity, and why and how complexity becomes the enemy. While few would argue that complex is better, that’s what we often end up creating, because complex applications or source code may make us feel smart. But if someone says our software design or core algorithm looks simple, well, we feel bad—perhaps the problem was easy and obvious.

Subramaniam, who’s president of Agile Developer and an instructional professor at the University of Houston, urges us instead to take pride in coming up with a simple solution. “It takes a lot of courage to say, ‘we don’t need to make this complex,’” he argues. (See his full keynote, or register for an upcoming Oracle Code event.)

Simplicity Is Not Simple

Simplicity is hard to define, so let’s start by considering what simple is not, says Subramaniam. In most cases, our first attempts at solving a problem won’t be simple at all. The most intuitive solution might be overly verbose, or inefficient, or perhaps difficult to understand, even by its programmers after the fact. Thus:

Simple is not clever. Clever software, or clever solutions, may feel worthwhile, and might cause people to pat developers on the back. But ultimately, it’s hard to understand, and can be hard to change later. “Clever code is self-obfuscating,” says Subramaniam, meaning that it can be incomprehensible. “Even programmers can’t understand their clever code a week later.”

Simple is not necessarily familiar. Subramaniam insists that we are drawn to the old, comfortable ways of writing software, even when those methods are terribly inefficient. He mentioned someone who wrote code with 70 “if/then” questions in a series—because it was familiar. But it certainly wasn’t simple, and would be nearly impossible to debug or modify later. Something that we’re not familiar with may actually be simpler than what we’re comfortable with. To fight complexity, Subramaniam recommends learning new approaches and staying up with the latest thinking and the latest paradigms.

Simple is not over-engineered. Sometimes you can overthink the problem. Perhaps that means trying to develop a generalized algorithm that can be reused to solve many problems, when the situation calls for a fast, basic solution to a single problem. Subramaniam cited Occam’s Razor: When choosing between two solutions, the simplest may be the best.

Simple is not terse. Program source code should be concise, which means that it’s small, but also clearly communicate the programmer’s intent. By contrast, something that’s terse may still execute correctly when compiled into software, but the human understanding may be lost. “Don’t confuse terse with concise,” warns Subramaniam. “Both are really small, but terse code is waiting to hurt you when you least expect it.”

Simple Code Holds Your Attention

Now that we know what simplicity is not, says Subramaniam, we can explore what it actually looks like.

Simple keeps you focused. If the solution to a problem keeps our attention while we code it, we may have a simple solution. If our attention wanders, if it doesn’t hold our interest, perhaps that’s a warning sign to keep looking for a better solution.

Simple hides inherent complexity. That’s the complexity that comes from the problem domain itself. Google’s sparse search bar page is the classic example of a simple solution that makes the complexity disappear for the end user.

Simple eliminates accidental complexity. That’s the complexity that’s not part of the problem, but comes as a result of the particular solution we’ve chosen, explains Subramaniam. If developers are spending a lot of time setting up the solution, or coding workarounds to language issues, or methodology implementation, that’s code designed to manage accidental complexity. But that same code still needs to be designed, written, tested, and managed. If another approach can eliminate that accidental complexity, that’s what should be done.

Simple fails less. Like a manhole cover that’s round so it can’t fall down into the hole, and also is easy to install, Subramaniam explains that simple solutions are inherently less likely to cause problems not only at runtime—but also beyond.

Subramaniam emphasized that simplicity doesn’t happen by accident. It’s an evolutionary outcome, often appearing after a more complex solution has been tried. Sometimes the complex solution fails, and the developers have to keep looking for something simpler. Sometimes the complex solution works, but should be replaced later during a refactoring process, which by design, generates simplified code by finding faster, more efficient solutions.

One approach to simplicity, Subramaniam recommends, is functional programming, which is at the heart of languages like Lisp and Clojure, and which is now baked into the Java programming language. “Michael Feathers once tweeted something brilliant: ‘In object-oriented programming, we encapsulate the moving parts. In functional programming, we eliminate the moving parts,’” Subramaniam says.

Functional programming, and the use of streams to handle data flow, can make many processes simpler than the old-school imperative methods of writing software.

“Functional programming can reduce complexity and introduce simplicity by forcing the language to do boring stuff, like iterating lists,” he explains. “Functional programming, once we become familiar with it, is less complex than imperative.”