Copy
View this email in your browser

I know my way around a React application or a REST API but when it comes to writing domain logic there is nothing to hold your hand anymore. No framework comes with a guide on how to implement the specifics of the business.

Resources on structure and design are usually implemented in context so you need imagination and creativity to turn them into more generic rules.

Each time I find myself navigating an implementation without known patterns to help me, I rely on some fundamental programming principles. In chess, if you follow the basic ideas you will naturally end up in good positions. The same is true in software.

Like chess players put their knights out before the bishops, here are the software development principles that I follow.
 

Actively Reduce Complexity

Complexity is cited as the root of all problems in software and I write each line with the intent of keeping it to manageable levels.

Complex code takes more time to understand and more effort to maintain. That makes it a potential hotbed for bugs that hide in the small misunderstandings and in between layers of abstraction.

The best way to deal with complexity is not to let it grow in the first place. That is easier said than done, though. We’re bombarded with information about design patterns and best practices that don’t take maintenance into account.

I prefer simpler implementations that read like prose instead of masterfully crafted software poems that you can’t even hope to change.

Because of that, I’ve put OOP’s hierarchies and functional programming’s monads aside to focus on simpler constructs. Nowadays most of the code I write is made up of functions and objects.
 

Factory Functions

Most applications work with representations of certain domain entities or models as we are used to calling them. Some are more generic, like a user model. Others like an article or an invoice are specific to the business.

Historically, these models are often implemented with classes since they’re a good way to hide complexity. But I don’t think that OOP’s hierarchical model of thinking fits into every problem and I try to avoid it.

I use factory functions to create complex objects because I find closures easier to understand. Plus, I avoid any potential problems with the this keyword in JavaScript.

I resort to composition instead of inheritance when I want to reuse logic across different entities. This is a flatter mental model and helps me understand the capabilities of an entity without jumping from class to class.
 

Focus on Data Structures

One of the greatest sources of complexity is the data structures that we use. Data is the backbone of every application and mismanaging it can have disastrous effects.

If you use an array where a map would have done a better job, you’d have to compensate for the missing functionality in your data structure with more complex logic.

Data structures’ complexity is hard to fully isolate and it tends to leak throughout the application. After learning this I became watchful about such decisions. When I was building a turn-based card game, I spent days researching the proper data structure to implement the game engine.

 

Application Structure

Structure helps the reader make sense of things. Even though we focus mostly on the design and APIs of modules and functions, the way they are organized in an application is just as important.

A glance at the folders and their contents should give the developer a high-level idea of the application’s purpose.

I’m not a fan of grouping things by their technical responsibilities. I believe that functions and modules which work together should be kept together. That creates a natural application structure that mirrors the dependencies in your code.

So a look at the codebase can tell you how modules in your application interact with one another.
 

Responsibilities and Interfaces

The single responsibility principle is something I take to heart. Making a function or a module do more than it should, adds more to the complexity scale and that is something we want to avoid. Simplicity should always take precedence.

But far too often, length is mistaken for responsibility. I’d rather have a lengthy function that completely does a single operation than break it up and end up with badly designed smaller functions.

You shouldn’t break a function up based on your standards of beauty. Functions should be split naturally, not only because of length. It’s easier to follow a focused but lengthy function than to jump around through multiple ones.

The other SOLID principle that I follow is the dependency inversion one. According to it, we should depend on abstractions not on concretions. In OOP this means that instead of requiring a parameter of a specific type, we should require one that implements a certain interface.

At the price of a little bit more verbosity, this decouples our codebase and makes it more testable.

 

Simple Is Not Easy

A focus on simplicity should not be mistaken for low-effort coding. Making a simple implementation takes a lot more energy and focus than a complex one. We are predisposed to over-engineering because of the belief that power rises together with complexity.

I focus on simplicity because you can always make things more complex. That’s not true the other way around. Simplicity keeps your options open.

Software design should help you achieve a goal. And from all the programming wisdom we have, I think simplicity is the best goal to strive for.







This email was sent to <<Email Address>>
why did I get this?    unsubscribe from this list    update subscription preferences
Code Philosophy · 7000 Ruse · Ruse 7000 · Bulgaria

Email Marketing Powered by Mailchimp