-3

Say I have a library function that expects a JSON-encoded object and does some things with it:

function foo(bar) {
    const baz = JSON.parse(bar);
    doTheThing(baz);
    doTheOtherThing(baz.qux);
}

Now I encounter an object that is not already JSON-encoded but I want to do the same things with it. Since I don't want to touch the library that includes foo(), I just do this:

const bar = JSON.stringify(baz);
foo(bar);

That this is bad should be obvious; we should have a version of foo() that takes as input the unencoded object to avoid the wasted (and perilous) roundtrip through JSON. However, I've seen variations on this antipattern repeated many times in my career. It has nothing to do with Javascript or JSON; it could be Unicode encodings or image file formats or whatever, but the essence of the problem is that the cost of changing the existing code is perceived to be so high that we end up writing code that shoehorns data into some format just so it can be translated back into a useable format later.

I have to think that someone has previously described this problem. My search-fu is probably not just not good enough to find that discussion. Does this antipattern have a name?

  • 2
    Maybe "Stringly typed code"? – user253751 Dec 17 '20 at 22:18
  • 1
    Not every antipattern does (or should) have a name. There are almost infinitely many ways to write bad code, and the obvious ones are just called "bad code": it's only when they're at least slightly subtle that it's worth discussing them, and if's only when we discuss a subject that it's useful to give it a name. – Philip Kendall Dec 17 '20 at 22:49
  • Perhaps I dumbed-down my example too much. This problem can be quite subtle sometimes. The motivation for writing this question was a system that saves 'image data' in PNG format suddenly having to deal with floating-point data. Someone suggested writing a transcoder that encodes the floating-point data into PNG and then decodes it back out, apparently not realizing that we could just save the raw floating-point data and read it back in directly, if we just dropped the PNG requirement. – Bart Robinson Dec 17 '20 at 23:08
  • 1
    It's often not enough to just tell my management that something is "obviously bad code". But if the SW engineering community has a name for it, along with books/blogs/Stack Exchange questions that explain why it is bad, and how it can crop up in subtle ways, then I'm more more likely to convince them to allocate resources to fix it. – Bart Robinson Dec 17 '20 at 23:13
  • Suppose we have to sort three adjectives: "redundant", "lossy", "fragile". How do we sort them in terms of severity? Lossy would be the most severe - you don't know if what is being lost is relevant or not; if it is relevant, you don't know if recovering what is lost is possible or not. "Fragile" is less severe - a fragile system requires more care to remain correct and functional; it may require more automated testing and manual testing; it's more difficult to understand to system. "Redundant" is the least severe - it's an inefficiency, but it doesn't seem to cause errors or data loss. – rwong Jan 12 '21 at 00:33
  • @BartRobinson shoehorning ? forcing data (originally 0-255, now floating-point) into an unnatural format (PNG). – rwong Jan 12 '21 at 12:01

2 Answers2

2

That this is bad should be obvious; we should have a version of foo() that takes as input the unencoded object to avoid the wasted (and perilous) roundtrip through JSON.

This only holds true if you are in control of the API. If this is an external API that only accepts JSON, you must deliver JSON to it. Think about a REST API. I may have data in a data format that is exactly what the REST implementation really needs for processing, still I can only feed that data as JSON to it so I must take the round-trip through JSON.

Thus I challenge the claim that this is an anti-pattern.

The anti-pattern here is not to transform data to JSON to use the existing API, the anti-pattern here is that the function (foo) does two completely unrelated things at once:

  1. Decoding JSON to data
  2. Processing the decoded data

A function should do one and only one thing. This applies to other aspects of coding as well (variables, objects, etc.) Jeff Atwood once named that Curly's Law. There is no official term for that, despite the fact that many text books describe it but if you want to give it a more more elevated name, it's actually a sub-pattern of Separation of Concerns.

Mecki
  • 2,320
  • 14
  • 20
  • An essential part of this question is that the API is internal, not external. If you're working with external APIs, then of course sometimes you are stuck transforming your data into and out of suboptimal formats. The anti-pattern emerges when you control the API as well as the client, but you don't recognize that refactoring the API would let you reduce complexity. – Bart Robinson Jan 13 '21 at 01:25
1

This is simply called bad factoring.

If it is at all conceivable that input might be made available in various forms, then there is no reason why a function should perform both decoding and processing inline, and in fact a very obvious reason why it shouldn't: because it very likely will lead to unnecessary code. And unnecessary code is bad because it opens the opportunity for defects. (Code that doesn't exist can't have defects.)

Functions are called "functions" for a reason. If you want a method that decodes and processes, then just compose a trivial two-liner from the two methods that fulfill one function each.

Edit: Oh, right. If you want a buzzword to hit management over the head with, you can invoke the venerable Single Responsibility Principle!

Kilian Foth
  • 109,273
  • Thanks for your response. I think what I'm really after is a name for the well-intentioned but purpose-defeating response to the initial badly-factored code, where instead of refactoring the downstream system, we end up adding more complexity. And more specifically, the situation where the better solution actually allows us to completely skip some data transformation step. – Bart Robinson Dec 18 '20 at 18:59