It is somewhat of a cliche, but simplicity often belies complexity. The simpler something appears, it is often the case that more thought goes into it than you’d think. I recently attended a class to learn a traditional dance, the debka. The steps seemed simple enough, when the teacher showed everyone the steps, it looked simple. Yet, despite that, I had severe trouble in reproducing the exact same steps, made somewhat (actually a lot) worse by a general lack of coordination on my part. They were the same steps, with roughly the same timing, yet my steps seem somewhat forced, without a sense of flow. Perhaps it’ll come with practice? Let’s see, I suppose. Just because it looks simple, doesn’t mean mastering it, is necessarily easy. The same is true of “simple” buildings. Take ones of Frank Gehry. They appear to have very natural lines to them on paper. Yet trying to engineer these structures is incredibly complex.
It’s the same with coding. Simplicity takes effort. Often the cleanest and well designed of code libraries look “simple”. They don’t use an excessive number of layers of abstraction, which are difficult to follow. Instead, they are generally very clean. They might use interfaces to help define what inputs/outputs are for methods, which are implemented by concrete classes elsewhere. We can get an idea of what it is, without having to go through acres of implementation details. Where they do have abstraction, it is to hide complexity and not wed the implementation to a particular dependency (such as the use of a specific database, service provider etc).
You probably wouldn’t want to change your whole code library, just because you wanted to switch from MySQL to PostgreSQL for example. That specific implementation logic should be dealt with in any easy to use adapter. In some cases, it can be very difficult to totally abstract away dependencies, and too time consuming. However, in other cases it is entirely appropriate to do so. It takes experience, to work out where that point is. There is also the issue of vendors. For obvious reasons, they are not always going to want to make it easy to abstract away their services or products. There is again a trade off between having a relatively vendor specific solution, and one that is totally agnostic to dependencies, which is likely to be more time consuming to setup.
Ideally, we should be able to see the building blocks of the code, but do not need to see inside them to get an overview. This contrasts to poorly designed monolithic code, where it isn’t really clear what bits of code do what. Maintaining code is expensive at the best of time, and poorly designed code is much more painful to maintain too.
Trading strategies can also exhibit this “simplicity”. You might spend a very long time cleaning data (usually most of the time is spent here!) and coming up with idea. The final trading signal you come up with, might seem somewhat simple (Admittedly, it is likely to be surrounded by a lot of complexity in other parts of the code base, to load data, cache it etc.). Obviously, in order to get to that point, was often not simple, and required years of experience. one thing we should note is that it isn’t always just about the signal. How you allocate risk between signals, how you execute etc. is also key. If your strategy has an amazing signal in terms of forecasting ability, but it is not executable in practice (eg. transaction costs are too high), it is of no use.
So next time, you see a hugely complex code base or trading strategy, it’s worth noting that the real skill is in distilling down complexity into something simple or at the very least to abstract away the complex elements into identifiable building blocks. As long as the music keeps playing, you just have to keep on dancing…!