The aspects of code complexity are broad; one of them result from the design of our code. Design weight is the cognitive effort reading and maintaining your code — a metaphor used for emphasizing how it weighs down your progress.
The main symptom (as I encountered) is code that forces you to remember specific areas to connect the dots between them. While relying on your memory has its benefits but also a capacity, leading to losing details, causing you to re-read with an effort to memorize it, reducing motivation to be engaged with the product.
“Keep it simple, as simple as possible, but not simpler.” — Albert Einstein
In an analogy, simple code is like bicycles, while a complex code is like a jet plane. The bikes are simple to use, once you’ve learned to balance yourself, you’ve basically mastered the whole thing, while the jet takes extensive training to use and understand. You have to go back and forth until you gain knowledge and enough confidence to use it.
KISS (Keep It Simple, Stupid), I bet you’ve heard it in the past (and keep forgetting it). It’s one of those sentences that are easier to say than done. In the following paragraphs, I’ll try to illustrate six principles in which adhering to them may assist you to apply simplification:
1. Refactor is an ongoing activity
Use YAGNI heuristic — an acronym stands for “You Aren’t Gonna Need It” (coming from extreme programming). Reduce costly design elements based on future benefits. Also, pay attention to “emergent design” — meaning improve code structure consistently. Don’t forget to use unit tests and automated regression tests as a robust safety net to make rapid changes.
2. Challenge complex features
Set a dedicated meeting with the product manager, UX designer, SW architect, and other project key players. Share with them your concerns and check the possibility for change, you’ll be surprised how random some of the requirements may be.
3. APIs, like diamonds, are forever
You can always add functionality to your software, but the other way around isn’t so simple. Skimp with requirements. Before implementation, perform a requirements review meeting with all the project’s stakeholders and delete the less valuable ones. Otherwise, you find yourself with a lot of code to maintain, among with public APIs, which make it difficult to make changes.
4. Occam’s Razor — more facts, fewer assumptions
Make as few assumptions as you can (as explained by Michael Lant), avoid basing your design on them. Ask as early as you can, “Are you certain that everything you are doing is truly being done in support of the project objectives?”
In the case of some, the answer will be no; remove anything that doesn’t support the project’s goals. According to Michael, choosing which assumption to use is based on your experience. This is where the real work gets started.
5. End Of Life
Find what’s not necessary anymore and retire it. Perform user research to have a better understanding of used features and concepts. Identify the unused ones and remove them.
6. Domain-Driven Design (DDD)
This notable approach by Eric Evans (as published in his book with the same name) comes to the rescue. Evans suggests a way to deal with the bias created when requirements from the domain expert (DE) are going into a specification by simplification chain.
From the DE to PM/Marketing/PO, each modifies the requirements to professional language. Then the Architect/Tech Lead does the shift to the system’s infrastructures for the SW engineers, which “now understand” and can do the work.
Well, that’s true on theory. In real-life scenarios, it may go the wrong way. Where information is being lost, and details are being filled naturally by our educational guess instincts.
In DDD, creating a ubiquitous language for all stakeholders is a must. Using it, everyone can understand each other, for creating an accurate conceptual model. Thus, investing in deeply learning the domain, classify objects into Entities (usually UML is being used) along with identifying entities’ patterns to aggregate them by context. Eventually, reducing any future mistakes that may increase complexity.
If you’d like to go deeper on that, head to Sara Miteva’s blog-post:
The Concept of Domain-Driven Design Explained
Using microservices means creating applications from loosely coupling services. The application consists of several…
Simplicity has its price. Invest your efforts, and you’ll profit a maintainable product and happy teams. Eventually, it’s all about people and continuous improvement.