mit-dci / opencbdc-tx

A transaction processor for a hypothetical, general-purpose, central bank digital currency
Other
896 stars 198 forks source link

Adding Feature Flags #81

Open HalosGhost opened 2 years ago

HalosGhost commented 2 years ago

Background

Right now, we have two architectures (2PC and Atomizer), so when a new feature gets proposed, there is a high-probability that implementation will require touching two separate locations in the code. As we explore more architectures, or solutions which involve changes to the two we have, this problem is likely going to be more pronounced. It results in the following draw-backs:

In short, we know there are a lot more architectures and features worth exploring; to ensure project sustainability, we need to develop patterns and conventions for adding new features in such a way that we can minimize the potential combinatorial expansion shown above.

A Solution

Feature flags offer a potential avenue to refactor the code such that new features are gated behind a flag to change the system's behavior at run-time. This would allow, for example, many different implementations of a sentinel to coexist and for which implementation gets used to be selected at run-time (rather than build- or deploy-time).

Taking this possibility to its logical conclusion, an architecture becomes a particular set of components being run with a particular set of feature flags (e.g., 2PC and Atomizer could be partly merged and we could have a flat component list).

Pros

Assuming we could come up with a perfect feature flags system, it would enable the following benefits:

Cons

Still assuming that the system we adopt/implement is ideal for our use-case, there are some draw-backs:

Practical Issues

Beyond the costs and benefits mentioned above, there's a much more practical problem: feature flags aren't well standardized (because they operate inside the application-layer's logic, implementations tend to be very domain-specific). Additionally, using feature-flags may result in control flow in hot-path code (significant performance degradation).

Pre-made

There are off-the-shelf solutions we could try to adopt, but they each have their draw-backs; below is a list of known solutions we could adopt and known draw-backs (more will be added as they are suggested and evaluated):

Custom-built

This is the most sensible option in a lot of ways (we build it for exactly what we need and want to support), but has its own issues. Namely, we actually need to design and implement a feature flags system (which, though enabling future work, is orthogonal to our research purpose).

Conclusion

Ultimately, some version of organizing the code to handle the combinatorial expansion of features/architectures is probably necessary, but I do not know if feature-flags is really the correct solution (another might be to maintain separate branches for each architecture, though that also has obvious drawbacks). Comments, questions, clarifications, and suggestions are all welcome!

davebryson commented 2 years ago

@HalosGhost Why not simplify it by refactoring architectures into their own separate repositories? utils and 3rdparty would be a shared dependency. This could reduce the complexity, isolate testcases per architecture, reduce build time, and potentially simplify benchmarking. It also lays the structure for any future architectures.

HalosGhost commented 2 years ago

@davebryson this is a reasonable idea. It does somewhat guarantee that the architectures will not be in-sync with one another (perhaps that's ideal in this case because it means that interest will drive contributions to each architecture—rather Darwinian). It does mean that if a given change is useful for multiple architectures, it's quite a bit more work for contributors to ensure the change is ported to each one (a PR per architecture, at least; though, each PR is likely to be more self-contained and clear).

The only big drawback I can see is a documentation burden for tracking what architectures exist (for that decoupling, we'd probably want to make a documentation-only repo to track all the repos that exist). And, that's a drawback already present in some of the paths I laid out in the initial proposal. The slightly more contained version of this would be to have a branch per-architecture (which has a lot of the same benefits and a couple fewer drawbacks, but adds a discoverability problem that's a little more work to mitigate).

I also think the big win of feature flags would be that if you wanted to test the system, you'd only clone once, and just configure it to run as you'd like (regardless of what you'd be testing)—though, this assumes that it's actually possible to build the code in such a way that that level of configuration is reasonable.

@metalicjames, @anders94, @narula, I'd be interested in hearing all of your thoughts on this.

HalosGhost commented 1 year ago

Some more recent, relevant art: https://www.libelektra.org/home

Personally, this is one of the more compelling ones I've seen so far. Not quite sure it covers all our cases, but I'm exploring it.