dirtydozen

XI. Writing too generic code

Solving all problems at once

Writing code gives you great opportunity to create something that can be written to address a very specific problem - with a razor sharp focus. Without too much thought and design-time spent upfront, however, there’s a risk that your code will be doing too many different things. In the end code becomes difficult to understand as to what the purpose of it was in the first place.

This is often related to the over configuration and no unit tests dirty code smells. Code effectively is written with no safety net and thought. It’s also accompanied with tons of config that only the programmer producing it really understood (only at the time of writing it - of course).

Let’s consider this high-level code for a hypothetical Data API Endpoint:

1
2
3
4
5
// Data Controller
[HttpGet("get-data")]
public async Task<IActionResult> GetData(string selectParameter) { ... }
// Data Service
public async Task<Data> RetrieveData(string selectParameter) { ... }

And configuration associated with this endpoint:

1
2
3
4
5
6
{
    "databaseConnection": "db://abcdef",
    "clientDataSelect": "SELECT * FROM Clients",
    "shoppingCartDataSelect": "SELECT * FORM ShoppingCarts",
    "productDataSelect": "SELECT * FROM Products"
}

Problem Statement

The API Endpoint above demonstrates a simple case where one single endpoint has been programmed so that the response the client receives from it depends on the selectParameter passed as input to the call. In other words, the API data context is driven off API input parameter, which can be either:

  1. Get Clients Data
  2. Get ShoppingCarts Data
  3. Get Products Data

As consumer of this Data API, all I have to do is to call it as follows:

1
2
3
https://data.api.com?selectParameter=clientDataSelect
https://data.api.com?selectParameter=shoppingCartDataSelect
https://data.api.com?selectParameter=productDataSelect

It’s hard to believe the above could actually happen, but yes, it does happen when proper logic separation is not in place and SOLID principles are not considered at all.

Solution

Simply divide your code into reusable and hence testable components. Moreover, when it comes to designing APIs consider the concept of Data, context and interaction.

Our hypothetical example could now be divided into three separate controllers and services serving the data back to clients.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Client Controller
[HttpGet]
public async Task<IActionResult> GetClients() { ... }
// Client Service
public async Task<List<Client>> RetrieveClients() { ... }

// ShoppingCart Controller
[HttpGet]
public async Task<IActionResult> GetShoppingCarts() { ... }
// ShoppingCart Service
public async Task<List<ShoppingCart>> RetrieveShoppingCarts() { ... }

// Product Controller
[HttpGet]
public async Task<IActionResult> GetProducts() { ... }
// Product Service
public async Task<List<Product>> RetrieveProducts() { ... }

As far as config is concerned, it’s now just a single database connection entry and the rest of database connectivity logic and data manipulation is handled in database framework, such as Microsoft Entity Framework Core.

1
2
3
{
    "databaseConnection": "db://abcdef"
}

Summary

Don’t try to be clever and don't over engineer your code. We all know well that almost anything is possible to achieve in the wonderful world of programming.

Simply try to apply common sense and standards in your coding and you’ll do just great!

« Previous | Next »