Codebases grow, they never shrink.
New functionality chunks appear, old ones very rarely get decomissioned.
More code means more complexity, more dependencies, more space for regression & even more troublesome testing - that's sad reality & you can fool yourself with dreaming about full decoupling, atomic services & independent modules, but in majority of cases - this is not going to happen.
Back to reality then - you can still consider yourself lucky if:
- you're not doing big projects (but the series of small ones) or you're doing big projects that are splittable into small increments (1-2 weeks of coding)
- you're doing punctual projects that don't span between systems / applications / modules
- you have the comfort of working in 1 long-living development workstream
These cases above are managable, sometimes painful but manageable.
Shit hits the fan once ...
- ... you spawn more long-living workstreams, ...
- ... especially if they span across multiple apps ...
- ... and it's get even more interesting if branches' go-live order is unspecified (or a subject to change)
I've already written about that a bit, so let me skip the rationale. The more important is what can we actually do to avoid at least some of problems with merging & mutual impact of introduced changes.
Option that has caught my attention recently is ...
Idea is quite simple:
- You keep one, single long-living development branch
- You keep this branch release-ready 100% of the time, because ...
- All the code that is not YET considered complete & ready for production usage is running conditionally depending on so-called "Features"
- Features have their identity & they are named
- Features are conceptually bound to chunks of functionality & they don't encapsulate all the changes (in code / configuration / other artifacts), but are using in condition checks (imperative or declarative)
- Features can be enabled / disabled based on several criterions: date, value in configuration file, etc.
Ain't it branching without branching? Or (in other words) faking branching in code? Aren't we multiplying code technical debt with this forced feature sections (a lot of ifs in many places in code)? Aren't data structures supposed to serve multiple versions pretty much in parallel?
What's the actual benefit to compensate these?
- No merging, NO MERGING! And no quarrels about merging strategy :) And YES - merging is a problem in multi-project reality, where different inter-dependent projects may be merged in different moments of time.
- Feature enabling / disabling is extremely easy (& fast), so you can run automated test runs on any kind of feature combo you want (you don't have to do it manually, use CI server)
- Feature is a code construct (no "magic strings"), so it's very easily trackable / searchable / navigable (for instance, to remove it once you don't need it anymore)
- Actual deployment may happen (transparently) before actual change enablement
- Feature can be very easily rolled-back, just with config switch or something similar
- Actually conditional character of features doesn't force you to create millions of new ifs, it may actually help in shaping your code-base to be more feature-oriented by structuring it better for instance by using strategy / policy pattern.
- You don't have to create infrastructure code for features on your own, there are some libraries you can use ...
Libs, libs, libs
First one has a nice course on Pluralsight & works nice with WPF or Windows Phone.
And the second has a nice fluent syntax & what I liked most - its author brings a VERY nice rationale why he has decided to create his own Feature library (what he was missing in other ones: good read).
If (for any kind of reason) you want to check some more, here's a nice list for you:
So, is Feature Toggling a solution to dealing with long-living parallel workstreams? Not necessarily. Features are not intended to live too long (conditions may get cluster-f@#$ked) & they require a lot of discipline (unless you want the feast of condition cheks in your code). But when reasonably used, they may save you a lot of unnecessary branching & merging.